/*\r
- * Copyright 2007 Google Inc.\r
+ * Copyright 2007 ZXing authors\r
*\r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
\r
package com.google.zxing.qrcode.detector;\r
\r
-import com.google.zxing.MonochromeBitmapSource;\r
-import com.google.zxing.ReaderException;\r
-import com.google.zxing.common.BitArray;\r
+import com.google.zxing.NotFoundException;\r
+import com.google.zxing.ResultPoint;\r
+import com.google.zxing.ResultPointCallback;\r
+import com.google.zxing.common.BitMatrix;\r
\r
import java.util.Vector;\r
\r
* pasted and stripped down here for maximum performance but does unfortunately duplicate\r
* some code.</p>\r
*\r
- * <p>This class is not thread-safe.</p>\r
+ * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.\r
*\r
- * @author srowen@google.com (Sean Owen)\r
+ * @author Sean Owen\r
*/\r
final class AlignmentPatternFinder {\r
\r
- private final MonochromeBitmapSource image;\r
+ private final BitMatrix image;\r
private final Vector possibleCenters;\r
private final int startX;\r
private final int startY;\r
private final int width;\r
private final int height;\r
private final float moduleSize;\r
+ private final int[] crossCheckStateCount;\r
+ private final ResultPointCallback resultPointCallback;\r
\r
/**\r
* <p>Creates a finder that will look in a portion of the whole image.</p>\r
* @param height height of region to search\r
* @param moduleSize estimated module size so far\r
*/\r
- AlignmentPatternFinder(MonochromeBitmapSource image,\r
+ AlignmentPatternFinder(BitMatrix image,\r
int startX,\r
int startY,\r
int width,\r
int height,\r
- float moduleSize) {\r
+ float moduleSize,\r
+ ResultPointCallback resultPointCallback) {\r
this.image = image;\r
this.possibleCenters = new Vector(5);\r
this.startX = startX;\r
this.width = width;\r
this.height = height;\r
this.moduleSize = moduleSize;\r
+ this.crossCheckStateCount = new int[3];\r
+ this.resultPointCallback = resultPointCallback;\r
}\r
\r
/**\r
* it's pretty performance-critical and so is written to be fast foremost.</p>\r
*\r
* @return {@link AlignmentPattern} if found\r
- * @throws ReaderException if not found\r
+ * @throws NotFoundException if not found\r
*/\r
- AlignmentPattern find() throws ReaderException {\r
+ AlignmentPattern find() throws NotFoundException {\r
int startX = this.startX;\r
int height = this.height;\r
int maxJ = startX + width;\r
int middleI = startY + (height >> 1);\r
- BitArray luminanceRow = new BitArray(width);\r
// We are looking for black/white/black modules in 1:1:1 ratio;\r
// this tracks the number of black/white/black modules seen so far\r
int[] stateCount = new int[3];\r
for (int iGen = 0; iGen < height; iGen++) {\r
// Search from middle outwards\r
int i = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1));\r
- image.getBlackRow(i, luminanceRow, startX, width);\r
stateCount[0] = 0;\r
stateCount[1] = 0;\r
stateCount[2] = 0;\r
- int currentState = 0;\r
int j = startX;\r
// Burn off leading white pixels before anything else; if we start in the middle of\r
// a white run, it doesn't make sense to count its length, since we don't know if the\r
// white run continued to the left of the start point\r
- while (!luminanceRow.get(j - startX) && j < maxJ) {\r
+ while (j < maxJ && !image.get(j, i)) {\r
j++;\r
}\r
+ int currentState = 0;\r
while (j < maxJ) {\r
- if (luminanceRow.get(j - startX)) {\r
+ if (image.get(j, i)) {\r
// Black pixel\r
if (currentState == 1) { // Counting black pixels\r
stateCount[currentState]++;\r
return (AlignmentPattern) possibleCenters.elementAt(0);\r
}\r
\r
- throw new ReaderException("Could not find alignment pattern");\r
+ throw NotFoundException.getNotFoundInstance();\r
}\r
\r
/**\r
/**\r
* @param stateCount count of black/white/black pixels just read\r
* @return true iff the proportions of the counts is close enough to the 1/1/1 ratios\r
- * used by alignment patterns to be considered a match\r
+ * used by alignment patterns to be considered a match\r
*/\r
private boolean foundPatternCross(int[] stateCount) {\r
float moduleSize = this.moduleSize;\r
- float maxVariance = moduleSize / 2.5f;\r
+ float maxVariance = moduleSize / 2.0f;\r
for (int i = 0; i < 3; i++) {\r
if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) {\r
return false;\r
* @param startI row where an alignment pattern was detected\r
* @param centerJ center of the section that appears to cross an alignment pattern\r
* @param maxCount maximum reasonable number of modules that should be\r
- * observed in any reading state, based on the results of the horizontal scan\r
+ * observed in any reading state, based on the results of the horizontal scan\r
* @return vertical center of alignment pattern, or {@link Float#NaN} if not found\r
*/\r
- private float crossCheckVertical(int startI, int centerJ, int maxCount) {\r
- MonochromeBitmapSource image = this.image;\r
+ private float crossCheckVertical(int startI, int centerJ, int maxCount,\r
+ int originalStateCountTotal) {\r
+ BitMatrix image = this.image;\r
\r
int maxI = image.getHeight();\r
- int[] stateCount = new int[3];\r
+ int[] stateCount = crossCheckStateCount;\r
+ stateCount[0] = 0;\r
+ stateCount[1] = 0;\r
+ stateCount[2] = 0;\r
\r
// Start counting up from center\r
int i = startI;\r
- while (i >= 0 && image.isBlack(centerJ, i) && stateCount[1] <= maxCount) {\r
+ while (i >= 0 && image.get(centerJ, i) && stateCount[1] <= maxCount) {\r
stateCount[1]++;\r
i--;\r
}\r
if (i < 0 || stateCount[1] > maxCount) {\r
return Float.NaN;\r
}\r
- while (i >= 0 && !image.isBlack(centerJ, i) && stateCount[0] <= maxCount) {\r
+ while (i >= 0 && !image.get(centerJ, i) && stateCount[0] <= maxCount) {\r
stateCount[0]++;\r
i--;\r
}\r
\r
// Now also count down from center\r
i = startI + 1;\r
- while (i < maxI && image.isBlack(centerJ, i) && stateCount[1] <= maxCount) {\r
+ while (i < maxI && image.get(centerJ, i) && stateCount[1] <= maxCount) {\r
stateCount[1]++;\r
i++;\r
}\r
if (i == maxI || stateCount[1] > maxCount) {\r
return Float.NaN;\r
}\r
- while (i < maxI && !image.isBlack(centerJ, i) && stateCount[2] <= maxCount) {\r
+ while (i < maxI && !image.get(centerJ, i) && stateCount[2] <= maxCount) {\r
stateCount[2]++;\r
i++;\r
}\r
return Float.NaN;\r
}\r
\r
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];\r
+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {\r
+ return Float.NaN;\r
+ }\r
+\r
return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;\r
}\r
\r
* @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not\r
*/\r
private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j) {\r
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];\r
float centerJ = centerFromEnd(stateCount, j);\r
- float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1]);\r
+ float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1], stateCountTotal);\r
if (!Float.isNaN(centerI)) {\r
float estimatedModuleSize = (float) (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;\r
int max = possibleCenters.size();\r
}\r
}\r
// Hadn't found this before; save it\r
- possibleCenters.addElement(new AlignmentPattern(centerJ, centerI, estimatedModuleSize));\r
+ ResultPoint point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);\r
+ possibleCenters.addElement(point);\r
+ if (resultPointCallback != null) {\r
+ resultPointCallback.foundPossibleResultPoint(point);\r
+ }\r
}\r
return null;\r
}\r
\r
-}
\ No newline at end of file
+}\r