mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
detection_result builds
This commit is contained in:
614
src/pdf417/decoder/pdf_417_scanning_decoder.rs
Normal file
614
src/pdf417/decoder/pdf_417_scanning_decoder.rs
Normal file
@@ -0,0 +1,614 @@
|
||||
/*
|
||||
* Copyright 2013 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.
|
||||
*/
|
||||
|
||||
use crate::Exceptions;
|
||||
|
||||
|
||||
/**
|
||||
* @author Guenther Grau
|
||||
*/
|
||||
|
||||
const CODEWORD_SKEW_SIZE :u32= 2;
|
||||
|
||||
const MAX_ERRORS :u32= 3;
|
||||
const MAX_EC_CODEWORDS:u32 = 512;
|
||||
// const errorCorrection:ErrorCorrection = ErrorCorrection::new();
|
||||
|
||||
|
||||
// TODO don't pass in minCodewordWidth and maxCodewordWidth, pass in barcode columns for start and stop pattern
|
||||
// columns. That way width can be deducted from the pattern column.
|
||||
// This approach also allows to detect more details about the barcode, e.g. if a bar type (white or black) is wider
|
||||
// than it should be. This can happen if the scanner used a bad blackpoint.
|
||||
pub fn decode( image:&BitMatrix,
|
||||
imageTopLeft:RXingResultPoint,
|
||||
imageBottomLeft:RXingResultPoint,
|
||||
imageTopRight:RXingResultPoint,
|
||||
imageBottomRight:RXingResultPoint,
|
||||
minCodewordWidth:u32,
|
||||
maxCodewordWidth:u32)
|
||||
-> Result<DecoderRXingResult,Exceptions> {
|
||||
BoundingBox boundingBox = new BoundingBox(image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight);
|
||||
DetectionRXingResultRowIndicatorColumn leftRowIndicatorColumn = null;
|
||||
DetectionRXingResultRowIndicatorColumn rightRowIndicatorColumn = null;
|
||||
DetectionRXingResult detectionRXingResult;
|
||||
for (boolean firstPass = true; ; firstPass = false) {
|
||||
if (imageTopLeft != null) {
|
||||
leftRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopLeft, true, minCodewordWidth,
|
||||
maxCodewordWidth);
|
||||
}
|
||||
if (imageTopRight != null) {
|
||||
rightRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopRight, false, minCodewordWidth,
|
||||
maxCodewordWidth);
|
||||
}
|
||||
detectionRXingResult = merge(leftRowIndicatorColumn, rightRowIndicatorColumn);
|
||||
if (detectionRXingResult == null) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
BoundingBox resultBox = detectionRXingResult.getBoundingBox();
|
||||
if (firstPass && resultBox != null &&
|
||||
(resultBox.getMinY() < boundingBox.getMinY() || resultBox.getMaxY() > boundingBox.getMaxY())) {
|
||||
boundingBox = resultBox;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
detectionRXingResult.setBoundingBox(boundingBox);
|
||||
int maxBarcodeColumn = detectionRXingResult.getBarcodeColumnCount() + 1;
|
||||
detectionRXingResult.setDetectionRXingResultColumn(0, leftRowIndicatorColumn);
|
||||
detectionRXingResult.setDetectionRXingResultColumn(maxBarcodeColumn, rightRowIndicatorColumn);
|
||||
|
||||
boolean leftToRight = leftRowIndicatorColumn != null;
|
||||
for (int barcodeColumnCount = 1; barcodeColumnCount <= maxBarcodeColumn; barcodeColumnCount++) {
|
||||
int barcodeColumn = leftToRight ? barcodeColumnCount : maxBarcodeColumn - barcodeColumnCount;
|
||||
if (detectionRXingResult.getDetectionRXingResultColumn(barcodeColumn) != null) {
|
||||
// This will be the case for the opposite row indicator column, which doesn't need to be decoded again.
|
||||
continue;
|
||||
}
|
||||
DetectionRXingResultColumn detectionRXingResultColumn;
|
||||
if (barcodeColumn == 0 || barcodeColumn == maxBarcodeColumn) {
|
||||
detectionRXingResultColumn = new DetectionRXingResultRowIndicatorColumn(boundingBox, barcodeColumn == 0);
|
||||
} else {
|
||||
detectionRXingResultColumn = new DetectionRXingResultColumn(boundingBox);
|
||||
}
|
||||
detectionRXingResult.setDetectionRXingResultColumn(barcodeColumn, detectionRXingResultColumn);
|
||||
int startColumn = -1;
|
||||
int previousStartColumn = startColumn;
|
||||
// TODO start at a row for which we know the start position, then detect upwards and downwards from there.
|
||||
for (int imageRow = boundingBox.getMinY(); imageRow <= boundingBox.getMaxY(); imageRow++) {
|
||||
startColumn = getStartColumn(detectionRXingResult, barcodeColumn, imageRow, leftToRight);
|
||||
if (startColumn < 0 || startColumn > boundingBox.getMaxX()) {
|
||||
if (previousStartColumn == -1) {
|
||||
continue;
|
||||
}
|
||||
startColumn = previousStartColumn;
|
||||
}
|
||||
Codeword codeword = detectCodeword(image, boundingBox.getMinX(), boundingBox.getMaxX(), leftToRight,
|
||||
startColumn, imageRow, minCodewordWidth, maxCodewordWidth);
|
||||
if (codeword != null) {
|
||||
detectionRXingResultColumn.setCodeword(imageRow, codeword);
|
||||
previousStartColumn = startColumn;
|
||||
minCodewordWidth = Math.min(minCodewordWidth, codeword.getWidth());
|
||||
maxCodewordWidth = Math.max(maxCodewordWidth, codeword.getWidth());
|
||||
}
|
||||
}
|
||||
}
|
||||
return createDecoderRXingResult(detectionRXingResult);
|
||||
}
|
||||
|
||||
fn merge( leftRowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn,
|
||||
rightRowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn)
|
||||
-> Result<DetectionRXingResult,Exceptions> {
|
||||
if (leftRowIndicatorColumn == null && rightRowIndicatorColumn == null) {
|
||||
return null;
|
||||
}
|
||||
BarcodeMetadata barcodeMetadata = getBarcodeMetadata(leftRowIndicatorColumn, rightRowIndicatorColumn);
|
||||
if (barcodeMetadata == null) {
|
||||
return null;
|
||||
}
|
||||
BoundingBox boundingBox = BoundingBox.merge(adjustBoundingBox(leftRowIndicatorColumn),
|
||||
adjustBoundingBox(rightRowIndicatorColumn));
|
||||
return new DetectionRXingResult(barcodeMetadata, boundingBox);
|
||||
}
|
||||
|
||||
fn adjustBoundingBox( rowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn)
|
||||
-> Result<BoundingBox,Exceptions> {
|
||||
if (rowIndicatorColumn == null) {
|
||||
return null;
|
||||
}
|
||||
int[] rowHeights = rowIndicatorColumn.getRowHeights();
|
||||
if (rowHeights == null) {
|
||||
return null;
|
||||
}
|
||||
int maxRowHeight = getMax(rowHeights);
|
||||
int missingStartRows = 0;
|
||||
for (int rowHeight : rowHeights) {
|
||||
missingStartRows += maxRowHeight - rowHeight;
|
||||
if (rowHeight > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Codeword[] codewords = rowIndicatorColumn.getCodewords();
|
||||
for (int row = 0; missingStartRows > 0 && codewords[row] == null; row++) {
|
||||
missingStartRows--;
|
||||
}
|
||||
int missingEndRows = 0;
|
||||
for (int row = rowHeights.length - 1; row >= 0; row--) {
|
||||
missingEndRows += maxRowHeight - rowHeights[row];
|
||||
if (rowHeights[row] > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int row = codewords.length - 1; missingEndRows > 0 && codewords[row] == null; row--) {
|
||||
missingEndRows--;
|
||||
}
|
||||
return rowIndicatorColumn.getBoundingBox().addMissingRows(missingStartRows, missingEndRows,
|
||||
rowIndicatorColumn.isLeft());
|
||||
}
|
||||
|
||||
fn getMax( values:&[u32]) -> u32{
|
||||
int maxValue = -1;
|
||||
for (int value : values) {
|
||||
maxValue = Math.max(maxValue, value);
|
||||
}
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
fn getBarcodeMetadata( leftRowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn,
|
||||
rightRowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn) -> BarcodeMetadata{
|
||||
BarcodeMetadata leftBarcodeMetadata;
|
||||
if (leftRowIndicatorColumn == null ||
|
||||
(leftBarcodeMetadata = leftRowIndicatorColumn.getBarcodeMetadata()) == null) {
|
||||
return rightRowIndicatorColumn == null ? null : rightRowIndicatorColumn.getBarcodeMetadata();
|
||||
}
|
||||
BarcodeMetadata rightBarcodeMetadata;
|
||||
if (rightRowIndicatorColumn == null ||
|
||||
(rightBarcodeMetadata = rightRowIndicatorColumn.getBarcodeMetadata()) == null) {
|
||||
return leftBarcodeMetadata;
|
||||
}
|
||||
|
||||
if (leftBarcodeMetadata.getColumnCount() != rightBarcodeMetadata.getColumnCount() &&
|
||||
leftBarcodeMetadata.getErrorCorrectionLevel() != rightBarcodeMetadata.getErrorCorrectionLevel() &&
|
||||
leftBarcodeMetadata.getRowCount() != rightBarcodeMetadata.getRowCount()) {
|
||||
return null;
|
||||
}
|
||||
return leftBarcodeMetadata;
|
||||
}
|
||||
|
||||
fn getRowIndicatorColumn( image:&BitMatrix,
|
||||
boundingBox:&BoundingBox,
|
||||
startPoint:&RXingResultPoint,
|
||||
leftToRight:u32,
|
||||
minCodewordWidth:u32,
|
||||
maxCodewordWidth:u32)-> DetectionRXingResultRowIndicatorColumn {
|
||||
DetectionRXingResultRowIndicatorColumn rowIndicatorColumn = new DetectionRXingResultRowIndicatorColumn(boundingBox,
|
||||
leftToRight);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int increment = i == 0 ? 1 : -1;
|
||||
int startColumn = (int) startPoint.getX();
|
||||
for (int imageRow = (int) startPoint.getY(); imageRow <= boundingBox.getMaxY() &&
|
||||
imageRow >= boundingBox.getMinY(); imageRow += increment) {
|
||||
Codeword codeword = detectCodeword(image, 0, image.getWidth(), leftToRight, startColumn, imageRow,
|
||||
minCodewordWidth, maxCodewordWidth);
|
||||
if (codeword != null) {
|
||||
rowIndicatorColumn.setCodeword(imageRow, codeword);
|
||||
if (leftToRight) {
|
||||
startColumn = codeword.getStartX();
|
||||
} else {
|
||||
startColumn = codeword.getEndX();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rowIndicatorColumn;
|
||||
}
|
||||
|
||||
fn adjustCodewordCount( detectionRXingResult:&DetectionRXingResult, barcodeMatrix:&Vec<Vec<BarcodeValue>>)
|
||||
throws NotFoundException {
|
||||
BarcodeValue barcodeMatrix01 = barcodeMatrix[0][1];
|
||||
int[] numberOfCodewords = barcodeMatrix01.getValue();
|
||||
int calculatedNumberOfCodewords = detectionRXingResult.getBarcodeColumnCount() *
|
||||
detectionRXingResult.getBarcodeRowCount() -
|
||||
getNumberOfECCodeWords(detectionRXingResult.getBarcodeECLevel());
|
||||
if (numberOfCodewords.length == 0) {
|
||||
if (calculatedNumberOfCodewords < 1 || calculatedNumberOfCodewords > PDF417Common.MAX_CODEWORDS_IN_BARCODE) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
barcodeMatrix01.setValue(calculatedNumberOfCodewords);
|
||||
} else if (numberOfCodewords[0] != calculatedNumberOfCodewords) {
|
||||
if (calculatedNumberOfCodewords >= 1 && calculatedNumberOfCodewords <= PDF417Common.MAX_CODEWORDS_IN_BARCODE) {
|
||||
// The calculated one is more reliable as it is derived from the row indicator columns
|
||||
barcodeMatrix01.setValue(calculatedNumberOfCodewords);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn createDecoderRXingResult( detectionRXingResult:&DetectionRXingResult) -> Result<DecoderRXingResult,Exceptions> {
|
||||
BarcodeValue[][] barcodeMatrix = createBarcodeMatrix(detectionRXingResult);
|
||||
adjustCodewordCount(detectionRXingResult, barcodeMatrix);
|
||||
Collection<Integer> erasures = new ArrayList<>();
|
||||
int[] codewords = new int[detectionRXingResult.getBarcodeRowCount() * detectionRXingResult.getBarcodeColumnCount()];
|
||||
List<int[]> ambiguousIndexValuesList = new ArrayList<>();
|
||||
Collection<Integer> ambiguousIndexesList = new ArrayList<>();
|
||||
for (int row = 0; row < detectionRXingResult.getBarcodeRowCount(); row++) {
|
||||
for (int column = 0; column < detectionRXingResult.getBarcodeColumnCount(); column++) {
|
||||
int[] values = barcodeMatrix[row][column + 1].getValue();
|
||||
int codewordIndex = row * detectionRXingResult.getBarcodeColumnCount() + column;
|
||||
if (values.length == 0) {
|
||||
erasures.add(codewordIndex);
|
||||
} else if (values.length == 1) {
|
||||
codewords[codewordIndex] = values[0];
|
||||
} else {
|
||||
ambiguousIndexesList.add(codewordIndex);
|
||||
ambiguousIndexValuesList.add(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
int[][] ambiguousIndexValues = new int[ambiguousIndexValuesList.size()][];
|
||||
for (int i = 0; i < ambiguousIndexValues.length; i++) {
|
||||
ambiguousIndexValues[i] = ambiguousIndexValuesList.get(i);
|
||||
}
|
||||
return createDecoderRXingResultFromAmbiguousValues(detectionRXingResult.getBarcodeECLevel(), codewords,
|
||||
PDF417Common.toIntArray(erasures), PDF417Common.toIntArray(ambiguousIndexesList), ambiguousIndexValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method deals with the fact, that the decoding process doesn't always yield a single most likely value. The
|
||||
* current error correction implementation doesn't deal with erasures very well, so it's better to provide a value
|
||||
* for these ambiguous codewords instead of treating it as an erasure. The problem is that we don't know which of
|
||||
* the ambiguous values to choose. We try decode using the first value, and if that fails, we use another of the
|
||||
* ambiguous values and try to decode again. This usually only happens on very hard to read and decode barcodes,
|
||||
* so decoding the normal barcodes is not affected by this.
|
||||
*
|
||||
* @param erasureArray contains the indexes of erasures
|
||||
* @param ambiguousIndexes array with the indexes that have more than one most likely value
|
||||
* @param ambiguousIndexValues two dimensional array that contains the ambiguous values. The first dimension must
|
||||
* be the same length as the ambiguousIndexes array
|
||||
*/
|
||||
fn createDecoderRXingResultFromAmbiguousValues( ecLevel:u21,
|
||||
codewords:&[u32],
|
||||
erasureArray&[u32],
|
||||
ambiguousIndexes&[u32],
|
||||
ambiguousIndexValues:&Vec<Vec<u32>>)
|
||||
-> Result<DecoderRXingResult,Exceptions> {
|
||||
int[] ambiguousIndexCount = new int[ambiguousIndexes.length];
|
||||
|
||||
int tries = 100;
|
||||
while (tries-- > 0) {
|
||||
for (int i = 0; i < ambiguousIndexCount.length; i++) {
|
||||
codewords[ambiguousIndexes[i]] = ambiguousIndexValues[i][ambiguousIndexCount[i]];
|
||||
}
|
||||
try {
|
||||
return decodeCodewords(codewords, ecLevel, erasureArray);
|
||||
} catch (ChecksumException ignored) {
|
||||
//
|
||||
}
|
||||
if (ambiguousIndexCount.length == 0) {
|
||||
throw ChecksumException.getChecksumInstance();
|
||||
}
|
||||
for (int i = 0; i < ambiguousIndexCount.length; i++) {
|
||||
if (ambiguousIndexCount[i] < ambiguousIndexValues[i].length - 1) {
|
||||
ambiguousIndexCount[i]++;
|
||||
break;
|
||||
} else {
|
||||
ambiguousIndexCount[i] = 0;
|
||||
if (i == ambiguousIndexCount.length - 1) {
|
||||
throw ChecksumException.getChecksumInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw ChecksumException.getChecksumInstance();
|
||||
}
|
||||
|
||||
fn createBarcodeMatrix( detectionRXingResult:&DetectionRXingResult) -> Vec<Vec<BarcodeValue>>{
|
||||
BarcodeValue[][] barcodeMatrix =
|
||||
new BarcodeValue[detectionRXingResult.getBarcodeRowCount()][detectionRXingResult.getBarcodeColumnCount() + 2];
|
||||
for (int row = 0; row < barcodeMatrix.length; row++) {
|
||||
for (int column = 0; column < barcodeMatrix[row].length; column++) {
|
||||
barcodeMatrix[row][column] = new BarcodeValue();
|
||||
}
|
||||
}
|
||||
|
||||
int column = 0;
|
||||
for (DetectionRXingResultColumn detectionRXingResultColumn : detectionRXingResult.getDetectionRXingResultColumns()) {
|
||||
if (detectionRXingResultColumn != null) {
|
||||
for (Codeword codeword : detectionRXingResultColumn.getCodewords()) {
|
||||
if (codeword != null) {
|
||||
int rowNumber = codeword.getRowNumber();
|
||||
if (rowNumber >= 0) {
|
||||
if (rowNumber >= barcodeMatrix.length) {
|
||||
// We have more rows than the barcode metadata allows for, ignore them.
|
||||
continue;
|
||||
}
|
||||
barcodeMatrix[rowNumber][column].setValue(codeword.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
column++;
|
||||
}
|
||||
return barcodeMatrix;
|
||||
}
|
||||
|
||||
fn isValidBarcodeColumn( detectionRXingResult:&DetectionRXingResult, barcodeColumn:u32) -> bool{
|
||||
return barcodeColumn >= 0 && barcodeColumn <= detectionRXingResult.getBarcodeColumnCount() + 1;
|
||||
}
|
||||
|
||||
fn getStartColumn( detectionRXingResult:&DetectionRXingResult,
|
||||
barcodeColumn:u32,
|
||||
imageRow:u32,
|
||||
leftToRight:bool) -> u32{
|
||||
int offset = leftToRight ? 1 : -1;
|
||||
Codeword codeword = null;
|
||||
if (isValidBarcodeColumn(detectionRXingResult, barcodeColumn - offset)) {
|
||||
codeword = detectionRXingResult.getDetectionRXingResultColumn(barcodeColumn - offset).getCodeword(imageRow);
|
||||
}
|
||||
if (codeword != null) {
|
||||
return leftToRight ? codeword.getEndX() : codeword.getStartX();
|
||||
}
|
||||
codeword = detectionRXingResult.getDetectionRXingResultColumn(barcodeColumn).getCodewordNearby(imageRow);
|
||||
if (codeword != null) {
|
||||
return leftToRight ? codeword.getStartX() : codeword.getEndX();
|
||||
}
|
||||
if (isValidBarcodeColumn(detectionRXingResult, barcodeColumn - offset)) {
|
||||
codeword = detectionRXingResult.getDetectionRXingResultColumn(barcodeColumn - offset).getCodewordNearby(imageRow);
|
||||
}
|
||||
if (codeword != null) {
|
||||
return leftToRight ? codeword.getEndX() : codeword.getStartX();
|
||||
}
|
||||
int skippedColumns = 0;
|
||||
|
||||
while (isValidBarcodeColumn(detectionRXingResult, barcodeColumn - offset)) {
|
||||
barcodeColumn -= offset;
|
||||
for (Codeword previousRowCodeword : detectionRXingResult.getDetectionRXingResultColumn(barcodeColumn).getCodewords()) {
|
||||
if (previousRowCodeword != null) {
|
||||
return (leftToRight ? previousRowCodeword.getEndX() : previousRowCodeword.getStartX()) +
|
||||
offset *
|
||||
skippedColumns *
|
||||
(previousRowCodeword.getEndX() - previousRowCodeword.getStartX());
|
||||
}
|
||||
}
|
||||
skippedColumns++;
|
||||
}
|
||||
return leftToRight ? detectionRXingResult.getBoundingBox().getMinX() : detectionRXingResult.getBoundingBox().getMaxX();
|
||||
}
|
||||
|
||||
fn detectCodeword( image:&BitMatrix,
|
||||
minColumn:u32,
|
||||
maxColumn:u32,
|
||||
leftToRight:bool,
|
||||
startColumn:u32,
|
||||
imageRow:u32,
|
||||
minCodewordWidth:u32,
|
||||
maxCodewordWidth:u32) -> Codeword{
|
||||
startColumn = adjustCodewordStartColumn(image, minColumn, maxColumn, leftToRight, startColumn, imageRow);
|
||||
// we usually know fairly exact now how long a codeword is. We should provide minimum and maximum expected length
|
||||
// and try to adjust the read pixels, e.g. remove single pixel errors or try to cut off exceeding pixels.
|
||||
// min and maxCodewordWidth should not be used as they are calculated for the whole barcode an can be inaccurate
|
||||
// for the current position
|
||||
int[] moduleBitCount = getModuleBitCount(image, minColumn, maxColumn, leftToRight, startColumn, imageRow);
|
||||
if (moduleBitCount == null) {
|
||||
return null;
|
||||
}
|
||||
int endColumn;
|
||||
int codewordBitCount = MathUtils.sum(moduleBitCount);
|
||||
if (leftToRight) {
|
||||
endColumn = startColumn + codewordBitCount;
|
||||
} else {
|
||||
for (int i = 0; i < moduleBitCount.length / 2; i++) {
|
||||
int tmpCount = moduleBitCount[i];
|
||||
moduleBitCount[i] = moduleBitCount[moduleBitCount.length - 1 - i];
|
||||
moduleBitCount[moduleBitCount.length - 1 - i] = tmpCount;
|
||||
}
|
||||
endColumn = startColumn;
|
||||
startColumn = endColumn - codewordBitCount;
|
||||
}
|
||||
// TODO implement check for width and correction of black and white bars
|
||||
// use start (and maybe stop pattern) to determine if black bars are wider than white bars. If so, adjust.
|
||||
// should probably done only for codewords with a lot more than 17 bits.
|
||||
// The following fixes 10-1.png, which has wide black bars and small white bars
|
||||
// for (int i = 0; i < moduleBitCount.length; i++) {
|
||||
// if (i % 2 == 0) {
|
||||
// moduleBitCount[i]--;
|
||||
// } else {
|
||||
// moduleBitCount[i]++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// We could also use the width of surrounding codewords for more accurate results, but this seems
|
||||
// sufficient for now
|
||||
if (!checkCodewordSkew(codewordBitCount, minCodewordWidth, maxCodewordWidth)) {
|
||||
// We could try to use the startX and endX position of the codeword in the same column in the previous row,
|
||||
// create the bit count from it and normalize it to 8. This would help with single pixel errors.
|
||||
return null;
|
||||
}
|
||||
|
||||
int decodedValue = PDF417CodewordDecoder.getDecodedValue(moduleBitCount);
|
||||
int codeword = PDF417Common.getCodeword(decodedValue);
|
||||
if (codeword == -1) {
|
||||
return null;
|
||||
}
|
||||
return new Codeword(startColumn, endColumn, getCodewordBucketNumber(decodedValue), codeword);
|
||||
}
|
||||
|
||||
fn getModuleBitCount( image:&BitMatrix,
|
||||
minColumn:u32,
|
||||
maxColumn:u32,
|
||||
leftToRight:bool,
|
||||
startColumn:u32,
|
||||
imageRow:u32) -> Option<[u32;8]>{
|
||||
int imageColumn = startColumn;
|
||||
int[] moduleBitCount = new int[8];
|
||||
int moduleNumber = 0;
|
||||
int increment = leftToRight ? 1 : -1;
|
||||
boolean previousPixelValue = leftToRight;
|
||||
while ((leftToRight ? imageColumn < maxColumn : imageColumn >= minColumn) &&
|
||||
moduleNumber < moduleBitCount.length) {
|
||||
if (image.get(imageColumn, imageRow) == previousPixelValue) {
|
||||
moduleBitCount[moduleNumber]++;
|
||||
imageColumn += increment;
|
||||
} else {
|
||||
moduleNumber++;
|
||||
previousPixelValue = !previousPixelValue;
|
||||
}
|
||||
}
|
||||
if (moduleNumber == moduleBitCount.length ||
|
||||
((imageColumn == (leftToRight ? maxColumn : minColumn)) &&
|
||||
moduleNumber == moduleBitCount.length - 1)) {
|
||||
return moduleBitCount;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn getNumberOfECCodeWords(int barcodeECLevel) -> u32{
|
||||
return 2 << barcodeECLevel;
|
||||
}
|
||||
|
||||
fn adjustCodewordStartColumn( image:&BitMatrix,
|
||||
minColumn:u32,
|
||||
maxColumn:u32,
|
||||
leftToRight:bool,
|
||||
codewordStartColumn:u32,
|
||||
imageRow:u32) -> u32{
|
||||
int correctedStartColumn = codewordStartColumn;
|
||||
int increment = leftToRight ? -1 : 1;
|
||||
// there should be no black pixels before the start column. If there are, then we need to start earlier.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
while ((leftToRight ? correctedStartColumn >= minColumn : correctedStartColumn < maxColumn) &&
|
||||
leftToRight == image.get(correctedStartColumn, imageRow)) {
|
||||
if (Math.abs(codewordStartColumn - correctedStartColumn) > CODEWORD_SKEW_SIZE) {
|
||||
return codewordStartColumn;
|
||||
}
|
||||
correctedStartColumn += increment;
|
||||
}
|
||||
increment = -increment;
|
||||
leftToRight = !leftToRight;
|
||||
}
|
||||
return correctedStartColumn;
|
||||
}
|
||||
|
||||
fn checkCodewordSkew( codewordSize:u32, minCodewordWidth:u32, maxCodewordWidth:u32) -> bool{
|
||||
return minCodewordWidth - CODEWORD_SKEW_SIZE <= codewordSize &&
|
||||
codewordSize <= maxCodewordWidth + CODEWORD_SKEW_SIZE;
|
||||
}
|
||||
|
||||
fn decodeCodewords( codewords:&[u32], ecLevel:u32, erasures:&[u32]) -> Result<DecoderRXingResult,Exceptions>{
|
||||
if (codewords.length == 0) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
int numECCodewords = 1 << (ecLevel + 1);
|
||||
int correctedErrorsCount = correctErrors(codewords, erasures, numECCodewords);
|
||||
verifyCodewordCount(codewords, numECCodewords);
|
||||
|
||||
// Decode the codewords
|
||||
DecoderRXingResult decoderRXingResult = DecodedBitStreamParser.decode(codewords, String.valueOf(ecLevel));
|
||||
decoderRXingResult.setErrorsCorrected(correctedErrorsCount);
|
||||
decoderRXingResult.setErasures(erasures.length);
|
||||
return decoderRXingResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
* 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 codewords that are available in codewords
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
fn correctErrors( codewords:&[u32], erasures:&[u32], numECCodewords:u32) -> Result<u32,Exceptions> {
|
||||
if (erasures != null &&
|
||||
erasures.length > numECCodewords / 2 + MAX_ERRORS ||
|
||||
numECCodewords < 0 ||
|
||||
numECCodewords > MAX_EC_CODEWORDS) {
|
||||
// Too many errors or EC Codewords is corrupted
|
||||
throw ChecksumException.getChecksumInstance();
|
||||
}
|
||||
return errorCorrection.decode(codewords, numECCodewords, erasures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that all is OK with the codeword array.
|
||||
*/
|
||||
fn verifyCodewordCount( codewords:&[u32], numECCodewords:u32) -> Result<(),Exceptions> {
|
||||
if (codewords.length < 4) {
|
||||
// Codeword array size should be at least 4 allowing for
|
||||
// Count CW, At least one Data CW, Error Correction CW, Error Correction CW
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
// The first codeword, the Symbol Length Descriptor, shall always encode the total number of data
|
||||
// codewords in the symbol, including the Symbol Length Descriptor itself, data codewords and pad
|
||||
// codewords, but excluding the number of error correction codewords.
|
||||
int numberOfCodewords = codewords[0];
|
||||
if (numberOfCodewords > codewords.length) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
if (numberOfCodewords == 0) {
|
||||
// Reset to the length of the array - 8 (Allow for at least level 3 Error Correction (8 Error Codewords)
|
||||
if (numECCodewords < codewords.length) {
|
||||
codewords[0] = codewords.length - numECCodewords;
|
||||
} else {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn getBitCountForCodeword( codeword:u32) -> [u32;8]{
|
||||
int[] result = new int[8];
|
||||
int previousValue = 0;
|
||||
int i = result.length - 1;
|
||||
while (true) {
|
||||
if ((codeword & 0x1) != previousValue) {
|
||||
previousValue = codeword & 0x1;
|
||||
i--;
|
||||
if (i < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result[i]++;
|
||||
codeword >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn getCodewordBucketNumber( codeword:u32) -> u32{
|
||||
return getCodewordBucketNumber(getBitCountForCodeword(codeword));
|
||||
}
|
||||
|
||||
fn getCodewordBucketNumber( moduleBitCount:&[u32]) -> u32{
|
||||
return (moduleBitCount[0] - moduleBitCount[2] + moduleBitCount[4] - moduleBitCount[6] + 9) % 9;
|
||||
}
|
||||
|
||||
fn toString( barcodeMatrix:Vec<Vec<BarcodeValue>>) -> String{
|
||||
try (Formatter formatter = new Formatter()) {
|
||||
for (int row = 0; row < barcodeMatrix.length; row++) {
|
||||
formatter.format("Row %2d: ", row);
|
||||
for (int column = 0; column < barcodeMatrix[row].length; column++) {
|
||||
BarcodeValue barcodeValue = barcodeMatrix[row][column];
|
||||
if (barcodeValue.getValue().length == 0) {
|
||||
formatter.format(" ", (Object[]) null);
|
||||
} else {
|
||||
formatter.format("%4d(%2d)", barcodeValue.getValue()[0],
|
||||
barcodeValue.getConfidence(barcodeValue.getValue()[0]));
|
||||
}
|
||||
}
|
||||
formatter.format("%n");
|
||||
}
|
||||
return formatter.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user