Issue 1605 Frank's patch to improve Aztec detection

git-svn-id: https://zxing.googlecode.com/svn/trunk@2811 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen@gmail.com 2013-05-28 21:51:20 +00:00
parent dcbc2e9bd6
commit 2ecec34f74
3 changed files with 120 additions and 129 deletions

View file

@ -37,6 +37,7 @@ Eric Kobrin (Velocitude)
evansepdx evansepdx
Erik Barbara Erik Barbara
Francois B. (Google) Francois B. (Google)
Frank Yellin
Fred Lin (Anobiit) Fred Lin (Anobiit)
gcstang gcstang
Guenther Grau Guenther Grau

View file

@ -32,6 +32,7 @@ import com.google.zxing.common.reedsolomon.ReedSolomonException;
* is rotated or skewed, or partially obscured. * is rotated or skewed, or partially obscured.
* *
* @author David Olivier * @author David Olivier
* @author Frank Yellin
*/ */
public final class Detector { public final class Detector {
@ -58,17 +59,20 @@ public final class Detector {
// 1. Get the center of the aztec matrix // 1. Get the center of the aztec matrix
Point pCenter = getMatrixCenter(); Point pCenter = getMatrixCenter();
// 2. Get the corners of the center bull's eye // 2. Get the center points of the four diagonal points just outside the bull's eye
Point[] bullEyeCornerPoints = getBullEyeCornerPoints(pCenter); // [topRight, bottomRight, bottomLeft, topLeft]
ResultPoint[] bullsEyeCorners = getBullsEyeCorners(pCenter);
// 3. Get the size of the matrix from the bull's eye // 3. Get the size of the matrix and other parameters from the bull's eye
extractParameters(bullEyeCornerPoints); extractParameters(bullsEyeCorners);
// 4. Get the corners of the matrix // 4. Sample the grid
ResultPoint[] corners = getMatrixCornerPoints(bullEyeCornerPoints); BitMatrix bits = sampleGrid(image,
bullsEyeCorners[shift%4], bullsEyeCorners[(shift+1)%4],
bullsEyeCorners[(shift+2)%4], bullsEyeCorners[(shift+3)%4]);
// 5. Sample the grid // 5. Get the corners of the matrix.
BitMatrix bits = sampleGrid(image, corners[shift%4], corners[(shift+3)%4], corners[(shift+2)%4], corners[(shift+1)%4]); ResultPoint[] corners = getMatrixCornerPoints(bullsEyeCorners);
return new AztecDetectorResult(bits, corners, compact, nbDataBlocks, nbLayers); return new AztecDetectorResult(bits, corners, compact, nbDataBlocks, nbLayers);
} }
@ -76,18 +80,16 @@ public final class Detector {
/** /**
* Extracts the number of data layers and data blocks from the layer around the bull's eye. * Extracts the number of data layers and data blocks from the layer around the bull's eye.
* *
* @param bullEyeCornerPoints the array of bull's eye corners * @param bullsEyeCorners the array of bull's eye corners
* @throws NotFoundException in case of too many errors or invalid parameters * @throws NotFoundException in case of too many errors or invalid parameters
*/ */
private void extractParameters(Point[] bullEyeCornerPoints) throws NotFoundException { private void extractParameters(ResultPoint[] bullsEyeCorners) throws NotFoundException {
int twoCenterLayers = 2 * nbCenterLayers; int twoCenterLayers = 2 * nbCenterLayers;
// Get the bits around the bull's eye // Get the bits around the bull's eye
boolean[] resab = sampleLine(bullEyeCornerPoints[0], bullEyeCornerPoints[1], twoCenterLayers+1); boolean[] resab = sampleLine(bullsEyeCorners[0], bullsEyeCorners[1], twoCenterLayers+1);
boolean[] resbc = sampleLine(bullEyeCornerPoints[1], bullEyeCornerPoints[2], twoCenterLayers+1); boolean[] resbc = sampleLine(bullsEyeCorners[1], bullsEyeCorners[2], twoCenterLayers+1);
boolean[] rescd = sampleLine(bullEyeCornerPoints[2], bullEyeCornerPoints[3], twoCenterLayers+1); boolean[] rescd = sampleLine(bullsEyeCorners[2], bullsEyeCorners[3], twoCenterLayers+1);
boolean[] resda = sampleLine(bullEyeCornerPoints[3], bullEyeCornerPoints[0], twoCenterLayers+1); boolean[] resda = sampleLine(bullsEyeCorners[3], bullsEyeCorners[0], twoCenterLayers+1);
// Determine the orientation of the matrix // Determine the orientation of the matrix
if (resab[0] && resab[twoCenterLayers]) { if (resab[0] && resab[twoCenterLayers]) {
@ -155,49 +157,12 @@ public final class Detector {
/** /**
* Gets the Aztec code corners from the bull's eye corners and the parameters. * Gets the Aztec code corners from the bull's eye corners and the parameters.
* *
* @param bullEyeCornerPoints the array of bull's eye corners * @param bullsEyeCorners the array of bull's eye corners
* @return the array of aztec code corners * @return the array of aztec code corners
* @throws NotFoundException if the corner points do not fit in the image * @throws NotFoundException if the corner points do not fit in the image
*/ */
private ResultPoint[] getMatrixCornerPoints(Point[] bullEyeCornerPoints) throws NotFoundException { private ResultPoint[] getMatrixCornerPoints(ResultPoint[] bullsEyeCorners) throws NotFoundException {
return expandSquare(bullsEyeCorners, 2 * nbCenterLayers, getDimension());
float ratio = (2 * nbLayers + (nbLayers > 4 ? 1 : 0) + (nbLayers - 4) / 8)
/ (2.0f * nbCenterLayers);
int dx = bullEyeCornerPoints[0].getX() - bullEyeCornerPoints[2].getX();
dx+=dx>0?1:-1;
int dy = bullEyeCornerPoints[0].getY() - bullEyeCornerPoints[2].getY();
dy+=dy>0?1:-1;
int targetcx = MathUtils.round(bullEyeCornerPoints[2].getX() - ratio * dx);
int targetcy = MathUtils.round(bullEyeCornerPoints[2].getY() - ratio * dy);
int targetax = MathUtils.round(bullEyeCornerPoints[0].getX() + ratio * dx);
int targetay = MathUtils.round(bullEyeCornerPoints[0].getY() + ratio * dy);
dx = bullEyeCornerPoints[1].getX() - bullEyeCornerPoints[3].getX();
dx+=dx>0?1:-1;
dy = bullEyeCornerPoints[1].getY() - bullEyeCornerPoints[3].getY();
dy+=dy>0?1:-1;
int targetdx = MathUtils.round(bullEyeCornerPoints[3].getX() - ratio * dx);
int targetdy = MathUtils.round(bullEyeCornerPoints[3].getY() - ratio * dy);
int targetbx = MathUtils.round(bullEyeCornerPoints[1].getX() + ratio * dx);
int targetby = MathUtils.round(bullEyeCornerPoints[1].getY() +ratio*dy);
if (!isValid(targetax, targetay) ||
!isValid(targetbx, targetby) ||
!isValid(targetcx, targetcy) ||
!isValid(targetdx, targetdy)) {
throw NotFoundException.getNotFoundInstance();
}
return new ResultPoint[]{
new ResultPoint(targetax, targetay),
new ResultPoint(targetbx, targetby),
new ResultPoint(targetcx, targetcy),
new ResultPoint(targetdx, targetdy)
};
} }
/** /**
@ -252,12 +217,14 @@ public final class Detector {
/** /**
* Finds the corners of a bull-eye centered on the passed point. * Finds the corners of a bull-eye centered on the passed point.
* This returns the centers of the diagonal points just outside the bull's eye
* Returns [topRight, bottomRight, bottomLeft, topLeft]
* *
* @param pCenter Center point * @param pCenter Center point
* @return The corners of the bull-eye * @return The corners of the bull-eye
* @throws NotFoundException If no valid bull-eye can be found * @throws NotFoundException If no valid bull-eye can be found
*/ */
private Point[] getBullEyeCornerPoints(Point pCenter) throws NotFoundException { private ResultPoint[] getBullsEyeCorners(Point pCenter) throws NotFoundException {
Point pina = pCenter; Point pina = pCenter;
Point pinb = pCenter; Point pinb = pCenter;
@ -297,36 +264,18 @@ public final class Detector {
compact = nbCenterLayers==5; compact = nbCenterLayers==5;
float ratio = 0.75f*2/(2*nbCenterLayers-3); // Expand the square by .5 pixel in each direction so that we're on the border
// between the white square and the black square
ResultPoint pinax = new ResultPoint(pina.getX() + 0.5f, pina.getY() - 0.5f);
ResultPoint pinbx = new ResultPoint(pinb.getX() + 0.5f, pinb.getY() + 0.5f);
ResultPoint pincx = new ResultPoint(pinc.getX() - 0.5f, pinc.getY() + 0.5f);
ResultPoint pindx = new ResultPoint(pind.getX() - 0.5f, pind.getY() - 0.5f);
int dx = pina.getX() - pinc.getX(); // Expand the square so that its corners are the centers of the points
int dy = pina.getY() - pinc.getY(); // just outside the bull's eye.
int targetcx = MathUtils.round(pinc.getX() -ratio*dx); return expandSquare(new ResultPoint[]{pinax, pinbx, pincx, pindx},
int targetcy = MathUtils.round(pinc.getY() -ratio*dy); 2 * nbCenterLayers - 3,
int targetax = MathUtils.round(pina.getX() +ratio*dx); 2 * nbCenterLayers);
int targetay = MathUtils.round(pina.getY() +ratio*dy);
dx = pinb.getX() - pind.getX();
dy = pinb.getY() - pind.getY();
int targetdx = MathUtils.round(pind.getX() -ratio*dx);
int targetdy = MathUtils.round(pind.getY() -ratio*dy);
int targetbx = MathUtils.round(pinb.getX() +ratio*dx);
int targetby = MathUtils.round(pinb.getY() +ratio*dy);
if (!isValid(targetax, targetay) ||
!isValid(targetbx, targetby) ||
!isValid(targetcx, targetcy) ||
!isValid(targetdx, targetdy)) {
throw NotFoundException.getNotFoundInstance();
}
return new Point[] {
new Point(targetax,targetay),
new Point(targetbx,targetby),
new Point(targetcx,targetcy),
new Point(targetdx,targetdy)
};
} }
/** /**
@ -377,14 +326,12 @@ public final class Detector {
pointC = cornerPoints[2]; pointC = cornerPoints[2];
pointD = cornerPoints[3]; pointD = cornerPoints[3];
} catch (NotFoundException e) { } catch (NotFoundException e) {
// This exception can be in case the initial rectangle is white // This exception can be in case the initial rectangle is white
// In that case we try to expand the rectangle. // In that case we try to expand the rectangle.
pointA = getFirstDifferent(new Point(cx+7, cy-7), false, 1, -1).toResultPoint(); pointA = getFirstDifferent(new Point(cx+7, cy-7), false, 1, -1).toResultPoint();
pointB = getFirstDifferent(new Point(cx+7, cy+7), false, 1, 1).toResultPoint(); pointB = getFirstDifferent(new Point(cx+7, cy+7), false, 1, 1).toResultPoint();
pointC = getFirstDifferent(new Point(cx-7, cy+7), false, -1, 1).toResultPoint(); pointC = getFirstDifferent(new Point(cx-7, cy+7), false, -1, 1).toResultPoint();
pointD = getFirstDifferent(new Point(cx-7, cy-7), false, -1, -1).toResultPoint(); pointD = getFirstDifferent(new Point(cx-7, cy-7), false, -1, -1).toResultPoint();
} }
// Recompute the center of the rectangle // Recompute the center of the rectangle
@ -395,46 +342,33 @@ public final class Detector {
} }
/** /**
* Samples an Aztec matrix from an image * Creates a BitMatrix by sampling the provided image.
* topLeft, topRight, bottomRight, and bottomLeft are the centers of the squares on the
* diagonal just outside the bull's eye.
*/ */
private BitMatrix sampleGrid(BitMatrix image, private BitMatrix sampleGrid(BitMatrix image,
ResultPoint topLeft, ResultPoint topLeft,
ResultPoint bottomLeft, ResultPoint topRight,
ResultPoint bottomRight, ResultPoint bottomRight,
ResultPoint topRight) throws NotFoundException { ResultPoint bottomLeft) throws NotFoundException {
int dimension;
if (compact) {
dimension = 4*nbLayers+11;
} else {
if (nbLayers <= 4) {
dimension = 4*nbLayers + 15;
} else {
dimension = 4*nbLayers + 2*((nbLayers-4)/8 + 1) + 15 ;
}
}
GridSampler sampler = GridSampler.getInstance(); GridSampler sampler = GridSampler.getInstance();
int dimension = getDimension();
float low = dimension/2.0f - nbCenterLayers;
float high = dimension/2.0f + nbCenterLayers;
return sampler.sampleGrid(image, return sampler.sampleGrid(image,
dimension, dimension,
dimension, dimension,
0.5f, low, low, // topleft
0.5f, high, low, // topright
dimension - 0.5f, high, high, // bottomright
0.5f, low, high, // bottomleft
dimension - 0.5f, topLeft.getX(), topLeft.getY(),
dimension - 0.5f, topRight.getX(), topRight.getY(),
0.5f, bottomRight.getX(), bottomRight.getY(),
dimension - 0.5f, bottomLeft.getX(), bottomLeft.getY());
topLeft.getX(),
topLeft.getY(),
topRight.getX(),
topRight.getY(),
bottomRight.getX(),
bottomRight.getY(),
bottomLeft.getX(),
bottomLeft.getY());
} }
/** /**
@ -479,7 +413,7 @@ public final class Detector {
* @param size number of bits * @param size number of bits
* @return the array of bits * @return the array of bits
*/ */
private boolean[] sampleLine(Point p1, Point p2, int size) { private boolean[] sampleLine(ResultPoint p1, ResultPoint p2, int size) {
boolean[] res = new boolean[size]; boolean[] res = new boolean[size];
float d = distance(p1,p2); float d = distance(p1,p2);
@ -563,7 +497,7 @@ public final class Detector {
} }
} }
float errRatio = (float)error/d; float errRatio = error / d;
if (errRatio > 0.1f && errRatio < 0.9f) { if (errRatio > 0.1f && errRatio < 0.9f) {
return 0; return 0;
@ -600,14 +534,66 @@ public final class Detector {
return new Point(x,y); return new Point(x,y);
} }
/**
* Expand the square represented by the corner points by pushing out equally in all directions
*
* @param cornerPoints the corners of the square, which has the bull's eye at its center
* @param oldSide the original length of the side of the square in the target bit matrix
* @param newSide the new length of the size of the square in the target bit matrix
* @return the corners of the expanded square
*/
private ResultPoint[] expandSquare(ResultPoint[] cornerPoints, float oldSide, float newSide)
throws NotFoundException {
float ratio = newSide / (2 * oldSide);
float dx = cornerPoints[0].getX() - cornerPoints[2].getX();
float dy = cornerPoints[0].getY() - cornerPoints[2].getY();
float centerx = (cornerPoints[0].getX() + cornerPoints[2].getX()) / 2.0f;
float centery = (cornerPoints[0].getY() + cornerPoints[2].getY()) / 2.0f;
ResultPoint result0 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);
ResultPoint result2 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);
dx = cornerPoints[1].getX() - cornerPoints[3].getX();
dy = cornerPoints[1].getY() - cornerPoints[3].getY();
centerx = (cornerPoints[1].getX() + cornerPoints[3].getX()) / 2.0f;
centery = (cornerPoints[1].getY() + cornerPoints[3].getY()) / 2.0f;
ResultPoint result1 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);
ResultPoint result3 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);
if (!isValid(result0) || !isValid(result1) || !isValid(result2) || !isValid(result3)) {
throw NotFoundException.getNotFoundInstance();
}
return new ResultPoint[] { result0, result1, result2, result3 };
}
private boolean isValid(int x, int y) { private boolean isValid(int x, int y) {
return x >= 0 && x < image.getWidth() && y > 0 && y < image.getHeight(); return x >= 0 && x < image.getWidth() && y > 0 && y < image.getHeight();
} }
private boolean isValid(ResultPoint point) {
int x = MathUtils.round(point.getX());
int y = MathUtils.round(point.getY());
return isValid(x, y);
}
private static float distance(Point a, Point b) { private static float distance(Point a, Point b) {
return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY()); return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY());
} }
private static float distance(ResultPoint a, ResultPoint b) {
return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY());
}
private int getDimension() {
if (compact) {
return 4 * nbLayers + 11;
}
if (nbLayers <= 4) {
return 4 * nbLayers + 15;
}
return 4 * nbLayers + 2 * ((nbLayers-4)/8 + 1) + 15;
}
static final class Point { static final class Point {
private final int x; private final int x;
private final int y; private final int y;
@ -628,6 +614,10 @@ public final class Detector {
int getY() { int getY() {
return y; return y;
} }
}
@Override
public String toString() {
return "<" + x + ' ' + y + '>';
}
}
} }

View file

@ -28,10 +28,10 @@ public final class AztecBlackBox2TestCase extends AbstractBlackBoxTestCase {
public AztecBlackBox2TestCase() { public AztecBlackBox2TestCase() {
super("test/data/blackbox/aztec-2", new AztecReader(), BarcodeFormat.AZTEC); super("test/data/blackbox/aztec-2", new AztecReader(), BarcodeFormat.AZTEC);
addTest(2, 2, 0.0f); addTest(5, 5, 0.0f);
addTest(2, 2, 90.0f); addTest(4, 4, 90.0f);
addTest(3, 3, 180.0f); addTest(6, 6, 180.0f);
addTest(2, 2, 270.0f); addTest(3, 3, 270.0f);
} }
} }