mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
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:
parent
a107d0c0a4
commit
0b005747ff
|
@ -78,7 +78,7 @@ public final class Decoder {
|
||||||
int numECCodewords = 1 << (ecLevel + 1);
|
int numECCodewords = 1 << (ecLevel + 1);
|
||||||
int[] erasures = parser.getErasures();
|
int[] erasures = parser.getErasures();
|
||||||
|
|
||||||
correctErrors(codewords, erasures.length, numECCodewords);
|
correctErrors(codewords, erasures, numECCodewords);
|
||||||
verifyCodewordCount(codewords, numECCodewords);
|
verifyCodewordCount(codewords, numECCodewords);
|
||||||
|
|
||||||
// Decode the codewords
|
// Decode the codewords
|
||||||
|
@ -119,17 +119,19 @@ public final class Decoder {
|
||||||
* correct the errors in-place.</p>
|
* correct the errors in-place.</p>
|
||||||
*
|
*
|
||||||
* @param codewords data and error correction codewords
|
* @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
|
* @throws ChecksumException if error correction fails
|
||||||
*/
|
*/
|
||||||
private void correctErrors(int[] codewords,
|
private void correctErrors(int[] codewords,
|
||||||
int numErasures,
|
int[] erasures,
|
||||||
int numECCodewords) throws ChecksumException {
|
int numECCodewords) throws ChecksumException {
|
||||||
if (numErasures > numECCodewords / 2 + MAX_ERRORS ||
|
if (erasures.length > numECCodewords / 2 + MAX_ERRORS ||
|
||||||
numECCodewords < 0 || numECCodewords > MAX_EC_CODEWORDS) {
|
numECCodewords < 0 || numECCodewords > MAX_EC_CODEWORDS) {
|
||||||
// Too many errors or EC Codewords is corrupted
|
// Too many errors or EC Codewords is corrupted
|
||||||
throw ChecksumException.getChecksumInstance();
|
throw ChecksumException.getChecksumInstance();
|
||||||
}
|
}
|
||||||
errorCorrection.decode(codewords, numECCodewords);
|
errorCorrection.decode(codewords, numECCodewords, erasures);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,10 @@ public final class ErrorCorrection {
|
||||||
this.field = ModulusGF.PDF417_GF;
|
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);
|
ModulusPoly poly = new ModulusPoly(field, received);
|
||||||
int[] S = new int[numECCodewords];
|
int[] S = new int[numECCodewords];
|
||||||
boolean error = false;
|
boolean error = false;
|
||||||
|
@ -46,15 +49,30 @@ public final class ErrorCorrection {
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
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);
|
ModulusPoly syndrome = new ModulusPoly(field, S);
|
||||||
ErrorCorrection ec = new ErrorCorrection();
|
syndrome = syndrome.multiply(knownErrors);
|
||||||
|
|
||||||
ModulusPoly[] sigmaOmega =
|
ModulusPoly[] sigmaOmega =
|
||||||
ec.runEuclideanAlgorithm(field.buildMonomial(numECCodewords, 1), syndrome, numECCodewords);
|
runEuclideanAlgorithm(field.buildMonomial(numECCodewords, 1), syndrome, numECCodewords);
|
||||||
ModulusPoly sigma = sigmaOmega[0];
|
ModulusPoly sigma = sigmaOmega[0];
|
||||||
ModulusPoly omega = sigmaOmega[1];
|
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++) {
|
for (int i = 0; i < errorLocations.length; i++) {
|
||||||
int position = received.length - 1 - field.log(errorLocations[i]);
|
int position = received.length - 1 - field.log(errorLocations[i]);
|
||||||
if (position < 0) {
|
if (position < 0) {
|
||||||
|
|
|
@ -29,8 +29,8 @@ public final class PDF417BlackBox2TestCase extends AbstractBlackBoxTestCase {
|
||||||
|
|
||||||
public PDF417BlackBox2TestCase() {
|
public PDF417BlackBox2TestCase() {
|
||||||
super("test/data/blackbox/pdf417-2", new MultiFormatReader(), BarcodeFormat.PDF_417);
|
super("test/data/blackbox/pdf417-2", new MultiFormatReader(), BarcodeFormat.PDF_417);
|
||||||
addTest(12, 12, 0, 0, 0.0f);
|
addTest(19, 19, 0, 0, 0.0f);
|
||||||
addTest(16, 16, 0, 0, 180.0f);
|
addTest(17, 17, 0, 0, 180.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
static Random getRandom() {
|
||||||
return new SecureRandom(new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF});
|
return new SecureRandom(new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF});
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,12 @@ public final class ErrorCorrectionTestCase extends AbstractErrorCorrectionTestCa
|
||||||
private static final int[] PDF417_TEST_WITH_EC =
|
private static final int[] PDF417_TEST_WITH_EC =
|
||||||
{ 5, 453, 178, 121, 239, 452, 327, 657, 619 };
|
{ 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 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();
|
private final ErrorCorrection ec = new ErrorCorrection();
|
||||||
|
|
||||||
|
@ -58,7 +63,7 @@ public final class ErrorCorrectionTestCase extends AbstractErrorCorrectionTestCa
|
||||||
Random random = getRandom();
|
Random random = getRandom();
|
||||||
for (int test : PDF417_TEST) { // # iterations is kind of arbitrary
|
for (int test : PDF417_TEST) { // # iterations is kind of arbitrary
|
||||||
int[] received = PDF417_TEST_WITH_EC.clone();
|
int[] received = PDF417_TEST_WITH_EC.clone();
|
||||||
corrupt(received, CORRECTABLE, random);
|
corrupt(received, MAX_ERRORS, random);
|
||||||
checkDecode(received);
|
checkDecode(received);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +72,7 @@ public final class ErrorCorrectionTestCase extends AbstractErrorCorrectionTestCa
|
||||||
public void testTooManyErrors() {
|
public void testTooManyErrors() {
|
||||||
int[] received = PDF417_TEST_WITH_EC.clone();
|
int[] received = PDF417_TEST_WITH_EC.clone();
|
||||||
Random random = getRandom();
|
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 {
|
try {
|
||||||
checkDecode(received);
|
checkDecode(received);
|
||||||
fail("Should not have decoded");
|
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 {
|
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++) {
|
for (int i = 0; i < PDF417_TEST.length; i++) {
|
||||||
assertEquals(received[i], PDF417_TEST[i]);
|
assertEquals(received[i], PDF417_TEST[i]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue