Related to Issue 205, but not the direct issue: read both copies of the format info and use both in determining the right one. This avoids a close, but incorrect first match throwing off the decoding. Also fix creating an image from ByteMatrix -- was inverted

git-svn-id: https://zxing.googlecode.com/svn/trunk@1226 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2010-03-01 11:04:22 +00:00
parent 5afa0fd861
commit 508697a318
4 changed files with 54 additions and 31 deletions

View file

@ -54,36 +54,31 @@ final class BitMatrixParser {
} }
// Read top-left format info bits // Read top-left format info bits
int formatInfoBits = 0; int formatInfoBits1 = 0;
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
formatInfoBits = copyBit(i, 8, formatInfoBits); formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
} }
// .. and skip a bit in the timing pattern ... // .. and skip a bit in the timing pattern ...
formatInfoBits = copyBit(7, 8, formatInfoBits); formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
formatInfoBits = copyBit(8, 8, formatInfoBits); formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
formatInfoBits = copyBit(8, 7, formatInfoBits); formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
// .. and skip a bit in the timing pattern ... // .. and skip a bit in the timing pattern ...
for (int j = 5; j >= 0; j--) { for (int j = 5; j >= 0; j--) {
formatInfoBits = copyBit(8, j, formatInfoBits); formatInfoBits1 = copyBit(8, j, formatInfoBits1);
} }
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits); // Read the top-right/bottom-left pattern too
if (parsedFormatInfo != null) {
return parsedFormatInfo;
}
// Hmm, failed. Try the top-right/bottom-left pattern
int dimension = bitMatrix.getDimension(); int dimension = bitMatrix.getDimension();
formatInfoBits = 0; int formatInfoBits2 = 0;
int iMin = dimension - 8; int iMin = dimension - 8;
for (int i = dimension - 1; i >= iMin; i--) { for (int i = dimension - 1; i >= iMin; i--) {
formatInfoBits = copyBit(i, 8, formatInfoBits); formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
} }
for (int j = dimension - 7; j < dimension; j++) { for (int j = dimension - 7; j < dimension; j++) {
formatInfoBits = copyBit(8, j, formatInfoBits); formatInfoBits2 = copyBit(8, j, formatInfoBits2);
} }
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits); parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);
if (parsedFormatInfo != null) { if (parsedFormatInfo != null) {
return parsedFormatInfo; return parsedFormatInfo;
} }

View file

@ -96,37 +96,48 @@ final class FormatInformation {
} }
/** /**
* @param maskedFormatInfo format info indicator, with mask still applied * @param maskedFormatInfo1 format info indicator, with mask still applied
* @param maskedFormatInfo2 second copy of same info; both are checked at the same time
* to establish best match
* @return information about the format it specifies, or <code>null</code> * @return information about the format it specifies, or <code>null</code>
* if doesn't seem to match any known pattern * if doesn't seem to match any known pattern
*/ */
static FormatInformation decodeFormatInformation(int maskedFormatInfo) { static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo); FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
if (formatInfo != null) { if (formatInfo != null) {
return formatInfo; return formatInfo;
} }
// Should return null, but, some QR codes apparently // Should return null, but, some QR codes apparently
// do not mask this info. Try again by actually masking the pattern // do not mask this info. Try again by actually masking the pattern
// first // first
return doDecodeFormatInformation(maskedFormatInfo ^ FORMAT_INFO_MASK_QR); return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
} }
private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo) { private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
int bestDifference = Integer.MAX_VALUE; int bestDifference = Integer.MAX_VALUE;
int bestFormatInfo = 0; int bestFormatInfo = 0;
for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) { for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i]; int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
int targetInfo = decodeInfo[0]; int targetInfo = decodeInfo[0];
if (targetInfo == maskedFormatInfo) { if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
// Found an exact match // Found an exact match
return new FormatInformation(decodeInfo[1]); return new FormatInformation(decodeInfo[1]);
} }
int bitsDifference = numBitsDiffering(maskedFormatInfo, targetInfo); int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
if (bitsDifference < bestDifference) { if (bitsDifference < bestDifference) {
bestFormatInfo = decodeInfo[1]; bestFormatInfo = decodeInfo[1];
bestDifference = bitsDifference; bestDifference = bitsDifference;
} }
if (maskedFormatInfo1 != maskedFormatInfo2) {
// also try the other option
bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
if (bitsDifference < bestDifference) {
bestFormatInfo = decodeInfo[1];
bestDifference = bitsDifference;
}
}
} }
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
// differing means we found a match // differing means we found a match

View file

@ -35,17 +35,34 @@ public final class FormatInformationTestCase extends TestCase {
public void testDecode() { public void testDecode() {
// Normal case // Normal case
FormatInformation expected = FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO); FormatInformation expected =
FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO);
assertEquals((byte) 0x07, expected.getDataMask()); assertEquals((byte) 0x07, expected.getDataMask());
assertEquals(ErrorCorrectionLevel.Q, expected.getErrorCorrectionLevel()); assertSame(ErrorCorrectionLevel.Q, expected.getErrorCorrectionLevel());
// where the code forgot the mask! // where the code forgot the mask!
assertEquals(expected, FormatInformation.decodeFormatInformation(UNMASKED_TEST_FORMAT_INFO)); assertEquals(expected,
FormatInformation.decodeFormatInformation(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO));
}
public void testDecodeWithBitDifference() {
FormatInformation expected =
FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO);
// 1,2,3,4 bits difference // 1,2,3,4 bits difference
assertEquals(expected, FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x01)); assertEquals(expected, FormatInformation.decodeFormatInformation(
assertEquals(expected, FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x03)); MASKED_TEST_FORMAT_INFO ^ 0x01, MASKED_TEST_FORMAT_INFO ^ 0x01));
assertEquals(expected, FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x07)); assertEquals(expected, FormatInformation.decodeFormatInformation(
assertNull(FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x0F)); MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO ^ 0x03));
assertEquals(expected, FormatInformation.decodeFormatInformation(
MASKED_TEST_FORMAT_INFO ^ 0x07, MASKED_TEST_FORMAT_INFO ^ 0x07));
assertNull(FormatInformation.decodeFormatInformation(
MASKED_TEST_FORMAT_INFO ^ 0x0F, MASKED_TEST_FORMAT_INFO ^ 0x0F));
}
public void testDecodeWithMisread() {
FormatInformation expected =
FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO);
assertEquals(expected, FormatInformation.decodeFormatInformation(
MASKED_TEST_FORMAT_INFO ^ 0x03, MASKED_TEST_FORMAT_INFO ^ 0x0F));
} }
} }

View file

@ -68,7 +68,7 @@ public final class MatrixToImageWriter {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
image.setRGB(x, y, matrix.get(x, y) == 0 ? BLACK : WHITE); image.setRGB(x, y, matrix.get(x, y) == 0 ? WHITE : BLACK);
} }
} }
return image; return image;