Issue 520
[zxing.git] / cpp / core / src / zxing / qrcode / detector / FinderPatternFinder.cpp
1 /*
2  *  FinderPatternFinder.cpp
3  *  zxing
4  *
5  *  Created by Christian Brunschen on 13/05/2008.
6  *  Copyright 2008 ZXing authors All rights reserved.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #include <zxing/qrcode/detector/FinderPatternFinder.h>
22 #include <zxing/ReaderException.h>
23 #include <zxing/DecodeHints.h>
24 #include <vector>
25 #include <cmath>
26 #include <cstdlib>
27 #include <algorithm>
28
29 namespace zxing {
30 namespace qrcode {
31
32 using namespace std;
33
34 class FurthestFromAverageComparator {
35 private:
36   const float averageModuleSize_;
37 public:
38   FurthestFromAverageComparator(float averageModuleSize) :
39     averageModuleSize_(averageModuleSize) {
40   }
41   bool operator()(Ref<FinderPattern> a, Ref<FinderPattern> b) {
42     float dA = abs(a->getEstimatedModuleSize() - averageModuleSize_);
43     float dB = abs(b->getEstimatedModuleSize() - averageModuleSize_);
44     return dA > dB;
45   }
46 };
47
48 class CenterComparator {
49   const float averageModuleSize_;
50 public:
51   CenterComparator(float averageModuleSize) :
52     averageModuleSize_(averageModuleSize) {
53   }
54   bool operator()(Ref<FinderPattern> a, Ref<FinderPattern> b) {
55     // N.B.: we want the result in descending order ...
56     if (a->getCount() != b->getCount()) {
57       return a->getCount() > b->getCount();
58     } else {
59       float dA = abs(a->getEstimatedModuleSize() - averageModuleSize_);
60       float dB = abs(b->getEstimatedModuleSize() - averageModuleSize_);
61       return dA < dB;
62     }
63   }
64 };
65
66 int FinderPatternFinder::CENTER_QUORUM = 2;
67 int FinderPatternFinder::MIN_SKIP = 3;
68 int FinderPatternFinder::MAX_MODULES = 57;
69
70 float FinderPatternFinder::centerFromEnd(int* stateCount, int end) {
71   return (float)(end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f;
72 }
73
74 bool FinderPatternFinder::foundPatternCross(int* stateCount) {
75   int totalModuleSize = 0;
76   for (int i = 0; i < 5; i++) {
77     if (stateCount[i] == 0) {
78       return false;
79     }
80     totalModuleSize += stateCount[i];
81   }
82   if (totalModuleSize < 7) {
83     return false;
84   }
85   float moduleSize = (float)totalModuleSize / 7.0f;
86   float maxVariance = moduleSize / 2.0f;
87   // Allow less than 50% variance from 1-1-3-1-1 proportions
88   return abs(moduleSize - stateCount[0]) < maxVariance && abs(moduleSize - stateCount[1]) < maxVariance && abs(3.0f
89          * moduleSize - stateCount[2]) < 3.0f * maxVariance && abs(moduleSize - stateCount[3]) < maxVariance && abs(
90            moduleSize - stateCount[4]) < maxVariance;
91 }
92
93 float FinderPatternFinder::crossCheckVertical(size_t startI, size_t centerJ, int maxCount, int originalStateCountTotal) {
94
95   int maxI = image_->getHeight();
96   int stateCount[5];
97   for (int i = 0; i < 5; i++)
98     stateCount[i] = 0;
99
100
101   // Start counting up from center
102   int i = startI;
103   while (i >= 0 && image_->get(centerJ, i)) {
104     stateCount[2]++;
105     i--;
106   }
107   if (i < 0) {
108     return NAN;
109   }
110   while (i >= 0 && !image_->get(centerJ, i) && stateCount[1] <= maxCount) {
111     stateCount[1]++;
112     i--;
113   }
114   // If already too many modules in this state or ran off the edge:
115   if (i < 0 || stateCount[1] > maxCount) {
116     return NAN;
117   }
118   while (i >= 0 && image_->get(centerJ, i) && stateCount[0] <= maxCount) {
119     stateCount[0]++;
120     i--;
121   }
122   if (stateCount[0] > maxCount) {
123     return NAN;
124   }
125
126   // Now also count down from center
127   i = startI + 1;
128   while (i < maxI && image_->get(centerJ, i)) {
129     stateCount[2]++;
130     i++;
131   }
132   if (i == maxI) {
133     return NAN;
134   }
135   while (i < maxI && !image_->get(centerJ, i) && stateCount[3] < maxCount) {
136     stateCount[3]++;
137     i++;
138   }
139   if (i == maxI || stateCount[3] >= maxCount) {
140     return NAN;
141   }
142   while (i < maxI && image_->get(centerJ, i) && stateCount[4] < maxCount) {
143     stateCount[4]++;
144     i++;
145   }
146   if (stateCount[4] >= maxCount) {
147     return NAN;
148   }
149
150   // If we found a finder-pattern-like section, but its size is more than 40% different than
151   // the original, assume it's a false positive
152   int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
153   if (5 * abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
154     return NAN;
155   }
156
157   return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : NAN;
158 }
159
160 float FinderPatternFinder::crossCheckHorizontal(size_t startJ, size_t centerI, int maxCount,
161     int originalStateCountTotal) {
162
163   int maxJ = image_->getWidth();
164   int stateCount[5];
165   for (int i = 0; i < 5; i++)
166     stateCount[i] = 0;
167
168   int j = startJ;
169   while (j >= 0 && image_->get(j, centerI)) {
170     stateCount[2]++;
171     j--;
172   }
173   if (j < 0) {
174     return NAN;
175   }
176   while (j >= 0 && !image_->get(j, centerI) && stateCount[1] <= maxCount) {
177     stateCount[1]++;
178     j--;
179   }
180   if (j < 0 || stateCount[1] > maxCount) {
181     return NAN;
182   }
183   while (j >= 0 && image_->get(j, centerI) && stateCount[0] <= maxCount) {
184     stateCount[0]++;
185     j--;
186   }
187   if (stateCount[0] > maxCount) {
188     return NAN;
189   }
190
191   j = startJ + 1;
192   while (j < maxJ && image_->get(j, centerI)) {
193     stateCount[2]++;
194     j++;
195   }
196   if (j == maxJ) {
197     return NAN;
198   }
199   while (j < maxJ && !image_->get(j, centerI) && stateCount[3] < maxCount) {
200     stateCount[3]++;
201     j++;
202   }
203   if (j == maxJ || stateCount[3] >= maxCount) {
204     return NAN;
205   }
206   while (j < maxJ && image_->get(j, centerI) && stateCount[4] < maxCount) {
207     stateCount[4]++;
208     j++;
209   }
210   if (stateCount[4] >= maxCount) {
211     return NAN;
212   }
213
214   // If we found a finder-pattern-like section, but its size is significantly different than
215   // the original, assume it's a false positive
216   int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
217   if (5 * abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {
218     return NAN;
219   }
220
221   return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : NAN;
222 }
223
224 bool FinderPatternFinder::handlePossibleCenter(int* stateCount, size_t i, size_t j) {
225   int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
226   float centerJ = centerFromEnd(stateCount, j);
227   float centerI = crossCheckVertical(i, (size_t)centerJ, stateCount[2], stateCountTotal);
228   if (!isnan(centerI)) {
229     // Re-cross check
230     centerJ = crossCheckHorizontal((size_t)centerJ, (size_t)centerI, stateCount[2], stateCountTotal);
231     if (!isnan(centerJ)) {
232       float estimatedModuleSize = (float)stateCountTotal / 7.0f;
233       bool found = false;
234       size_t max = possibleCenters_.size();
235       for (size_t index = 0; index < max; index++) {
236         Ref<FinderPattern> center = possibleCenters_[index];
237         // Look for about the same center and module size:
238         if (center->aboutEquals(estimatedModuleSize, centerI, centerJ)) {
239           center->incrementCount();
240           found = true;
241           break;
242         }
243       }
244       if (!found) {
245         Ref<FinderPattern> newPattern(new FinderPattern(centerJ, centerI, estimatedModuleSize));
246         possibleCenters_.push_back(newPattern);
247         if (callback_ != 0) {
248           callback_->foundPossibleResultPoint(*newPattern);
249         }
250       }
251       return true;
252     }
253   }
254   return false;
255 }
256
257 int FinderPatternFinder::findRowSkip() {
258   size_t max = possibleCenters_.size();
259   if (max <= 1) {
260     return 0;
261   }
262   Ref<FinderPattern> firstConfirmedCenter;
263   for (size_t i = 0; i < max; i++) {
264     Ref<FinderPattern> center = possibleCenters_[i];
265     if (center->getCount() >= CENTER_QUORUM) {
266       if (firstConfirmedCenter == 0) {
267         firstConfirmedCenter = center;
268       } else {
269         // We have two confirmed centers
270         // How far down can we skip before resuming looking for the next
271         // pattern? In the worst case, only the difference between the
272         // difference in the x / y coordinates of the two centers.
273         // This is the case where you find top left first. Draw it out.
274         hasSkipped_ = true;
275         return (int)(abs(firstConfirmedCenter->getX() - center->getX()) - abs(firstConfirmedCenter->getY()
276                      - center->getY()))/2;
277       }
278     }
279   }
280   return 0;
281 }
282
283 bool FinderPatternFinder::haveMultiplyConfirmedCenters() {
284   int confirmedCount = 0;
285   float totalModuleSize = 0.0f;
286   size_t max = possibleCenters_.size();
287   for (size_t i = 0; i < max; i++) {
288     Ref<FinderPattern> pattern = possibleCenters_[i];
289     if (pattern->getCount() >= CENTER_QUORUM) {
290       confirmedCount++;
291       totalModuleSize += pattern->getEstimatedModuleSize();
292     }
293   }
294   if (confirmedCount < 3) {
295     return false;
296   }
297   // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
298   // and that we need to keep looking. We detect this by asking if the estimated module sizes
299   // vary too much. We arbitrarily say that when the total deviation from average exceeds
300   // 5% of the total module size estimates, it's too much.
301   float average = totalModuleSize / max;
302   float totalDeviation = 0.0f;
303   for (size_t i = 0; i < max; i++) {
304     Ref<FinderPattern> pattern = possibleCenters_[i];
305     totalDeviation += abs(pattern->getEstimatedModuleSize() - average);
306   }
307   return totalDeviation <= 0.05f * totalModuleSize;
308 }
309
310 vector<Ref<FinderPattern> > FinderPatternFinder::selectBestPatterns() {
311   size_t startSize = possibleCenters_.size();
312
313   if (startSize < 3) {
314     // Couldn't find enough finder patterns
315     throw zxing::ReaderException("Could not find three finder patterns");
316   }
317
318   // Filter outlier possibilities whose module size is too different
319   if (startSize > 3) {
320     // But we can only afford to do so if we have at least 4 possibilities to choose from
321     float totalModuleSize = 0.0f;
322     float square = 0.0f;
323     for (size_t i = 0; i < startSize; i++) {
324       float size = possibleCenters_[i]->getEstimatedModuleSize();
325       totalModuleSize += size;
326       square += size * size;
327     }
328     float average = totalModuleSize / (float) startSize;
329     float stdDev = (float)sqrt(square / startSize - average * average);
330
331     sort(possibleCenters_.begin(), possibleCenters_.end(), FurthestFromAverageComparator(average));
332     
333     float limit = max(0.2f * average, stdDev);
334
335     for (size_t i = 0; i < possibleCenters_.size() && possibleCenters_.size() > 3; i++) {
336       if (abs(possibleCenters_[i]->getEstimatedModuleSize() - average) > limit) {
337         possibleCenters_.erase(possibleCenters_.begin()+i);
338         i--;
339       }
340     }
341   }
342
343   if (possibleCenters_.size() > 3) {
344     // Throw away all but those first size candidate points we found.
345     float totalModuleSize = 0.0f;
346     for (size_t i = 0; i < startSize; i++) {
347       float size = possibleCenters_[i]->getEstimatedModuleSize();
348       totalModuleSize += size;
349     }
350     float average = totalModuleSize / (float) startSize;
351     sort(possibleCenters_.begin(), possibleCenters_.end(), CenterComparator(average));
352   }
353
354   if (possibleCenters_.size() > 3) {
355     possibleCenters_.erase(possibleCenters_.begin()+3,possibleCenters_.end());
356   }
357
358   vector<Ref<FinderPattern> > result(3);
359   result[0] = possibleCenters_[0];
360   result[1] = possibleCenters_[1];
361   result[2] = possibleCenters_[2];
362   return result;
363 }
364
365 vector<Ref<FinderPattern> > FinderPatternFinder::orderBestPatterns(vector<Ref<FinderPattern> > patterns) {
366   // Find distances between pattern centers
367   float abDistance = distance(patterns[0], patterns[1]);
368   float bcDistance = distance(patterns[1], patterns[2]);
369   float acDistance = distance(patterns[0], patterns[2]);
370
371   Ref<FinderPattern> topLeft;
372   Ref<FinderPattern> topRight;
373   Ref<FinderPattern> bottomLeft;
374   // Assume one closest to other two is top left;
375   // topRight and bottomLeft will just be guesses below at first
376   if (bcDistance >= abDistance && bcDistance >= acDistance) {
377     topLeft = patterns[0];
378     topRight = patterns[1];
379     bottomLeft = patterns[2];
380   } else if (acDistance >= bcDistance && acDistance >= abDistance) {
381     topLeft = patterns[1];
382     topRight = patterns[0];
383     bottomLeft = patterns[2];
384   } else {
385     topLeft = patterns[2];
386     topRight = patterns[0];
387     bottomLeft = patterns[1];
388   }
389
390   // Use cross product to figure out which of other1/2 is the bottom left
391   // pattern. The vector "top-left -> bottom-left" x "top-left -> top-right"
392   // should yield a vector with positive z component
393   if ((bottomLeft->getY() - topLeft->getY()) * (topRight->getX() - topLeft->getX()) < (bottomLeft->getX()
394       - topLeft->getX()) * (topRight->getY() - topLeft->getY())) {
395     Ref<FinderPattern> temp = topRight;
396     topRight = bottomLeft;
397     bottomLeft = temp;
398   }
399
400   vector<Ref<FinderPattern> > results(3);
401   results[0] = bottomLeft;
402   results[1] = topLeft;
403   results[2] = topRight;
404   return results;
405 }
406
407 float FinderPatternFinder::distance(Ref<ResultPoint> p1, Ref<ResultPoint> p2) {
408   float dx = p1->getX() - p2->getX();
409   float dy = p1->getY() - p2->getY();
410   return (float)sqrt(dx * dx + dy * dy);
411 }
412
413 FinderPatternFinder::FinderPatternFinder(Ref<BitMatrix> image,
414                                            Ref<ResultPointCallback>const& callback) :
415     image_(image), possibleCenters_(), hasSkipped_(false), callback_(callback) {
416 }
417
418 Ref<FinderPatternInfo> FinderPatternFinder::find(DecodeHints const& hints) {
419   bool tryHarder = hints.getTryHarder();
420
421   size_t maxI = image_->getHeight();
422   size_t maxJ = image_->getWidth();
423
424
425   // We are looking for black/white/black/white/black modules in
426   // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
427
428   // As this is used often, we use an integer array instead of vector
429   int stateCount[5];
430   bool done = false;
431
432
433   // Let's assume that the maximum version QR Code we support takes up 1/4
434   // the height of the image, and then account for the center being 3
435   // modules in size. This gives the smallest number of pixels the center
436   // could be, so skip this often. When trying harder, look for all
437   // QR versions regardless of how dense they are.
438   int iSkip = (3 * maxI) / (4 * MAX_MODULES);
439   if (iSkip < MIN_SKIP || tryHarder) {
440       iSkip = MIN_SKIP;
441   }
442
443   // This is slightly faster than using the Ref. Efficiency is important here
444   BitMatrix& matrix = *image_;
445
446   for (size_t i = iSkip - 1; i < maxI && !done; i += iSkip) {
447     // Get a row of black/white values
448
449     stateCount[0] = 0;
450     stateCount[1] = 0;
451     stateCount[2] = 0;
452     stateCount[3] = 0;
453     stateCount[4] = 0;
454     int currentState = 0;
455     for (size_t j = 0; j < maxJ; j++) {
456       if (matrix.get(j, i)) {
457         // Black pixel
458         if ((currentState & 1) == 1) { // Counting white pixels
459           currentState++;
460         }
461         stateCount[currentState]++;
462       } else { // White pixel
463         if ((currentState & 1) == 0) { // Counting black pixels
464           if (currentState == 4) { // A winner?
465             if (foundPatternCross(stateCount)) { // Yes
466               bool confirmed = handlePossibleCenter(stateCount, i, j);
467               if (confirmed) {
468                 // Start examining every other line. Checking each line turned out to be too
469                 // expensive and didn't improve performance.
470                 iSkip = 2;
471                 if (hasSkipped_) {
472                   done = haveMultiplyConfirmedCenters();
473                 } else {
474                   int rowSkip = findRowSkip();
475                   if (rowSkip > stateCount[2]) {
476                     // Skip rows between row of lower confirmed center
477                     // and top of presumed third confirmed center
478                     // but back up a bit to get a full chance of detecting
479                     // it, entire width of center of finder pattern
480
481                     // Skip by rowSkip, but back off by stateCount[2] (size
482                     // of last center of pattern we saw) to be conservative,
483                     // and also back off by iSkip which is about to be
484                     // re-added
485                     i += rowSkip - stateCount[2] - iSkip;
486                     j = maxJ - 1;
487                   }
488                 }
489               } else {
490                 stateCount[0] = stateCount[2];
491                 stateCount[1] = stateCount[3];
492                 stateCount[2] = stateCount[4];
493                 stateCount[3] = 1;
494                 stateCount[4] = 0;
495                 currentState = 3;
496                 continue;
497               }
498               // Clear state to start looking again
499               currentState = 0;
500               stateCount[0] = 0;
501               stateCount[1] = 0;
502               stateCount[2] = 0;
503               stateCount[3] = 0;
504               stateCount[4] = 0;
505             } else { // No, shift counts back by two
506               stateCount[0] = stateCount[2];
507               stateCount[1] = stateCount[3];
508               stateCount[2] = stateCount[4];
509               stateCount[3] = 1;
510               stateCount[4] = 0;
511               currentState = 3;
512             }
513           } else {
514             stateCount[++currentState]++;
515           }
516         } else { // Counting white pixels
517           stateCount[currentState]++;
518         }
519       }
520     }
521     if (foundPatternCross(stateCount)) {
522       bool confirmed = handlePossibleCenter(stateCount, i, maxJ);
523       if (confirmed) {
524         iSkip = stateCount[0];
525         if (hasSkipped_) {
526           // Found a third one
527           done = haveMultiplyConfirmedCenters();
528         }
529       }
530     }
531   }
532
533   vector<Ref<FinderPattern> > patternInfo = selectBestPatterns();
534   patternInfo = orderBestPatterns(patternInfo);
535
536   Ref<FinderPatternInfo> result(new FinderPatternInfo(patternInfo));
537   return result;
538 }
539 }
540 }