From 0b005747ff042723b6103cd8d83026dec46b68de Mon Sep 17 00:00:00 2001 From: srowen Date: Tue, 1 May 2012 00:02:23 +0000 Subject: [PATCH] Update black box tests to reflect much better decoding with EC. Add support for correcting erasures plus tests. git-svn-id: https://zxing.googlecode.com/svn/trunk@2280 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- .../google/zxing/pdf417/decoder/Decoder.java | 10 ++-- .../pdf417/decoder/ec/ErrorCorrection.java | 28 +++++++-- .../zxing/pdf417/PDF417BlackBox2TestCase.java | 4 +- .../ec/AbstractErrorCorrectionTestCase.java | 17 ++++++ .../decoder/ec/ErrorCorrectionTestCase.java | 58 +++++++++++++++++-- 5 files changed, 102 insertions(+), 15 deletions(-) diff --git a/core/src/com/google/zxing/pdf417/decoder/Decoder.java b/core/src/com/google/zxing/pdf417/decoder/Decoder.java index db908c37d..45af4817c 100644 --- a/core/src/com/google/zxing/pdf417/decoder/Decoder.java +++ b/core/src/com/google/zxing/pdf417/decoder/Decoder.java @@ -78,7 +78,7 @@ public final class Decoder { int numECCodewords = 1 << (ecLevel + 1); int[] erasures = parser.getErasures(); - correctErrors(codewords, erasures.length, numECCodewords); + correctErrors(codewords, erasures, numECCodewords); verifyCodewordCount(codewords, numECCodewords); // Decode the codewords @@ -119,17 +119,19 @@ public final class Decoder { * correct the errors in-place.

