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:
@@ -85,7 +85,7 @@ impl Detector {
|
|||||||
let mut bulls_eye_corners = self.get_bulls_eye_corners(p_center)?;
|
let mut bulls_eye_corners = self.get_bulls_eye_corners(p_center)?;
|
||||||
|
|
||||||
if is_mirror {
|
if is_mirror {
|
||||||
bulls_eye_corners.swap(0,2);
|
bulls_eye_corners.swap(0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Get the size of the matrix and other parameters from the bull's eye
|
// 3. Get the size of the matrix and other parameters from the bull's eye
|
||||||
|
|||||||
@@ -214,7 +214,6 @@ impl GlobalHistogramBinarizer {
|
|||||||
|
|
||||||
// Make sure firstPeak corresponds to the black peak.
|
// Make sure firstPeak corresponds to the black peak.
|
||||||
if firstPeak > secondPeak {
|
if firstPeak > secondPeak {
|
||||||
|
|
||||||
std::mem::swap(&mut firstPeak, &mut secondPeak);
|
std::mem::swap(&mut firstPeak, &mut secondPeak);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ impl GenericGFPoly {
|
|||||||
let mut smallerCoefficients = self.coefficients.clone();
|
let mut smallerCoefficients = self.coefficients.clone();
|
||||||
let mut largerCoefficients = other.coefficients.clone();
|
let mut largerCoefficients = other.coefficients.clone();
|
||||||
if smallerCoefficients.len() > largerCoefficients.len() {
|
if smallerCoefficients.len() > largerCoefficients.len() {
|
||||||
std::mem::swap(&mut smallerCoefficients,&mut largerCoefficients)
|
std::mem::swap(&mut smallerCoefficients, &mut largerCoefficients)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sumDiff = vec![0; largerCoefficients.len()];
|
let mut sumDiff = vec![0; largerCoefficients.len()];
|
||||||
|
|||||||
@@ -307,8 +307,8 @@ impl RSS14Reader {
|
|||||||
let mut j = counters.len() - 1;
|
let mut j = counters.len() - 1;
|
||||||
while i < j {
|
while i < j {
|
||||||
// for (int i = 0, j = counters.length - 1; i < j; i++, j--) {
|
// for (int i = 0, j = counters.length - 1; i < j; i++, j--) {
|
||||||
|
|
||||||
counters.swap(i,j);
|
counters.swap(i, j);
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
j -= 1;
|
j -= 1;
|
||||||
|
|||||||
@@ -1,299 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.zxing.pdf417.decoder;
|
|
||||||
|
|
||||||
import com.google.zxing.pdf417.PDF417Common;
|
|
||||||
|
|
||||||
import java.util.Formatter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Guenther Grau
|
|
||||||
*/
|
|
||||||
final class DetectionRXingResult {
|
|
||||||
|
|
||||||
private static final int ADJUST_ROW_NUMBER_SKIP = 2;
|
|
||||||
|
|
||||||
private final BarcodeMetadata barcodeMetadata;
|
|
||||||
private final DetectionRXingResultColumn[] detectionRXingResultColumns;
|
|
||||||
private BoundingBox boundingBox;
|
|
||||||
private final int barcodeColumnCount;
|
|
||||||
|
|
||||||
DetectionRXingResult(BarcodeMetadata barcodeMetadata, BoundingBox boundingBox) {
|
|
||||||
this.barcodeMetadata = barcodeMetadata;
|
|
||||||
this.barcodeColumnCount = barcodeMetadata.getColumnCount();
|
|
||||||
this.boundingBox = boundingBox;
|
|
||||||
detectionRXingResultColumns = new DetectionRXingResultColumn[barcodeColumnCount + 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
DetectionRXingResultColumn[] getDetectionRXingResultColumns() {
|
|
||||||
adjustIndicatorColumnRowNumbers(detectionRXingResultColumns[0]);
|
|
||||||
adjustIndicatorColumnRowNumbers(detectionRXingResultColumns[barcodeColumnCount + 1]);
|
|
||||||
int unadjustedCodewordCount = PDF417Common.MAX_CODEWORDS_IN_BARCODE;
|
|
||||||
int previousUnadjustedCount;
|
|
||||||
do {
|
|
||||||
previousUnadjustedCount = unadjustedCodewordCount;
|
|
||||||
unadjustedCodewordCount = adjustRowNumbers();
|
|
||||||
} while (unadjustedCodewordCount > 0 && unadjustedCodewordCount < previousUnadjustedCount);
|
|
||||||
return detectionRXingResultColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void adjustIndicatorColumnRowNumbers(DetectionRXingResultColumn detectionRXingResultColumn) {
|
|
||||||
if (detectionRXingResultColumn != null) {
|
|
||||||
((DetectionRXingResultRowIndicatorColumn) detectionRXingResultColumn)
|
|
||||||
.adjustCompleteIndicatorColumnRowNumbers(barcodeMetadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO ensure that no detected codewords with unknown row number are left
|
|
||||||
// we should be able to estimate the row height and use it as a hint for the row number
|
|
||||||
// we should also fill the rows top to bottom and bottom to top
|
|
||||||
/**
|
|
||||||
* @return number of codewords which don't have a valid row number. Note that the count is not accurate as codewords
|
|
||||||
* will be counted several times. It just serves as an indicator to see when we can stop adjusting row numbers
|
|
||||||
*/
|
|
||||||
private int adjustRowNumbers() {
|
|
||||||
int unadjustedCount = adjustRowNumbersByRow();
|
|
||||||
if (unadjustedCount == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for (int barcodeColumn = 1; barcodeColumn < barcodeColumnCount + 1; barcodeColumn++) {
|
|
||||||
Codeword[] codewords = detectionRXingResultColumns[barcodeColumn].getCodewords();
|
|
||||||
for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
|
|
||||||
if (codewords[codewordsRow] == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!codewords[codewordsRow].hasValidRowNumber()) {
|
|
||||||
adjustRowNumbers(barcodeColumn, codewordsRow, codewords);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return unadjustedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int adjustRowNumbersByRow() {
|
|
||||||
adjustRowNumbersFromBothRI();
|
|
||||||
// TODO we should only do full row adjustments if row numbers of left and right row indicator column match.
|
|
||||||
// Maybe it's even better to calculated the height (in codeword rows) and divide it by the number of barcode
|
|
||||||
// rows. This, together with the LRI and RRI row numbers should allow us to get a good estimate where a row
|
|
||||||
// number starts and ends.
|
|
||||||
int unadjustedCount = adjustRowNumbersFromLRI();
|
|
||||||
return unadjustedCount + adjustRowNumbersFromRRI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void adjustRowNumbersFromBothRI() {
|
|
||||||
if (detectionRXingResultColumns[0] == null || detectionRXingResultColumns[barcodeColumnCount + 1] == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Codeword[] LRIcodewords = detectionRXingResultColumns[0].getCodewords();
|
|
||||||
Codeword[] RRIcodewords = detectionRXingResultColumns[barcodeColumnCount + 1].getCodewords();
|
|
||||||
for (int codewordsRow = 0; codewordsRow < LRIcodewords.length; codewordsRow++) {
|
|
||||||
if (LRIcodewords[codewordsRow] != null &&
|
|
||||||
RRIcodewords[codewordsRow] != null &&
|
|
||||||
LRIcodewords[codewordsRow].getRowNumber() == RRIcodewords[codewordsRow].getRowNumber()) {
|
|
||||||
for (int barcodeColumn = 1; barcodeColumn <= barcodeColumnCount; barcodeColumn++) {
|
|
||||||
Codeword codeword = detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
|
||||||
if (codeword == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
codeword.setRowNumber(LRIcodewords[codewordsRow].getRowNumber());
|
|
||||||
if (!codeword.hasValidRowNumber()) {
|
|
||||||
detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int adjustRowNumbersFromRRI() {
|
|
||||||
if (detectionRXingResultColumns[barcodeColumnCount + 1] == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int unadjustedCount = 0;
|
|
||||||
Codeword[] codewords = detectionRXingResultColumns[barcodeColumnCount + 1].getCodewords();
|
|
||||||
for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
|
|
||||||
if (codewords[codewordsRow] == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber();
|
|
||||||
int invalidRowCounts = 0;
|
|
||||||
for (int barcodeColumn = barcodeColumnCount + 1;
|
|
||||||
barcodeColumn > 0 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP;
|
|
||||||
barcodeColumn--) {
|
|
||||||
Codeword codeword = detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
|
||||||
if (codeword != null) {
|
|
||||||
invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);
|
|
||||||
if (!codeword.hasValidRowNumber()) {
|
|
||||||
unadjustedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return unadjustedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int adjustRowNumbersFromLRI() {
|
|
||||||
if (detectionRXingResultColumns[0] == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int unadjustedCount = 0;
|
|
||||||
Codeword[] codewords = detectionRXingResultColumns[0].getCodewords();
|
|
||||||
for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
|
|
||||||
if (codewords[codewordsRow] == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber();
|
|
||||||
int invalidRowCounts = 0;
|
|
||||||
for (int barcodeColumn = 1;
|
|
||||||
barcodeColumn < barcodeColumnCount + 1 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP;
|
|
||||||
barcodeColumn++) {
|
|
||||||
Codeword codeword = detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
|
||||||
if (codeword != null) {
|
|
||||||
invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);
|
|
||||||
if (!codeword.hasValidRowNumber()) {
|
|
||||||
unadjustedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return unadjustedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int adjustRowNumberIfValid(int rowIndicatorRowNumber, int invalidRowCounts, Codeword codeword) {
|
|
||||||
if (codeword == null) {
|
|
||||||
return invalidRowCounts;
|
|
||||||
}
|
|
||||||
if (!codeword.hasValidRowNumber()) {
|
|
||||||
if (codeword.isValidRowNumber(rowIndicatorRowNumber)) {
|
|
||||||
codeword.setRowNumber(rowIndicatorRowNumber);
|
|
||||||
invalidRowCounts = 0;
|
|
||||||
} else {
|
|
||||||
++invalidRowCounts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return invalidRowCounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void adjustRowNumbers(int barcodeColumn, int codewordsRow, Codeword[] codewords) {
|
|
||||||
Codeword codeword = codewords[codewordsRow];
|
|
||||||
Codeword[] previousColumnCodewords = detectionRXingResultColumns[barcodeColumn - 1].getCodewords();
|
|
||||||
Codeword[] nextColumnCodewords = previousColumnCodewords;
|
|
||||||
if (detectionRXingResultColumns[barcodeColumn + 1] != null) {
|
|
||||||
nextColumnCodewords = detectionRXingResultColumns[barcodeColumn + 1].getCodewords();
|
|
||||||
}
|
|
||||||
|
|
||||||
Codeword[] otherCodewords = new Codeword[14];
|
|
||||||
|
|
||||||
otherCodewords[2] = previousColumnCodewords[codewordsRow];
|
|
||||||
otherCodewords[3] = nextColumnCodewords[codewordsRow];
|
|
||||||
|
|
||||||
if (codewordsRow > 0) {
|
|
||||||
otherCodewords[0] = codewords[codewordsRow - 1];
|
|
||||||
otherCodewords[4] = previousColumnCodewords[codewordsRow - 1];
|
|
||||||
otherCodewords[5] = nextColumnCodewords[codewordsRow - 1];
|
|
||||||
}
|
|
||||||
if (codewordsRow > 1) {
|
|
||||||
otherCodewords[8] = codewords[codewordsRow - 2];
|
|
||||||
otherCodewords[10] = previousColumnCodewords[codewordsRow - 2];
|
|
||||||
otherCodewords[11] = nextColumnCodewords[codewordsRow - 2];
|
|
||||||
}
|
|
||||||
if (codewordsRow < codewords.length - 1) {
|
|
||||||
otherCodewords[1] = codewords[codewordsRow + 1];
|
|
||||||
otherCodewords[6] = previousColumnCodewords[codewordsRow + 1];
|
|
||||||
otherCodewords[7] = nextColumnCodewords[codewordsRow + 1];
|
|
||||||
}
|
|
||||||
if (codewordsRow < codewords.length - 2) {
|
|
||||||
otherCodewords[9] = codewords[codewordsRow + 2];
|
|
||||||
otherCodewords[12] = previousColumnCodewords[codewordsRow + 2];
|
|
||||||
otherCodewords[13] = nextColumnCodewords[codewordsRow + 2];
|
|
||||||
}
|
|
||||||
for (Codeword otherCodeword : otherCodewords) {
|
|
||||||
if (adjustRowNumber(codeword, otherCodeword)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true, if row number was adjusted, false otherwise
|
|
||||||
*/
|
|
||||||
private static boolean adjustRowNumber(Codeword codeword, Codeword otherCodeword) {
|
|
||||||
if (otherCodeword == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (otherCodeword.hasValidRowNumber() && otherCodeword.getBucket() == codeword.getBucket()) {
|
|
||||||
codeword.setRowNumber(otherCodeword.getRowNumber());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getBarcodeColumnCount() {
|
|
||||||
return barcodeColumnCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getBarcodeRowCount() {
|
|
||||||
return barcodeMetadata.getRowCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
int getBarcodeECLevel() {
|
|
||||||
return barcodeMetadata.getErrorCorrectionLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBoundingBox(BoundingBox boundingBox) {
|
|
||||||
this.boundingBox = boundingBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
BoundingBox getBoundingBox() {
|
|
||||||
return boundingBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDetectionRXingResultColumn(int barcodeColumn, DetectionRXingResultColumn detectionRXingResultColumn) {
|
|
||||||
detectionRXingResultColumns[barcodeColumn] = detectionRXingResultColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
DetectionRXingResultColumn getDetectionRXingResultColumn(int barcodeColumn) {
|
|
||||||
return detectionRXingResultColumns[barcodeColumn];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
DetectionRXingResultColumn rowIndicatorColumn = detectionRXingResultColumns[0];
|
|
||||||
if (rowIndicatorColumn == null) {
|
|
||||||
rowIndicatorColumn = detectionRXingResultColumns[barcodeColumnCount + 1];
|
|
||||||
}
|
|
||||||
try (Formatter formatter = new Formatter()) {
|
|
||||||
for (int codewordsRow = 0; codewordsRow < rowIndicatorColumn.getCodewords().length; codewordsRow++) {
|
|
||||||
formatter.format("CW %3d:", codewordsRow);
|
|
||||||
for (int barcodeColumn = 0; barcodeColumn < barcodeColumnCount + 2; barcodeColumn++) {
|
|
||||||
if (detectionRXingResultColumns[barcodeColumn] == null) {
|
|
||||||
formatter.format(" | ");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Codeword codeword = detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
|
||||||
if (codeword == null) {
|
|
||||||
formatter.format(" | ");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
formatter.format(" %3d|%3d", codeword.getRowNumber(), codeword.getValue());
|
|
||||||
}
|
|
||||||
formatter.format("%n");
|
|
||||||
}
|
|
||||||
return formatter.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.zxing.pdf417.decoder;
|
|
||||||
|
|
||||||
import com.google.zxing.RXingResultPoint;
|
|
||||||
import com.google.zxing.pdf417.PDF417Common;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Guenther Grau
|
|
||||||
*/
|
|
||||||
final class DetectionRXingResultRowIndicatorColumn extends DetectionRXingResultColumn {
|
|
||||||
|
|
||||||
private final boolean isLeft;
|
|
||||||
|
|
||||||
DetectionRXingResultRowIndicatorColumn(BoundingBox boundingBox, boolean isLeft) {
|
|
||||||
super(boundingBox);
|
|
||||||
this.isLeft = isLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRowNumbers() {
|
|
||||||
for (Codeword codeword : getCodewords()) {
|
|
||||||
if (codeword != null) {
|
|
||||||
codeword.setRowNumberAsRowIndicatorColumn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO implement properly
|
|
||||||
// TODO maybe we should add missing codewords to store the correct row number to make
|
|
||||||
// finding row numbers for other columns easier
|
|
||||||
// use row height count to make detection of invalid row numbers more reliable
|
|
||||||
void adjustCompleteIndicatorColumnRowNumbers(BarcodeMetadata barcodeMetadata) {
|
|
||||||
Codeword[] codewords = getCodewords();
|
|
||||||
setRowNumbers();
|
|
||||||
removeIncorrectCodewords(codewords, barcodeMetadata);
|
|
||||||
BoundingBox boundingBox = getBoundingBox();
|
|
||||||
RXingResultPoint top = isLeft ? boundingBox.getTopLeft() : boundingBox.getTopRight();
|
|
||||||
RXingResultPoint bottom = isLeft ? boundingBox.getBottomLeft() : boundingBox.getBottomRight();
|
|
||||||
int firstRow = imageRowToCodewordIndex((int) top.getY());
|
|
||||||
int lastRow = imageRowToCodewordIndex((int) bottom.getY());
|
|
||||||
// We need to be careful using the average row height. Barcode could be skewed so that we have smaller and
|
|
||||||
// taller rows
|
|
||||||
//float averageRowHeight = (lastRow - firstRow) / (float) barcodeMetadata.getRowCount();
|
|
||||||
int barcodeRow = -1;
|
|
||||||
int maxRowHeight = 1;
|
|
||||||
int currentRowHeight = 0;
|
|
||||||
for (int codewordsRow = firstRow; codewordsRow < lastRow; codewordsRow++) {
|
|
||||||
if (codewords[codewordsRow] == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Codeword codeword = codewords[codewordsRow];
|
|
||||||
|
|
||||||
int rowDifference = codeword.getRowNumber() - barcodeRow;
|
|
||||||
|
|
||||||
// TODO improve handling with case where first row indicator doesn't start with 0
|
|
||||||
|
|
||||||
if (rowDifference == 0) {
|
|
||||||
currentRowHeight++;
|
|
||||||
} else if (rowDifference == 1) {
|
|
||||||
maxRowHeight = Math.max(maxRowHeight, currentRowHeight);
|
|
||||||
currentRowHeight = 1;
|
|
||||||
barcodeRow = codeword.getRowNumber();
|
|
||||||
} else if (rowDifference < 0 ||
|
|
||||||
codeword.getRowNumber() >= barcodeMetadata.getRowCount() ||
|
|
||||||
rowDifference > codewordsRow) {
|
|
||||||
codewords[codewordsRow] = null;
|
|
||||||
} else {
|
|
||||||
int checkedRows;
|
|
||||||
if (maxRowHeight > 2) {
|
|
||||||
checkedRows = (maxRowHeight - 2) * rowDifference;
|
|
||||||
} else {
|
|
||||||
checkedRows = rowDifference;
|
|
||||||
}
|
|
||||||
boolean closePreviousCodewordFound = checkedRows >= codewordsRow;
|
|
||||||
for (int i = 1; i <= checkedRows && !closePreviousCodewordFound; i++) {
|
|
||||||
// there must be (height * rowDifference) number of codewords missing. For now we assume height = 1.
|
|
||||||
// This should hopefully get rid of most problems already.
|
|
||||||
closePreviousCodewordFound = codewords[codewordsRow - i] != null;
|
|
||||||
}
|
|
||||||
if (closePreviousCodewordFound) {
|
|
||||||
codewords[codewordsRow] = null;
|
|
||||||
} else {
|
|
||||||
barcodeRow = codeword.getRowNumber();
|
|
||||||
currentRowHeight = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//return (int) (averageRowHeight + 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
int[] getRowHeights() {
|
|
||||||
BarcodeMetadata barcodeMetadata = getBarcodeMetadata();
|
|
||||||
if (barcodeMetadata == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
adjustIncompleteIndicatorColumnRowNumbers(barcodeMetadata);
|
|
||||||
int[] result = new int[barcodeMetadata.getRowCount()];
|
|
||||||
for (Codeword codeword : getCodewords()) {
|
|
||||||
if (codeword != null) {
|
|
||||||
int rowNumber = codeword.getRowNumber();
|
|
||||||
if (rowNumber >= result.length) {
|
|
||||||
// We have more rows than the barcode metadata allows for, ignore them.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result[rowNumber]++;
|
|
||||||
} // else throw exception?
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO maybe we should add missing codewords to store the correct row number to make
|
|
||||||
// finding row numbers for other columns easier
|
|
||||||
// use row height count to make detection of invalid row numbers more reliable
|
|
||||||
private void adjustIncompleteIndicatorColumnRowNumbers(BarcodeMetadata barcodeMetadata) {
|
|
||||||
BoundingBox boundingBox = getBoundingBox();
|
|
||||||
RXingResultPoint top = isLeft ? boundingBox.getTopLeft() : boundingBox.getTopRight();
|
|
||||||
RXingResultPoint bottom = isLeft ? boundingBox.getBottomLeft() : boundingBox.getBottomRight();
|
|
||||||
int firstRow = imageRowToCodewordIndex((int) top.getY());
|
|
||||||
int lastRow = imageRowToCodewordIndex((int) bottom.getY());
|
|
||||||
//float averageRowHeight = (lastRow - firstRow) / (float) barcodeMetadata.getRowCount();
|
|
||||||
Codeword[] codewords = getCodewords();
|
|
||||||
int barcodeRow = -1;
|
|
||||||
int maxRowHeight = 1;
|
|
||||||
int currentRowHeight = 0;
|
|
||||||
for (int codewordsRow = firstRow; codewordsRow < lastRow; codewordsRow++) {
|
|
||||||
if (codewords[codewordsRow] == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Codeword codeword = codewords[codewordsRow];
|
|
||||||
|
|
||||||
codeword.setRowNumberAsRowIndicatorColumn();
|
|
||||||
|
|
||||||
int rowDifference = codeword.getRowNumber() - barcodeRow;
|
|
||||||
|
|
||||||
// TODO improve handling with case where first row indicator doesn't start with 0
|
|
||||||
|
|
||||||
if (rowDifference == 0) {
|
|
||||||
currentRowHeight++;
|
|
||||||
} else if (rowDifference == 1) {
|
|
||||||
maxRowHeight = Math.max(maxRowHeight, currentRowHeight);
|
|
||||||
currentRowHeight = 1;
|
|
||||||
barcodeRow = codeword.getRowNumber();
|
|
||||||
} else if (codeword.getRowNumber() >= barcodeMetadata.getRowCount()) {
|
|
||||||
codewords[codewordsRow] = null;
|
|
||||||
} else {
|
|
||||||
barcodeRow = codeword.getRowNumber();
|
|
||||||
currentRowHeight = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//return (int) (averageRowHeight + 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
BarcodeMetadata getBarcodeMetadata() {
|
|
||||||
Codeword[] codewords = getCodewords();
|
|
||||||
BarcodeValue barcodeColumnCount = new BarcodeValue();
|
|
||||||
BarcodeValue barcodeRowCountUpperPart = new BarcodeValue();
|
|
||||||
BarcodeValue barcodeRowCountLowerPart = new BarcodeValue();
|
|
||||||
BarcodeValue barcodeECLevel = new BarcodeValue();
|
|
||||||
for (Codeword codeword : codewords) {
|
|
||||||
if (codeword == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
codeword.setRowNumberAsRowIndicatorColumn();
|
|
||||||
int rowIndicatorValue = codeword.getValue() % 30;
|
|
||||||
int codewordRowNumber = codeword.getRowNumber();
|
|
||||||
if (!isLeft) {
|
|
||||||
codewordRowNumber += 2;
|
|
||||||
}
|
|
||||||
switch (codewordRowNumber % 3) {
|
|
||||||
case 0:
|
|
||||||
barcodeRowCountUpperPart.setValue(rowIndicatorValue * 3 + 1);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
barcodeECLevel.setValue(rowIndicatorValue / 3);
|
|
||||||
barcodeRowCountLowerPart.setValue(rowIndicatorValue % 3);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
barcodeColumnCount.setValue(rowIndicatorValue + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Maybe we should check if we have ambiguous values?
|
|
||||||
if ((barcodeColumnCount.getValue().length == 0) ||
|
|
||||||
(barcodeRowCountUpperPart.getValue().length == 0) ||
|
|
||||||
(barcodeRowCountLowerPart.getValue().length == 0) ||
|
|
||||||
(barcodeECLevel.getValue().length == 0) ||
|
|
||||||
barcodeColumnCount.getValue()[0] < 1 ||
|
|
||||||
barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0] <
|
|
||||||
PDF417Common.MIN_ROWS_IN_BARCODE ||
|
|
||||||
barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0] >
|
|
||||||
PDF417Common.MAX_ROWS_IN_BARCODE) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
BarcodeMetadata barcodeMetadata = new BarcodeMetadata(barcodeColumnCount.getValue()[0],
|
|
||||||
barcodeRowCountUpperPart.getValue()[0], barcodeRowCountLowerPart.getValue()[0], barcodeECLevel.getValue()[0]);
|
|
||||||
removeIncorrectCodewords(codewords, barcodeMetadata);
|
|
||||||
return barcodeMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeIncorrectCodewords(Codeword[] codewords, BarcodeMetadata barcodeMetadata) {
|
|
||||||
// Remove codewords which do not match the metadata
|
|
||||||
// TODO Maybe we should keep the incorrect codewords for the start and end positions?
|
|
||||||
for (int codewordRow = 0; codewordRow < codewords.length; codewordRow++) {
|
|
||||||
Codeword codeword = codewords[codewordRow];
|
|
||||||
if (codewords[codewordRow] == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int rowIndicatorValue = codeword.getValue() % 30;
|
|
||||||
int codewordRowNumber = codeword.getRowNumber();
|
|
||||||
if (codewordRowNumber > barcodeMetadata.getRowCount()) {
|
|
||||||
codewords[codewordRow] = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!isLeft) {
|
|
||||||
codewordRowNumber += 2;
|
|
||||||
}
|
|
||||||
switch (codewordRowNumber % 3) {
|
|
||||||
case 0:
|
|
||||||
if (rowIndicatorValue * 3 + 1 != barcodeMetadata.getRowCountUpperPart()) {
|
|
||||||
codewords[codewordRow] = null;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (rowIndicatorValue / 3 != barcodeMetadata.getErrorCorrectionLevel() ||
|
|
||||||
rowIndicatorValue % 3 != barcodeMetadata.getRowCountLowerPart()) {
|
|
||||||
codewords[codewordRow] = null;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if (rowIndicatorValue + 1 != barcodeMetadata.getColumnCount()) {
|
|
||||||
codewords[codewordRow] = null;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isLeft() {
|
|
||||||
return isLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "IsLeft: " + isLeft + '\n' + super.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
/**
|
/**
|
||||||
* @author Guenther Grau
|
* @author Guenther Grau
|
||||||
*/
|
*/
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct BarcodeMetadata {
|
pub struct BarcodeMetadata {
|
||||||
columnCount: u32,
|
columnCount: u32,
|
||||||
errorCorrectionLevel: u32,
|
errorCorrectionLevel: u32,
|
||||||
|
|||||||
564
src/pdf417/decoder/detection_result.rs
Normal file
564
src/pdf417/decoder/detection_result.rs
Normal file
@@ -0,0 +1,564 @@
|
|||||||
|
/*
|
||||||
|
* 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 std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::pdf417::pdf_417_common;
|
||||||
|
|
||||||
|
use super::{BarcodeMetadata, BoundingBox, Codeword, DetectionRXingResultColumn};
|
||||||
|
|
||||||
|
const ADJUST_ROW_NUMBER_SKIP: u32 = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Guenther Grau
|
||||||
|
*/
|
||||||
|
pub struct DetectionRXingResult<'a> {
|
||||||
|
barcodeMetadata: BarcodeMetadata,
|
||||||
|
detectionRXingResultColumns: Vec<Option<DetectionRXingResultColumn<'a>>>,
|
||||||
|
boundingBox: BoundingBox<'a>,
|
||||||
|
barcodeColumnCount: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DetectionRXingResult<'_> {
|
||||||
|
pub fn new(
|
||||||
|
barcodeMetadata: BarcodeMetadata,
|
||||||
|
boundingBox: BoundingBox<'a>,
|
||||||
|
) -> DetectionRXingResult<'a> {
|
||||||
|
DetectionRXingResult {
|
||||||
|
barcodeColumnCount: barcodeMetadata.getColumnCount() as usize,
|
||||||
|
detectionRXingResultColumns: vec![None; barcodeMetadata.getColumnCount() as usize + 2],
|
||||||
|
barcodeMetadata,
|
||||||
|
boundingBox,
|
||||||
|
}
|
||||||
|
// this.barcodeMetadata = barcodeMetadata;
|
||||||
|
// this.barcodeColumnCount = barcodeMetadata.getColumnCount();
|
||||||
|
// this.boundingBox = boundingBox;
|
||||||
|
// detectionRXingResultColumns = new DetectionRXingResultColumn[barcodeColumnCount + 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getDetectionRXingResultColumns(&mut self) -> &Vec<Option<DetectionRXingResultColumn>> {
|
||||||
|
self.adjustIndicatorColumnRowNumbers(&self.detectionRXingResultColumns[0]);
|
||||||
|
self.adjustIndicatorColumnRowNumbers(
|
||||||
|
&self.detectionRXingResultColumns[self.barcodeColumnCount + 1],
|
||||||
|
);
|
||||||
|
let mut unadjustedCodewordCount = pdf_417_common::MAX_CODEWORDS_IN_BARCODE;
|
||||||
|
let mut previousUnadjustedCount;
|
||||||
|
loop {
|
||||||
|
previousUnadjustedCount = unadjustedCodewordCount;
|
||||||
|
unadjustedCodewordCount = self.adjustRowNumbers();
|
||||||
|
if !(unadjustedCodewordCount > 0 && unadjustedCodewordCount < previousUnadjustedCount) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} //while (unadjustedCodewordCount > 0 && unadjustedCodewordCount < previousUnadjustedCount);
|
||||||
|
&self.detectionRXingResultColumns
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjustIndicatorColumnRowNumbers(
|
||||||
|
&self,
|
||||||
|
detectionRXingResultColumn: &Option<DetectionRXingResultColumn>,
|
||||||
|
) {
|
||||||
|
if let Some(col) = detectionRXingResultColumn {
|
||||||
|
// if (detectionRXingResultColumn != null) {
|
||||||
|
// ((DetectionRXingResultRowIndicatorColumn) detectionRXingResultColumn)
|
||||||
|
// .adjustCompleteIndicatorColumnRowNumbers(barcodeMetadata);
|
||||||
|
// }
|
||||||
|
col.as_row_indicator()
|
||||||
|
.adjustCompleteIndicatorColumnRowNumbers(&self.barcodeMetadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO ensure that no detected codewords with unknown row number are left
|
||||||
|
// we should be able to estimate the row height and use it as a hint for the row number
|
||||||
|
// we should also fill the rows top to bottom and bottom to top
|
||||||
|
/**
|
||||||
|
* @return number of codewords which don't have a valid row number. Note that the count is not accurate as codewords
|
||||||
|
* will be counted several times. It just serves as an indicator to see when we can stop adjusting row numbers
|
||||||
|
*/
|
||||||
|
fn adjustRowNumbers(&mut self) -> u32 {
|
||||||
|
let unadjustedCount = self.adjustRowNumbersByRow();
|
||||||
|
if unadjustedCount == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for barcodeColumn in 1..(self.barcodeColumnCount + 1) {
|
||||||
|
// for (int barcodeColumn = 1; barcodeColumn < barcodeColumnCount + 1; barcodeColumn++) {
|
||||||
|
if self.detectionRXingResultColumns[barcodeColumn].is_some() {
|
||||||
|
let codewords = self.detectionRXingResultColumns[barcodeColumn]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewords();
|
||||||
|
for codewordsRow in 0..codewords.len() {
|
||||||
|
// for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
|
||||||
|
if let Some(cw_row) = codewords[codewordsRow] {
|
||||||
|
if !cw_row.hasValidRowNumber() {
|
||||||
|
self.adjustRowNumbersWithCodewords(
|
||||||
|
barcodeColumn,
|
||||||
|
codewordsRow,
|
||||||
|
codewords,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// if (codewords[codewordsRow] == null) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// if (!codewords[codewordsRow].hasValidRowNumber()) {
|
||||||
|
// self.adjustRowNumbers(barcodeColumn, codewordsRow, codewords);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unadjustedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjustRowNumbersByRow(&mut self) -> u32 {
|
||||||
|
self.adjustRowNumbersFromBothRI();
|
||||||
|
// TODO we should only do full row adjustments if row numbers of left and right row indicator column match.
|
||||||
|
// Maybe it's even better to calculated the height (in codeword rows) and divide it by the number of barcode
|
||||||
|
// rows. This, together with the LRI and RRI row numbers should allow us to get a good estimate where a row
|
||||||
|
// number starts and ends.
|
||||||
|
let unadjustedCount = self.adjustRowNumbersFromLRI();
|
||||||
|
unadjustedCount + self.adjustRowNumbersFromRRI()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjustRowNumbersFromBothRI(&mut self) {
|
||||||
|
if self.detectionRXingResultColumns[0].is_some()
|
||||||
|
&& self.detectionRXingResultColumns[self.barcodeColumnCount as usize + 1].is_some()
|
||||||
|
{
|
||||||
|
// let LRIcodewords = self.detectionRXingResultColumns[0].as_ref().unwrap().getCodewords();
|
||||||
|
// let RRIcodewords = self.detectionRXingResultColumns[self.barcodeColumnCount as usize + 1].as_ref().unwrap().getCodewords();
|
||||||
|
for codewordsRow in 0..self.detectionRXingResultColumns[0]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewords()
|
||||||
|
.len()
|
||||||
|
{
|
||||||
|
// for (int codewordsRow = 0; codewordsRow < LRIcodewords.length; codewordsRow++) {
|
||||||
|
if
|
||||||
|
//let (Some(lricw), Some(rricw)) =
|
||||||
|
self.detectionRXingResultColumns[0]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewords()[codewordsRow]
|
||||||
|
.is_some()
|
||||||
|
&& self.detectionRXingResultColumns[self.barcodeColumnCount as usize + 1]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewords()[codewordsRow]
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
if self.detectionRXingResultColumns[0]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewords()[codewordsRow]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getRowNumber()
|
||||||
|
== self.detectionRXingResultColumns[self.barcodeColumnCount as usize + 1]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewords()[codewordsRow]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getRowNumber()
|
||||||
|
{
|
||||||
|
// if (LRIcodewords[codewordsRow] != null &&
|
||||||
|
// RRIcodewords[codewordsRow] != null &&
|
||||||
|
// LRIcodewords[codewordsRow].getRowNumber() == RRIcodewords[codewordsRow].getRowNumber()) {
|
||||||
|
for barcodeColumn in 1..=self.barcodeColumnCount {
|
||||||
|
// for (int barcodeColumn = 1; barcodeColumn <= barcodeColumnCount; barcodeColumn++) {
|
||||||
|
if self.detectionRXingResultColumns[barcodeColumn].is_some()
|
||||||
|
//let Some(dc_col) =
|
||||||
|
//&mut self.detectionRXingResultColumns[barcodeColumn]
|
||||||
|
{
|
||||||
|
if self.detectionRXingResultColumns[barcodeColumn]
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewordsMut()[codewordsRow]
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
//let Some(codeword) = &mut self.detectionRXingResultColumns[barcodeColumn].as_mut().unwrap().getCodewordsMut()[codewordsRow] {
|
||||||
|
let new_row_number = self.detectionRXingResultColumns[0]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewords()[codewordsRow]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getRowNumber();
|
||||||
|
self.detectionRXingResultColumns[barcodeColumn]
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewordsMut()[codewordsRow]
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.setRowNumber(new_row_number);
|
||||||
|
if !self.detectionRXingResultColumns[barcodeColumn]
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewordsMut()[codewordsRow]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.hasValidRowNumber()
|
||||||
|
{
|
||||||
|
// self.detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow] = None;
|
||||||
|
self.detectionRXingResultColumns[barcodeColumn]
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewordsMut()[codewordsRow] = None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// let codeword = self.detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
||||||
|
// if (codeword == null) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if (detectionRXingResultColumns[0] == null || detectionRXingResultColumns[barcodeColumnCount + 1] == null) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjustRowNumbersFromRRI(&self) -> u32 {
|
||||||
|
if let Some(col) = &self.detectionRXingResultColumns[self.barcodeColumnCount as usize + 1] {
|
||||||
|
let mut unadjustedCount = 0;
|
||||||
|
let codewords = col.getCodewords();
|
||||||
|
for codewordsRow in 0..codewords.len() {
|
||||||
|
// for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
|
||||||
|
if let Some(codeword_col) = codewords[codewordsRow] {
|
||||||
|
let rowIndicatorRowNumber = codeword_col.getRowNumber();
|
||||||
|
let mut invalidRowCounts = 0;
|
||||||
|
let mut barcodeColumn = self.barcodeColumnCount as usize + 1;
|
||||||
|
while barcodeColumn > 0 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP {
|
||||||
|
// for (int barcodeColumn = barcodeColumnCount + 1;
|
||||||
|
// barcodeColumn > 0 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP;
|
||||||
|
// barcodeColumn--) {
|
||||||
|
if let Some(bc_col) = &self.detectionRXingResultColumns[barcodeColumn] {
|
||||||
|
if let Some(codeword) = bc_col.getCodewords()[codewordsRow] {
|
||||||
|
invalidRowCounts = Self::adjustRowNumberIfValid(
|
||||||
|
rowIndicatorRowNumber,
|
||||||
|
invalidRowCounts,
|
||||||
|
&mut Some(codeword),
|
||||||
|
);
|
||||||
|
if !codeword.hasValidRowNumber() {
|
||||||
|
unadjustedCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
barcodeColumn -= 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unadjustedCount
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
// if (detectionRXingResultColumns[barcodeColumnCount + 1] == null) {
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
// int unadjustedCount = 0;
|
||||||
|
// Codeword[] codewords = detectionRXingResultColumns[barcodeColumnCount + 1].getCodewords();
|
||||||
|
// for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
|
||||||
|
// if (codewords[codewordsRow] == null) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// int rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber();
|
||||||
|
// int invalidRowCounts = 0;
|
||||||
|
// for (int barcodeColumn = barcodeColumnCount + 1;
|
||||||
|
// barcodeColumn > 0 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP;
|
||||||
|
// barcodeColumn--) {
|
||||||
|
// Codeword codeword = detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
||||||
|
// if (codeword != null) {
|
||||||
|
// invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);
|
||||||
|
// if (!codeword.hasValidRowNumber()) {
|
||||||
|
// unadjustedCount++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return unadjustedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjustRowNumbersFromLRI(&self) -> u32 {
|
||||||
|
if let Some(col) = &self.detectionRXingResultColumns[0] {
|
||||||
|
let mut unadjustedCount = 0;
|
||||||
|
let codewords = col.getCodewords();
|
||||||
|
for codewordsRow in 0..codewords.len() {
|
||||||
|
// for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
|
||||||
|
if let Some(codeword_in_row) = codewords[codewordsRow] {
|
||||||
|
let rowIndicatorRowNumber = codeword_in_row.getRowNumber();
|
||||||
|
let mut invalidRowCounts = 0;
|
||||||
|
let mut barcodeColumn = 1_usize;
|
||||||
|
while barcodeColumn < self.barcodeColumnCount as usize + 1
|
||||||
|
&& invalidRowCounts < ADJUST_ROW_NUMBER_SKIP
|
||||||
|
{
|
||||||
|
// for (int barcodeColumn = 1;
|
||||||
|
// barcodeColumn < barcodeColumnCount + 1 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP;
|
||||||
|
// barcodeColumn++) {
|
||||||
|
if let Some(bc_column) = &self.detectionRXingResultColumns[barcodeColumn] {
|
||||||
|
if let Some(codeword) = bc_column.getCodewords()[codewordsRow] {
|
||||||
|
invalidRowCounts = Self::adjustRowNumberIfValid(
|
||||||
|
rowIndicatorRowNumber,
|
||||||
|
invalidRowCounts,
|
||||||
|
&mut Some(codeword),
|
||||||
|
);
|
||||||
|
if !codeword.hasValidRowNumber() {
|
||||||
|
unadjustedCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// let codeword = self.detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
||||||
|
// if (codeword != null) {
|
||||||
|
// invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);
|
||||||
|
// if (!codeword.hasValidRowNumber()) {
|
||||||
|
// unadjustedCount+=1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
barcodeColumn += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// if (codewords[codewordsRow] == null) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// let rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber();
|
||||||
|
// let invalidRowCounts = 0;
|
||||||
|
// for (int barcodeColumn = 1;
|
||||||
|
// barcodeColumn < barcodeColumnCount + 1 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP;
|
||||||
|
// barcodeColumn++) {
|
||||||
|
// Codeword codeword = detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
||||||
|
// if (codeword != null) {
|
||||||
|
// invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);
|
||||||
|
// if (!codeword.hasValidRowNumber()) {
|
||||||
|
// unadjustedCount++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
unadjustedCount
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (detectionRXingResultColumns[0] == null) {
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
// int unadjustedCount = 0;
|
||||||
|
// Codeword[] codewords = detectionRXingResultColumns[0].getCodewords();
|
||||||
|
// for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {
|
||||||
|
// if (codewords[codewordsRow] == null) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// int rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber();
|
||||||
|
// int invalidRowCounts = 0;
|
||||||
|
// for (int barcodeColumn = 1;
|
||||||
|
// barcodeColumn < barcodeColumnCount + 1 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP;
|
||||||
|
// barcodeColumn++) {
|
||||||
|
// Codeword codeword = detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
||||||
|
// if (codeword != null) {
|
||||||
|
// invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);
|
||||||
|
// if (!codeword.hasValidRowNumber()) {
|
||||||
|
// unadjustedCount++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return unadjustedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjustRowNumberIfValid(
|
||||||
|
rowIndicatorRowNumber: i32,
|
||||||
|
mut invalidRowCounts: u32,
|
||||||
|
codeword: &mut Option<Codeword>,
|
||||||
|
) -> u32 {
|
||||||
|
if let Some(codeword) = codeword {
|
||||||
|
if !codeword.hasValidRowNumber() {
|
||||||
|
if codeword.isValidRowNumber(rowIndicatorRowNumber) {
|
||||||
|
codeword.setRowNumber(rowIndicatorRowNumber);
|
||||||
|
invalidRowCounts = 0;
|
||||||
|
} else {
|
||||||
|
invalidRowCounts += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invalidRowCounts
|
||||||
|
} else {
|
||||||
|
invalidRowCounts
|
||||||
|
}
|
||||||
|
// if (codeword == null) {
|
||||||
|
// return invalidRowCounts;
|
||||||
|
// }
|
||||||
|
// if (!codeword.hasValidRowNumber()) {
|
||||||
|
// if (codeword.isValidRowNumber(rowIndicatorRowNumber)) {
|
||||||
|
// codeword.setRowNumber(rowIndicatorRowNumber);
|
||||||
|
// invalidRowCounts = 0;
|
||||||
|
// } else {
|
||||||
|
// invalidRowCounts+=1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return invalidRowCounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjustRowNumbersWithCodewords(
|
||||||
|
&self,
|
||||||
|
barcodeColumn: usize,
|
||||||
|
codewordsRow: usize,
|
||||||
|
codewords: &[Option<Codeword>],
|
||||||
|
) {
|
||||||
|
let mut codeword = codewords[codewordsRow];
|
||||||
|
let previousColumnCodewords = self.detectionRXingResultColumns[barcodeColumn - 1]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewords();
|
||||||
|
let mut nextColumnCodewords = previousColumnCodewords;
|
||||||
|
if self.detectionRXingResultColumns[barcodeColumn + 1].is_some() {
|
||||||
|
nextColumnCodewords = self.detectionRXingResultColumns[barcodeColumn + 1]
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.getCodewords(); //col.getCodewords();
|
||||||
|
}
|
||||||
|
// if (self.detectionRXingResultColumns[barcodeColumn + 1] != null) {
|
||||||
|
// nextColumnCodewords = self.detectionRXingResultColumns[barcodeColumn + 1].getCodewords();
|
||||||
|
// }
|
||||||
|
|
||||||
|
let mut otherCodewords = [None; 14]; // new Codeword[14];
|
||||||
|
|
||||||
|
otherCodewords[2] = previousColumnCodewords[codewordsRow];
|
||||||
|
otherCodewords[3] = nextColumnCodewords[codewordsRow];
|
||||||
|
|
||||||
|
if codewordsRow > 0 {
|
||||||
|
otherCodewords[0] = codewords[codewordsRow - 1];
|
||||||
|
otherCodewords[4] = previousColumnCodewords[codewordsRow - 1];
|
||||||
|
otherCodewords[5] = nextColumnCodewords[codewordsRow - 1];
|
||||||
|
}
|
||||||
|
if codewordsRow > 1 {
|
||||||
|
otherCodewords[8] = codewords[codewordsRow - 2];
|
||||||
|
otherCodewords[10] = previousColumnCodewords[codewordsRow - 2];
|
||||||
|
otherCodewords[11] = nextColumnCodewords[codewordsRow - 2];
|
||||||
|
}
|
||||||
|
if codewordsRow < codewords.len() - 1 {
|
||||||
|
otherCodewords[1] = codewords[codewordsRow + 1];
|
||||||
|
otherCodewords[6] = previousColumnCodewords[codewordsRow + 1];
|
||||||
|
otherCodewords[7] = nextColumnCodewords[codewordsRow + 1];
|
||||||
|
}
|
||||||
|
if codewordsRow < codewords.len() - 2 {
|
||||||
|
otherCodewords[9] = codewords[codewordsRow + 2];
|
||||||
|
otherCodewords[12] = previousColumnCodewords[codewordsRow + 2];
|
||||||
|
otherCodewords[13] = nextColumnCodewords[codewordsRow + 2];
|
||||||
|
}
|
||||||
|
for otherCodeword in otherCodewords {
|
||||||
|
if Self::adjustRowNumber(codeword.as_mut().unwrap(), &otherCodeword) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// for (Codeword otherCodeword : otherCodewords) {
|
||||||
|
// if (adjustRowNumber(codeword, otherCodeword)) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true, if row number was adjusted, false otherwise
|
||||||
|
*/
|
||||||
|
fn adjustRowNumber(codeword: &mut Codeword, otherCodeword: &Option<Codeword>) -> bool {
|
||||||
|
if let Some(otherCodeword) = otherCodeword {
|
||||||
|
if otherCodeword.hasValidRowNumber()
|
||||||
|
&& otherCodeword.getBucket() == codeword.getBucket()
|
||||||
|
{
|
||||||
|
codeword.setRowNumber(otherCodeword.getRowNumber());
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getBarcodeColumnCount(&self) -> usize {
|
||||||
|
self.barcodeColumnCount
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getBarcodeRowCount(&self) -> u32 {
|
||||||
|
self.barcodeMetadata.getRowCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getBarcodeECLevel(&self) -> u32 {
|
||||||
|
self.barcodeMetadata.getErrorCorrectionLevel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn setBoundingBox(&'a mut self, boundingBox:BoundingBox<'a>) {
|
||||||
|
// self.boundingBox = boundingBox;
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn getBoundingBox(&self) -> &BoundingBox {
|
||||||
|
&self.boundingBox
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn setDetectionRXingResultColumn(&mut self, barcodeColumn:usize, detectionRXingResultColumn:Option<DetectionRXingResultColumn<'a>>) {
|
||||||
|
// self.detectionRXingResultColumns[barcodeColumn] = detectionRXingResultColumn;
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn getDetectionRXingResultColumn(
|
||||||
|
&self,
|
||||||
|
barcodeColumn: usize,
|
||||||
|
) -> &Option<DetectionRXingResultColumn> {
|
||||||
|
&self.detectionRXingResultColumns[barcodeColumn]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DetectionRXingResult<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public String toString() {
|
||||||
|
// DetectionRXingResultColumn rowIndicatorColumn = detectionRXingResultColumns[0];
|
||||||
|
// if (rowIndicatorColumn == null) {
|
||||||
|
// rowIndicatorColumn = detectionRXingResultColumns[barcodeColumnCount + 1];
|
||||||
|
// }
|
||||||
|
// try (Formatter formatter = new Formatter()) {
|
||||||
|
// for (int codewordsRow = 0; codewordsRow < rowIndicatorColumn.getCodewords().length; codewordsRow++) {
|
||||||
|
// formatter.format("CW %3d:", codewordsRow);
|
||||||
|
// for (int barcodeColumn = 0; barcodeColumn < barcodeColumnCount + 2; barcodeColumn++) {
|
||||||
|
// if (detectionRXingResultColumns[barcodeColumn] == null) {
|
||||||
|
// formatter.format(" | ");
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// Codeword codeword = detectionRXingResultColumns[barcodeColumn].getCodewords()[codewordsRow];
|
||||||
|
// if (codeword == null) {
|
||||||
|
// formatter.format(" | ");
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// formatter.format(" %3d|%3d", codeword.getRowNumber(), codeword.getValue());
|
||||||
|
// }
|
||||||
|
// formatter.format("%n");
|
||||||
|
// }
|
||||||
|
// return formatter.toString();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -16,13 +16,14 @@
|
|||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use super::{BoundingBox, Codeword};
|
use super::{BoundingBox, Codeword, DetectionRXingResultRowIndicatorColumn};
|
||||||
|
|
||||||
const MAX_NEARBY_DISTANCE: u32 = 5;
|
const MAX_NEARBY_DISTANCE: u32 = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Guenther Grau
|
* @author Guenther Grau
|
||||||
*/
|
*/
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct DetectionRXingResultColumn<'a> {
|
pub struct DetectionRXingResultColumn<'a> {
|
||||||
boundingBox: BoundingBox<'a>,
|
boundingBox: BoundingBox<'a>,
|
||||||
codewords: Vec<Option<Codeword>>,
|
codewords: Vec<Option<Codeword>>,
|
||||||
@@ -83,6 +84,12 @@ impl<'a> DetectionRXingResultColumn<'_> {
|
|||||||
pub fn getCodewords(&self) -> &[Option<Codeword>] {
|
pub fn getCodewords(&self) -> &[Option<Codeword>] {
|
||||||
&self.codewords
|
&self.codewords
|
||||||
}
|
}
|
||||||
|
pub(super) fn getCodewordsMut(&mut self) -> &mut [Option<Codeword>] {
|
||||||
|
&mut self.codewords
|
||||||
|
}
|
||||||
|
pub fn as_row_indicator(&self) -> DetectionRXingResultRowIndicatorColumn {
|
||||||
|
DetectionRXingResultRowIndicatorColumn::new(&self.boundingBox, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for DetectionRXingResultColumn<'_> {
|
impl Display for DetectionRXingResultColumn<'_> {
|
||||||
|
|||||||
311
src/pdf417/decoder/detection_result_row_indicator_column.rs
Normal file
311
src/pdf417/decoder/detection_result_row_indicator_column.rs
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
* 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 std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::{pdf417::pdf_417_common, ResultPoint};
|
||||||
|
|
||||||
|
use super::{BarcodeMetadata, BarcodeValue, BoundingBox, Codeword, DetectionRXingResultColumn};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Guenther Grau
|
||||||
|
*/
|
||||||
|
pub struct DetectionRXingResultRowIndicatorColumn<'a>(DetectionRXingResultColumn<'a>, bool);
|
||||||
|
impl<'a> DetectionRXingResultRowIndicatorColumn<'_> {
|
||||||
|
// private final boolean isLeft;
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
boundingBox: &'a BoundingBox,
|
||||||
|
isLeft: bool,
|
||||||
|
) -> DetectionRXingResultRowIndicatorColumn<'a> {
|
||||||
|
DetectionRXingResultRowIndicatorColumn(DetectionRXingResultColumn::new(boundingBox), isLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setRowNumbers(&mut self) {
|
||||||
|
for codeword_opt in self.0.getCodewordsMut() {
|
||||||
|
// for (Codeword codeword : getCodewords()) {
|
||||||
|
if let Some(codeword) = codeword_opt {
|
||||||
|
// if (codeword != null) {
|
||||||
|
codeword.setRowNumberAsRowIndicatorColumn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO implement properly
|
||||||
|
// TODO maybe we should add missing codewords to store the correct row number to make
|
||||||
|
// finding row numbers for other columns easier
|
||||||
|
// use row height count to make detection of invalid row numbers more reliable
|
||||||
|
pub fn adjustCompleteIndicatorColumnRowNumbers(&mut self, barcodeMetadata: &BarcodeMetadata) {
|
||||||
|
// let codewords = self.0.getCodewordsMut();
|
||||||
|
self.setRowNumbers();
|
||||||
|
Self::removeIncorrectCodewords(self.0.getCodewordsMut(), barcodeMetadata, self.1);
|
||||||
|
let boundingBox = self.0.getBoundingBox();
|
||||||
|
let top = if self.1 {
|
||||||
|
boundingBox.getTopLeft()
|
||||||
|
} else {
|
||||||
|
boundingBox.getTopRight()
|
||||||
|
};
|
||||||
|
let bottom = if self.1 {
|
||||||
|
boundingBox.getBottomLeft()
|
||||||
|
} else {
|
||||||
|
boundingBox.getBottomRight()
|
||||||
|
};
|
||||||
|
let firstRow = self.0.imageRowToCodewordIndex(top.getY() as u32);
|
||||||
|
let lastRow = self.0.imageRowToCodewordIndex(bottom.getY() as u32);
|
||||||
|
// We need to be careful using the average row height. Barcode could be skewed so that we have smaller and
|
||||||
|
// taller rows
|
||||||
|
//float averageRowHeight = (lastRow - firstRow) / (float) barcodeMetadata.getRowCount();
|
||||||
|
let mut barcodeRow = -1;
|
||||||
|
let mut maxRowHeight = 1;
|
||||||
|
let mut currentRowHeight = 0;
|
||||||
|
for codewordsRow in firstRow..lastRow {
|
||||||
|
// for (int codewordsRow = firstRow; codewordsRow < lastRow; codewordsRow++) {
|
||||||
|
if let Some(codeword) = self.0.getCodewordsMut()[codewordsRow] {
|
||||||
|
// if (codewords[codewordsRow] == null) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// let codeword = codewords[codewordsRow];
|
||||||
|
|
||||||
|
let rowDifference = codeword.getRowNumber() - barcodeRow;
|
||||||
|
|
||||||
|
// TODO improve handling with case where first row indicator doesn't start with 0
|
||||||
|
|
||||||
|
if rowDifference == 0 {
|
||||||
|
currentRowHeight += 1;
|
||||||
|
} else if rowDifference == 1 {
|
||||||
|
maxRowHeight = maxRowHeight.max(currentRowHeight);
|
||||||
|
currentRowHeight = 1;
|
||||||
|
barcodeRow = codeword.getRowNumber();
|
||||||
|
} else if rowDifference < 0
|
||||||
|
|| codeword.getRowNumber() >= barcodeMetadata.getRowCount() as i32
|
||||||
|
|| rowDifference > codewordsRow as i32
|
||||||
|
{
|
||||||
|
self.0.getCodewordsMut()[codewordsRow] = None;
|
||||||
|
} else {
|
||||||
|
let checkedRows;
|
||||||
|
if maxRowHeight > 2 {
|
||||||
|
checkedRows = (maxRowHeight - 2) * rowDifference;
|
||||||
|
} else {
|
||||||
|
checkedRows = rowDifference;
|
||||||
|
}
|
||||||
|
let mut closePreviousCodewordFound = checkedRows >= codewordsRow as i32;
|
||||||
|
let mut i = 1;
|
||||||
|
while i <= checkedRows && !closePreviousCodewordFound {
|
||||||
|
// for (int i = 1; i <= checkedRows && !closePreviousCodewordFound; i++) {
|
||||||
|
// there must be (height * rowDifference) number of codewords missing. For now we assume height = 1.
|
||||||
|
// This should hopefully get rid of most problems already.
|
||||||
|
closePreviousCodewordFound =
|
||||||
|
self.0.getCodewords()[codewordsRow as usize - i as usize].is_some();
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
if closePreviousCodewordFound {
|
||||||
|
self.0.getCodewordsMut()[codewordsRow] = None;
|
||||||
|
} else {
|
||||||
|
barcodeRow = codeword.getRowNumber();
|
||||||
|
currentRowHeight = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//return (int) (averageRowHeight + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getRowHeights(&mut self) -> Option<Vec<u32>> {
|
||||||
|
if let Some(barcodeMetadata) = self.getBarcodeMetadata() {
|
||||||
|
self.adjustIncompleteIndicatorColumnRowNumbers(&barcodeMetadata);
|
||||||
|
let mut result = vec![0; barcodeMetadata.getRowCount() as usize];
|
||||||
|
for codeword_opt in self.0.getCodewords() {
|
||||||
|
// for (Codeword codeword : getCodewords()) {
|
||||||
|
if let Some(codeword) = codeword_opt {
|
||||||
|
let rowNumber = codeword.getRowNumber();
|
||||||
|
if rowNumber as usize >= result.len() {
|
||||||
|
// We have more rows than the barcode metadata allows for, ignore them.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result[rowNumber as usize] += 1;
|
||||||
|
}
|
||||||
|
// else throw exception?
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(result)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO maybe we should add missing codewords to store the correct row number to make
|
||||||
|
// finding row numbers for other columns easier
|
||||||
|
// use row height count to make detection of invalid row numbers more reliable
|
||||||
|
fn adjustIncompleteIndicatorColumnRowNumbers(&mut self, barcodeMetadata: &BarcodeMetadata) {
|
||||||
|
let boundingBox = self.0.getBoundingBox();
|
||||||
|
let top = if self.1 {
|
||||||
|
boundingBox.getTopLeft()
|
||||||
|
} else {
|
||||||
|
boundingBox.getTopRight()
|
||||||
|
};
|
||||||
|
let bottom = if self.1 {
|
||||||
|
boundingBox.getBottomLeft()
|
||||||
|
} else {
|
||||||
|
boundingBox.getBottomRight()
|
||||||
|
};
|
||||||
|
let firstRow = self.0.imageRowToCodewordIndex(top.getY() as u32);
|
||||||
|
let lastRow = self.0.imageRowToCodewordIndex(bottom.getY() as u32);
|
||||||
|
//float averageRowHeight = (lastRow - firstRow) / (float) barcodeMetadata.getRowCount();
|
||||||
|
let codewords = self.0.getCodewordsMut();
|
||||||
|
let mut barcodeRow = -1;
|
||||||
|
let mut maxRowHeight = 1;
|
||||||
|
let mut currentRowHeight = 0;
|
||||||
|
for codewordsRow in firstRow..lastRow {
|
||||||
|
// for (int codewordsRow = firstRow; codewordsRow < lastRow; codewordsRow++) {
|
||||||
|
|
||||||
|
if let Some(codeword) = &mut codewords[codewordsRow] {
|
||||||
|
codeword.setRowNumberAsRowIndicatorColumn();
|
||||||
|
|
||||||
|
let rowDifference = codeword.getRowNumber() - barcodeRow;
|
||||||
|
|
||||||
|
// TODO improve handling with case where first row indicator doesn't start with 0
|
||||||
|
|
||||||
|
if rowDifference == 0 {
|
||||||
|
currentRowHeight += 1;
|
||||||
|
} else if rowDifference == 1 {
|
||||||
|
maxRowHeight = maxRowHeight.max(currentRowHeight);
|
||||||
|
currentRowHeight = 1;
|
||||||
|
barcodeRow = codeword.getRowNumber();
|
||||||
|
} else if codeword.getRowNumber() >= barcodeMetadata.getRowCount() as i32 {
|
||||||
|
codewords[codewordsRow] = None;
|
||||||
|
} else {
|
||||||
|
barcodeRow = codeword.getRowNumber();
|
||||||
|
currentRowHeight = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//return (int) (averageRowHeight + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getBarcodeMetadata(&mut self) -> Option<BarcodeMetadata> {
|
||||||
|
let codewords = self.0.getCodewordsMut();
|
||||||
|
let mut barcodeColumnCount = BarcodeValue::new();
|
||||||
|
let mut barcodeRowCountUpperPart = BarcodeValue::new();
|
||||||
|
let mut barcodeRowCountLowerPart = BarcodeValue::new();
|
||||||
|
let mut barcodeECLevel = BarcodeValue::new();
|
||||||
|
for codeword_opt in codewords.iter_mut() {
|
||||||
|
// for (Codeword codeword : codewords) {
|
||||||
|
if let Some(codeword) = codeword_opt {
|
||||||
|
codeword.setRowNumberAsRowIndicatorColumn();
|
||||||
|
let rowIndicatorValue = codeword.getValue() % 30;
|
||||||
|
let mut codewordRowNumber = codeword.getRowNumber();
|
||||||
|
if !self.1 {
|
||||||
|
codewordRowNumber += 2;
|
||||||
|
}
|
||||||
|
match codewordRowNumber % 3 {
|
||||||
|
0 => barcodeRowCountUpperPart.setValue(rowIndicatorValue * 3 + 1),
|
||||||
|
1 => {
|
||||||
|
barcodeECLevel.setValue(rowIndicatorValue / 3);
|
||||||
|
barcodeRowCountLowerPart.setValue(rowIndicatorValue % 3);
|
||||||
|
}
|
||||||
|
2 => barcodeColumnCount.setValue(rowIndicatorValue + 1),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Maybe we should check if we have ambiguous values?
|
||||||
|
if (barcodeColumnCount.getValue().len() == 0)
|
||||||
|
|| (barcodeRowCountUpperPart.getValue().len() == 0)
|
||||||
|
|| (barcodeRowCountLowerPart.getValue().len() == 0)
|
||||||
|
|| (barcodeECLevel.getValue().len() == 0)
|
||||||
|
|| barcodeColumnCount.getValue()[0] < 1
|
||||||
|
|| barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0]
|
||||||
|
< pdf_417_common::MIN_ROWS_IN_BARCODE
|
||||||
|
|| barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0]
|
||||||
|
> pdf_417_common::MAX_ROWS_IN_BARCODE
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let barcodeMetadata = BarcodeMetadata::new(
|
||||||
|
barcodeColumnCount.getValue()[0],
|
||||||
|
barcodeRowCountUpperPart.getValue()[0],
|
||||||
|
barcodeRowCountLowerPart.getValue()[0],
|
||||||
|
barcodeECLevel.getValue()[0],
|
||||||
|
);
|
||||||
|
Self::removeIncorrectCodewords(codewords, &barcodeMetadata, self.1);
|
||||||
|
|
||||||
|
Some(barcodeMetadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn removeIncorrectCodewords(
|
||||||
|
codewords: &mut [Option<Codeword>],
|
||||||
|
barcodeMetadata: &BarcodeMetadata,
|
||||||
|
isLeft: bool,
|
||||||
|
) {
|
||||||
|
// Remove codewords which do not match the metadata
|
||||||
|
// TODO Maybe we should keep the incorrect codewords for the start and end positions?
|
||||||
|
for codewordRow in 0..codewords.len() {
|
||||||
|
// for (int codewordRow = 0; codewordRow < codewords.length; codewordRow++) {
|
||||||
|
if let Some(codeword) = codewords[codewordRow] {
|
||||||
|
let rowIndicatorValue = codeword.getValue() % 30;
|
||||||
|
let mut codewordRowNumber = codeword.getRowNumber();
|
||||||
|
if codewordRowNumber > barcodeMetadata.getRowCount() as i32 {
|
||||||
|
codewords[codewordRow] = None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !isLeft {
|
||||||
|
codewordRowNumber += 2;
|
||||||
|
}
|
||||||
|
match codewordRowNumber % 3 {
|
||||||
|
0 => {
|
||||||
|
if rowIndicatorValue * 3 + 1 != barcodeMetadata.getRowCountUpperPart() {
|
||||||
|
codewords[codewordRow] = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
if rowIndicatorValue / 3 != barcodeMetadata.getErrorCorrectionLevel()
|
||||||
|
|| rowIndicatorValue % 3 != barcodeMetadata.getRowCountLowerPart()
|
||||||
|
{
|
||||||
|
codewords[codewordRow] = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
if rowIndicatorValue + 1 != barcodeMetadata.getColumnCount() {
|
||||||
|
codewords[codewordRow] = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isLeft(&self) -> bool {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DetectionRXingResultRowIndicatorColumn<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "IsLeft: {} \n {}", self.1, self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -124,7 +124,7 @@ fn runEuclideanAlgorithm(
|
|||||||
let mut a = a;
|
let mut a = a;
|
||||||
let mut b = b;
|
let mut b = b;
|
||||||
if a.getDegree() < b.getDegree() {
|
if a.getDegree() < b.getDegree() {
|
||||||
std::mem::swap(&mut a,&mut b);
|
std::mem::swap(&mut a, &mut b);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rLast = a;
|
let mut rLast = a;
|
||||||
|
|||||||
@@ -16,3 +16,11 @@ mod detection_result_column;
|
|||||||
pub use detection_result_column::*;
|
pub use detection_result_column::*;
|
||||||
|
|
||||||
pub mod pdf_417_codeword_decoder;
|
pub mod pdf_417_codeword_decoder;
|
||||||
|
|
||||||
|
mod detection_result_row_indicator_column;
|
||||||
|
pub use detection_result_row_indicator_column::*;
|
||||||
|
|
||||||
|
mod detection_result;
|
||||||
|
pub use detection_result::*;
|
||||||
|
|
||||||
|
// pub mod pdf_417_scanning_decoder;
|
||||||
@@ -14,49 +14,32 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.google.zxing.pdf417.decoder;
|
use crate::Exceptions;
|
||||||
|
|
||||||
import com.google.zxing.ChecksumException;
|
|
||||||
import com.google.zxing.FormatException;
|
|
||||||
import com.google.zxing.NotFoundException;
|
|
||||||
import com.google.zxing.RXingResultPoint;
|
|
||||||
import com.google.zxing.common.BitMatrix;
|
|
||||||
import com.google.zxing.common.DecoderRXingResult;
|
|
||||||
import com.google.zxing.common.detector.MathUtils;
|
|
||||||
import com.google.zxing.pdf417.PDF417Common;
|
|
||||||
import com.google.zxing.pdf417.decoder.ec.ErrorCorrection;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Formatter;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Guenther Grau
|
* @author Guenther Grau
|
||||||
*/
|
*/
|
||||||
public final class PDF417ScanningDecoder {
|
|
||||||
|
|
||||||
private static final int CODEWORD_SKEW_SIZE = 2;
|
const CODEWORD_SKEW_SIZE :u32= 2;
|
||||||
|
|
||||||
private static final int MAX_ERRORS = 3;
|
const MAX_ERRORS :u32= 3;
|
||||||
private static final int MAX_EC_CODEWORDS = 512;
|
const MAX_EC_CODEWORDS:u32 = 512;
|
||||||
private static final ErrorCorrection errorCorrection = new ErrorCorrection();
|
// const errorCorrection:ErrorCorrection = ErrorCorrection::new();
|
||||||
|
|
||||||
private PDF417ScanningDecoder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO don't pass in minCodewordWidth and maxCodewordWidth, pass in barcode columns for start and stop pattern
|
// 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.
|
// 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
|
// 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.
|
// than it should be. This can happen if the scanner used a bad blackpoint.
|
||||||
public static DecoderRXingResult decode(BitMatrix image,
|
pub fn decode( image:&BitMatrix,
|
||||||
RXingResultPoint imageTopLeft,
|
imageTopLeft:RXingResultPoint,
|
||||||
RXingResultPoint imageBottomLeft,
|
imageBottomLeft:RXingResultPoint,
|
||||||
RXingResultPoint imageTopRight,
|
imageTopRight:RXingResultPoint,
|
||||||
RXingResultPoint imageBottomRight,
|
imageBottomRight:RXingResultPoint,
|
||||||
int minCodewordWidth,
|
minCodewordWidth:u32,
|
||||||
int maxCodewordWidth)
|
maxCodewordWidth:u32)
|
||||||
throws NotFoundException, FormatException, ChecksumException {
|
-> Result<DecoderRXingResult,Exceptions> {
|
||||||
BoundingBox boundingBox = new BoundingBox(image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight);
|
BoundingBox boundingBox = new BoundingBox(image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight);
|
||||||
DetectionRXingResultRowIndicatorColumn leftRowIndicatorColumn = null;
|
DetectionRXingResultRowIndicatorColumn leftRowIndicatorColumn = null;
|
||||||
DetectionRXingResultRowIndicatorColumn rightRowIndicatorColumn = null;
|
DetectionRXingResultRowIndicatorColumn rightRowIndicatorColumn = null;
|
||||||
@@ -125,9 +108,9 @@ public final class PDF417ScanningDecoder {
|
|||||||
return createDecoderRXingResult(detectionRXingResult);
|
return createDecoderRXingResult(detectionRXingResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DetectionRXingResult merge(DetectionRXingResultRowIndicatorColumn leftRowIndicatorColumn,
|
fn merge( leftRowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn,
|
||||||
DetectionRXingResultRowIndicatorColumn rightRowIndicatorColumn)
|
rightRowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn)
|
||||||
throws NotFoundException {
|
-> Result<DetectionRXingResult,Exceptions> {
|
||||||
if (leftRowIndicatorColumn == null && rightRowIndicatorColumn == null) {
|
if (leftRowIndicatorColumn == null && rightRowIndicatorColumn == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -140,8 +123,8 @@ public final class PDF417ScanningDecoder {
|
|||||||
return new DetectionRXingResult(barcodeMetadata, boundingBox);
|
return new DetectionRXingResult(barcodeMetadata, boundingBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BoundingBox adjustBoundingBox(DetectionRXingResultRowIndicatorColumn rowIndicatorColumn)
|
fn adjustBoundingBox( rowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn)
|
||||||
throws NotFoundException {
|
-> Result<BoundingBox,Exceptions> {
|
||||||
if (rowIndicatorColumn == null) {
|
if (rowIndicatorColumn == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -175,7 +158,7 @@ public final class PDF417ScanningDecoder {
|
|||||||
rowIndicatorColumn.isLeft());
|
rowIndicatorColumn.isLeft());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getMax(int[] values) {
|
fn getMax( values:&[u32]) -> u32{
|
||||||
int maxValue = -1;
|
int maxValue = -1;
|
||||||
for (int value : values) {
|
for (int value : values) {
|
||||||
maxValue = Math.max(maxValue, value);
|
maxValue = Math.max(maxValue, value);
|
||||||
@@ -183,8 +166,8 @@ public final class PDF417ScanningDecoder {
|
|||||||
return maxValue;
|
return maxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BarcodeMetadata getBarcodeMetadata(DetectionRXingResultRowIndicatorColumn leftRowIndicatorColumn,
|
fn getBarcodeMetadata( leftRowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn,
|
||||||
DetectionRXingResultRowIndicatorColumn rightRowIndicatorColumn) {
|
rightRowIndicatorColumn:&DetectionRXingResultRowIndicatorColumn) -> BarcodeMetadata{
|
||||||
BarcodeMetadata leftBarcodeMetadata;
|
BarcodeMetadata leftBarcodeMetadata;
|
||||||
if (leftRowIndicatorColumn == null ||
|
if (leftRowIndicatorColumn == null ||
|
||||||
(leftBarcodeMetadata = leftRowIndicatorColumn.getBarcodeMetadata()) == null) {
|
(leftBarcodeMetadata = leftRowIndicatorColumn.getBarcodeMetadata()) == null) {
|
||||||
@@ -204,12 +187,12 @@ public final class PDF417ScanningDecoder {
|
|||||||
return leftBarcodeMetadata;
|
return leftBarcodeMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DetectionRXingResultRowIndicatorColumn getRowIndicatorColumn(BitMatrix image,
|
fn getRowIndicatorColumn( image:&BitMatrix,
|
||||||
BoundingBox boundingBox,
|
boundingBox:&BoundingBox,
|
||||||
RXingResultPoint startPoint,
|
startPoint:&RXingResultPoint,
|
||||||
boolean leftToRight,
|
leftToRight:u32,
|
||||||
int minCodewordWidth,
|
minCodewordWidth:u32,
|
||||||
int maxCodewordWidth) {
|
maxCodewordWidth:u32)-> DetectionRXingResultRowIndicatorColumn {
|
||||||
DetectionRXingResultRowIndicatorColumn rowIndicatorColumn = new DetectionRXingResultRowIndicatorColumn(boundingBox,
|
DetectionRXingResultRowIndicatorColumn rowIndicatorColumn = new DetectionRXingResultRowIndicatorColumn(boundingBox,
|
||||||
leftToRight);
|
leftToRight);
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
@@ -232,7 +215,7 @@ public final class PDF417ScanningDecoder {
|
|||||||
return rowIndicatorColumn;
|
return rowIndicatorColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void adjustCodewordCount(DetectionRXingResult detectionRXingResult, BarcodeValue[][] barcodeMatrix)
|
fn adjustCodewordCount( detectionRXingResult:&DetectionRXingResult, barcodeMatrix:&Vec<Vec<BarcodeValue>>)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
BarcodeValue barcodeMatrix01 = barcodeMatrix[0][1];
|
BarcodeValue barcodeMatrix01 = barcodeMatrix[0][1];
|
||||||
int[] numberOfCodewords = barcodeMatrix01.getValue();
|
int[] numberOfCodewords = barcodeMatrix01.getValue();
|
||||||
@@ -252,8 +235,7 @@ public final class PDF417ScanningDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DecoderRXingResult createDecoderRXingResult(DetectionRXingResult detectionRXingResult) throws FormatException,
|
fn createDecoderRXingResult( detectionRXingResult:&DetectionRXingResult) -> Result<DecoderRXingResult,Exceptions> {
|
||||||
ChecksumException, NotFoundException {
|
|
||||||
BarcodeValue[][] barcodeMatrix = createBarcodeMatrix(detectionRXingResult);
|
BarcodeValue[][] barcodeMatrix = createBarcodeMatrix(detectionRXingResult);
|
||||||
adjustCodewordCount(detectionRXingResult, barcodeMatrix);
|
adjustCodewordCount(detectionRXingResult, barcodeMatrix);
|
||||||
Collection<Integer> erasures = new ArrayList<>();
|
Collection<Integer> erasures = new ArrayList<>();
|
||||||
@@ -295,12 +277,12 @@ public final class PDF417ScanningDecoder {
|
|||||||
* @param ambiguousIndexValues two dimensional array that contains the ambiguous values. The first dimension must
|
* @param ambiguousIndexValues two dimensional array that contains the ambiguous values. The first dimension must
|
||||||
* be the same length as the ambiguousIndexes array
|
* be the same length as the ambiguousIndexes array
|
||||||
*/
|
*/
|
||||||
private static DecoderRXingResult createDecoderRXingResultFromAmbiguousValues(int ecLevel,
|
fn createDecoderRXingResultFromAmbiguousValues( ecLevel:u21,
|
||||||
int[] codewords,
|
codewords:&[u32],
|
||||||
int[] erasureArray,
|
erasureArray&[u32],
|
||||||
int[] ambiguousIndexes,
|
ambiguousIndexes&[u32],
|
||||||
int[][] ambiguousIndexValues)
|
ambiguousIndexValues:&Vec<Vec<u32>>)
|
||||||
throws FormatException, ChecksumException {
|
-> Result<DecoderRXingResult,Exceptions> {
|
||||||
int[] ambiguousIndexCount = new int[ambiguousIndexes.length];
|
int[] ambiguousIndexCount = new int[ambiguousIndexes.length];
|
||||||
|
|
||||||
int tries = 100;
|
int tries = 100;
|
||||||
@@ -331,7 +313,7 @@ public final class PDF417ScanningDecoder {
|
|||||||
throw ChecksumException.getChecksumInstance();
|
throw ChecksumException.getChecksumInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BarcodeValue[][] createBarcodeMatrix(DetectionRXingResult detectionRXingResult) {
|
fn createBarcodeMatrix( detectionRXingResult:&DetectionRXingResult) -> Vec<Vec<BarcodeValue>>{
|
||||||
BarcodeValue[][] barcodeMatrix =
|
BarcodeValue[][] barcodeMatrix =
|
||||||
new BarcodeValue[detectionRXingResult.getBarcodeRowCount()][detectionRXingResult.getBarcodeColumnCount() + 2];
|
new BarcodeValue[detectionRXingResult.getBarcodeRowCount()][detectionRXingResult.getBarcodeColumnCount() + 2];
|
||||||
for (int row = 0; row < barcodeMatrix.length; row++) {
|
for (int row = 0; row < barcodeMatrix.length; row++) {
|
||||||
@@ -361,14 +343,14 @@ public final class PDF417ScanningDecoder {
|
|||||||
return barcodeMatrix;
|
return barcodeMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isValidBarcodeColumn(DetectionRXingResult detectionRXingResult, int barcodeColumn) {
|
fn isValidBarcodeColumn( detectionRXingResult:&DetectionRXingResult, barcodeColumn:u32) -> bool{
|
||||||
return barcodeColumn >= 0 && barcodeColumn <= detectionRXingResult.getBarcodeColumnCount() + 1;
|
return barcodeColumn >= 0 && barcodeColumn <= detectionRXingResult.getBarcodeColumnCount() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getStartColumn(DetectionRXingResult detectionRXingResult,
|
fn getStartColumn( detectionRXingResult:&DetectionRXingResult,
|
||||||
int barcodeColumn,
|
barcodeColumn:u32,
|
||||||
int imageRow,
|
imageRow:u32,
|
||||||
boolean leftToRight) {
|
leftToRight:bool) -> u32{
|
||||||
int offset = leftToRight ? 1 : -1;
|
int offset = leftToRight ? 1 : -1;
|
||||||
Codeword codeword = null;
|
Codeword codeword = null;
|
||||||
if (isValidBarcodeColumn(detectionRXingResult, barcodeColumn - offset)) {
|
if (isValidBarcodeColumn(detectionRXingResult, barcodeColumn - offset)) {
|
||||||
@@ -404,14 +386,14 @@ public final class PDF417ScanningDecoder {
|
|||||||
return leftToRight ? detectionRXingResult.getBoundingBox().getMinX() : detectionRXingResult.getBoundingBox().getMaxX();
|
return leftToRight ? detectionRXingResult.getBoundingBox().getMinX() : detectionRXingResult.getBoundingBox().getMaxX();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Codeword detectCodeword(BitMatrix image,
|
fn detectCodeword( image:&BitMatrix,
|
||||||
int minColumn,
|
minColumn:u32,
|
||||||
int maxColumn,
|
maxColumn:u32,
|
||||||
boolean leftToRight,
|
leftToRight:bool,
|
||||||
int startColumn,
|
startColumn:u32,
|
||||||
int imageRow,
|
imageRow:u32,
|
||||||
int minCodewordWidth,
|
minCodewordWidth:u32,
|
||||||
int maxCodewordWidth) {
|
maxCodewordWidth:u32) -> Codeword{
|
||||||
startColumn = adjustCodewordStartColumn(image, minColumn, maxColumn, leftToRight, startColumn, imageRow);
|
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
|
// 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.
|
// and try to adjust the read pixels, e.g. remove single pixel errors or try to cut off exceeding pixels.
|
||||||
@@ -462,12 +444,12 @@ public final class PDF417ScanningDecoder {
|
|||||||
return new Codeword(startColumn, endColumn, getCodewordBucketNumber(decodedValue), codeword);
|
return new Codeword(startColumn, endColumn, getCodewordBucketNumber(decodedValue), codeword);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] getModuleBitCount(BitMatrix image,
|
fn getModuleBitCount( image:&BitMatrix,
|
||||||
int minColumn,
|
minColumn:u32,
|
||||||
int maxColumn,
|
maxColumn:u32,
|
||||||
boolean leftToRight,
|
leftToRight:bool,
|
||||||
int startColumn,
|
startColumn:u32,
|
||||||
int imageRow) {
|
imageRow:u32) -> Option<[u32;8]>{
|
||||||
int imageColumn = startColumn;
|
int imageColumn = startColumn;
|
||||||
int[] moduleBitCount = new int[8];
|
int[] moduleBitCount = new int[8];
|
||||||
int moduleNumber = 0;
|
int moduleNumber = 0;
|
||||||
@@ -491,16 +473,16 @@ public final class PDF417ScanningDecoder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getNumberOfECCodeWords(int barcodeECLevel) {
|
fn getNumberOfECCodeWords(int barcodeECLevel) -> u32{
|
||||||
return 2 << barcodeECLevel;
|
return 2 << barcodeECLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int adjustCodewordStartColumn(BitMatrix image,
|
fn adjustCodewordStartColumn( image:&BitMatrix,
|
||||||
int minColumn,
|
minColumn:u32,
|
||||||
int maxColumn,
|
maxColumn:u32,
|
||||||
boolean leftToRight,
|
leftToRight:bool,
|
||||||
int codewordStartColumn,
|
codewordStartColumn:u32,
|
||||||
int imageRow) {
|
imageRow:u32) -> u32{
|
||||||
int correctedStartColumn = codewordStartColumn;
|
int correctedStartColumn = codewordStartColumn;
|
||||||
int increment = leftToRight ? -1 : 1;
|
int increment = leftToRight ? -1 : 1;
|
||||||
// there should be no black pixels before the start column. If there are, then we need to start earlier.
|
// there should be no black pixels before the start column. If there are, then we need to start earlier.
|
||||||
@@ -518,13 +500,12 @@ public final class PDF417ScanningDecoder {
|
|||||||
return correctedStartColumn;
|
return correctedStartColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkCodewordSkew(int codewordSize, int minCodewordWidth, int maxCodewordWidth) {
|
fn checkCodewordSkew( codewordSize:u32, minCodewordWidth:u32, maxCodewordWidth:u32) -> bool{
|
||||||
return minCodewordWidth - CODEWORD_SKEW_SIZE <= codewordSize &&
|
return minCodewordWidth - CODEWORD_SKEW_SIZE <= codewordSize &&
|
||||||
codewordSize <= maxCodewordWidth + CODEWORD_SKEW_SIZE;
|
codewordSize <= maxCodewordWidth + CODEWORD_SKEW_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DecoderRXingResult decodeCodewords(int[] codewords, int ecLevel, int[] erasures) throws FormatException,
|
fn decodeCodewords( codewords:&[u32], ecLevel:u32, erasures:&[u32]) -> Result<DecoderRXingResult,Exceptions>{
|
||||||
ChecksumException {
|
|
||||||
if (codewords.length == 0) {
|
if (codewords.length == 0) {
|
||||||
throw FormatException.getFormatInstance();
|
throw FormatException.getFormatInstance();
|
||||||
}
|
}
|
||||||
@@ -549,7 +530,7 @@ public final class PDF417ScanningDecoder {
|
|||||||
* @param numECCodewords number of error correction codewords that are available in codewords
|
* @param numECCodewords number of error correction codewords that are available in codewords
|
||||||
* @throws ChecksumException if error correction fails
|
* @throws ChecksumException if error correction fails
|
||||||
*/
|
*/
|
||||||
private static int correctErrors(int[] codewords, int[] erasures, int numECCodewords) throws ChecksumException {
|
fn correctErrors( codewords:&[u32], erasures:&[u32], numECCodewords:u32) -> Result<u32,Exceptions> {
|
||||||
if (erasures != null &&
|
if (erasures != null &&
|
||||||
erasures.length > numECCodewords / 2 + MAX_ERRORS ||
|
erasures.length > numECCodewords / 2 + MAX_ERRORS ||
|
||||||
numECCodewords < 0 ||
|
numECCodewords < 0 ||
|
||||||
@@ -563,7 +544,7 @@ public final class PDF417ScanningDecoder {
|
|||||||
/**
|
/**
|
||||||
* Verify that all is OK with the codeword array.
|
* Verify that all is OK with the codeword array.
|
||||||
*/
|
*/
|
||||||
private static void verifyCodewordCount(int[] codewords, int numECCodewords) throws FormatException {
|
fn verifyCodewordCount( codewords:&[u32], numECCodewords:u32) -> Result<(),Exceptions> {
|
||||||
if (codewords.length < 4) {
|
if (codewords.length < 4) {
|
||||||
// Codeword array size should be at least 4 allowing for
|
// Codeword array size should be at least 4 allowing for
|
||||||
// Count CW, At least one Data CW, Error Correction CW, Error Correction CW
|
// Count CW, At least one Data CW, Error Correction CW, Error Correction CW
|
||||||
@@ -586,7 +567,7 @@ public final class PDF417ScanningDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] getBitCountForCodeword(int codeword) {
|
fn getBitCountForCodeword( codeword:u32) -> [u32;8]{
|
||||||
int[] result = new int[8];
|
int[] result = new int[8];
|
||||||
int previousValue = 0;
|
int previousValue = 0;
|
||||||
int i = result.length - 1;
|
int i = result.length - 1;
|
||||||
@@ -604,15 +585,15 @@ public final class PDF417ScanningDecoder {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getCodewordBucketNumber(int codeword) {
|
fn getCodewordBucketNumber( codeword:u32) -> u32{
|
||||||
return getCodewordBucketNumber(getBitCountForCodeword(codeword));
|
return getCodewordBucketNumber(getBitCountForCodeword(codeword));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getCodewordBucketNumber(int[] moduleBitCount) {
|
fn getCodewordBucketNumber( moduleBitCount:&[u32]) -> u32{
|
||||||
return (moduleBitCount[0] - moduleBitCount[2] + moduleBitCount[4] - moduleBitCount[6] + 9) % 9;
|
return (moduleBitCount[0] - moduleBitCount[2] + moduleBitCount[4] - moduleBitCount[6] + 9) % 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toString(BarcodeValue[][] barcodeMatrix) {
|
fn toString( barcodeMatrix:Vec<Vec<BarcodeValue>>) -> String{
|
||||||
try (Formatter formatter = new Formatter()) {
|
try (Formatter formatter = new Formatter()) {
|
||||||
for (int row = 0; row < barcodeMatrix.length; row++) {
|
for (int row = 0; row < barcodeMatrix.length; row++) {
|
||||||
formatter.format("Row %2d: ", row);
|
formatter.format("Row %2d: ", row);
|
||||||
@@ -631,4 +612,3 @@ public final class PDF417ScanningDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user