bitcoin-atm

bitcoin atm for pyc inc.

git clone https://9o.is/git/bitcoin-atm.git

detector.js

(13528B)


      1 /*
      2   Ported to JavaScript by Lazar Laszlo 2011 
      3   
      4   lazarsoft@gmail.com, www.lazarsoft.info
      5   
      6 */
      7 
      8 /*
      9 *
     10 * Copyright 2007 ZXing authors
     11 *
     12 * Licensed under the Apache License, Version 2.0 (the "License");
     13 * you may not use this file except in compliance with the License.
     14 * You may obtain a copy of the License at
     15 *
     16 *      http://www.apache.org/licenses/LICENSE-2.0
     17 *
     18 * Unless required by applicable law or agreed to in writing, software
     19 * distributed under the License is distributed on an "AS IS" BASIS,
     20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     21 * See the License for the specific language governing permissions and
     22 * limitations under the License.
     23 */
     24 
     25 
     26 function PerspectiveTransform( a11,  a21,  a31,  a12,  a22,  a32,  a13,  a23,  a33)
     27 {
     28 	this.a11 = a11;
     29 	this.a12 = a12;
     30 	this.a13 = a13;
     31 	this.a21 = a21;
     32 	this.a22 = a22;
     33 	this.a23 = a23;
     34 	this.a31 = a31;
     35 	this.a32 = a32;
     36 	this.a33 = a33;
     37 	this.transformPoints1=function( points)
     38 		{
     39 			var max = points.length;
     40 			var a11 = this.a11;
     41 			var a12 = this.a12;
     42 			var a13 = this.a13;
     43 			var a21 = this.a21;
     44 			var a22 = this.a22;
     45 			var a23 = this.a23;
     46 			var a31 = this.a31;
     47 			var a32 = this.a32;
     48 			var a33 = this.a33;
     49 			for (var i = 0; i < max; i += 2)
     50 			{
     51 				var x = points[i];
     52 				var y = points[i + 1];
     53 				var denominator = a13 * x + a23 * y + a33;
     54 				points[i] = (a11 * x + a21 * y + a31) / denominator;
     55 				points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
     56 			}
     57 		}
     58 	this. transformPoints2=function(xValues, yValues)
     59 		{
     60 			var n = xValues.length;
     61 			for (var i = 0; i < n; i++)
     62 			{
     63 				var x = xValues[i];
     64 				var y = yValues[i];
     65 				var denominator = this.a13 * x + this.a23 * y + this.a33;
     66 				xValues[i] = (this.a11 * x + this.a21 * y + this.a31) / denominator;
     67 				yValues[i] = (this.a12 * x + this.a22 * y + this.a32) / denominator;
     68 			}
     69 		}
     70 
     71 	this.buildAdjoint=function()
     72 		{
     73 			// Adjoint is the transpose of the cofactor matrix:
     74 			return new PerspectiveTransform(this.a22 * this.a33 - this.a23 * this.a32, this.a23 * this.a31 - this.a21 * this.a33, this.a21 * this.a32 - this.a22 * this.a31, this.a13 * this.a32 - this.a12 * this.a33, this.a11 * this.a33 - this.a13 * this.a31, this.a12 * this.a31 - this.a11 * this.a32, this.a12 * this.a23 - this.a13 * this.a22, this.a13 * this.a21 - this.a11 * this.a23, this.a11 * this.a22 - this.a12 * this.a21);
     75 		}
     76 	this.times=function( other)
     77 		{
     78 			return new PerspectiveTransform(this.a11 * other.a11 + this.a21 * other.a12 + this.a31 * other.a13, this.a11 * other.a21 + this.a21 * other.a22 + this.a31 * other.a23, this.a11 * other.a31 + this.a21 * other.a32 + this.a31 * other.a33, this.a12 * other.a11 + this.a22 * other.a12 + this.a32 * other.a13, this.a12 * other.a21 + this.a22 * other.a22 + this.a32 * other.a23, this.a12 * other.a31 + this.a22 * other.a32 + this.a32 * other.a33, this.a13 * other.a11 + this.a23 * other.a12 +this.a33 * other.a13, this.a13 * other.a21 + this.a23 * other.a22 + this.a33 * other.a23, this.a13 * other.a31 + this.a23 * other.a32 + this.a33 * other.a33);
     79 		}
     80 
     81 }
     82 
     83 PerspectiveTransform.quadrilateralToQuadrilateral=function( x0,  y0,  x1,  y1,  x2,  y2,  x3,  y3,  x0p,  y0p,  x1p,  y1p,  x2p,  y2p,  x3p,  y3p)
     84 {
     85 	
     86 	var qToS = this.quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
     87 	var sToQ = this.squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
     88 	return sToQ.times(qToS);
     89 }
     90 
     91 PerspectiveTransform.squareToQuadrilateral=function( x0,  y0,  x1,  y1,  x2,  y2,  x3,  y3)
     92 {
     93 	 dy2 = y3 - y2;
     94 	 dy3 = y0 - y1 + y2 - y3;
     95 	if (dy2 == 0.0 && dy3 == 0.0)
     96 	{
     97 		return new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0, 0.0, 1.0);
     98 	}
     99 	else
    100 	{
    101 		 dx1 = x1 - x2;
    102 		 dx2 = x3 - x2;
    103 		 dx3 = x0 - x1 + x2 - x3;
    104 		 dy1 = y1 - y2;
    105 		 denominator = dx1 * dy2 - dx2 * dy1;
    106 		 a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
    107 		 a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
    108 		return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1.0);
    109 	}
    110 }
    111 
    112 PerspectiveTransform.quadrilateralToSquare=function( x0,  y0,  x1,  y1,  x2,  y2,  x3,  y3)
    113 {
    114 	// Here, the adjoint serves as the inverse:
    115 	return this.squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();
    116 }
    117 
    118 function DetectorResult(bits,  points)
    119 {
    120 	this.bits = bits;
    121 	this.points = points;
    122 }
    123 
    124 
    125 function Detector(image)
    126 {
    127 	this.image=image;
    128 	this.resultPointCallback = null;
    129 	
    130 	this.sizeOfBlackWhiteBlackRun=function( fromX,  fromY,  toX,  toY)
    131 		{
    132 			// Mild variant of Bresenham's algorithm;
    133 			// see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
    134 			var steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);
    135 			if (steep)
    136 			{
    137 				var temp = fromX;
    138 				fromX = fromY;
    139 				fromY = temp;
    140 				temp = toX;
    141 				toX = toY;
    142 				toY = temp;
    143 			}
    144 			
    145 			var dx = Math.abs(toX - fromX);
    146 			var dy = Math.abs(toY - fromY);
    147 			var error = - dx >> 1;
    148 			var ystep = fromY < toY?1:- 1;
    149 			var xstep = fromX < toX?1:- 1;
    150 			var state = 0; // In black pixels, looking for white, first or second time
    151 			for (var x = fromX, y = fromY; x != toX; x += xstep)
    152 			{
    153 				
    154 				var realX = steep?y:x;
    155 				var realY = steep?x:y;
    156 				if (state == 1)
    157 				{
    158 					// In white pixels, looking for black
    159 					if (this.image[realX + realY*qrcode.width])
    160 					{
    161 						state++;
    162 					}
    163 				}
    164 				else
    165 				{
    166 					if (!this.image[realX + realY*qrcode.width])
    167 					{
    168 						state++;
    169 					}
    170 				}
    171 				
    172 				if (state == 3)
    173 				{
    174 					// Found black, white, black, and stumbled back onto white; done
    175 					var diffX = x - fromX;
    176 					var diffY = y - fromY;
    177 					return  Math.sqrt( (diffX * diffX + diffY * diffY));
    178 				}
    179 				error += dy;
    180 				if (error > 0)
    181 				{
    182 					if (y == toY)
    183 					{
    184 						break;
    185 					}
    186 					y += ystep;
    187 					error -= dx;
    188 				}
    189 			}
    190 			var diffX2 = toX - fromX;
    191 			var diffY2 = toY - fromY;
    192 			return  Math.sqrt( (diffX2 * diffX2 + diffY2 * diffY2));
    193 		}
    194 
    195 	
    196 	this.sizeOfBlackWhiteBlackRunBothWays=function( fromX,  fromY,  toX,  toY)
    197 		{
    198 			
    199 			var result = this.sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
    200 			
    201 			// Now count other way -- don't run off image though of course
    202 			var scale = 1.0;
    203 			var otherToX = fromX - (toX - fromX);
    204 			if (otherToX < 0)
    205 			{
    206 				scale =  fromX /  (fromX - otherToX);
    207 				otherToX = 0;
    208 			}
    209 			else if (otherToX >= qrcode.width)
    210 			{
    211 				scale =  (qrcode.width - 1 - fromX) /  (otherToX - fromX);
    212 				otherToX = qrcode.width - 1;
    213 			}
    214 			var otherToY = Math.floor (fromY - (toY - fromY) * scale);
    215 			
    216 			scale = 1.0;
    217 			if (otherToY < 0)
    218 			{
    219 				scale =  fromY /  (fromY - otherToY);
    220 				otherToY = 0;
    221 			}
    222 			else if (otherToY >= qrcode.height)
    223 			{
    224 				scale =  (qrcode.height - 1 - fromY) /  (otherToY - fromY);
    225 				otherToY = qrcode.height - 1;
    226 			}
    227 			otherToX = Math.floor (fromX + (otherToX - fromX) * scale);
    228 			
    229 			result += this.sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
    230 			return result - 1.0; // -1 because we counted the middle pixel twice
    231 		}
    232 		
    233 
    234 	
    235 	this.calculateModuleSizeOneWay=function( pattern,  otherPattern)
    236 		{
    237 			var moduleSizeEst1 = this.sizeOfBlackWhiteBlackRunBothWays(Math.floor( pattern.X), Math.floor( pattern.Y), Math.floor( otherPattern.X), Math.floor(otherPattern.Y));
    238 			var moduleSizeEst2 = this.sizeOfBlackWhiteBlackRunBothWays(Math.floor(otherPattern.X), Math.floor(otherPattern.Y), Math.floor( pattern.X), Math.floor(pattern.Y));
    239 			if (isNaN(moduleSizeEst1))
    240 			{
    241 				return moduleSizeEst2 / 7.0;
    242 			}
    243 			if (isNaN(moduleSizeEst2))
    244 			{
    245 				return moduleSizeEst1 / 7.0;
    246 			}
    247 			// Average them, and divide by 7 since we've counted the width of 3 black modules,
    248 			// and 1 white and 1 black module on either side. Ergo, divide sum by 14.
    249 			return (moduleSizeEst1 + moduleSizeEst2) / 14.0;
    250 		}
    251 
    252 	
    253 	this.calculateModuleSize=function( topLeft,  topRight,  bottomLeft)
    254 		{
    255 			// Take the average
    256 			return (this.calculateModuleSizeOneWay(topLeft, topRight) + this.calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0;
    257 		}
    258 
    259 	this.distance=function( pattern1,  pattern2)
    260 	{
    261 		xDiff = pattern1.X - pattern2.X;
    262 		yDiff = pattern1.Y - pattern2.Y;
    263 		return  Math.sqrt( (xDiff * xDiff + yDiff * yDiff));
    264 	}
    265 	this.computeDimension=function( topLeft,  topRight,  bottomLeft,  moduleSize)
    266 		{
    267 			
    268 			var tltrCentersDimension = Math.round(this.distance(topLeft, topRight) / moduleSize);
    269 			var tlblCentersDimension = Math.round(this.distance(topLeft, bottomLeft) / moduleSize);
    270 			var dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
    271 			switch (dimension & 0x03)
    272 			{
    273 				
    274 				// mod 4
    275 				case 0: 
    276 					dimension++;
    277 					break;
    278 					// 1? do nothing
    279 				
    280 				case 2: 
    281 					dimension--;
    282 					break;
    283 				
    284 				case 3: 
    285 					throw "Error";
    286 				}
    287 			return dimension;
    288 		}
    289 
    290 	this.findAlignmentInRegion=function( overallEstModuleSize,  estAlignmentX,  estAlignmentY,  allowanceFactor)
    291 		{
    292 			// Look for an alignment pattern (3 modules in size) around where it
    293 			// should be
    294 			var allowance = Math.floor (allowanceFactor * overallEstModuleSize);
    295 			var alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);
    296 			var alignmentAreaRightX = Math.min(qrcode.width - 1, estAlignmentX + allowance);
    297 			if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3)
    298 			{
    299 				throw "Error";
    300 			}
    301 			
    302 			var alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);
    303 			var alignmentAreaBottomY = Math.min(qrcode.height - 1, estAlignmentY + allowance);
    304 			
    305 			var alignmentFinder = new AlignmentPatternFinder(this.image, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, this.resultPointCallback);
    306 			return alignmentFinder.find();
    307 		}
    308 		
    309 	this.createTransform=function( topLeft,  topRight,  bottomLeft, alignmentPattern, dimension)
    310 		{
    311 			var dimMinusThree =  dimension - 3.5;
    312 			var bottomRightX;
    313 			var bottomRightY;
    314 			var sourceBottomRightX;
    315 			var sourceBottomRightY;
    316 			if (alignmentPattern != null)
    317 			{
    318 				bottomRightX = alignmentPattern.X;
    319 				bottomRightY = alignmentPattern.Y;
    320 				sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0;
    321 			}
    322 			else
    323 			{
    324 				// Don't have an alignment pattern, just make up the bottom-right point
    325 				bottomRightX = (topRight.X - topLeft.X) + bottomLeft.X;
    326 				bottomRightY = (topRight.Y - topLeft.Y) + bottomLeft.Y;
    327 				sourceBottomRightX = sourceBottomRightY = dimMinusThree;
    328 			}
    329 			
    330 			var transform = PerspectiveTransform.quadrilateralToQuadrilateral(3.5, 3.5, dimMinusThree, 3.5, sourceBottomRightX, sourceBottomRightY, 3.5, dimMinusThree, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRightX, bottomRightY, bottomLeft.X, bottomLeft.Y);
    331 			
    332 			return transform;
    333 		}		
    334 	
    335 	this.sampleGrid=function( image,  transform,  dimension)
    336 		{
    337 			
    338 			var sampler = GridSampler;
    339 			return sampler.sampleGrid3(image, dimension, transform);
    340 		}
    341 	
    342 	this.processFinderPatternInfo = function( info)
    343 		{
    344 			
    345 			var topLeft = info.TopLeft;
    346 			var topRight = info.TopRight;
    347 			var bottomLeft = info.BottomLeft;
    348 			
    349 			var moduleSize = this.calculateModuleSize(topLeft, topRight, bottomLeft);
    350 			if (moduleSize < 1.0)
    351 			{
    352 				throw "Error";
    353 			}
    354 			var dimension = this.computeDimension(topLeft, topRight, bottomLeft, moduleSize);
    355 			var provisionalVersion = Version.getProvisionalVersionForDimension(dimension);
    356 			var modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7;
    357 			
    358 			var alignmentPattern = null;
    359 			// Anything above version 1 has an alignment pattern
    360 			if (provisionalVersion.AlignmentPatternCenters.length > 0)
    361 			{
    362 				
    363 				// Guess where a "bottom right" finder pattern would have been
    364 				var bottomRightX = topRight.X - topLeft.X + bottomLeft.X;
    365 				var bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y;
    366 				
    367 				// Estimate that alignment pattern is closer by 3 modules
    368 				// from "bottom right" to known top left location
    369 				var correctionToTopLeft = 1.0 - 3.0 /  modulesBetweenFPCenters;
    370 				var estAlignmentX = Math.floor (topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.X));
    371 				var estAlignmentY = Math.floor (topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y));
    372 				
    373 				// Kind of arbitrary -- expand search radius before giving up
    374 				for (var i = 4; i <= 16; i <<= 1)
    375 				{
    376 					//try
    377 					//{
    378 						alignmentPattern = this.findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY,  i);
    379 						break;
    380 					//}
    381 					//catch (re)
    382 					//{
    383 						// try next round
    384 					//}
    385 				}
    386 				// If we didn't find alignment pattern... well try anyway without it
    387 			}
    388 			
    389 			var transform = this.createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
    390 			
    391 			var bits = this.sampleGrid(this.image, transform, dimension);
    392 			
    393 			var points;
    394 			if (alignmentPattern == null)
    395 			{
    396 				points = new Array(bottomLeft, topLeft, topRight);
    397 			}
    398 			else
    399 			{
    400 				points = new Array(bottomLeft, topLeft, topRight, alignmentPattern);
    401 			}
    402 			return new DetectorResult(bits, points);
    403 		}
    404 		
    405 
    406 	
    407 	this.detect=function()
    408 	{
    409 		var info =  new FinderPatternFinder().findFinderPattern(this.image);
    410 			
    411 		return this.processFinderPatternInfo(info); 
    412 	}
    413 }