Issue 563: Support non-rectangular Data Matrix

git-svn-id: https://zxing.googlecode.com/svn/trunk@1616 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
dav.olivier@gmail.com 2010-10-05 20:19:53 +00:00
parent c988952a07
commit 94679de34d
19 changed files with 208 additions and 79 deletions

View file

@ -38,44 +38,63 @@ public final class DefaultGridSampler extends GridSampler {
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
return sampleGrid(image, dimension, transform);
return sampleGrid(image, dimension, dimension, transform);
}
public BitMatrix sampleGrid(BitMatrix image,
int dimensionX,
int dimensionY,
float p1ToX, float p1ToY,
float p2ToX, float p2ToY,
float p3ToX, float p3ToY,
float p4ToX, float p4ToY,
float p1FromX, float p1FromY,
float p2FromX, float p2FromY,
float p3FromX, float p3FromY,
float p4FromX, float p4FromY) throws NotFoundException {
PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
return sampleGrid(image, dimensionX, dimensionY, transform);
}
public BitMatrix sampleGrid(BitMatrix image,
int dimension,
PerspectiveTransform transform) throws NotFoundException {
BitMatrix bits = new BitMatrix(dimension);
float[] points = new float[dimension << 1];
for (int y = 0; y < dimension; y++) {
int max = points.length;
float iValue = (float) y + 0.5f;
for (int x = 0; x < max; x += 2) {
points[x] = (float) (x >> 1) + 0.5f;
points[x + 1] = iValue;
}
transform.transformPoints(points);
// Quick check to see if points transformed to something inside the image;
// sufficient to check the endpoints
checkAndNudgePoints(image, points);
try {
for (int x = 0; x < max; x += 2) {
if (image.get((int) points[x], (int) points[x + 1])) {
// Black(-ish) pixel
bits.set(x >> 1, y);
}
}
} catch (ArrayIndexOutOfBoundsException aioobe) {
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
// transform gets "twisted" such that it maps a straight line of points to a set of points
// whose endpoints are in bounds, but others are not. There is probably some mathematical
// way to detect this about the transformation that I don't know yet.
// This results in an ugly runtime exception despite our clever checks above -- can't have
// that. We could check each point's coordinates but that feels duplicative. We settle for
// catching and wrapping ArrayIndexOutOfBoundsException.
throw NotFoundException.getNotFoundInstance();
}
}
return bits;
}
int dimensionX, int dimensionY,
PerspectiveTransform transform) throws NotFoundException {
BitMatrix bits = new BitMatrix(dimensionX, dimensionY);
float[] points = new float[dimensionX << 1];
for (int y = 0; y < dimensionY; y++) {
int max = points.length;
float iValue = (float) y + 0.5f;
for (int x = 0; x < max; x += 2) {
points[x] = (float) (x >> 1) + 0.5f;
points[x + 1] = iValue;
}
transform.transformPoints(points);
// Quick check to see if points transformed to something inside the image;
// sufficient to check the endpoints
checkAndNudgePoints(image, points);
try {
for (int x = 0; x < max; x += 2) {
if (image.get((int) points[x], (int) points[x + 1])) {
// Black(-ish) pixel
bits.set(x >> 1, y);
}
}
} catch (ArrayIndexOutOfBoundsException aioobe) {
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
// transform gets "twisted" such that it maps a straight line of points to a set of points
// whose endpoints are in bounds, but others are not. There is probably some mathematical
// way to detect this about the transformation that I don't know yet.
// This results in an ugly runtime exception despite our clever checks above -- can't have
// that. We could check each point's coordinates but that feels duplicative. We settle for
// catching and wrapping ArrayIndexOutOfBoundsException.
throw NotFoundException.getNotFoundInstance();
}
}
return bits;
}
}

View file

