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
int formatInfoBits = 0;
int formatInfoBits1 = 0;
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 ...
formatInfoBits = copyBit(7, 8, formatInfoBits);
formatInfoBits = copyBit(8, 8, formatInfoBits);
formatInfoBits = copyBit(8, 7, formatInfoBits);
formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
// .. and skip a bit in the timing pattern ...
for (int j = 5; j >= 0; j--) {
formatInfoBits = copyBit(8, j, formatInfoBits);
formatInfoBits1 = copyBit(8, j, formatInfoBits1);
}
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
if (parsedFormatInfo != null) {
return parsedFormatInfo;
}
// Hmm, failed. Try the top-right/bottom-left pattern
// Read the top-right/bottom-left pattern too
int dimension = bitMatrix.getDimension();
formatInfoBits = 0;
int formatInfoBits2 = 0;
int iMin = dimension - 8;
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++) {
formatInfoBits = copyBit(8, j, formatInfoBits);
formatInfoBits2 = copyBit(8, j, formatInfoBits2);
}
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);
if (parsedFormatInfo != null) {
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>
* if doesn't seem to match any known pattern
*/
static FormatInformation decodeFormatInformation(int maskedFormatInfo) {
FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo);
static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
if (formatInfo != null) {
return formatInfo;
}
// Should return null, but, some QR codes apparently
// do not mask this info. Try again by actually masking the pattern
// 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
int bestDifference = Integer.MAX_VALUE;
int bestFormatInfo = 0;
for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
int targetInfo = decodeInfo[0];
if (targetInfo == maskedFormatInfo) {
if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
// Found an exact match
return new FormatInformation(decodeInfo[1]);
}
int bitsDifference = numBitsDiffering(maskedFormatInfo, targetInfo);
int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
if (bitsDifference < bestDifference) {
bestFormatInfo = decodeInfo[1];
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
// differing means we found a match

View file

@ -35,17 +35,34 @@ public final class FormatInformationTestCase extends TestCase {
public void testDecode() {
// 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(ErrorCorrectionLevel.Q, expected.getErrorCorrectionLevel());
assertSame(ErrorCorrectionLevel.Q, expected.getErrorCorrectionLevel());
// 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
assertEquals(expected, FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x01));
assertEquals(expected, FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x03));
assertEquals(expected, FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x07));
assertNull(FormatInformation.decodeFormatInformation(MASKED_TEST_FORMAT_INFO ^ 0x0F));
assertEquals(expected, FormatInformation.decodeFormatInformation(
MASKED_TEST_FORMAT_INFO ^ 0x01, MASKED_TEST_FORMAT_INFO ^ 0x01));
assertEquals(expected, FormatInformation.decodeFormatInformation(
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);
for (int x = 0; x < width; x++) {
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;