* * @param codewords data and error correction codewords + * @param erasures positions of any known erasures + * @param numECCodewords number of error correction codewards that were available in codewords * @throws ChecksumException if error correction fails */ private void correctErrors(int[] codewords, - int numErasures, + int[] erasures, int numECCodewords) throws ChecksumException { - if (numErasures > numECCodewords / 2 + MAX_ERRORS || + if (erasures.length > numECCodewords / 2 + MAX_ERRORS || numECCodewords < 0 || numECCodewords > MAX_EC_CODEWORDS) { // Too many errors or EC Codewords is corrupted throw ChecksumException.getChecksumInstance(); } - errorCorrection.decode(codewords, numECCodewords); + errorCorrection.decode(codewords, numECCodewords, erasures); } } diff --git a/core/src/com/google/zxing/pdf417/decoder/ec/ErrorCorrection.java b/core/src/com/google/zxing/pdf417/decoder/ec/ErrorCorrection.java index 7898d4cf0..aa72c7a35 100644 --- a/core/src/com/google/zxing/pdf417/decoder/ec/ErrorCorrection.java +++ b/core/src/com/google/zxing/pdf417/decoder/ec/ErrorCorrection.java @@ -35,7 +35,10 @@ public final class ErrorCorrection { this.field = ModulusGF.PDF417_GF; } - public void decode(int[] received, int numECCodewords) throws ChecksumException { + public void decode(int[] received, + int numECCodewords, + int[] erasures) throws ChecksumException { + ModulusPoly poly = new ModulusPoly(field, received); int[] S = new int[numECCodewords]; boolean error = false; @@ -46,15 +49,30 @@ public final class ErrorCorrection { error = true; } } + if (error) { + + ModulusPoly knownErrors = field.getOne(); + for (int erasure : erasures) { + int b = field.exp(received.length - 1 - erasure); + // Add (1 - bx) term: + ModulusPoly term = new ModulusPoly(field, new int[] { field.subtract(0, b), 1 }); + knownErrors = knownErrors.multiply(term); + } + ModulusPoly syndrome = new ModulusPoly(field, S); - ErrorCorrection ec = new ErrorCorrection(); + syndrome = syndrome.multiply(knownErrors); + ModulusPoly[] sigmaOmega = - ec.runEuclideanAlgorithm(field.buildMonomial(numECCodewords, 1), syndrome, numECCodewords); + runEuclideanAlgorithm(field.buildMonomial(numECCodewords, 1), syndrome, numECCodewords); ModulusPoly sigma = sigmaOmega[0]; ModulusPoly omega = sigmaOmega[1]; - int[] errorLocations = ec.findErrorLocations(sigma); - int[] errorMagnitudes = ec.findErrorMagnitudes(omega, sigma, errorLocations); + + sigma = sigma.multiply(knownErrors); + + int[] errorLocations = findErrorLocations(sigma); + int[] errorMagnitudes = findErrorMagnitudes(omega, sigma, errorLocations); + for (int i = 0; i < errorLocations.length; i++) { int position = received.length - 1 - field.log(errorLocations[i]); if (position < 0) { diff --git a/core/test/src/com/google/zxing/pdf417/PDF417BlackBox2TestCase.java b/core/test/src/com/google/zxing/pdf417/PDF417BlackBox2TestCase.java index 18e222ef5..2b5ee2bb6 100644 --- a/core/test/src/com/google/zxing/pdf417/PDF417BlackBox2TestCase.java +++ b/core/test/src/com/google/zxing/pdf417/PDF417BlackBox2TestCase.java @@ -29,8 +29,8 @@ public final class PDF417BlackBox2TestCase extends AbstractBlackBoxTestCase { public PDF417BlackBox2TestCase() { super("test/data/blackbox/pdf417-2", new MultiFormatReader(), BarcodeFormat.PDF_417); - addTest(12, 12, 0, 0, 0.0f); - addTest(16, 16, 0, 0, 180.0f); + addTest(19, 19, 0, 0, 0.0f); + addTest(17, 17, 0, 0, 180.0f); } } diff --git a/core/test/src/com/google/zxing/pdf417/decoder/ec/AbstractErrorCorrectionTestCase.java b/core/test/src/com/google/zxing/pdf417/decoder/ec/AbstractErrorCorrectionTestCase.java index 10a7f94a9..ed7872db6 100644 --- a/core/test/src/com/google/zxing/pdf417/decoder/ec/AbstractErrorCorrectionTestCase.java +++ b/core/test/src/com/google/zxing/pdf417/decoder/ec/AbstractErrorCorrectionTestCase.java @@ -40,6 +40,23 @@ abstract class AbstractErrorCorrectionTestCase extends Assert { } } + static int[] erase(int[] received, int howMany, Random random) { + BitSet erased = new BitSet(received.length); + int[] erasures = new int[howMany]; + int erasureOffset = 0; + for (int j = 0; j < howMany; j++) { + int location = random.nextInt(received.length); + if (erased.get(location)) { + j--; + } else { + erased.set(location); + received[location] = 0; + erasures[erasureOffset++] = location; + } + } + return erasures; + } + static Random getRandom() { return new SecureRandom(new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF}); } diff --git a/core/test/src/com/google/zxing/pdf417/decoder/ec/ErrorCorrectionTestCase.java b/core/test/src/com/google/zxing/pdf417/decoder/ec/ErrorCorrectionTestCase.java index d89951c7f..54698d86d 100644 --- a/core/test/src/com/google/zxing/pdf417/decoder/ec/ErrorCorrectionTestCase.java +++ b/core/test/src/com/google/zxing/pdf417/decoder/ec/ErrorCorrectionTestCase.java @@ -32,7 +32,12 @@ public final class ErrorCorrectionTestCase extends AbstractErrorCorrectionTestCa private static final int[] PDF417_TEST_WITH_EC = { 5, 453, 178, 121, 239, 452, 327, 657, 619 }; private static final int ECC_BYTES = PDF417_TEST_WITH_EC.length - PDF417_TEST.length; - private static final int CORRECTABLE = ECC_BYTES / 2; + // Example is EC level 1 (s=1). The number of erasures (l) and substitutions (f) must obey: + // l + 2f <= 2^(s+1) - 3 + private static final int EC_LEVEL = 1; + private static final int ERROR_LIMIT = (1 << (EC_LEVEL + 1)) - 3; + private static final int MAX_ERRORS = ERROR_LIMIT / 2; + private static final int MAX_ERASURES = ERROR_LIMIT; private final ErrorCorrection ec = new ErrorCorrection(); @@ -58,7 +63,7 @@ public final class ErrorCorrectionTestCase extends AbstractErrorCorrectionTestCa Random random = getRandom(); for (int test : PDF417_TEST) { // # iterations is kind of arbitrary int[] received = PDF417_TEST_WITH_EC.clone(); - corrupt(received, CORRECTABLE, random); + corrupt(received, MAX_ERRORS, random); checkDecode(received); } } @@ -67,7 +72,7 @@ public final class ErrorCorrectionTestCase extends AbstractErrorCorrectionTestCa public void testTooManyErrors() { int[] received = PDF417_TEST_WITH_EC.clone(); Random random = getRandom(); - corrupt(received, CORRECTABLE + 1, random); + corrupt(received, MAX_ERRORS + 3, random); // +3 since the algo can actually correct 2 more than it should here try { checkDecode(received); fail("Should not have decoded"); @@ -76,8 +81,53 @@ public final class ErrorCorrectionTestCase extends AbstractErrorCorrectionTestCa } } + @Test + public void testMaxErasures() throws ChecksumException { + Random random = getRandom(); + for (int test : PDF417_TEST) { // # iterations is kind of arbitrary + int[] received = PDF417_TEST_WITH_EC.clone(); + int[] erasures = erase(received, MAX_ERASURES, random); + checkDecode(received, erasures); + } + } + + @Test + public void testTooManyErasures() { + Random random = getRandom(); + int[] received = PDF417_TEST_WITH_EC.clone(); + int[] erasures = erase(received, MAX_ERASURES + 1, random); + try { + checkDecode(received, erasures); + fail("Should not have decoded"); + } catch (ChecksumException ce) { + // good + } + } + + @Test + public void testErasureAndError() throws ChecksumException { + // Not sure this is valid according to the spec but it's correctable + Random random = getRandom(); + for (int i = 0; i < PDF417_TEST_WITH_EC.length; i++) { + int[] received = PDF417_TEST_WITH_EC.clone(); + received[i] = random.nextInt(256); + for (int j = 0; j < PDF417_TEST_WITH_EC.length; j++) { + if (i == j) { + continue; + } + received[j] = 0; + int[] erasures = {j}; + checkDecode(received, erasures); + } + } + } + private void checkDecode(int[] received) throws ChecksumException { - ec.decode(received, ECC_BYTES); + checkDecode(received, new int[0]); + } + + private void checkDecode(int[] received, int[] erasures) throws ChecksumException { + ec.decode(received, ECC_BYTES, erasures); for (int i = 0; i < PDF417_TEST.length; i++) { assertEquals(received[i], PDF417_TEST[i]); }