@ -92,6 +92,28 @@ public abstract class GridSampler {
float p3FromX, float p3FromY,
float p4FromX, float p4FromY) throws NotFoundException;
/**
* Samples an image for a rectangular matrix of bits of the given dimension.
* @param image image to sample
* @param dimensionX width of {@link BitMatrix} to sample from image
* @param dimensionY height of {@link BitMatrix} to sample from image
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region
* defined by the "from" parameters
* @throws NotFoundException if image can't be sampled, for example, if the transformation defined
* by the given points is invalid or results in sampling outside the image boundaries
*/
public abstract BitMatrix sampleGrid(BitMatrix image,
int dimensionX,
int dimensionY,
float p1ToX, float p1ToY,
float p2ToX, float p2ToY,
float p3ToX, float p3ToY,
float p4ToX, float p4ToY,
float p1FromX, float p1FromY,
float p2FromX, float p2FromY,
float p3FromX, float p3FromY,
float p4FromX, float p4FromY) throws NotFoundException;
public BitMatrix sampleGrid(BitMatrix image,
int dimension,
PerspectiveTransform transform) throws NotFoundException {

View file

@ -30,18 +30,17 @@ final class BitMatrixParser {
/**
* @param bitMatrix {@link BitMatrix} to parse
* @throws FormatException if dimension is < 10 or > 144 or not 0 mod 2
* @throws FormatException if dimension is < 8 or > 144 or not 0 mod 2
*/
BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
int dimension = bitMatrix.getHeight();
if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) {
if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0) {
throw FormatException.getFormatInstance();
}
version = readVersion(bitMatrix);
this.mappingBitMatrix = extractDataRegion(bitMatrix);
// TODO(bbrown): Make this work for rectangular symbols
this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getHeight());
this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getWidth(), this.mappingBitMatrix.getHeight());
}
/**
@ -61,9 +60,8 @@ final class BitMatrixParser {
return version;
}
// TODO(bbrown): make this work for rectangular dimensions as well.
int numRows = bitMatrix.getHeight();
int numColumns = numRows;
int numColumns = bitMatrix.getWidth();
return Version.getVersionForDimensions(numRows, numColumns);
}
@ -83,9 +81,9 @@ final class BitMatrixParser {
int row = 4;
int column = 0;
// TODO(bbrown): Data Matrix can be rectangular, assuming square for now
int numRows = mappingBitMatrix.getHeight();
int numColumns = numRows;
int numColumns = mappingBitMatrix.getWidth();
boolean corner1Read = false;
boolean corner2Read = false;
@ -407,7 +405,6 @@ final class BitMatrixParser {
int symbolSizeRows = version.getSymbolSizeRows();
int symbolSizeColumns = version.getSymbolSizeColumns();
// TODO(bbrown): Make this work with rectangular codes
if (bitMatrix.getHeight() != symbolSizeRows) {
throw new IllegalArgumentException("Dimension of bitMarix must match the version size");
}
@ -419,10 +416,10 @@ final class BitMatrixParser {
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
//int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
// TODO(bbrown): Make this work with rectangular codes
BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionRow);
BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow);
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {

View file

@ -232,7 +232,7 @@ public final class Version {
new ECBlocks(14, new ECB(1, 16))),
new Version(28, 12, 36, 10, 16,
new ECBlocks(18, new ECB(1, 22))),
new Version(29, 16, 36, 10, 16,
new Version(29, 16, 36, 14, 16,
new ECBlocks(24, new ECB(1, 32))),
new Version(30, 16, 48, 14, 22,
new ECBlocks(28, new ECB(1, 49)))

View file

@ -141,35 +141,118 @@ public final class Detector {
// The top right point is actually the corner of a module, which is one of the two black modules
// adjacent to the white module at the top right. Tracing to that corner from either the top left
// or bottom right should work here.
int dimension = Math.min(transitionsBetween(topLeft, topRight).getTransitions(),
transitionsBetween(bottomRight, topRight).getTransitions());
if ((dimension & 0x01) == 1) {
int dimensionTop = transitionsBetween(topLeft, topRight).getTransitions();
int dimensionRight = transitionsBetween(bottomRight, topRight).getTransitions();
if ((dimensionTop & 0x01) == 1) {
// it can't be odd, so, round... up?
dimension++;
dimensionTop++;
}
dimension += 2;
dimensionTop += 2;
if ((dimensionRight & 0x01) == 1) {
// it can't be odd, so, round... up?
dimensionRight++;
}
dimensionRight += 2;
//correct top right point to match the white module
ResultPoint correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension);
if (correctedTopRight == null){
correctedTopRight = topRight;
BitMatrix bits = null;
ResultPoint correctedTopRight = null;
if (dimensionTop >= dimensionRight * 2 || dimensionRight >= dimensionTop * 2){
//The matrix is rectangular
correctedTopRight = correctTopRightRectangular(bottomLeft, bottomRight, topLeft, topRight, dimensionTop, dimensionRight);
if (correctedTopRight == null){
correctedTopRight = topRight;
}
dimensionTop = transitionsBetween(topLeft, correctedTopRight).getTransitions();
dimensionRight = transitionsBetween(bottomRight, correctedTopRight).getTransitions();
if ((dimensionTop & 0x01) == 1) {
// it can't be odd, so, round... up?
dimensionTop++;
}
if ((dimensionRight & 0x01) == 1) {
// it can't be odd, so, round... up?
dimensionRight++;
}
bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, correctedTopRight, dimensionTop, dimensionRight);
} else {
//The matrix is square
int dimension = Math.min(dimensionRight, dimensionTop);
//correct top right point to match the white module
correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension);
if (correctedTopRight == null){
correctedTopRight = topRight;
}
//We redetermine the dimension using the corrected top right point
int dimensionCorrected = Math.max(transitionsBetween(topLeft, correctedTopRight).getTransitions(),
transitionsBetween(bottomRight, correctedTopRight).getTransitions());
dimensionCorrected++;
if ((dimensionCorrected & 0x01) == 1) {
dimensionCorrected++;
}
bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, correctedTopRight, dimensionCorrected, dimensionCorrected);
}
//We redetermine the dimension using the corrected top right point
int dimension2 = Math.max(transitionsBetween(topLeft, correctedTopRight).getTransitions(),
transitionsBetween(bottomRight, correctedTopRight).getTransitions());
dimension2++;
if ((dimension2 & 0x01) == 1) {
dimension2++;
}
BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, correctedTopRight, dimension2);
return new DetectorResult(bits, new ResultPoint[]{topLeft, bottomLeft, bottomRight, correctedTopRight});
}
/**
* Calculates the position of the white top right module using the output of the rectangle detector
* Calculates the position of the white top right module using the output of the rectangle detector for a rectangular matrix
*/
private ResultPoint correctTopRightRectangular(ResultPoint bottomLeft,
ResultPoint bottomRight, ResultPoint topLeft, ResultPoint topRight,
int dimensionTop, int dimensionRight) {
float corr = distance(bottomLeft, bottomRight) / (float)dimensionTop;
int norm = distance(topLeft, topRight);
float cos = (topRight.getX() - topLeft.getX()) / norm;
float sin = (topRight.getY() - topLeft.getY()) / norm;
ResultPoint c1 = new ResultPoint(topRight.getX()+corr*cos, topRight.getY()+corr*sin);
corr = distance(bottomLeft, topLeft) / (float)dimensionRight;
norm = distance(bottomRight, topRight);
cos = (topRight.getX() - bottomRight.getX()) / norm;
sin = (topRight.getY() - bottomRight.getY()) / norm;
ResultPoint c2 = new ResultPoint(topRight.getX()+corr*cos, topRight.getY()+corr*sin);
if (!isValid(c1)){
if (isValid(c2)){
return c2;
}
return null;
} else if (!isValid(c2)){
return c1;
}
int l1 = Math.abs(dimensionTop - transitionsBetween(topLeft, c1).getTransitions()) +
Math.abs(dimensionRight - transitionsBetween(bottomRight, c1).getTransitions());
int l2 = Math.abs(dimensionTop - transitionsBetween(topLeft, c2).getTransitions()) +
Math.abs(dimensionRight - transitionsBetween(bottomRight, c2).getTransitions());
if (l1 <= l2){
return c1;
}
return c2;
}
/**
* Calculates the position of the white top right module using the output of the rectangle detector for a square matrix
*/
private ResultPoint correctTopRight(ResultPoint bottomLeft,
ResultPoint bottomRight,
@ -242,20 +325,22 @@ public final class Detector {
ResultPoint bottomLeft,
ResultPoint bottomRight,
ResultPoint topRight,
int dimension) throws NotFoundException {
int dimensionX,
int dimensionY) throws NotFoundException {
GridSampler sampler = GridSampler.getInstance();
return sampler.sampleGrid(image,
dimension,
dimensionX,
dimensionY,
0.5f,
0.5f,
dimension - 0.5f,
dimensionX - 0.5f,
0.5f,
dimension - 0.5f,
dimension - 0.5f,
dimensionX - 0.5f,
dimensionY - 0.5f,
0.5f,
dimension - 0.5f,
dimensionY - 0.5f,
topLeft.getX(),
topLeft.getY(),
topRight.getX(),

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

View file

@ -0,0 +1 @@
abcde

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

View file

@ -0,0 +1 @@
abcdefghijklm

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

View file

@ -0,0 +1 @@
abcdef

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1 @@
abcdefghijklmnopq

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyz

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW

View file

@ -27,10 +27,10 @@ public final class DataMatrixBlackBox1TestCase extends AbstractBlackBoxTestCase
public DataMatrixBlackBox1TestCase() {
// TODO use MultiFormatReader here once Data Matrix decoder is done
super("test/data/blackbox/datamatrix-1", new DataMatrixReader(), BarcodeFormat.DATA_MATRIX);
addTest(7, 7, 0.0f);
addTest(7, 7, 90.0f);
addTest(7, 7, 180.0f);
addTest(7, 7, 270.0f);
addTest(13, 13, 0.0f);
addTest(13, 13, 90.0f);
addTest(13, 13, 180.0f);
addTest(13, 13, 270.0f);
}
}

View file

@ -30,7 +30,7 @@ public final class DataMatrixBlackBox2TestCase extends AbstractBlackBoxTestCase
addTest(10, 10, 0.0f);
addTest(13, 13, 90.0f);
addTest(16, 16, 180.0f);
addTest(12, 12, 270.0f);
addTest(13, 13, 270.0f);
}
}