From feb768bba4b752ffe9ae48f6460675280517ec16 Mon Sep 17 00:00:00 2001 From: srowen Date: Mon, 1 Mar 2010 11:04:22 +0000 Subject: [PATCH] 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: http://zxing.googlecode.com/svn/trunk@1226 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- .../zxing/qrcode/decoder/BitMatrixParser.java | 27 +++++++--------- .../qrcode/decoder/FormatInformation.java | 25 ++++++++++----- .../decoder/FormatInformationTestCase.java | 31 ++++++++++++++----- .../client/j2se/MatrixToImageWriter.java | 2 +- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java b/core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java index c6f79b3d..2a820ec8 100644 --- a/core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java +++ b/core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java @@ -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; } diff --git a/core/src/com/google/zxing/qrcode/decoder/FormatInformation.java b/core/src/com/google/zxing/qrcode/decoder/FormatInformation.java index f7980ee7..1b76b0de 100644 --- a/core/src/com/google/zxing/qrcode/decoder/FormatInformation.java +++ b/core/src/com/google/zxing/qrcode/decoder/FormatInformation.java @@ -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 null * 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 diff --git a/core/test/src/com/google/zxing/qrcode/decoder/FormatInformationTestCase.java b/core/test/src/com/google/zxing/qrcode/decoder/FormatInformationTestCase.java index e737993e..6bf1ef7a 100644 --- a/core/test/src/com/google/zxing/qrcode/decoder/FormatInformationTestCase.java +++ b/core/test/src/com/google/zxing/qrcode/decoder/FormatInformationTestCase.java @@ -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)); } } \ No newline at end of file diff --git a/javase/src/com/google/zxing/client/j2se/MatrixToImageWriter.java b/javase/src/com/google/zxing/client/j2se/MatrixToImageWriter.java index 3c82a56d..4a65de11 100644 --- a/javase/src/com/google/zxing/client/j2se/MatrixToImageWriter.java +++ b/javase/src/com/google/zxing/client/j2se/MatrixToImageWriter.java @@ -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; -- 2.20.1