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 }