2 * FinderPatternFinder.cpp
5 * Created by Christian Brunschen on 13/05/2008.
6 * Copyright 2008 ZXing authors All rights reserved.
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "FinderPatternFinder.h"
22 #include "../../ReaderException.h"
30 using namespace common;
32 class ClosestToAverageComparator {
34 float averageModuleSize_;
36 ClosestToAverageComparator(float averageModuleSize) :
37 averageModuleSize_(averageModuleSize) { }
38 int operator()(Ref<FinderPattern> a, Ref<FinderPattern> b) {
39 float dA = abs(a->getEstimatedModuleSize() - averageModuleSize_);
40 float dB = abs(b->getEstimatedModuleSize() - averageModuleSize_);
41 return dA < dB ? -1 : dA > dB ? 1 : 0;
45 class CenterComparator {
47 int operator()(Ref<FinderPattern> a, Ref<FinderPattern> b) {
48 return b->getCount() - a->getCount();
52 int FinderPatternFinder::CENTER_QUORUM = 2;
53 int FinderPatternFinder::MIN_SKIP = 3;
54 int FinderPatternFinder::MAX_MODULES = 57;
56 float FinderPatternFinder::centerFromEnd(valarray<int> &stateCount,
58 return (float) (end - stateCount[4] - stateCount[3]) -
62 bool FinderPatternFinder::foundPatternCross(valarray<int> &stateCount) {
63 int totalModuleSize = 0;
64 for (int i = 0; i < 5; i++) {
65 if (stateCount[i] == 0) {
68 totalModuleSize += stateCount[i];
70 if (totalModuleSize < 7) {
73 float moduleSize = (float) totalModuleSize / 7.0f;
74 float maxVariance = moduleSize / 2.0f;
75 // Allow less than 50% variance from 1-1-3-1-1 proportions
76 return abs(moduleSize - stateCount[0]) < maxVariance &&
77 abs(moduleSize - stateCount[1]) < maxVariance &&
78 abs(3.0f * moduleSize - stateCount[2]) < 3.0f * maxVariance &&
79 abs(moduleSize - stateCount[3]) < maxVariance &&
80 abs(moduleSize - stateCount[4]) < maxVariance;
83 float FinderPatternFinder::crossCheckVertical(size_t startI, size_t centerJ,
85 int originalStateCountTotal) {
87 int maxI = image_->getHeight();
88 valarray<int> stateCount((const int)0, 5);
90 // Start counting up from center
92 while (i >= 0 && image_->isBlack(centerJ, i)) {
99 while (i >= 0 && !image_->isBlack(centerJ, i) && stateCount[1] <= maxCount) {
103 // If already too many modules in this state or ran off the edge:
104 if (i < 0 || stateCount[1] > maxCount) {
107 while (i >= 0 && image_->isBlack(centerJ, i) && stateCount[0] <= maxCount) {
111 if (stateCount[0] > maxCount) {
115 // Now also count down from center
117 while (i < maxI && image_->isBlack(centerJ, i)) {
124 while (i < maxI && !image_->isBlack(centerJ, i) && stateCount[3] < maxCount) {
128 if (i == maxI || stateCount[3] >= maxCount) {
131 while (i < maxI && image_->isBlack(centerJ, i) && stateCount[4] < maxCount) {
135 if (stateCount[4] >= maxCount) {
139 // If we found a finder-pattern-like section, but its size is more than 20% different than
140 // the original, assume it's a false positive
141 int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4];
142 if (5 * abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {
146 return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : NAN;
149 float FinderPatternFinder::crossCheckHorizontal(size_t startJ, size_t centerI,
151 int originalStateCountTotal)
154 int maxJ = image_->getWidth();
155 valarray<int> stateCount((const int)0, 5);
158 while (j >= 0 && image_->isBlack(j, centerI)) {
165 while (j >= 0 && !image_->isBlack(j, centerI) &&
166 stateCount[1] <= maxCount) {
170 if (j < 0 || stateCount[1] > maxCount) {
173 while (j >= 0 && image_->isBlack(j, centerI) &&
174 stateCount[0] <= maxCount) {
178 if (stateCount[0] > maxCount) {
183 while (j < maxJ && image_->isBlack(j, centerI)) {
190 while (j < maxJ && !image_->isBlack(j, centerI) &&
191 stateCount[3] < maxCount) {
195 if (j == maxJ || stateCount[3] >= maxCount) {
198 while (j < maxJ && image_->isBlack(j, centerI) && stateCount[4] < maxCount) {
202 if (stateCount[4] >= maxCount) {
206 // If we found a finder-pattern-like section, but its size is significantly different than
207 // the original, assume it's a false positive
208 int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] +
209 stateCount[3] + stateCount[4];
210 if (5 * abs(stateCountTotal - originalStateCountTotal) >=
211 originalStateCountTotal) {
215 return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : NAN;
218 bool FinderPatternFinder::handlePossibleCenter(valarray<int> &stateCount,
219 size_t i, size_t j) {
220 int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] +
221 stateCount[3] + stateCount[4];
222 float centerJ = centerFromEnd(stateCount, j);
223 float centerI = crossCheckVertical(i, centerJ, stateCount[2],
225 if (!isnan(centerI)) {
227 centerJ = crossCheckHorizontal(centerJ, centerI, stateCount[2],
229 if (!isnan(centerJ)) {
230 float estimatedModuleSize = (float) stateCountTotal / 7.0f;
232 size_t max = possibleCenters_.size();
233 for (size_t index = 0; index < max; index++) {
234 Ref<FinderPattern> center = possibleCenters_[index];
235 // Look for about the same center and module size:
236 if (center->aboutEquals(estimatedModuleSize, centerI, centerJ)) {
237 center->incrementCount();
243 Ref<FinderPattern> newPattern
244 (new FinderPattern(centerJ, centerI,
245 estimatedModuleSize));
246 possibleCenters_.push_back(newPattern);
254 int FinderPatternFinder::findRowSkip() {
255 size_t max = possibleCenters_.size();
259 Ref<FinderPattern> firstConfirmedCenter;
260 for (size_t i = 0; i < max; i++) {
261 Ref<FinderPattern> center = possibleCenters_[i];
262 if (center->getCount() >= CENTER_QUORUM) {
263 if (firstConfirmedCenter == 0) {
264 firstConfirmedCenter = center;
266 // We have two confirmed centers
267 // How far down can we skip before resuming looking for the next
268 // pattern? In the worst case, only the difference between the
269 // difference in the x / y coordinates of the two centers.
270 // This is the case where you find top left first. Draw it out.
272 return (int) (abs(firstConfirmedCenter->getX() - center->getX()) -
273 abs(firstConfirmedCenter->getY() - center->getY()));
280 bool FinderPatternFinder::haveMultiplyConfirmedCenters() {
281 int confirmedCount = 0;
282 float totalModuleSize = 0.0f;
283 size_t max = possibleCenters_.size();
284 for (size_t i = 0; i < max; i++) {
285 Ref<FinderPattern> pattern = possibleCenters_[i];
286 if (pattern->getCount() >= CENTER_QUORUM) {
288 totalModuleSize += pattern->getEstimatedModuleSize();
291 if (confirmedCount < 3) {
294 // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
295 // and that we need to keep looking. We detect this by asking if the estimated module sizes
296 // vary too much. We arbitrarily say that when the total deviation from average exceeds
297 // 15% of the total module size estimates, it's too much.
298 float average = totalModuleSize / max;
299 float totalDeviation = 0.0f;
300 for (size_t i = 0; i < max; i++) {
301 Ref<FinderPattern> pattern = possibleCenters_[i];
302 totalDeviation += abs(pattern->getEstimatedModuleSize() - average);
304 return totalDeviation <= 0.15f * totalModuleSize;
307 ArrayRef<Ref<FinderPattern> > FinderPatternFinder::selectBestPatterns() {
308 sort(possibleCenters_.begin(), possibleCenters_.end(),
311 size_t max = possibleCenters_.size();
313 if (possibleCenters_[size]->getCount() < CENTER_QUORUM) {
320 // Couldn't find enough finder patterns
321 throw new ReaderException("Could not find three finder patterns");
325 // Found just enough -- hope these are good!
326 Ref<FinderPattern> rawResult[] = {
327 possibleCenters_[0], possibleCenters_[1], possibleCenters_[2]
329 ArrayRef<Ref<FinderPattern> > result(rawResult, 3);
333 // Hmm, multiple found. We need to pick the best three. Find the most
334 // popular ones whose module size is nearest the average
335 float averageModuleSize = 0.0f;
336 for (size_t i = 0; i < size; i++) {
337 averageModuleSize += possibleCenters_[i]->getEstimatedModuleSize();
339 averageModuleSize /= (float) size;
341 sort(possibleCenters_.begin(), possibleCenters_.end(),
342 ClosestToAverageComparator(averageModuleSize));
344 Ref<FinderPattern> rawResult[] = {
345 possibleCenters_[0], possibleCenters_[1], possibleCenters_[2]
347 ArrayRef<Ref<FinderPattern> > result(rawResult, 3);
351 ArrayRef<Ref<FinderPattern> > FinderPatternFinder::orderBestPatterns
352 (ArrayRef<Ref<FinderPattern> > patterns) {
353 // Find distances between pattern centers
354 float abDistance = distance(patterns[0], patterns[1]);
355 float bcDistance = distance(patterns[1], patterns[2]);
356 float acDistance = distance(patterns[0], patterns[2]);
358 Ref<FinderPattern> topLeft;
359 Ref<FinderPattern> topRight;
360 Ref<FinderPattern> bottomLeft;
361 // Assume one closest to other two is top left;
362 // topRight and bottomLeft will just be guesses below at first
363 if (bcDistance >= abDistance && bcDistance >= acDistance) {
364 topLeft = patterns[0];
365 topRight = patterns[1];
366 bottomLeft = patterns[2];
367 } else if (acDistance >= bcDistance && acDistance >= abDistance) {
368 topLeft = patterns[1];
369 topRight = patterns[0];
370 bottomLeft = patterns[2];
372 topLeft = patterns[2];
373 topRight = patterns[0];
374 bottomLeft = patterns[1];
377 // Use cross product to figure out which of other1/2 is the bottom left
378 // pattern. The vector "top-left -> bottom-left" x "top-left -> top-right"
379 // should yield a vector with positive z component
380 if ((bottomLeft->getY() - topLeft->getY()) *
381 (topRight->getX() - topLeft->getX()) <
382 (bottomLeft->getX() - topLeft->getX()) *
383 (topRight->getY() - topLeft->getY())) {
384 Ref<FinderPattern> temp = topRight;
385 topRight = bottomLeft;
389 ArrayRef<Ref<FinderPattern> > results(3);
390 results[0] = bottomLeft;
391 results[1] = topLeft;
392 results[2] = topRight;
396 float FinderPatternFinder::distance(Ref<ResultPoint> p1,
397 Ref<ResultPoint> p2) {
398 float dx = p1->getX() - p2->getX();
399 float dy = p1->getY() - p2->getY();
400 return (float)sqrt(dx*dx + dy*dy);
403 FinderPatternFinder::FinderPatternFinder(Ref<MonochromeBitmapSource> image)
404 : image_(image), possibleCenters_(), hasSkipped_(false) {
407 Ref<FinderPatternInfo> FinderPatternFinder::find() {
408 size_t maxI = image_->getHeight();
409 size_t maxJ = image_->getWidth();
411 // We are looking for black/white/black/white/black modules in
412 // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
413 valarray<int> stateCount(5);
416 // Let's assume that the maximum version QR Code we support takes up 1/4
417 // the height of the image, and then account for the center being 3
418 // modules in size. This gives the smallest number of pixels the center
419 // could be, so skip this often. When trying harder, look for all
420 // QR versions regardless of how dense they are.
421 size_t iSkip = MIN_SKIP;
423 for (size_t i = iSkip - 1; i < maxI && !done; i += iSkip) {
424 // Get a row of black/white values
426 Ref<BitArray> blackRow = image_->getBlackRow(i, null, 0, maxJ);
432 int currentState = 0;
433 for (size_t j = 0; j < maxJ; j++) {
434 if (blackRow->get(j)) {
436 if ((currentState & 1) == 1) { // Counting white pixels
439 stateCount[currentState]++;
440 } else { // White pixel
441 if ((currentState & 1) == 0) { // Counting black pixels
442 if (currentState == 4) { // A winner?
443 if (foundPatternCross(stateCount)) { // Yes
444 bool confirmed = handlePossibleCenter(stateCount, i, j);
446 iSkip = 1; // Go back to examining each line
448 done = haveMultiplyConfirmedCenters();
450 int rowSkip = findRowSkip();
451 if (rowSkip > stateCount[2]) {
452 // Skip rows between row of lower confirmed center
453 // and top of presumed third confirmed center
454 // but back up a bit to get a full chance of detecting
455 // it, entire width of center of finder pattern
457 // Skip by rowSkip, but back off by stateCount[2] (size
458 // of last center of pattern we saw) to be conservative,
459 // and also back off by iSkip which is about to be
461 i += rowSkip - stateCount[2] - iSkip;
466 // Advance to next black pixel
469 } while (j < maxJ && !blackRow->get(j));
470 j--; // back up to that last white pixel
472 // Clear state to start looking again
479 } else { // No, shift counts back by two
480 stateCount[0] = stateCount[2];
481 stateCount[1] = stateCount[3];
482 stateCount[2] = stateCount[4];
488 stateCount[++currentState]++;
490 } else { // Counting white pixels
491 stateCount[currentState]++;
495 if (foundPatternCross(stateCount)) {
496 bool confirmed = handlePossibleCenter(stateCount, i, maxJ);
498 iSkip = stateCount[0];
501 done = haveMultiplyConfirmedCenters();
507 ArrayRef<Ref<FinderPattern> > patternInfo = selectBestPatterns();
508 patternInfo = orderBestPatterns(patternInfo);
510 Ref<FinderPatternInfo> result(new FinderPatternInfo(patternInfo));