mirror of
https://github.com/zxing/zxing.git
synced 2025-01-27 11:01:00 -08:00
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:
parent
c988952a07
commit
94679de34d
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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(),
|
||||
|
|
BIN
core/test/data/blackbox/datamatrix-1/abcd-18x8.png
Normal file
BIN
core/test/data/blackbox/datamatrix-1/abcd-18x8.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 540 B |
1
core/test/data/blackbox/datamatrix-1/abcd-18x8.txt
Normal file
1
core/test/data/blackbox/datamatrix-1/abcd-18x8.txt
Normal file
|
@ -0,0 +1 @@
|
|||
abcde
|
BIN
core/test/data/blackbox/datamatrix-1/abcd-26x12.png
Normal file
BIN
core/test/data/blackbox/datamatrix-1/abcd-26x12.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 857 B |
1
core/test/data/blackbox/datamatrix-1/abcd-26x12.txt
Normal file
1
core/test/data/blackbox/datamatrix-1/abcd-26x12.txt
Normal file
|
@ -0,0 +1 @@
|
|||
abcdefghijklm
|
BIN
core/test/data/blackbox/datamatrix-1/abcd-32x8.png
Normal file
BIN
core/test/data/blackbox/datamatrix-1/abcd-32x8.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 758 B |
1
core/test/data/blackbox/datamatrix-1/abcd-32x8.txt
Normal file
1
core/test/data/blackbox/datamatrix-1/abcd-32x8.txt
Normal file
|
@ -0,0 +1 @@
|
|||
abcdef
|
BIN
core/test/data/blackbox/datamatrix-1/abcd-36x12.png
Normal file
BIN
core/test/data/blackbox/datamatrix-1/abcd-36x12.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
1
core/test/data/blackbox/datamatrix-1/abcd-36x12.txt
Normal file
1
core/test/data/blackbox/datamatrix-1/abcd-36x12.txt
Normal file
|
@ -0,0 +1 @@
|
|||
abcdefghijklmnopq
|
BIN
core/test/data/blackbox/datamatrix-1/abcd-36x16.png
Normal file
BIN
core/test/data/blackbox/datamatrix-1/abcd-36x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
1
core/test/data/blackbox/datamatrix-1/abcd-36x16.txt
Normal file
1
core/test/data/blackbox/datamatrix-1/abcd-36x16.txt
Normal file
|
@ -0,0 +1 @@
|
|||
abcdefghijklmnopqrstuvwxyz
|
BIN
core/test/data/blackbox/datamatrix-1/abcd-48x16.png
Normal file
BIN
core/test/data/blackbox/datamatrix-1/abcd-48x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2 KiB |
1
core/test/data/blackbox/datamatrix-1/abcd-48x16.txt
Normal file
1
core/test/data/blackbox/datamatrix-1/abcd-48x16.txt
Normal file
|
@ -0,0 +1 @@
|
|||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue