mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 12:22:34 +00:00
300 lines
11 KiB
Java
300 lines
11 KiB
Java
/*
|
|
* 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();
|
|
}
|
|
}
|
|
|
|
}
|