bitcoin-atm

bitcoin atm for pyc inc.

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

findpat.js

(18250B)


      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 var MIN_SKIP = 3;
     27 var MAX_MODULES = 57;
     28 var INTEGER_MATH_SHIFT = 8;
     29 var CENTER_QUORUM = 2;
     30 
     31 qrcode.orderBestPatterns=function(patterns)
     32 		{
     33 			
     34 			function distance( pattern1,  pattern2)
     35 			{
     36 				xDiff = pattern1.X - pattern2.X;
     37 				yDiff = pattern1.Y - pattern2.Y;
     38 				return  Math.sqrt( (xDiff * xDiff + yDiff * yDiff));
     39 			}
     40 			
     41 			/// <summary> Returns the z component of the cross product between vectors BC and BA.</summary>
     42 			function crossProductZ( pointA,  pointB,  pointC)
     43 			{
     44 				var bX = pointB.x;
     45 				var bY = pointB.y;
     46 				return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
     47 			}
     48 
     49 			
     50 			// Find distances between pattern centers
     51 			var zeroOneDistance = distance(patterns[0], patterns[1]);
     52 			var oneTwoDistance = distance(patterns[1], patterns[2]);
     53 			var zeroTwoDistance = distance(patterns[0], patterns[2]);
     54 			
     55 			var pointA, pointB, pointC;
     56 			// Assume one closest to other two is B; A and C will just be guesses at first
     57 			if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance)
     58 			{
     59 				pointB = patterns[0];
     60 				pointA = patterns[1];
     61 				pointC = patterns[2];
     62 			}
     63 			else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance)
     64 			{
     65 				pointB = patterns[1];
     66 				pointA = patterns[0];
     67 				pointC = patterns[2];
     68 			}
     69 			else
     70 			{
     71 				pointB = patterns[2];
     72 				pointA = patterns[0];
     73 				pointC = patterns[1];
     74 			}
     75 			
     76 			// Use cross product to figure out whether A and C are correct or flipped.
     77 			// This asks whether BC x BA has a positive z component, which is the arrangement
     78 			// we want for A, B, C. If it's negative, then we've got it flipped around and
     79 			// should swap A and C.
     80 			if (crossProductZ(pointA, pointB, pointC) < 0.0)
     81 			{
     82 				var temp = pointA;
     83 				pointA = pointC;
     84 				pointC = temp;
     85 			}
     86 			
     87 			patterns[0] = pointA;
     88 			patterns[1] = pointB;
     89 			patterns[2] = pointC;
     90 		}
     91 
     92 
     93 function FinderPattern(posX, posY,  estimatedModuleSize)
     94 {
     95 	this.x=posX;
     96 	this.y=posY;
     97 	this.count = 1;
     98 	this.estimatedModuleSize = estimatedModuleSize;
     99 	
    100 	this.__defineGetter__("EstimatedModuleSize", function()
    101 	{
    102 		return this.estimatedModuleSize;
    103 	}); 
    104 	this.__defineGetter__("Count", function()
    105 	{
    106 		return this.count;
    107 	});
    108 	this.__defineGetter__("X", function()
    109 	{
    110 		return this.x;
    111 	});
    112 	this.__defineGetter__("Y", function()
    113 	{
    114 		return this.y;
    115 	});
    116 	this.incrementCount = function()
    117 	{
    118 		this.count++;
    119 	}
    120 	this.aboutEquals=function( moduleSize,  i,  j)
    121 		{
    122 			if (Math.abs(i - this.y) <= moduleSize && Math.abs(j - this.x) <= moduleSize)
    123 			{
    124 				var moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize);
    125 				return moduleSizeDiff <= 1.0 || moduleSizeDiff / this.estimatedModuleSize <= 1.0;
    126 			}
    127 			return false;
    128 		}
    129 	
    130 }
    131 
    132 function FinderPatternInfo(patternCenters)
    133 {
    134 	this.bottomLeft = patternCenters[0];
    135 	this.topLeft = patternCenters[1];
    136 	this.topRight = patternCenters[2];
    137 	this.__defineGetter__("BottomLeft", function()
    138 	{
    139 		return this.bottomLeft;
    140 	}); 
    141 	this.__defineGetter__("TopLeft", function()
    142 	{
    143 		return this.topLeft;
    144 	}); 
    145 	this.__defineGetter__("TopRight", function()
    146 	{
    147 		return this.topRight;
    148 	}); 
    149 }
    150 
    151 function FinderPatternFinder()
    152 {
    153 	this.image=null;
    154 	this.possibleCenters = [];
    155 	this.hasSkipped = false;
    156 	this.crossCheckStateCount = new Array(0,0,0,0,0);
    157 	this.resultPointCallback = null;
    158 	
    159 	this.__defineGetter__("CrossCheckStateCount", function()
    160 	{
    161 		this.crossCheckStateCount[0] = 0;
    162 		this.crossCheckStateCount[1] = 0;
    163 		this.crossCheckStateCount[2] = 0;
    164 		this.crossCheckStateCount[3] = 0;
    165 		this.crossCheckStateCount[4] = 0;
    166 		return this.crossCheckStateCount;
    167 	}); 
    168 	
    169 	this.foundPatternCross=function( stateCount)
    170 		{
    171 			var totalModuleSize = 0;
    172 			for (var i = 0; i < 5; i++)
    173 			{
    174 				var count = stateCount[i];
    175 				if (count == 0)
    176 				{
    177 					return false;
    178 				}
    179 				totalModuleSize += count;
    180 			}
    181 			if (totalModuleSize < 7)
    182 			{
    183 				return false;
    184 			}
    185 			var moduleSize = Math.floor((totalModuleSize << INTEGER_MATH_SHIFT) / 7);
    186 			var maxVariance = Math.floor(moduleSize / 2);
    187 			// Allow less than 50% variance from 1-1-3-1-1 proportions
    188 			return Math.abs(moduleSize - (stateCount[0] << INTEGER_MATH_SHIFT)) < maxVariance && Math.abs(moduleSize - (stateCount[1] << INTEGER_MATH_SHIFT)) < maxVariance && Math.abs(3 * moduleSize - (stateCount[2] << INTEGER_MATH_SHIFT)) < 3 * maxVariance && Math.abs(moduleSize - (stateCount[3] << INTEGER_MATH_SHIFT)) < maxVariance && Math.abs(moduleSize - (stateCount[4] << INTEGER_MATH_SHIFT)) < maxVariance;
    189 		}
    190 	this.centerFromEnd=function( stateCount,  end)
    191 		{
    192 			return  (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0;
    193 		}
    194 	this.crossCheckVertical=function( startI,  centerJ,  maxCount,  originalStateCountTotal)
    195 		{
    196 			var image = this.image;
    197 			
    198 			var maxI = qrcode.height;
    199 			var stateCount = this.CrossCheckStateCount;
    200 			
    201 			// Start counting up from center
    202 			var i = startI;
    203 			while (i >= 0 && image[centerJ + i*qrcode.width])
    204 			{
    205 				stateCount[2]++;
    206 				i--;
    207 			}
    208 			if (i < 0)
    209 			{
    210 				return NaN;
    211 			}
    212 			while (i >= 0 && !image[centerJ +i*qrcode.width] && stateCount[1] <= maxCount)
    213 			{
    214 				stateCount[1]++;
    215 				i--;
    216 			}
    217 			// If already too many modules in this state or ran off the edge:
    218 			if (i < 0 || stateCount[1] > maxCount)
    219 			{
    220 				return NaN;
    221 			}
    222 			while (i >= 0 && image[centerJ + i*qrcode.width] && stateCount[0] <= maxCount)
    223 			{
    224 				stateCount[0]++;
    225 				i--;
    226 			}
    227 			if (stateCount[0] > maxCount)
    228 			{
    229 				return NaN;
    230 			}
    231 			
    232 			// Now also count down from center
    233 			i = startI + 1;
    234 			while (i < maxI && image[centerJ +i*qrcode.width])
    235 			{
    236 				stateCount[2]++;
    237 				i++;
    238 			}
    239 			if (i == maxI)
    240 			{
    241 				return NaN;
    242 			}
    243 			while (i < maxI && !image[centerJ + i*qrcode.width] && stateCount[3] < maxCount)
    244 			{
    245 				stateCount[3]++;
    246 				i++;
    247 			}
    248 			if (i == maxI || stateCount[3] >= maxCount)
    249 			{
    250 				return NaN;
    251 			}
    252 			while (i < maxI && image[centerJ + i*qrcode.width] && stateCount[4] < maxCount)
    253 			{
    254 				stateCount[4]++;
    255 				i++;
    256 			}
    257 			if (stateCount[4] >= maxCount)
    258 			{
    259 				return NaN;
    260 			}
    261 			
    262 			// If we found a finder-pattern-like section, but its size is more than 40% different than
    263 			// the original, assume it's a false positive
    264 			var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
    265 			if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal)
    266 			{
    267 				return NaN;
    268 			}
    269 			
    270 			return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, i):NaN;
    271 		}
    272 	this.crossCheckHorizontal=function( startJ,  centerI,  maxCount, originalStateCountTotal)
    273 		{
    274 			var image = this.image;
    275 			
    276 			var maxJ = qrcode.width;
    277 			var stateCount = this.CrossCheckStateCount;
    278 			
    279 			var j = startJ;
    280 			while (j >= 0 && image[j+ centerI*qrcode.width])
    281 			{
    282 				stateCount[2]++;
    283 				j--;
    284 			}
    285 			if (j < 0)
    286 			{
    287 				return NaN;
    288 			}
    289 			while (j >= 0 && !image[j+ centerI*qrcode.width] && stateCount[1] <= maxCount)
    290 			{
    291 				stateCount[1]++;
    292 				j--;
    293 			}
    294 			if (j < 0 || stateCount[1] > maxCount)
    295 			{
    296 				return NaN;
    297 			}
    298 			while (j >= 0 && image[j+ centerI*qrcode.width] && stateCount[0] <= maxCount)
    299 			{
    300 				stateCount[0]++;
    301 				j--;
    302 			}
    303 			if (stateCount[0] > maxCount)
    304 			{
    305 				return NaN;
    306 			}
    307 			
    308 			j = startJ + 1;
    309 			while (j < maxJ && image[j+ centerI*qrcode.width])
    310 			{
    311 				stateCount[2]++;
    312 				j++;
    313 			}
    314 			if (j == maxJ)
    315 			{
    316 				return NaN;
    317 			}
    318 			while (j < maxJ && !image[j+ centerI*qrcode.width] && stateCount[3] < maxCount)
    319 			{
    320 				stateCount[3]++;
    321 				j++;
    322 			}
    323 			if (j == maxJ || stateCount[3] >= maxCount)
    324 			{
    325 				return NaN;
    326 			}
    327 			while (j < maxJ && image[j+ centerI*qrcode.width] && stateCount[4] < maxCount)
    328 			{
    329 				stateCount[4]++;
    330 				j++;
    331 			}
    332 			if (stateCount[4] >= maxCount)
    333 			{
    334 				return NaN;
    335 			}
    336 			
    337 			// If we found a finder-pattern-like section, but its size is significantly different than
    338 			// the original, assume it's a false positive
    339 			var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
    340 			if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal)
    341 			{
    342 				return NaN;
    343 			}
    344 			
    345 			return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, j):NaN;
    346 		}
    347 	this.handlePossibleCenter=function( stateCount,  i,  j)
    348 		{
    349 			var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
    350 			var centerJ = this.centerFromEnd(stateCount, j); //float
    351 			var centerI = this.crossCheckVertical(i, Math.floor( centerJ), stateCount[2], stateCountTotal); //float
    352 			if (!isNaN(centerI))
    353 			{
    354 				// Re-cross check
    355 				centerJ = this.crossCheckHorizontal(Math.floor( centerJ), Math.floor( centerI), stateCount[2], stateCountTotal);
    356 				if (!isNaN(centerJ))
    357 				{
    358 					var estimatedModuleSize =   stateCountTotal / 7.0;
    359 					var found = false;
    360 					var max = this.possibleCenters.length;
    361 					for (var index = 0; index < max; index++)
    362 					{
    363 						var center = this.possibleCenters[index];
    364 						// Look for about the same center and module size:
    365 						if (center.aboutEquals(estimatedModuleSize, centerI, centerJ))
    366 						{
    367 							center.incrementCount();
    368 							found = true;
    369 							break;
    370 						}
    371 					}
    372 					if (!found)
    373 					{
    374 						var point = new FinderPattern(centerJ, centerI, estimatedModuleSize);
    375 						this.possibleCenters.push(point);
    376 						if (this.resultPointCallback != null)
    377 						{
    378 							this.resultPointCallback.foundPossibleResultPoint(point);
    379 						}
    380 					}
    381 					return true;
    382 				}
    383 			}
    384 			return false;
    385 		}
    386 		
    387 	this.selectBestPatterns=function()
    388 		{
    389 			
    390 			var startSize = this.possibleCenters.length;
    391 			if (startSize < 3)
    392 			{
    393 				// Couldn't find enough finder patterns
    394 				throw "Couldn't find enough finder patterns";
    395 			}
    396 			
    397 			// Filter outlier possibilities whose module size is too different
    398 			if (startSize > 3)
    399 			{
    400 				// But we can only afford to do so if we have at least 4 possibilities to choose from
    401 				var totalModuleSize = 0.0;
    402                 var square = 0.0;
    403 				for (var i = 0; i < startSize; i++)
    404 				{
    405 					//totalModuleSize +=  this.possibleCenters[i].EstimatedModuleSize;
    406                     var	centerValue=this.possibleCenters[i].EstimatedModuleSize;
    407 					totalModuleSize += centerValue;
    408 					square += (centerValue * centerValue);
    409 				}
    410 				var average = totalModuleSize /  startSize;
    411                 this.possibleCenters.sort(function(center1,center2) {
    412 				      var dA=Math.abs(center2.EstimatedModuleSize - average);
    413 				      var dB=Math.abs(center1.EstimatedModuleSize - average);
    414 				      if (dA < dB) {
    415 				    	  return (-1);
    416 				      } else if (dA == dB) {
    417 				    	  return 0;
    418 				      } else {
    419 				    	  return 1;
    420 				      }
    421 					});
    422 
    423 				var stdDev = Math.sqrt(square / startSize - average * average);
    424 				var limit = Math.max(0.2 * average, stdDev);
    425 				for (var i = 0; i < this.possibleCenters.length && this.possibleCenters.length > 3; i++)
    426 				{
    427 					var pattern =  this.possibleCenters[i];
    428 					//if (Math.abs(pattern.EstimatedModuleSize - average) > 0.2 * average)
    429                     if (Math.abs(pattern.EstimatedModuleSize - average) > limit)
    430 					{
    431 						this.possibleCenters.remove(i);
    432 						i--;
    433 					}
    434 				}
    435 			}
    436 			
    437 			if (this.possibleCenters.length > 3)
    438 			{
    439 				// Throw away all but those first size candidate points we found.
    440 				this.possibleCenters.sort(function(a, b){
    441 					if (a.count > b.count){return -1;}
    442 					if (a.count < b.count){return 1;}
    443 					return 0;
    444 				});
    445 			}
    446 			
    447 			return new Array( this.possibleCenters[0],  this.possibleCenters[1],  this.possibleCenters[2]);
    448 		}
    449 		
    450 	this.findRowSkip=function()
    451 		{
    452 			var max = this.possibleCenters.length;
    453 			if (max <= 1)
    454 			{
    455 				return 0;
    456 			}
    457 			var firstConfirmedCenter = null;
    458 			for (var i = 0; i < max; i++)
    459 			{
    460 				var center =  this.possibleCenters[i];
    461 				if (center.Count >= CENTER_QUORUM)
    462 				{
    463 					if (firstConfirmedCenter == null)
    464 					{
    465 						firstConfirmedCenter = center;
    466 					}
    467 					else
    468 					{
    469 						// We have two confirmed centers
    470 						// How far down can we skip before resuming looking for the next
    471 						// pattern? In the worst case, only the difference between the
    472 						// difference in the x / y coordinates of the two centers.
    473 						// This is the case where you find top left last.
    474 						this.hasSkipped = true;
    475 						return Math.floor ((Math.abs(firstConfirmedCenter.X - center.X) - Math.abs(firstConfirmedCenter.Y - center.Y)) / 2);
    476 					}
    477 				}
    478 			}
    479 			return 0;
    480 		}
    481 	
    482 	this.haveMultiplyConfirmedCenters=function()
    483 		{
    484 			var confirmedCount = 0;
    485 			var totalModuleSize = 0.0;
    486 			var max = this.possibleCenters.length;
    487 			for (var i = 0; i < max; i++)
    488 			{
    489 				var pattern =  this.possibleCenters[i];
    490 				if (pattern.Count >= CENTER_QUORUM)
    491 				{
    492 					confirmedCount++;
    493 					totalModuleSize += pattern.EstimatedModuleSize;
    494 				}
    495 			}
    496 			if (confirmedCount < 3)
    497 			{
    498 				return false;
    499 			}
    500 			// OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
    501 			// and that we need to keep looking. We detect this by asking if the estimated module sizes
    502 			// vary too much. We arbitrarily say that when the total deviation from average exceeds
    503 			// 5% of the total module size estimates, it's too much.
    504 			var average = totalModuleSize / max;
    505 			var totalDeviation = 0.0;
    506 			for (var i = 0; i < max; i++)
    507 			{
    508 				pattern = this.possibleCenters[i];
    509 				totalDeviation += Math.abs(pattern.EstimatedModuleSize - average);
    510 			}
    511 			return totalDeviation <= 0.05 * totalModuleSize;
    512 		}
    513 		
    514 	this.findFinderPattern = function(image){
    515 		var tryHarder = false;
    516 		this.image=image;
    517 		var maxI = qrcode.height;
    518 		var maxJ = qrcode.width;
    519 		var iSkip = Math.floor((3 * maxI) / (4 * MAX_MODULES));
    520 		if (iSkip < MIN_SKIP || tryHarder)
    521 		{
    522 				iSkip = MIN_SKIP;
    523 		}
    524 		
    525 		var done = false;
    526 		var stateCount = new Array(5);
    527 		for (var i = iSkip - 1; i < maxI && !done; i += iSkip)
    528 		{
    529 			// Get a row of black/white values
    530 			stateCount[0] = 0;
    531 			stateCount[1] = 0;
    532 			stateCount[2] = 0;
    533 			stateCount[3] = 0;
    534 			stateCount[4] = 0;
    535 			var currentState = 0;
    536 			for (var j = 0; j < maxJ; j++)
    537 			{
    538 				if (image[j+i*qrcode.width] )
    539 				{
    540 					// Black pixel
    541 					if ((currentState & 1) == 1)
    542 					{
    543 						// Counting white pixels
    544 						currentState++;
    545 					}
    546 					stateCount[currentState]++;
    547 				}
    548 				else
    549 				{
    550 					// White pixel
    551 					if ((currentState & 1) == 0)
    552 					{
    553 						// Counting black pixels
    554 						if (currentState == 4)
    555 						{
    556 							// A winner?
    557 							if (this.foundPatternCross(stateCount))
    558 							{
    559 								// Yes
    560 								var confirmed = this.handlePossibleCenter(stateCount, i, j);
    561 								if (confirmed)
    562 								{
    563 									// Start examining every other line. Checking each line turned out to be too
    564 									// expensive and didn't improve performance.
    565 									iSkip = 2;
    566 									if (this.hasSkipped)
    567 									{
    568 										done = this.haveMultiplyConfirmedCenters();
    569 									}
    570 									else
    571 									{
    572 										var rowSkip = this.findRowSkip();
    573 										if (rowSkip > stateCount[2])
    574 										{
    575 											// Skip rows between row of lower confirmed center
    576 											// and top of presumed third confirmed center
    577 											// but back up a bit to get a full chance of detecting
    578 											// it, entire width of center of finder pattern
    579 											
    580 											// Skip by rowSkip, but back off by stateCount[2] (size of last center
    581 											// of pattern we saw) to be conservative, and also back off by iSkip which
    582 											// is about to be re-added
    583 											i += rowSkip - stateCount[2] - iSkip;
    584 											j = maxJ - 1;
    585 										}
    586 									}
    587 								}
    588 								else
    589 								{
    590 									// Advance to next black pixel
    591 									do 
    592 									{
    593 										j++;
    594 									}
    595 									while (j < maxJ && !image[j + i*qrcode.width]);
    596 									j--; // back up to that last white pixel
    597 								}
    598 								// Clear state to start looking again
    599 								currentState = 0;
    600 								stateCount[0] = 0;
    601 								stateCount[1] = 0;
    602 								stateCount[2] = 0;
    603 								stateCount[3] = 0;
    604 								stateCount[4] = 0;
    605 							}
    606 							else
    607 							{
    608 								// No, shift counts back by two
    609 								stateCount[0] = stateCount[2];
    610 								stateCount[1] = stateCount[3];
    611 								stateCount[2] = stateCount[4];
    612 								stateCount[3] = 1;
    613 								stateCount[4] = 0;
    614 								currentState = 3;
    615 							}
    616 						}
    617 						else
    618 						{
    619 							stateCount[++currentState]++;
    620 						}
    621 					}
    622 					else
    623 					{
    624 						// Counting white pixels
    625 						stateCount[currentState]++;
    626 					}
    627 				}
    628 			}
    629 			if (this.foundPatternCross(stateCount))
    630 			{
    631 				var confirmed = this.handlePossibleCenter(stateCount, i, maxJ);
    632 				if (confirmed)
    633 				{
    634 					iSkip = stateCount[0];
    635 					if (this.hasSkipped)
    636 					{
    637 						// Found a third one
    638 						done = haveMultiplyConfirmedCenters();
    639 					}
    640 				}
    641 			}
    642 		}
    643 		
    644 		var patternInfo = this.selectBestPatterns();
    645 		qrcode.orderBestPatterns(patternInfo);
    646 		
    647 		return new FinderPatternInfo(patternInfo);
    648 	};
    649 }