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
This commit is contained in:
srowen 2012-05-01 00:02:23 +00:00
parent a107d0c0a4
commit 0b005747ff
5 changed files with 102 additions and 15 deletions

View file

@ -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.</p>
*
* @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);
}
}

View file

@ -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) {

View file

@ -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);
}
}

View file

@ -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});
}

View file

@ -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]);
}