Preliminary RSS-14 support. Not enabled yet in Android client.
git-svn-id: https://zxing.googlecode.com/svn/trunk@1233 59b500cc-1b3d-0410-9834-0bbf25fbcc57
|
@ -107,7 +107,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
PRODUCT_FORMATS.add(BarcodeFormat.UPC_E);
|
||||
PRODUCT_FORMATS.add(BarcodeFormat.EAN_13);
|
||||
PRODUCT_FORMATS.add(BarcodeFormat.EAN_8);
|
||||
PRODUCT_FORMATS.add(BarcodeFormat.RSS14);
|
||||
//PRODUCT_FORMATS.add(BarcodeFormat.RSS14);
|
||||
ONE_D_FORMATS = new Vector<BarcodeFormat>(PRODUCT_FORMATS.size() + 3);
|
||||
ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
|
||||
ONE_D_FORMATS.add(BarcodeFormat.CODE_39);
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.google.zxing.Reader;
|
|||
import com.google.zxing.ReaderException;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.oned.rss.RSS14Reader;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
@ -58,7 +59,7 @@ public final class MultiFormatOneDReader extends OneDReader {
|
|||
readers.addElement(new ITFReader());
|
||||
}
|
||||
if (possibleFormats.contains(BarcodeFormat.RSS14)) {
|
||||
// TODO enable later readers.addElement(new RSS14Reader());
|
||||
readers.addElement(new RSS14Reader());
|
||||
}
|
||||
}
|
||||
if (readers.isEmpty()) {
|
||||
|
@ -66,7 +67,7 @@ public final class MultiFormatOneDReader extends OneDReader {
|
|||
readers.addElement(new Code39Reader());
|
||||
readers.addElement(new Code128Reader());
|
||||
readers.addElement(new ITFReader());
|
||||
// TODO enable later readers.addElement(new RSS14Reader());
|
||||
readers.addElement(new RSS14Reader());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
37
core/src/com/google/zxing/oned/rss/DataCharacter.java
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.oned.rss;
|
||||
|
||||
class DataCharacter {
|
||||
|
||||
private final int value;
|
||||
private final int checksumPortion;
|
||||
|
||||
DataCharacter(int value, int checksumPortion) {
|
||||
this.value = value;
|
||||
this.checksumPortion = checksumPortion;
|
||||
}
|
||||
|
||||
int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
int getChecksumPortion() {
|
||||
return checksumPortion;
|
||||
}
|
||||
|
||||
}
|
48
core/src/com/google/zxing/oned/rss/FinderPattern.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.oned.rss;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
|
||||
final class FinderPattern {
|
||||
|
||||
private final int value;
|
||||
private final int[] startEnd;
|
||||
private final ResultPoint[] resultPoints;
|
||||
|
||||
FinderPattern(int value, int[] startEnd, int start, int end, int rowNumber) {
|
||||
this.value = value;
|
||||
this.startEnd = startEnd;
|
||||
this.resultPoints = new ResultPoint[] {
|
||||
new ResultPoint((float) start, (float) rowNumber),
|
||||
new ResultPoint((float) end, (float) rowNumber),
|
||||
};
|
||||
}
|
||||
|
||||
int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
int[] getStartEnd() {
|
||||
return startEnd;
|
||||
}
|
||||
|
||||
ResultPoint[] getResultPoints() {
|
||||
return resultPoints;
|
||||
}
|
||||
|
||||
}
|
32
core/src/com/google/zxing/oned/rss/Pair.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.oned.rss;
|
||||
|
||||
final class Pair extends DataCharacter {
|
||||
|
||||
private final FinderPattern finderPattern;
|
||||
|
||||
Pair(int value, int checksumPortion, FinderPattern finderPattern) {
|
||||
super(value, checksumPortion);
|
||||
this.finderPattern = finderPattern;
|
||||
}
|
||||
|
||||
FinderPattern getFinderPattern() {
|
||||
return finderPattern;
|
||||
}
|
||||
|
||||
}
|
557
core/src/com/google/zxing/oned/rss/RSS14Reader.java
Normal file
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.oned.rss;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.ResultPointCallback;
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.oned.OneDReader;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Decodes RSS-14, including truncated and stacked variants. See ISO/IEC 24724:2006.
|
||||
*/
|
||||
public final class RSS14Reader extends OneDReader {
|
||||
|
||||
private static final int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.2f);
|
||||
private static final int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.4f);
|
||||
|
||||
private static final float MIN_FINDER_PATTERN_RATIO = 9.5f / 12.0f;
|
||||
private static final float MAX_FINDER_PATTERN_RATIO = 12.5f / 14.0f;
|
||||
|
||||
private static final int[] OUTSIDE_EVEN_TOTAL_SUBSET = {1,10,34,70,126};
|
||||
private static final int[] INSIDE_ODD_TOTAL_SUBSET = {4,20,48,81};
|
||||
private static final int[] OUTSIDE_GSUM = {0,161,961,2015,2715};
|
||||
private static final int[] INSIDE_GSUM = {0,336,1036,1516};
|
||||
private static final int[] OUTSIDE_ODD_WIDEST = {8,6,4,3,1};
|
||||
private static final int[] INSIDE_ODD_WIDEST = {2,4,6,8};
|
||||
|
||||
private static final int[][] FINDER_PATTERNS = {
|
||||
{3,8,2,1},
|
||||
{3,5,5,1},
|
||||
{3,3,7,1},
|
||||
{3,1,9,1},
|
||||
{2,7,4,1},
|
||||
{2,5,6,1},
|
||||
{2,3,8,1},
|
||||
{1,5,7,1},
|
||||
{1,3,9,1},
|
||||
};
|
||||
|
||||
private final int[] decodeFinderCounters;
|
||||
private final int[] dataCharacterCounters;
|
||||
private final float[] oddRoundingErrors;
|
||||
private final float[] evenRoundingErrors;
|
||||
private final int[] oddCounts;
|
||||
private final int[] evenCounts;
|
||||
private final Vector possibleLeftPairs;
|
||||
private final Vector possibleRightPairs;
|
||||
|
||||
public RSS14Reader() {
|
||||
decodeFinderCounters = new int[4];
|
||||
dataCharacterCounters = new int[8];
|
||||
oddRoundingErrors = new float[4];
|
||||
evenRoundingErrors = new float[4];
|
||||
oddCounts = new int[dataCharacterCounters.length / 2];
|
||||
evenCounts = new int[dataCharacterCounters.length / 2];
|
||||
possibleLeftPairs = new Vector();
|
||||
possibleRightPairs = new Vector();
|
||||
}
|
||||
|
||||
public Result decodeRow(int rowNumber, BitArray row, Hashtable hints) throws NotFoundException {
|
||||
Pair leftPair = decodePair(row, false, rowNumber, hints);
|
||||
if (leftPair != null) {
|
||||
possibleLeftPairs.add(leftPair);
|
||||
}
|
||||
row.reverse();
|
||||
Pair rightPair = decodePair(row, true, rowNumber, hints);
|
||||
if (rightPair != null) {
|
||||
possibleRightPairs.add(rightPair);
|
||||
}
|
||||
row.reverse();
|
||||
int numLeftPairs = possibleLeftPairs.size();
|
||||
int numRightPairs = possibleRightPairs.size();
|
||||
for (int l = 0; l < numLeftPairs; l++) {
|
||||
Pair left = (Pair) possibleLeftPairs.elementAt(l);
|
||||
for (int r = 0; r < numRightPairs; r++) {
|
||||
Pair right = (Pair) possibleRightPairs.elementAt(r);
|
||||
if (checkChecksum(left, right)) {
|
||||
return constructResult(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
possibleLeftPairs.clear();
|
||||
possibleRightPairs.clear();
|
||||
}
|
||||
|
||||
private static Result constructResult(Pair leftPair, Pair rightPair) {
|
||||
long symbolValue = 4537077L * leftPair.getValue() + rightPair.getValue();
|
||||
String text = String.valueOf(symbolValue);
|
||||
|
||||
StringBuffer buffer = new StringBuffer(14);
|
||||
for (int i = 13 - text.length(); i > 0; i--) {
|
||||
buffer.append('0');
|
||||
}
|
||||
buffer.append(text);
|
||||
|
||||
int checkDigit = 0;
|
||||
for (int i = 0; i < 13; i++) {
|
||||
int digit = buffer.charAt(i) - '0';
|
||||
checkDigit += (((i & 0x01) == 0) ? 3 * digit : digit);
|
||||
}
|
||||
checkDigit = 10 - (checkDigit % 10);
|
||||
if (checkDigit == 10) {
|
||||
checkDigit = 0;
|
||||
}
|
||||
buffer.append(checkDigit);
|
||||
|
||||
ResultPoint[] leftPoints = leftPair.getFinderPattern().getResultPoints();
|
||||
ResultPoint[] rightPoints = rightPair.getFinderPattern().getResultPoints();
|
||||
return new Result(
|
||||
String.valueOf(buffer.toString()),
|
||||
null,
|
||||
new ResultPoint[] { leftPoints[0], leftPoints[1], rightPoints[0], rightPoints[1], },
|
||||
BarcodeFormat.RSS14);
|
||||
}
|
||||
|
||||
private static boolean checkChecksum(Pair leftPair, Pair rightPair) {
|
||||
int leftFPValue = leftPair.getFinderPattern().getValue();
|
||||
int rightFPValue = rightPair.getFinderPattern().getValue();
|
||||
if ((leftFPValue == 0 && rightFPValue == 8) ||
|
||||
(leftFPValue == 8 && rightFPValue == 0)) {
|
||||
}
|
||||
int checkValue = (leftPair.getChecksumPortion() + 16 * rightPair.getChecksumPortion()) % 79;
|
||||
int targetCheckValue =
|
||||
9 * leftPair.getFinderPattern().getValue() + rightPair.getFinderPattern().getValue();
|
||||
if (targetCheckValue > 72) {
|
||||
targetCheckValue--;
|
||||
}
|
||||
if (targetCheckValue > 8) {
|
||||
targetCheckValue--;
|
||||
}
|
||||
return checkValue == targetCheckValue;
|
||||
}
|
||||
|
||||
private Pair decodePair(BitArray row, boolean right, int rowNumber, Hashtable hints) {
|
||||
try {
|
||||
int[] startEnd = findFinderPattern(row, 0, right);
|
||||
FinderPattern pattern = parseFoundFinderPattern(row, rowNumber, right, startEnd);
|
||||
|
||||
ResultPointCallback resultPointCallback = hints == null ? null :
|
||||
(ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
|
||||
|
||||
if (resultPointCallback != null) {
|
||||
float center = (startEnd[0] + startEnd[1]) / 2.0f;
|
||||
if (right) {
|
||||
// row is actually reversed
|
||||
center = row.getSize() - 1 - center;
|
||||
}
|
||||
resultPointCallback.foundPossibleResultPoint(new ResultPoint(center, rowNumber));
|
||||
}
|
||||
|
||||
DataCharacter outside = decodeDataCharacter(row, pattern, true);
|
||||
DataCharacter inside = decodeDataCharacter(row, pattern, false);
|
||||
return new Pair(1597 * outside.getValue() + inside.getValue(),
|
||||
outside.getChecksumPortion() + 4 * inside.getChecksumPortion(),
|
||||
pattern);
|
||||
} catch (NotFoundException re) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private DataCharacter decodeDataCharacter(BitArray row, FinderPattern pattern, boolean outsideChar)
|
||||
throws NotFoundException {
|
||||
|
||||
int[] counters = dataCharacterCounters;
|
||||
counters[0] = 0;
|
||||
counters[1] = 0;
|
||||
counters[2] = 0;
|
||||
counters[3] = 0;
|
||||
counters[4] = 0;
|
||||
counters[5] = 0;
|
||||
counters[6] = 0;
|
||||
counters[7] = 0;
|
||||
|
||||
if (outsideChar) {
|
||||
recordPatternInReverse(row, pattern.getStartEnd()[0], counters);
|
||||
} else {
|
||||
recordPattern(row, pattern.getStartEnd()[1] + 1, counters);
|
||||
// reverse it
|
||||
for (int i = 0, j = counters.length - 1; i < j; i++, j--) {
|
||||
int temp = counters[i];
|
||||
counters[i] = counters[j];
|
||||
counters[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
int numModules = outsideChar ? 16 : 15;
|
||||
float elementWidth = (float) count(counters) / (float) numModules;
|
||||
|
||||
int[] oddCounts = this.oddCounts;
|
||||
int[] evenCounts = this.evenCounts;
|
||||
float[] oddRoundingErrors = this.oddRoundingErrors;
|
||||
float[] evenRoundingErrors = this.evenRoundingErrors;
|
||||
|
||||
for (int i = 0; i < counters.length; i++) {
|
||||
float value = (float) counters[i] / elementWidth;
|
||||
int count = (int) (value + 0.5f); // Round
|
||||
if (count < 1) {
|
||||
count = 1;
|
||||
} else if (count > 8) {
|
||||
count = 8;
|
||||
}
|
||||
int offset = i >> 1;
|
||||
if ((i & 0x01) == 0) {
|
||||
oddCounts[offset] = count;
|
||||
oddRoundingErrors[offset] = value - count;
|
||||
} else {
|
||||
evenCounts[offset] = count;
|
||||
evenRoundingErrors[offset] = value - count;
|
||||
}
|
||||
}
|
||||
|
||||
adjustOddEvenCounts(outsideChar, numModules);
|
||||
|
||||
int oddSum = 0;
|
||||
int oddChecksumPortion = 0;
|
||||
for (int i = oddCounts.length - 1; i >= 0; i--) {
|
||||
oddChecksumPortion *= 9;
|
||||
oddChecksumPortion += oddCounts[i];
|
||||
oddSum += oddCounts[i];
|
||||
}
|
||||
int evenChecksumPortion = 0;
|
||||
int evenSum = 0;
|
||||
for (int i = evenCounts.length - 1; i >= 0; i--) {
|
||||
evenChecksumPortion *= 9;
|
||||
evenChecksumPortion += evenCounts[i];
|
||||
evenSum += evenCounts[i];
|
||||
}
|
||||
int checksumPortion = oddChecksumPortion + 3*evenChecksumPortion;
|
||||
|
||||
if (outsideChar) {
|
||||
if ((oddSum & 0x01) != 0 || oddSum > 12 || oddSum < 4) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
int group = (12 - oddSum) / 2;
|
||||
int oddWidest = OUTSIDE_ODD_WIDEST[group];
|
||||
int evenWidest = 9 - oddWidest;
|
||||
int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, false);
|
||||
int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, true);
|
||||
int tEven = OUTSIDE_EVEN_TOTAL_SUBSET[group];
|
||||
int gSum = OUTSIDE_GSUM[group];
|
||||
return new DataCharacter(vOdd * tEven + vEven + gSum, checksumPortion);
|
||||
} else {
|
||||
if ((evenSum & 0x01) != 0 || evenSum > 10 || evenSum < 4) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
int group = (10 - evenSum) / 2;
|
||||
int oddWidest = INSIDE_ODD_WIDEST[group];
|
||||
int evenWidest = 9 - oddWidest;
|
||||
int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, true);
|
||||
int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, false);
|
||||
int tOdd = INSIDE_ODD_TOTAL_SUBSET[group];
|
||||
int gSum = INSIDE_GSUM[group];
|
||||
return new DataCharacter(vEven * tOdd + vOdd + gSum, checksumPortion);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int[] findFinderPattern(BitArray row, int rowOffset, boolean rightFinderPattern)
|
||||
throws NotFoundException {
|
||||
|
||||
int[] counters = decodeFinderCounters;
|
||||
counters[0] = 0;
|
||||
counters[1] = 0;
|
||||
counters[2] = 0;
|
||||
counters[3] = 0;
|
||||
|
||||
int width = row.getSize();
|
||||
boolean isWhite = false;
|
||||
while (rowOffset < width) {
|
||||
isWhite = !row.get(rowOffset);
|
||||
if (rightFinderPattern == isWhite) {
|
||||
// Will encounter white first when searching for right finder pattern
|
||||
break;
|
||||
}
|
||||
rowOffset++;
|
||||
}
|
||||
|
||||
int counterPosition = 0;
|
||||
int patternStart = rowOffset;
|
||||
for (int x = rowOffset; x < width; x++) {
|
||||
boolean pixel = row.get(x);
|
||||
if (pixel ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == 3) {
|
||||
if (isFinderPattern(counters)) {
|
||||
return new int[]{patternStart, x};
|
||||
}
|
||||
patternStart += counters[0] + counters[1];
|
||||
counters[0] = counters[2];
|
||||
counters[1] = counters[3];
|
||||
counters[2] = 0;
|
||||
counters[3] = 0;
|
||||
counterPosition--;
|
||||
} else {
|
||||
counterPosition++;
|
||||
}
|
||||
counters[counterPosition] = 1;
|
||||
isWhite = !isWhite;
|
||||
}
|
||||
}
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
|
||||
}
|
||||
|
||||
private static boolean isFinderPattern(int[] counters) {
|
||||
int firstTwoSum = counters[0] + counters[1];
|
||||
int sum = firstTwoSum + counters[2] + counters[3];
|
||||
float ratio = (float) firstTwoSum / (float) sum;
|
||||
if (ratio >= MIN_FINDER_PATTERN_RATIO && ratio <= MAX_FINDER_PATTERN_RATIO) {
|
||||
// passes ratio test in spec, but see if the counts are unreasonable
|
||||
int minCounter = Integer.MAX_VALUE;
|
||||
int maxCounter = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < counters.length; i++) {
|
||||
int counter = counters[i];
|
||||
if (counter > maxCounter) {
|
||||
maxCounter = counter;
|
||||
}
|
||||
if (counter < minCounter) {
|
||||
minCounter = counter;
|
||||
}
|
||||
}
|
||||
return maxCounter < 10 * minCounter;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private FinderPattern parseFoundFinderPattern(BitArray row, int rowNumber, boolean right, int[] startEnd)
|
||||
throws NotFoundException {
|
||||
// Actually we found elements 2-5
|
||||
boolean firstIsBlack = row.get(startEnd[0]);
|
||||
int firstElementStart = startEnd[0] - 1;
|
||||
// Locate element 1
|
||||
while (firstElementStart >= 0 && firstIsBlack ^ row.get(firstElementStart)) {
|
||||
firstElementStart--;
|
||||
}
|
||||
firstElementStart++;
|
||||
int firstCounter = startEnd[0] - firstElementStart;
|
||||
// Make 'counters' hold 1-4
|
||||
int[] counters = decodeFinderCounters;
|
||||
for (int i = counters.length - 1; i > 0; i--) {
|
||||
counters[i] = counters[i-1];
|
||||
}
|
||||
counters[0] = firstCounter;
|
||||
int value = parseFinderValue(counters);
|
||||
int start = firstElementStart;
|
||||
int end = startEnd[1];
|
||||
if (right) {
|
||||
// row is actually reversed
|
||||
start = row.getSize() - 1 - start;
|
||||
end = row.getSize() - 1 - end;
|
||||
}
|
||||
return new FinderPattern(value, new int[] {firstElementStart, startEnd[1]}, start, end, rowNumber);
|
||||
}
|
||||
|
||||
private static int parseFinderValue(int[] counters) throws NotFoundException {
|
||||
for (int value = 0; value < FINDER_PATTERNS.length; value++) {
|
||||
if (patternMatchVariance(counters, FINDER_PATTERNS[value], MAX_INDIVIDUAL_VARIANCE) <
|
||||
MAX_AVG_VARIANCE) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
/*
|
||||
private static int[] normalizeE2SEValues(int[] counters) {
|
||||
int p = 0;
|
||||
for (int i = 0; i < counters.length; i++) {
|
||||
p += counters[i];
|
||||
}
|
||||
int[] normalized = new int[counters.length - 2];
|
||||
for (int i = 0; i < normalized.length; i++) {
|
||||
int e = counters[i] + counters[i+1];
|
||||
float eRatio = (float) e / (float) p;
|
||||
float E = ((eRatio * 32.0f) + 1.0f) / 2.0f;
|
||||
normalized[i] = (int) E;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
*/
|
||||
|
||||
private static int count(int[] array) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
count += array[i];
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void increment(int[] array, float[] errors) {
|
||||
int index = 0;
|
||||
float biggestError = errors[0];
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
if (errors[i] > biggestError) {
|
||||
biggestError = errors[i];
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
array[index]++;
|
||||
}
|
||||
|
||||
private static void decrement(int[] array, float[] errors) {
|
||||
int index = 0;
|
||||
float biggestError = errors[0];
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
if (errors[i] < biggestError) {
|
||||
biggestError = errors[i];
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
array[index]--;
|
||||
}
|
||||
|
||||
private void adjustOddEvenCounts(boolean outsideChar, int numModules) throws NotFoundException {
|
||||
|
||||
int oddSum = count(oddCounts);
|
||||
int evenSum = count(evenCounts);
|
||||
int mismatch = oddSum + evenSum - numModules;
|
||||
boolean oddParityBad = (oddSum & 0x01) == (outsideChar ? 1 : 0);
|
||||
boolean evenParityBad = (evenSum & 0x01) == 1;
|
||||
|
||||
boolean incrementOdd = false;
|
||||
boolean decrementOdd = false;
|
||||
boolean incrementEven = false;
|
||||
boolean decrementEven = false;
|
||||
|
||||
if (outsideChar) {
|
||||
if (oddSum > 12) {
|
||||
decrementOdd = true;
|
||||
} else if (oddSum < 4) {
|
||||
incrementOdd = true;
|
||||
}
|
||||
if (evenSum > 12) {
|
||||
decrementEven = true;
|
||||
} else if (evenSum < 4) {
|
||||
incrementEven = true;
|
||||
}
|
||||
} else {
|
||||
if (oddSum > 11) {
|
||||
decrementOdd = true;
|
||||
} else if (oddSum < 5) {
|
||||
incrementOdd = true;
|
||||
}
|
||||
if (evenSum > 10) {
|
||||
decrementEven = true;
|
||||
} else if (evenSum < 4) {
|
||||
incrementEven = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*if (mismatch == 2) {
|
||||
if (!(oddParityBad && evenParityBad)) {
|
||||
throw ReaderException.getInstance();
|
||||
}
|
||||
decrementOdd = true;
|
||||
decrementEven = true;
|
||||
} else if (mismatch == -2) {
|
||||
if (!(oddParityBad && evenParityBad)) {
|
||||
throw ReaderException.getInstance();
|
||||
}
|
||||
incrementOdd = true;
|
||||
incrementEven = true;
|
||||
} else */if (mismatch == 1) {
|
||||
if (oddParityBad) {
|
||||
if (evenParityBad) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
decrementOdd = true;
|
||||
} else {
|
||||
if (!evenParityBad) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
decrementEven = true;
|
||||
}
|
||||
} else if (mismatch == -1) {
|
||||
if (oddParityBad) {
|
||||
if (evenParityBad) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
incrementOdd = true;
|
||||
} else {
|
||||
if (!evenParityBad) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
incrementEven = true;
|
||||
}
|
||||
} else if (mismatch == 0) {
|
||||
if (oddParityBad) {
|
||||
if (!evenParityBad) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
// Both bad
|
||||
if (oddSum < evenSum) {
|
||||
incrementOdd = true;
|
||||
decrementEven = true;
|
||||
} else {
|
||||
decrementOdd = true;
|
||||
incrementEven = true;
|
||||
}
|
||||
} else {
|
||||
if (evenParityBad) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
// Nothing to do!
|
||||
}
|
||||
} else {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
if (incrementOdd) {
|
||||
if (decrementOdd) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
increment(oddCounts, oddRoundingErrors);
|
||||
}
|
||||
if (decrementOdd) {
|
||||
decrement(oddCounts, oddRoundingErrors);
|
||||
}
|
||||
if (incrementEven) {
|
||||
if (decrementEven) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
increment(evenCounts, oddRoundingErrors);
|
||||
}
|
||||
if (decrementEven) {
|
||||
decrement(evenCounts, evenRoundingErrors);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
155
core/src/com/google/zxing/oned/rss/RSSUtils.java
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.oned.rss;
|
||||
|
||||
/** Adapted from listings in ISO/IEC 24724 Appendix B and Appendix G. */
|
||||
public final class RSSUtils {
|
||||
|
||||
private RSSUtils() {}
|
||||
|
||||
static int[] getRSSwidths(int val, int n, int elements, int maxWidth, boolean noNarrow) {
|
||||
int[] widths = new int[elements];
|
||||
int bar;
|
||||
int narrowMask = 0;
|
||||
for (bar = 0; bar < elements - 1; bar++) {
|
||||
narrowMask |= (1 << bar);
|
||||
int elmWidth = 1;
|
||||
int subVal;
|
||||
while (true) {
|
||||
subVal = combins(n - elmWidth - 1, elements - bar - 2);
|
||||
if (noNarrow && (narrowMask == 0) &&
|
||||
(n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
|
||||
subVal -= combins(n - elmWidth - (elements - bar), elements - bar - 2);
|
||||
}
|
||||
if (elements - bar - 1 > 1) {
|
||||
int lessVal = 0;
|
||||
for (int mxwElement = n - elmWidth - (elements - bar - 2);
|
||||
mxwElement > maxWidth;
|
||||
mxwElement--) {
|
||||
lessVal += combins(n - elmWidth - mxwElement - 1, elements - bar - 3);
|
||||
}
|
||||
subVal -= lessVal * (elements - 1 - bar);
|
||||
} else if (n - elmWidth > maxWidth) {
|
||||
subVal--;
|
||||
}
|
||||
val -= subVal;
|
||||
if (val < 0) {
|
||||
break;
|
||||
}
|
||||
elmWidth++;
|
||||
narrowMask &= ~(1 << bar);
|
||||
}
|
||||
val += subVal;
|
||||
n -= elmWidth;
|
||||
widths[bar] = elmWidth;
|
||||
}
|
||||
widths[bar] = n;
|
||||
return widths;
|
||||
}
|
||||
|
||||
static int getRSSvalue(int[] widths, int maxWidth, boolean noNarrow) {
|
||||
int elements = widths.length;
|
||||
int n = 0;
|
||||
for (int i = 0; i < elements; i++) {
|
||||
n += widths[i];
|
||||
}
|
||||
int val = 0;
|
||||
int narrowMask = 0;
|
||||
for (int bar = 0; bar < elements - 1; bar++) {
|
||||
int elmWidth;
|
||||
for (elmWidth = 1, narrowMask |= (1 << bar);
|
||||
elmWidth < widths[bar];
|
||||
elmWidth++, narrowMask &= ~(1 << bar)) {
|
||||
int subVal = combins(n - elmWidth - 1, elements - bar - 2);
|
||||
if (noNarrow && (narrowMask == 0) &&
|
||||
(n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
|
||||
subVal -= combins(n - elmWidth - (elements - bar),
|
||||
elements - bar - 2);
|
||||
}
|
||||
if (elements - bar - 1 > 1) {
|
||||
int lessVal = 0;
|
||||
for (int mxwElement = n - elmWidth - (elements - bar - 2);
|
||||
mxwElement > maxWidth; mxwElement--) {
|
||||
lessVal += combins(n - elmWidth - mxwElement - 1,
|
||||
elements - bar - 3);
|
||||
}
|
||||
subVal -= lessVal * (elements - 1 - bar);
|
||||
} else if (n - elmWidth > maxWidth) {
|
||||
subVal--;
|
||||
}
|
||||
val += subVal;
|
||||
}
|
||||
n -= elmWidth;
|
||||
}
|
||||
return (val);
|
||||
}
|
||||
|
||||
static int combins(int n, int r) {
|
||||
int maxDenom, minDenom;
|
||||
if (n - r > r) {
|
||||
minDenom = r;
|
||||
maxDenom = n - r;
|
||||
} else {
|
||||
minDenom = n - r;
|
||||
maxDenom = r;
|
||||
}
|
||||
int val = 1;
|
||||
int j = 1;
|
||||
for (int i = n; i > maxDenom; i--) {
|
||||
val *= i;
|
||||
if (j <= minDenom) {
|
||||
val /= j;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
while (j <= minDenom) {
|
||||
val /= j;
|
||||
j++;
|
||||
}
|
||||
return (val);
|
||||
}
|
||||
|
||||
static int[] elements(int[] eDist, int N, int K) {
|
||||
int[] widths = new int[eDist.length + 2];
|
||||
int twoK = K << 1;
|
||||
widths[0] = 1;
|
||||
int i;
|
||||
int minEven = 10;
|
||||
int barSum = 1;
|
||||
for (i = 1; i < twoK - 2; i += 2) {
|
||||
widths[i] = eDist[i - 1] - widths[i - 1];
|
||||
widths[i + 1] = eDist[i] - widths[i];
|
||||
barSum += widths[i] + widths[i + 1];
|
||||
if (widths[i] < minEven) {
|
||||
minEven = widths[i];
|
||||
}
|
||||
}
|
||||
widths[twoK - 1] = N - barSum;
|
||||
if (widths[twoK - 1] < minEven) {
|
||||
minEven = widths[twoK - 1];
|
||||
}
|
||||
if (minEven > 1) {
|
||||
for (i = 0; i < twoK; i += 2) {
|
||||
widths[i] += minEven - 1;
|
||||
widths[i + 1] -= minEven - 1;
|
||||
}
|
||||
}
|
||||
return widths;
|
||||
}
|
||||
|
||||
|
||||
}
|
BIN
core/test/data/blackbox/rss14-1/1.png
Normal file
After Width: | Height: | Size: 17 KiB |
1
core/test/data/blackbox/rss14-1/1.txt
Normal file
|
@ -0,0 +1 @@
|
|||
04412345678909
|
BIN
core/test/data/blackbox/rss14-1/2.gif
Normal file
After Width: | Height: | Size: 8.6 KiB |
1
core/test/data/blackbox/rss14-1/2.txt
Normal file
|
@ -0,0 +1 @@
|
|||
00821935106427
|
BIN
core/test/data/blackbox/rss14-1/3.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
1
core/test/data/blackbox/rss14-1/3.txt
Normal file
|
@ -0,0 +1 @@
|
|||
00075678164125
|
BIN
core/test/data/blackbox/rss14-1/4.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
1
core/test/data/blackbox/rss14-1/4.txt
Normal file
|
@ -0,0 +1 @@
|
|||
20012345678909
|
BIN
core/test/data/blackbox/rss14-1/5.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
1
core/test/data/blackbox/rss14-1/5.txt
Normal file
|
@ -0,0 +1 @@
|
|||
00034567890125
|
BIN
core/test/data/blackbox/rss14-1/6.jpg
Normal file
After Width: | Height: | Size: 26 KiB |
1
core/test/data/blackbox/rss14-1/6.txt
Normal file
|
@ -0,0 +1 @@
|
|||
00012345678905
|
BIN
core/test/data/blackbox/rss14-2/1.png
Executable file
After Width: | Height: | Size: 68 KiB |
1
core/test/data/blackbox/rss14-2/1.txt
Normal file
|
@ -0,0 +1 @@
|
|||
04412345678909
|
BIN
core/test/data/blackbox/rss14-2/10.png
Executable file
After Width: | Height: | Size: 41 KiB |
1
core/test/data/blackbox/rss14-2/10.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/11.png
Executable file
After Width: | Height: | Size: 44 KiB |
1
core/test/data/blackbox/rss14-2/11.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/12.png
Executable file
After Width: | Height: | Size: 38 KiB |
1
core/test/data/blackbox/rss14-2/12.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/13.png
Executable file
After Width: | Height: | Size: 43 KiB |
1
core/test/data/blackbox/rss14-2/13.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/14.png
Executable file
After Width: | Height: | Size: 50 KiB |
1
core/test/data/blackbox/rss14-2/14.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/15.png
Executable file
After Width: | Height: | Size: 45 KiB |
1
core/test/data/blackbox/rss14-2/15.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/16.png
Executable file
After Width: | Height: | Size: 57 KiB |
1
core/test/data/blackbox/rss14-2/16.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/17.png
Executable file
After Width: | Height: | Size: 50 KiB |
1
core/test/data/blackbox/rss14-2/17.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/18.png
Executable file
After Width: | Height: | Size: 51 KiB |
1
core/test/data/blackbox/rss14-2/18.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/19.png
Executable file
After Width: | Height: | Size: 42 KiB |
1
core/test/data/blackbox/rss14-2/19.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/2.png
Executable file
After Width: | Height: | Size: 61 KiB |
1
core/test/data/blackbox/rss14-2/2.txt
Normal file
|
@ -0,0 +1 @@
|
|||
04412345678909
|
BIN
core/test/data/blackbox/rss14-2/20.png
Executable file
After Width: | Height: | Size: 47 KiB |
1
core/test/data/blackbox/rss14-2/20.txt
Normal file
|
@ -0,0 +1 @@
|
|||
00012345678905
|
BIN
core/test/data/blackbox/rss14-2/21.png
Executable file
After Width: | Height: | Size: 49 KiB |
1
core/test/data/blackbox/rss14-2/21.txt
Normal file
|
@ -0,0 +1 @@
|
|||
00012345678905
|
BIN
core/test/data/blackbox/rss14-2/22.png
Executable file
After Width: | Height: | Size: 46 KiB |
1
core/test/data/blackbox/rss14-2/22.txt
Normal file
|
@ -0,0 +1 @@
|
|||
00012345678905
|
BIN
core/test/data/blackbox/rss14-2/23.png
Executable file
After Width: | Height: | Size: 45 KiB |
1
core/test/data/blackbox/rss14-2/23.txt
Normal file
|
@ -0,0 +1 @@
|
|||
00012345678905
|
BIN
core/test/data/blackbox/rss14-2/24.png
Executable file
After Width: | Height: | Size: 43 KiB |
1
core/test/data/blackbox/rss14-2/24.txt
Normal file
|
@ -0,0 +1 @@
|
|||
00012345678905
|
BIN
core/test/data/blackbox/rss14-2/3.png
Executable file
After Width: | Height: | Size: 67 KiB |
1
core/test/data/blackbox/rss14-2/3.txt
Normal file
|
@ -0,0 +1 @@
|
|||
04412345678909
|
BIN
core/test/data/blackbox/rss14-2/4.png
Executable file
After Width: | Height: | Size: 45 KiB |
1
core/test/data/blackbox/rss14-2/4.txt
Normal file
|
@ -0,0 +1 @@
|
|||
04412345678909
|
BIN
core/test/data/blackbox/rss14-2/5.png
Executable file
After Width: | Height: | Size: 42 KiB |
1
core/test/data/blackbox/rss14-2/5.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/6.png
Executable file
After Width: | Height: | Size: 52 KiB |
1
core/test/data/blackbox/rss14-2/6.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/7.png
Executable file
After Width: | Height: | Size: 78 KiB |
1
core/test/data/blackbox/rss14-2/7.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/8.png
Executable file
After Width: | Height: | Size: 53 KiB |
1
core/test/data/blackbox/rss14-2/8.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
BIN
core/test/data/blackbox/rss14-2/9.png
Executable file
After Width: | Height: | Size: 52 KiB |
1
core/test/data/blackbox/rss14-2/9.txt
Normal file
|
@ -0,0 +1 @@
|
|||
02001234567893
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.oned.rss;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
import com.google.zxing.common.AbstractBlackBoxTestCase;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class RSS14BlackBox1TestCase extends AbstractBlackBoxTestCase {
|
||||
|
||||
public RSS14BlackBox1TestCase() {
|
||||
super("test/data/blackbox/rss14-1", new MultiFormatReader(), BarcodeFormat.RSS14);
|
||||
addTest(6, 6, 0.0f);
|
||||
addTest(6, 6, 180.0f);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.oned.rss;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
import com.google.zxing.common.AbstractBlackBoxTestCase;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class RSS14BlackBox2TestCase extends AbstractBlackBoxTestCase {
|
||||
|
||||
public RSS14BlackBox2TestCase() {
|
||||
super("test/data/blackbox/rss14-2", new MultiFormatReader(), BarcodeFormat.RSS14);
|
||||
addTest(7, 9, 0.0f);
|
||||
addTest(6, 9, 180.0f);
|
||||
}
|
||||
|
||||
}
|