mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Support for aztec codes
git-svn-id: https://zxing.googlecode.com/svn/trunk@1668 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
0c3a1650d2
commit
e1c047c54b
|
@ -35,6 +35,9 @@ public final class BarcodeFormat {
|
|||
/** Data Matrix 2D barcode format. */
|
||||
public static final BarcodeFormat DATA_MATRIX = new BarcodeFormat("DATA_MATRIX");
|
||||
|
||||
/** Aztec 2D barcode format. */
|
||||
public static final BarcodeFormat AZTEC = new BarcodeFormat("AZTEC");
|
||||
|
||||
/** UPC-E 1D format. */
|
||||
public static final BarcodeFormat UPC_E = new BarcodeFormat("UPC_E");
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.aztec.AztecReader;
|
||||
import com.google.zxing.datamatrix.DataMatrixReader;
|
||||
import com.google.zxing.oned.MultiFormatOneDReader;
|
||||
import com.google.zxing.pdf417.PDF417Reader;
|
||||
|
@ -116,6 +117,9 @@ public final class MultiFormatReader implements Reader {
|
|||
if (formats.contains(BarcodeFormat.DATA_MATRIX)) {
|
||||
readers.addElement(new DataMatrixReader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.AZTEC)) {
|
||||
readers.addElement(new AztecReader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.PDF417)) {
|
||||
readers.addElement(new PDF417Reader());
|
||||
}
|
||||
|
|
32
core/src/com/google/zxing/aztec/AztecDetectorResult.java
Normal file
32
core/src/com/google/zxing/aztec/AztecDetectorResult.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package com.google.zxing.aztec;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.DetectorResult;
|
||||
|
||||
public class AztecDetectorResult extends DetectorResult {
|
||||
|
||||
private final boolean compact;
|
||||
private final int nbDatablocks;
|
||||
private final int nbLayers;
|
||||
|
||||
public AztecDetectorResult(BitMatrix bits, ResultPoint[] points, boolean compact, int nbDatablocks, int nbLayers) {
|
||||
super(bits, points);
|
||||
this.compact = compact;
|
||||
this.nbDatablocks = nbDatablocks;
|
||||
this.nbLayers = nbLayers;
|
||||
}
|
||||
|
||||
public int getNbLayers() {
|
||||
return nbLayers;
|
||||
}
|
||||
|
||||
public int getNbDatablocks() {
|
||||
return nbDatablocks;
|
||||
}
|
||||
|
||||
public boolean isCompact() {
|
||||
return compact;
|
||||
}
|
||||
|
||||
}
|
93
core/src/com/google/zxing/aztec/AztecReader.java
Normal file
93
core/src/com/google/zxing/aztec/AztecReader.java
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2007 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.aztec;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.ChecksumException;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.Reader;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultMetadataType;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.ResultPointCallback;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
import com.google.zxing.common.DetectorResult;
|
||||
import com.google.zxing.aztec.decoder.Decoder;
|
||||
import com.google.zxing.aztec.detector.Detector;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode Aztec codes in an image.
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class AztecReader implements Reader {
|
||||
|
||||
private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
|
||||
|
||||
/**
|
||||
* Locates and decodes a Data Matrix code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the Data Matrix code
|
||||
* @throws NotFoundException if a Data Matrix code cannot be found
|
||||
* @throws FormatException if a Data Matrix code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
public Result decode(BinaryBitmap image, Hashtable hints)
|
||||
throws NotFoundException, ChecksumException, FormatException {
|
||||
DecoderResult decoderResult;
|
||||
ResultPoint[] points;
|
||||
|
||||
AztecDetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect();
|
||||
points = detectorResult.getPoints();
|
||||
|
||||
if (hints != null && detectorResult.getPoints() != null){
|
||||
ResultPointCallback rpcb = (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
|
||||
if (rpcb != null){
|
||||
for (int i = 0; i < detectorResult.getPoints().length; i++){
|
||||
rpcb.foundPossibleResultPoint(detectorResult.getPoints()[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decoderResult = new Decoder().decode(detectorResult);
|
||||
|
||||
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.AZTEC);
|
||||
|
||||
if (decoderResult.getByteSegments() != null) {
|
||||
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult.getByteSegments());
|
||||
}
|
||||
if (decoderResult.getECLevel() != null) {
|
||||
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult.getECLevel().toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
467
core/src/com/google/zxing/aztec/decoder/Decoder.java
Normal file
467
core/src/com/google/zxing/aztec/decoder/Decoder.java
Normal file
|
@ -0,0 +1,467 @@
|
|||
/*
|
||||
* Copyright 2007 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.aztec.decoder;
|
||||
|
||||
import com.google.zxing.ChecksumException;
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.aztec.AztecDetectorResult;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||
|
||||
/**
|
||||
* <p>The main class which implements Aztec Code decoding -- as opposed to locating and extracting
|
||||
* the Aztec Code from an image.</p>
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class Decoder {
|
||||
|
||||
int numCodewords;
|
||||
int codewordSize;
|
||||
AztecDetectorResult ddata;
|
||||
|
||||
public Decoder() {}
|
||||
|
||||
public DecoderResult decode(AztecDetectorResult detectorResult) throws FormatException, ChecksumException {
|
||||
ddata = detectorResult;
|
||||
BitMatrix matrix = detectorResult.getBits();
|
||||
|
||||
if (!ddata.isCompact()){
|
||||
matrix = removeDashedLines(ddata.getBits());
|
||||
}
|
||||
|
||||
boolean[] rawbits = extractBits(matrix);
|
||||
|
||||
boolean[] correctedBits = correctBits(rawbits);
|
||||
|
||||
String result = getEncodedData(correctedBits);
|
||||
|
||||
return new DecoderResult(null, result, null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Gets the string encoded in the aztec code bits
|
||||
*
|
||||
* @param correctedBits
|
||||
* @return the decoded string
|
||||
* @throws FormatException if the input is not valid
|
||||
*/
|
||||
private String getEncodedData(boolean[] correctedBits) throws FormatException {
|
||||
|
||||
int endIndex = codewordSize * ddata.getNbDatablocks();
|
||||
if (endIndex > correctedBits.length){
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
int lastTable = UPPER;
|
||||
int table = UPPER;
|
||||
int startIndex = 0;
|
||||
int code;
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean end = false;
|
||||
boolean shift = false;
|
||||
boolean switchShift = false;
|
||||
|
||||
while (!end){
|
||||
|
||||
if (shift){
|
||||
// the table is for the next character only
|
||||
switchShift = true;
|
||||
} else {
|
||||
// save the current table in case next one is a shift
|
||||
lastTable = table;
|
||||
}
|
||||
|
||||
switch (table) {
|
||||
case BINARY:
|
||||
if (endIndex - startIndex < 8){
|
||||
end = true;
|
||||
break;
|
||||
}
|
||||
code = readCode(correctedBits, startIndex, 8);
|
||||
startIndex += 8;
|
||||
|
||||
result.append((char)(code));
|
||||
break;
|
||||
|
||||
default:
|
||||
int size = 5;
|
||||
|
||||
if (table == DIGIT){
|
||||
size = 4;
|
||||
}
|
||||
|
||||
if (endIndex - startIndex < size){
|
||||
end = true;
|
||||
break;
|
||||
}
|
||||
|
||||
code = readCode(correctedBits, startIndex, size);
|
||||
startIndex += size;
|
||||
|
||||
String str = getCharacter(table, code);
|
||||
if (!str.startsWith("CTRL_")){
|
||||
result.append(str);
|
||||
} else {
|
||||
// Table changes
|
||||
table = getTable(str.charAt(5));
|
||||
|
||||
if (str.charAt(6) == 'S'){
|
||||
shift = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (switchShift){
|
||||
table = lastTable;
|
||||
shift = false;
|
||||
switchShift = false;
|
||||
}
|
||||
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* gets the table corresponding to the char passed
|
||||
*
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
private int getTable(char t) {
|
||||
int table = UPPER;
|
||||
|
||||
switch (t){
|
||||
case 'U':
|
||||
table = UPPER; break;
|
||||
case 'L':
|
||||
table = LOWER; break;
|
||||
case 'P':
|
||||
table = PUNCT; break;
|
||||
case 'M':
|
||||
table = MIXED; break;
|
||||
case 'D':
|
||||
table = DIGIT; break;
|
||||
case 'B':
|
||||
table = BINARY; break;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Gets the character (or string) corresponding to the passed code in the given table
|
||||
*
|
||||
* @param table the table used
|
||||
* @param code the code of the character
|
||||
* @return
|
||||
*/
|
||||
private String getCharacter(int table, int code) {
|
||||
switch (table){
|
||||
case UPPER:
|
||||
return UPPER_TABLE[code];
|
||||
case LOWER:
|
||||
return LOWER_TABLE[code];
|
||||
case MIXED:
|
||||
return MIXED_TABLE[code];
|
||||
case PUNCT:
|
||||
return PUNCT_TABLE[code];
|
||||
case DIGIT:
|
||||
return DIGIT_TABLE[code];
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <p> performs RS error correction on an array of bits </p>
|
||||
*
|
||||
* @param rawbits
|
||||
* @return the corrected array
|
||||
* @throws FormatException if the input contains too many errors
|
||||
*/
|
||||
private boolean[] correctBits(boolean[] rawbits) throws FormatException {
|
||||
GenericGF gf;
|
||||
|
||||
if (ddata.getNbLayers() <= 2){
|
||||
codewordSize = 6;
|
||||
gf = GenericGF.AZTEC_DATA_6;
|
||||
} else if (ddata.getNbLayers() <= 8) {
|
||||
codewordSize = 8;
|
||||
gf = GenericGF.AZTEC_DATA_8;
|
||||
} else if (ddata.getNbLayers() <= 22) {
|
||||
codewordSize = 10;
|
||||
gf = GenericGF.AZTEC_DATA_10;
|
||||
} else {
|
||||
codewordSize = 12;
|
||||
gf = GenericGF.AZTEC_DATA_12;
|
||||
}
|
||||
|
||||
int numDataCodewords = ddata.getNbDatablocks();
|
||||
int numECCodewords = 0;
|
||||
int offset = 0;
|
||||
|
||||
if (ddata.isCompact()){
|
||||
offset = NbBitsCompact[ddata.getNbLayers()] - numCodewords*codewordSize;
|
||||
numECCodewords = NbDatablockCompact[ddata.getNbLayers()] - numDataCodewords;
|
||||
} else {
|
||||
offset = NbBits[ddata.getNbLayers()] - numCodewords*codewordSize;
|
||||
numECCodewords = NbDatablock[ddata.getNbLayers()] - numDataCodewords;
|
||||
}
|
||||
|
||||
int[] dataWords = new int[numCodewords];
|
||||
for (int i = 0; i < numCodewords; i++){
|
||||
int flag = 1;
|
||||
for (int j = 1; j <= codewordSize; j++){
|
||||
if (rawbits[codewordSize*i + codewordSize - j + offset]){
|
||||
dataWords[i] += flag;
|
||||
}
|
||||
flag <<= 1;
|
||||
}
|
||||
|
||||
if (dataWords[i] >= flag){
|
||||
flag++;
|
||||
}
|
||||
}
|
||||
rawbits = null;
|
||||
|
||||
try {
|
||||
ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(gf);
|
||||
rsDecoder.decode(dataWords, numECCodewords);
|
||||
} catch (ReedSolomonException rse) {
|
||||
System.out.println("END: invalid RS");
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
|
||||
boolean[] correctedBits = new boolean[numDataCodewords*codewordSize];
|
||||
for (int i = 0; i < numDataCodewords; i ++){
|
||||
|
||||
boolean seriesColor = false;
|
||||
int seriesCount = 0;
|
||||
int flag = 1 << (codewordSize - 1);
|
||||
|
||||
for (int j = 0; j < codewordSize; j++){
|
||||
|
||||
boolean color = (dataWords[i] & flag) == flag;
|
||||
|
||||
if (seriesCount != codewordSize - 1){
|
||||
|
||||
if (seriesColor == color){
|
||||
seriesCount++;
|
||||
} else {
|
||||
seriesCount = 1;
|
||||
seriesColor = color;
|
||||
}
|
||||
|
||||
correctedBits[i*codewordSize+j-offset] = color;
|
||||
|
||||
} else {
|
||||
|
||||
if (color == seriesColor){
|
||||
//bit must be inverted
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
seriesColor = false;
|
||||
seriesCount = 0;
|
||||
offset++;
|
||||
}
|
||||
|
||||
flag >>>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return correctedBits;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Gets the array of bits from an Aztec Code matrix
|
||||
*
|
||||
* @param matrix
|
||||
* @return the array of bits
|
||||
* @throws FormatException if the matrix is not a valid aztec code
|
||||
*/
|
||||
private boolean[] extractBits(BitMatrix matrix) throws FormatException {
|
||||
|
||||
boolean[] rawbits;
|
||||
if (ddata.isCompact()){
|
||||
if (ddata.getNbLayers() > NbBitsCompact.length){
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
rawbits = new boolean[NbBitsCompact[ddata.getNbLayers()]];
|
||||
numCodewords = NbDatablockCompact[ddata.getNbLayers()];
|
||||
} else {
|
||||
if (ddata.getNbLayers() > NbBits.length){
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
rawbits = new boolean[NbBits[ddata.getNbLayers()]];
|
||||
numCodewords = NbDatablock[ddata.getNbLayers()];
|
||||
}
|
||||
|
||||
int flip;
|
||||
int layer = ddata.getNbLayers();
|
||||
int size = matrix.height;
|
||||
int rawbitsOffset = 0;
|
||||
int matrixOffset = 0;
|
||||
|
||||
while (layer != 0){
|
||||
|
||||
flip = 0;
|
||||
for (int i = 0; i < 2*size - 4; i++){
|
||||
rawbits[rawbitsOffset+i] = matrix.get(matrixOffset + flip, matrixOffset + i/2);
|
||||
rawbits[rawbitsOffset+2*size - 4 + i] = matrix.get(matrixOffset + i/2, matrixOffset + size-1-flip);
|
||||
flip = (flip + 1)%2;
|
||||
}
|
||||
|
||||
flip = 0;
|
||||
for (int i = 2*size+1; i > 5; i--){
|
||||
rawbits[rawbitsOffset+4*size - 8 + (2*size-i) + 1] = matrix.get(matrixOffset + size-1-flip, matrixOffset + i/2 - 1);
|
||||
rawbits[rawbitsOffset+6*size - 12 + (2*size-i) + 1] = matrix.get(matrixOffset + i/2 - 1, matrixOffset + flip);
|
||||
flip = (flip + 1)%2;
|
||||
}
|
||||
|
||||
matrixOffset += 2;
|
||||
rawbitsOffset += 8*size-16;
|
||||
layer--;
|
||||
size-=4;
|
||||
}
|
||||
|
||||
return rawbits;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* <p> Transforms an aztec code matrix by removing the control dashed lines </p>
|
||||
*
|
||||
* @param matrix
|
||||
* @return
|
||||
*/
|
||||
private BitMatrix removeDashedLines(BitMatrix matrix) {
|
||||
int nbDashed = 1+ 2* ((matrix.width - 1)/2 / 16);
|
||||
BitMatrix newMatrix = new BitMatrix(matrix.width - nbDashed, matrix.height - nbDashed);
|
||||
|
||||
int nx = 0;
|
||||
int ny = 0;
|
||||
|
||||
for (int x = 0; x < matrix.width; x++){
|
||||
|
||||
ny = 0;
|
||||
|
||||
if ((matrix.width / 2 - x)%16 == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int y = 0; y < matrix.height; y++){
|
||||
|
||||
if ((matrix.width / 2 - y)%16 == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matrix.get(x, y)){
|
||||
newMatrix.set(nx, ny);
|
||||
}
|
||||
ny++;
|
||||
}
|
||||
nx++;
|
||||
}
|
||||
|
||||
return newMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Reads a code of given length and at given index in an array of bits
|
||||
*
|
||||
* @param rawbits
|
||||
* @param startIndex
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
private int readCode(boolean[] rawbits, int startIndex, int length) {
|
||||
int res = 0;
|
||||
|
||||
for (int i = startIndex; i < startIndex + length; i++){
|
||||
res = res << 1;
|
||||
if (rawbits[i]){
|
||||
res++;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static final int UPPER = 0;
|
||||
static final int LOWER = 1;
|
||||
static final int MIXED = 2;
|
||||
static final int DIGIT = 3;
|
||||
static final int PUNCT = 4;
|
||||
static final int BINARY = 5;
|
||||
|
||||
static final int NbBitsCompact[] = {
|
||||
0,104, 240, 408, 608
|
||||
};
|
||||
|
||||
static final int NbBits[] = {
|
||||
0, 128, 288, 480, 704, 960, 1248, 1568, 1920, 2304, 2720, 3168, 3648, 4160, 4704, 5280, 5888, 6528, 7200, 7904, 8640, 9408, 10208, 11040, 11904, 12800, 13728, 14688, 15680, 16704, 17760, 18848, 19968
|
||||
};
|
||||
|
||||
static final int NbDatablockCompact[] = {
|
||||
0, 17, 40, 51, 76
|
||||
};
|
||||
|
||||
static final int NbDatablock[] = {
|
||||
0, 21, 48, 60, 88, 120, 156, 196, 240, 230, 272, 316, 364, 416, 470, 528, 588, 652, 720, 790, 864, 940, 1020, 920, 992, 1066, 1144, 1224, 1306, 1392, 1480, 1570, 1664
|
||||
};
|
||||
|
||||
static final String UPPER_TABLE[] = {
|
||||
"CTRL_PS", " ", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "CTRL_LL", "CTRL_ML", "CTRL_DL", "CTRL_BS"
|
||||
};
|
||||
|
||||
static final String LOWER_TABLE[] = {
|
||||
"CTRL_PS", " ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "CTRL_US", "CTRL_ML", "CTRL_DL", "CTRL_BS"
|
||||
};
|
||||
|
||||
static final String MIXED_TABLE[] = {
|
||||
"CTRL_PS", " ", ""+(char)1, ""+(char)2, ""+(char)3, ""+(char)4, ""+(char)5, ""+(char)6, ""+(char)7, ""+(char)8, ""+(char)9, ""+(char)10, ""+(char)11, ""+(char)12, ""+(char)13, ""+(char)27, ""+(char)28, ""+(char)29, ""+(char)30, ""+(char)31, "@", "\\", "^", ""+(char)95, "`", "|", "~", ""+(char)127, "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS"
|
||||
};
|
||||
|
||||
static final String PUNCT_TABLE[] = {
|
||||
"", ""+(char)13, ""+(char)13+(char)10, ""+(char)46+(char)32, ""+(char)44+(char)32, ""+(char)58+(char)32, "!", "\"", "#", "$", "%", "&", "'", "(", ")", ""+(char)42, "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL"
|
||||
};
|
||||
|
||||
static final String DIGIT_TABLE[] = {
|
||||
"CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ".", "CTRL_UL", "CTRL_US"
|
||||
};
|
||||
}
|
667
core/src/com/google/zxing/aztec/detector/Detector.java
Normal file
667
core/src/com/google/zxing/aztec/detector/Detector.java
Normal file
|
@ -0,0 +1,667 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.detector;
|
||||
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.aztec.AztecDetectorResult;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.GridSampler;
|
||||
import com.google.zxing.common.detector.WhiteRectangleDetector;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates logic that can detect an Aztec Code in an image, even if the Aztec Code
|
||||
* is rotated or skewed, or partially obscured.</p>
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class Detector {
|
||||
|
||||
private final BitMatrix image;
|
||||
|
||||
private boolean compact;
|
||||
private int nbLayers;
|
||||
private int nbDataBlocks;
|
||||
private int nbCenterLayers;
|
||||
private int shift;
|
||||
|
||||
public Detector(BitMatrix image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Detects an Aztec Code in an image.</p>
|
||||
*
|
||||
* @return {@link AztecDetectorResult} encapsulating results of detecting an Aztec Code
|
||||
* @throws NotFoundException if no Aztec Code can be found
|
||||
*/
|
||||
public AztecDetectorResult detect() throws NotFoundException {
|
||||
|
||||
// 1. Get the center of the aztec matrix
|
||||
Point pCenter = getMatrixCenter();
|
||||
|
||||
// 2. Get the corners of the center bull's eye
|
||||
Point[] bullEyeCornerPoints = getBullEyeCornerPoints(pCenter);
|
||||
|
||||
// 3. Get the size of the matrix from the bull's eye
|
||||
extractParameters(bullEyeCornerPoints);
|
||||
|
||||
// 4. Get the corners of the matrix
|
||||
ResultPoint[] corners = getMatrixCornerPoints(bullEyeCornerPoints);
|
||||
|
||||
// 5. Sample the grid
|
||||
BitMatrix bits = sampleGrid(image, corners[shift%4], corners[(shift+3)%4], corners[(shift+2)%4], corners[(shift+1)%4]);
|
||||
|
||||
return new AztecDetectorResult(bits, corners, compact, nbDataBlocks, nbLayers);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Extracts the number of data layers and data blocks from the layer around the bull's eye </p>
|
||||
*
|
||||
* @param bullEyeCornerPoints the array of bull's eye corners
|
||||
* @throws NotFoundException in case of too many errors or invalid parameters
|
||||
*/
|
||||
private void extractParameters(Point[] bullEyeCornerPoints)
|
||||
throws NotFoundException {
|
||||
|
||||
// Get the bits around the bull's eye
|
||||
boolean[] resab = sampleLine(bullEyeCornerPoints[0], bullEyeCornerPoints[1], 2*nbCenterLayers+1);
|
||||
boolean[] resbc = sampleLine(bullEyeCornerPoints[1], bullEyeCornerPoints[2], 2*nbCenterLayers+1);
|
||||
boolean[] rescd = sampleLine(bullEyeCornerPoints[2], bullEyeCornerPoints[3], 2*nbCenterLayers+1);
|
||||
boolean[] resda = sampleLine(bullEyeCornerPoints[3], bullEyeCornerPoints[0], 2*nbCenterLayers+1);
|
||||
|
||||
boolean[] shiftedParameterData;
|
||||
boolean[] parameterData;
|
||||
|
||||
// Determine the orientation of the matrix
|
||||
if (resab[0]==true && resab[2*nbCenterLayers]==true){
|
||||
shift = 0;
|
||||
} else if (resbc[0]==true && resbc[2*nbCenterLayers]==true){
|
||||
shift = 1;
|
||||
} else if (rescd[0]==true && rescd[2*nbCenterLayers]==true){
|
||||
shift = 2;
|
||||
} else if (resda[0]==true && resda[2*nbCenterLayers]==true){
|
||||
shift = 3;
|
||||
} else {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
//d a
|
||||
//
|
||||
//c b
|
||||
|
||||
// Flatten the bits in a single array
|
||||
if (compact){
|
||||
shiftedParameterData = new boolean[28];
|
||||
for (int i = 0; i < 7; i++){
|
||||
shiftedParameterData[i] = resab[2+i];
|
||||
shiftedParameterData[i+7] = resbc[2+i];
|
||||
shiftedParameterData[i+14] = rescd[2+i];
|
||||
shiftedParameterData[i+21] = resda[2+i];
|
||||
}
|
||||
|
||||
parameterData = new boolean[28];
|
||||
for (int i = 0; i < 28; i++){
|
||||
parameterData[i] = shiftedParameterData[(i+shift*7)%28];
|
||||
}
|
||||
} else {
|
||||
shiftedParameterData = new boolean[40];
|
||||
for (int i = 0; i < 11; i++){
|
||||
if (i < 5){
|
||||
shiftedParameterData[i] = resab[2+i];
|
||||
shiftedParameterData[i+10] = resbc[2+i];
|
||||
shiftedParameterData[i+20] = rescd[2+i];
|
||||
shiftedParameterData[i+30] = resda[2+i];
|
||||
}
|
||||
if (i > 5){
|
||||
shiftedParameterData[i-1] = resab[2+i];
|
||||
shiftedParameterData[i+10-1] = resbc[2+i];
|
||||
shiftedParameterData[i+20-1] = rescd[2+i];
|
||||
shiftedParameterData[i+30-1] = resda[2+i];
|
||||
}
|
||||
}
|
||||
|
||||
parameterData = new boolean[40];
|
||||
for (int i = 0; i < 40; i++){
|
||||
parameterData[i] = shiftedParameterData[(i+shift*10)%40];
|
||||
}
|
||||
}
|
||||
|
||||
// corrects the error using RS algorithm
|
||||
correctParameterData(parameterData, compact);
|
||||
|
||||
// gets the parameters from the bit array
|
||||
getParameters(parameterData);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <p> Gets the aztec code corners from the bull's eye corners and the parameters </p>
|
||||
*
|
||||
* @param bullEyeCornerPoints the array of bull's eye corners
|
||||
* @return the array of aztec code corners
|
||||
* @throws NotFoundException if the corner points do not fit in the image
|
||||
*/
|
||||
private ResultPoint[] getMatrixCornerPoints(Point[] bullEyeCornerPoints) throws NotFoundException {
|
||||
|
||||
float ratio = (2*nbLayers+(nbLayers>4?1:0)+(nbLayers-4)/8)/(2.0f*nbCenterLayers);
|
||||
|
||||
int dx = bullEyeCornerPoints[0].x-bullEyeCornerPoints[2].x;
|
||||
dx+=dx>0?1:-1;
|
||||
int dy = bullEyeCornerPoints[0].y-bullEyeCornerPoints[2].y;
|
||||
dy+=dy>0?1:-1;
|
||||
|
||||
int targetcx = round(bullEyeCornerPoints[2].x-ratio*dx);
|
||||
int targetcy = round(bullEyeCornerPoints[2].y-ratio*dy);
|
||||
|
||||
int targetax = round(bullEyeCornerPoints[0].x+ratio*dx);
|
||||
int targetay = round(bullEyeCornerPoints[0].y+ratio*dy);
|
||||
|
||||
dx = bullEyeCornerPoints[1].x-bullEyeCornerPoints[3].x;
|
||||
dx+=dx>0?1:-1;
|
||||
dy = bullEyeCornerPoints[1].y-bullEyeCornerPoints[3].y;
|
||||
dy+=dy>0?1:-1;
|
||||
|
||||
int targetdx = round(bullEyeCornerPoints[3].x-ratio*dx);
|
||||
int targetdy = round(bullEyeCornerPoints[3].y-ratio*dy);
|
||||
int targetbx = round(bullEyeCornerPoints[1].x+ratio*dx);
|
||||
int targetby = round(bullEyeCornerPoints[1].y+ratio*dy);
|
||||
|
||||
if (!isValid(targetax, targetay) || !isValid(targetbx, targetby) || !isValid(targetcx, targetcy) || !isValid(targetdx, targetdy)){
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
return new ResultPoint[]{new ResultPoint(targetax, targetay), new ResultPoint(targetbx, targetby), new ResultPoint(targetcx, targetcy), new ResultPoint(targetdx, targetdy)};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <p> Corrects the parameter bits using Reed-Solomon algorithm </p>
|
||||
*
|
||||
* @param parameterData paremeter bits
|
||||
* @param compact true if this is a compact Aztec code
|
||||
* @throws NotFoundException if the array contains too many errors
|
||||
*/
|
||||
private void correctParameterData(boolean[] parameterData, boolean compact) throws NotFoundException {
|
||||
|
||||
int numCodewords;
|
||||
int numDataCodewords;
|
||||
int codewordSize = 4;
|
||||
|
||||
if (compact){
|
||||
numCodewords = 7;
|
||||
numDataCodewords = 2;
|
||||
} else {
|
||||
numCodewords = 10;
|
||||
numDataCodewords = 4;
|
||||
}
|
||||
|
||||
int numECCodewords = numCodewords - numDataCodewords;
|
||||
int[] parameterWords = new int[numCodewords];
|
||||
|
||||
for (int i = 0; i < numCodewords; i++){
|
||||
int flag = 1;
|
||||
for (int j = 1; j <= codewordSize; j++){
|
||||
if (parameterData[codewordSize*i + codewordSize - j]){
|
||||
parameterWords[i] += flag;
|
||||
}
|
||||
flag <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(GenericGF.AZTEC_PARAM);
|
||||
rsDecoder.decode(parameterWords, numECCodewords);
|
||||
} catch (ReedSolomonException rse) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
for (int i = 0; i < numDataCodewords; i ++){
|
||||
int flag = 1;
|
||||
for (int j = 1; j <= codewordSize; j++){
|
||||
parameterData[i*codewordSize+codewordSize-j] = (parameterWords[i] & flag) == flag;
|
||||
flag <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <p> Finds the corners of a bull-eye centered on the passed point </p>
|
||||
*
|
||||
* @param pCenter Center point
|
||||
* @return The corners of the bull-eye
|
||||
* @throws NotFoundException If no valid bull-eye can be found
|
||||
*/
|
||||
private Point[] getBullEyeCornerPoints(Point pCenter) throws NotFoundException {
|
||||
|
||||
Point pina = pCenter;
|
||||
Point pinb = pCenter;
|
||||
Point pinc = pCenter;
|
||||
Point pind = pCenter;
|
||||
Point pouta;
|
||||
Point poutb;
|
||||
Point poutc;
|
||||
Point poutd;
|
||||
|
||||
boolean color = true;
|
||||
|
||||
for (nbCenterLayers = 1; nbCenterLayers < 9; nbCenterLayers++){
|
||||
pouta = getFirstDifferent(pina, color, 1, -1);
|
||||
poutb = getFirstDifferent(pinb, color, 1, 1);
|
||||
poutc = getFirstDifferent(pinc, color, -1, 1);
|
||||
poutd = getFirstDifferent(pind, color, -1, -1);
|
||||
|
||||
//d a
|
||||
//
|
||||
//c b
|
||||
|
||||
if (nbCenterLayers>2){
|
||||
float q = (float)distance(poutd, pouta)*nbCenterLayers/(distance(pind, pina)*(nbCenterLayers+2));
|
||||
if ( q < 0.75 || q > 1.25 || !isWhiteOrBlackRectangle(pouta, poutb, poutc, poutd)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pina = pouta;
|
||||
pinb = poutb;
|
||||
pinc = poutc;
|
||||
pind = poutd;
|
||||
|
||||
color = !color;
|
||||
}
|
||||
|
||||
if (nbCenterLayers!=5 && nbCenterLayers !=7){
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
compact = nbCenterLayers==5;
|
||||
|
||||
float ratio = 0.75f*2/(2*nbCenterLayers-3);
|
||||
|
||||
int dx = pina.x-pinc.x;
|
||||
int dy = pina.y-pinc.y;
|
||||
int targetcx = round(pinc.x-ratio*dx);
|
||||
int targetcy = round(pinc.y-ratio*dy);
|
||||
int targetax = round(pina.x+ratio*dx);
|
||||
int targetay = round(pina.y+ratio*dy);
|
||||
|
||||
dx = pinb.x-pind.x;
|
||||
dy = pinb.y-pind.y;
|
||||
|
||||
int targetdx = round(pind.x-ratio*dx);
|
||||
int targetdy = round(pind.y-ratio*dy);
|
||||
int targetbx = round(pinb.x+ratio*dx);
|
||||
int targetby = round(pinb.y+ratio*dy);
|
||||
|
||||
if (!isValid(targetax, targetay) || !isValid(targetbx, targetby) || !isValid(targetcx, targetcy) || !isValid(targetdx, targetdy)){
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
Point pa = new Point(targetax,targetay);
|
||||
Point pb = new Point(targetbx,targetby);
|
||||
Point pc = new Point(targetcx,targetcy);
|
||||
Point pd = new Point(targetdx,targetdy);
|
||||
|
||||
return new Point[]{pa, pb, pc, pd};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Finds a candidate center point of an Aztec code from an image
|
||||
*
|
||||
* @return the center point
|
||||
*/
|
||||
private Point getMatrixCenter() {
|
||||
|
||||
ResultPoint pointA;
|
||||
ResultPoint pointB;
|
||||
ResultPoint pointC;
|
||||
ResultPoint pointD;
|
||||
|
||||
//Get a white rectangle that can be the border of the matrix in center bull's eye or
|
||||
try{
|
||||
|
||||
ResultPoint[] cornerPoints = new WhiteRectangleDetector(image).detect();
|
||||
pointA = cornerPoints[0];
|
||||
pointB = cornerPoints[1];
|
||||
pointC = cornerPoints[2];
|
||||
pointD = cornerPoints[3];
|
||||
|
||||
} catch (NotFoundException e){
|
||||
|
||||
// This exception can be in case the initial rectangle is white
|
||||
// In that case, surely in the bull's eye, we try to expand the rectangle.
|
||||
int cx = image.width/2;
|
||||
int cy = image.height/2;
|
||||
pointA = getFirstDifferent(new Point(cx+15/2, cy-15/2), false, 1, -1).toResultPoint();
|
||||
pointB = getFirstDifferent(new Point(cx+15/2, cy+15/2), false, 1, 1).toResultPoint();
|
||||
pointC = getFirstDifferent(new Point(cx-15/2, cy+15/2), false, -1, 1).toResultPoint();
|
||||
pointD = getFirstDifferent(new Point(cx-15/2, cy-15/2), false, -1, -1).toResultPoint();
|
||||
|
||||
}
|
||||
|
||||
//Compute the center of the rectangle
|
||||
int cx = round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX())/4);
|
||||
int cy = round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY())/4);
|
||||
|
||||
// Redetermine the white rectangle starting from previously computed center.
|
||||
// This will ensure that we end up with a white rectangle in center bull's eye
|
||||
// in order to compute a more accurate center.
|
||||
try{
|
||||
ResultPoint[] cornerPoints = new WhiteRectangleDetector(image, 15, cx, cy).detect();
|
||||
pointA = cornerPoints[0];
|
||||
pointB = cornerPoints[1];
|
||||
pointC = cornerPoints[2];
|
||||
pointD = cornerPoints[3];
|
||||
} catch (NotFoundException e){
|
||||
|
||||
// This exception can be in case the initial rectangle is white
|
||||
// In that case we try to expand the rectangle.
|
||||
pointA = getFirstDifferent(new Point(cx+15/2, cy-15/2), false, 1, -1).toResultPoint();
|
||||
pointB = getFirstDifferent(new Point(cx+15/2, cy+15/2), false, 1, 1).toResultPoint();
|
||||
pointC = getFirstDifferent(new Point(cx-15/2, cy+15/2), false, -1, 1).toResultPoint();
|
||||
pointD = getFirstDifferent(new Point(cx-15/2, cy-15/2), false, -1, -1).toResultPoint();
|
||||
|
||||
}
|
||||
|
||||
// Recompute the center of the rectangle
|
||||
cx = round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX())/4);
|
||||
cy = round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY())/4);
|
||||
|
||||
Point pCenter = new Point(cx, cy);
|
||||
|
||||
return pCenter;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Samples an Aztec matrix from an image
|
||||
*
|
||||
* @param image
|
||||
* @param topLeft
|
||||
* @param bottomLeft
|
||||
* @param bottomRight
|
||||
* @param topRight
|
||||
* @return
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
private BitMatrix sampleGrid(BitMatrix image,
|
||||
ResultPoint topLeft,
|
||||
ResultPoint bottomLeft,
|
||||
ResultPoint bottomRight,
|
||||
ResultPoint topRight) throws NotFoundException {
|
||||
|
||||
int dimension;
|
||||
if (compact){
|
||||
dimension = 4*nbLayers+11;
|
||||
} else {
|
||||
if (nbLayers <= 4){
|
||||
dimension = 4*nbLayers + 15;
|
||||
} else {
|
||||
dimension = 4*nbLayers + 2*((nbLayers-4)/8 + 1) + 15 ;
|
||||
}
|
||||
}
|
||||
|
||||
GridSampler sampler = GridSampler.getInstance();
|
||||
|
||||
return sampler.sampleGrid(image,
|
||||
dimension,
|
||||
dimension,
|
||||
0.5f,
|
||||
0.5f,
|
||||
dimension - 0.5f,
|
||||
0.5f,
|
||||
dimension - 0.5f,
|
||||
dimension - 0.5f,
|
||||
0.5f,
|
||||
dimension - 0.5f,
|
||||
topLeft.getX(),
|
||||
topLeft.getY(),
|
||||
topRight.getX(),
|
||||
topRight.getY(),
|
||||
bottomRight.getX(),
|
||||
bottomRight.getY(),
|
||||
bottomLeft.getX(),
|
||||
bottomLeft.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets number of layers and number of datablocks from parameter bits
|
||||
*
|
||||
* @param parameterData
|
||||
*/
|
||||
private void getParameters(boolean[] parameterData) {
|
||||
|
||||
int nbBitsForNbLayers = 0;
|
||||
int nbBitsForNbDatablocks = 0;
|
||||
|
||||
if (compact){
|
||||
nbBitsForNbLayers = 2;
|
||||
nbBitsForNbDatablocks = 6;
|
||||
} else {
|
||||
nbBitsForNbLayers = 5;
|
||||
nbBitsForNbDatablocks = 11;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nbBitsForNbLayers; i++){
|
||||
nbLayers = nbLayers << 1;
|
||||
if (parameterData[i]){
|
||||
nbLayers += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = nbBitsForNbLayers; i < nbBitsForNbLayers + nbBitsForNbDatablocks; i++){
|
||||
nbDataBlocks = nbDataBlocks << 1;
|
||||
if (parameterData[i]){
|
||||
nbDataBlocks += 1;
|
||||
}
|
||||
}
|
||||
|
||||
nbLayers ++;
|
||||
nbDataBlocks ++;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Samples a line
|
||||
*
|
||||
* @param p1 first point
|
||||
* @param p2 second point
|
||||
* @param size number of bits
|
||||
* @return the array of bits
|
||||
*/
|
||||
private boolean[] sampleLine(Point p1, Point p2,int size) {
|
||||
|
||||
boolean[] res = new boolean[size];
|
||||
float d = distance(p1,p2);
|
||||
float moduleSize = d/(size-1);
|
||||
float dx = moduleSize*(p2.x - p1.x)/(float)d;
|
||||
float dy = moduleSize*(p2.y - p1.y)/(float)d;
|
||||
|
||||
float px = p1.x;
|
||||
float py = p1.y;
|
||||
|
||||
for (int i = 0; i < size; i++){
|
||||
res[i] = image.get(round(px), round(py));
|
||||
px+=dx;
|
||||
py+=dy;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <p> returns true if the border of the rectangle passed in parameter is compound of white points only or black points only</p>
|
||||
*
|
||||
* @param p1
|
||||
* @param p2
|
||||
* @param p3
|
||||
* @param p4
|
||||
* @return
|
||||
*/
|
||||
private boolean isWhiteOrBlackRectangle(Point p1, Point p2, Point p3, Point p4) {
|
||||
|
||||
int corr = 3;
|
||||
|
||||
p1 = new Point(p1.x-corr, p1.y+corr);
|
||||
p2 = new Point(p2.x-corr, p2.y-corr);
|
||||
p3 = new Point(p3.x+corr, p3.y-corr);
|
||||
p4 = new Point(p4.x+corr, p4.y+corr);
|
||||
|
||||
int cInit = getColor(p4, p1);
|
||||
|
||||
if (cInit == 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
int c = getColor(p1, p2);
|
||||
|
||||
if (c!=cInit || c == 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
c = getColor(p2, p3);
|
||||
|
||||
if (c!=cInit || c == 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
c = getColor(p3, p4);
|
||||
|
||||
if (c!=cInit || c == 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Gets the color of a segment
|
||||
*
|
||||
* @param p1
|
||||
* @param p2
|
||||
* @return 1 if segment more than 90% black, -1 if segment is more than 90% white, 0 else
|
||||
*/
|
||||
private int getColor(Point p1, Point p2) {
|
||||
float d = distance(p1,p2);
|
||||
float dx = (p2.x - p1.x)/d;
|
||||
float dy = (p2.y - p1.y)/d;
|
||||
int error = 0;
|
||||
|
||||
float px = p1.x;
|
||||
float py = p1.y;
|
||||
|
||||
boolean colorModel = image.get(p1.x, p1.y);
|
||||
|
||||
for (int i = 0; i < d; i++){
|
||||
px+=dx;
|
||||
py+=dy;
|
||||
if (image.get(round(px), round(py)) != colorModel){
|
||||
error++;
|
||||
}
|
||||
}
|
||||
|
||||
float errRatio = (float)error/d;
|
||||
|
||||
if (errRatio > 0.1 && errRatio < 0.9){
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (errRatio <= 0.1){
|
||||
return colorModel?1:-1;
|
||||
} else {
|
||||
return colorModel?-1:1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Gets the coordinate of the first point with a different color in the given direction
|
||||
*
|
||||
* @param init Initial point
|
||||
* @param color Color wanted
|
||||
* @param dx
|
||||
* @param dy
|
||||
* @return
|
||||
*/
|
||||
private Point getFirstDifferent(Point init, boolean color, int dx, int dy){
|
||||
int x = init.x+dx;
|
||||
int y = init.y+dy;
|
||||
|
||||
while(isValid(x,y) && image.get(x,y) == color){
|
||||
x+=dx;
|
||||
y+=dy;
|
||||
}
|
||||
|
||||
x-=dx;
|
||||
y-=dy;
|
||||
|
||||
while(isValid(x,y) && image.get(x, y) == color){
|
||||
x+=dx;
|
||||
}
|
||||
x-=dx;
|
||||
|
||||
while(isValid(x,y) && image.get(x, y) == color){
|
||||
y+=dy;
|
||||
}
|
||||
y-=dy;
|
||||
|
||||
return new Point(x,y);
|
||||
}
|
||||
|
||||
private class Point {
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
public ResultPoint toResultPoint(){
|
||||
return new ResultPoint(x, y);
|
||||
}
|
||||
|
||||
public Point(int x, int y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValid(int x, int y) {
|
||||
return (x >= 0 && x < image.width && y > 0 && y < image.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends up being a bit faster than Math.round(). This merely rounds its
|
||||
* argument to the nearest int, where x.5 rounds up.
|
||||
*/
|
||||
private static int round(float d) {
|
||||
return (int) (d + 0.5f);
|
||||
}
|
||||
|
||||
// L2 distance
|
||||
private static float distance(Point a, Point b) {
|
||||
return (float) Math.sqrt((a.x - b.x)
|
||||
* (a.x - b.x) + (a.y - b.y)
|
||||
* (a.y - b.y));
|
||||
}
|
||||
|
||||
}
|
|
@ -25,7 +25,7 @@ import com.google.zxing.ResultPoint;
|
|||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class DetectorResult {
|
||||
public class DetectorResult {
|
||||
|
||||
private final BitMatrix bits;
|
||||
private final ResultPoint[] points;
|
||||
|
|
|
@ -32,17 +32,36 @@ import com.google.zxing.common.BitMatrix;
|
|||
*/
|
||||
public final class WhiteRectangleDetector {
|
||||
|
||||
private static final int INIT_SIZE = 40;
|
||||
private static final int CORR = 1;
|
||||
private final int INIT_SIZE = 30;
|
||||
private final int CORR = 1;
|
||||
|
||||
private final BitMatrix image;
|
||||
private final int height;
|
||||
private final int width;
|
||||
private final int left_init;
|
||||
private final int right_init;
|
||||
private final int down_init;
|
||||
private final int up_init;
|
||||
|
||||
public WhiteRectangleDetector(BitMatrix image) {
|
||||
this.image = image;
|
||||
height = image.getHeight();
|
||||
width = image.getWidth();
|
||||
left_init = (width - INIT_SIZE) >> 1;
|
||||
right_init = (width + INIT_SIZE) >> 1;
|
||||
up_init = (height - INIT_SIZE) >> 1;
|
||||
down_init = (height + INIT_SIZE) >> 1;
|
||||
}
|
||||
|
||||
public WhiteRectangleDetector(BitMatrix image, int INIT_SIZE, int x, int y) {
|
||||
this.image = image;
|
||||
height = image.getHeight();
|
||||
width = image.getWidth();
|
||||
int halfsize = INIT_SIZE >> 1;
|
||||
left_init = x - halfsize;
|
||||
right_init = x + halfsize;
|
||||
up_init = y - halfsize;
|
||||
down_init = y + halfsize;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,10 +80,10 @@ public final class WhiteRectangleDetector {
|
|||
*/
|
||||
public ResultPoint[] detect() throws NotFoundException {
|
||||
|
||||
int left = (width - INIT_SIZE) >> 1;
|
||||
int right = (width + INIT_SIZE) >> 1;
|
||||
int up = (height - INIT_SIZE) >> 1;
|
||||
int down = (height + INIT_SIZE) >> 1;
|
||||
int left = left_init;
|
||||
int right = right_init;
|
||||
int up = up_init;
|
||||
int down = down_init;
|
||||
boolean sizeExceeded = false;
|
||||
boolean aBlackPointFoundOnBorder = true;
|
||||
boolean atLeastOneBlackPointFoundOnBorder = false;
|
||||
|
|
BIN
core/test/data/blackbox/aztec-1/abc-19x19C.png
Normal file
BIN
core/test/data/blackbox/aztec-1/abc-19x19C.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 536 B |
1
core/test/data/blackbox/aztec-1/abc-19x19C.txt
Normal file
1
core/test/data/blackbox/aztec-1/abc-19x19C.txt
Normal file
|
@ -0,0 +1 @@
|
|||
abcdefghijklmnopqrstuvwxyz
|
BIN
core/test/data/blackbox/aztec-1/abc-37x37.png
Normal file
BIN
core/test/data/blackbox/aztec-1/abc-37x37.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
1
core/test/data/blackbox/aztec-1/abc-37x37.txt
Normal file
1
core/test/data/blackbox/aztec-1/abc-37x37.txt
Normal file
|
@ -0,0 +1 @@
|
|||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
|
BIN
core/test/data/blackbox/aztec-1/lorem-075x075.png
Normal file
BIN
core/test/data/blackbox/aztec-1/lorem-075x075.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
1
core/test/data/blackbox/aztec-1/lorem-075x075.txt
Normal file
1
core/test/data/blackbox/aztec-1/lorem-075x075.txt
Normal file
|
@ -0,0 +1 @@
|
|||
In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p
|
BIN
core/test/data/blackbox/aztec-1/lorem-105x105.png
Normal file
BIN
core/test/data/blackbox/aztec-1/lorem-105x105.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
1
core/test/data/blackbox/aztec-1/lorem-105x105.txt
Normal file
1
core/test/data/blackbox/aztec-1/lorem-105x105.txt
Normal file
|
@ -0,0 +1 @@
|
|||
In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p
|
BIN
core/test/data/blackbox/aztec-1/lorem-151x151.png
Normal file
BIN
core/test/data/blackbox/aztec-1/lorem-151x151.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
1
core/test/data/blackbox/aztec-1/lorem-151x151.txt
Normal file
1
core/test/data/blackbox/aztec-1/lorem-151x151.txt
Normal file
|
@ -0,0 +1 @@
|
|||
In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p. In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tris. In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem.
|
BIN
core/test/data/blackbox/aztec-1/tableShifts.png
Normal file
BIN
core/test/data/blackbox/aztec-1/tableShifts.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
1
core/test/data/blackbox/aztec-1/tableShifts.txt
Normal file
1
core/test/data/blackbox/aztec-1/tableShifts.txt
Normal file
|
@ -0,0 +1 @@
|
|||
AhUUDgdy672;..:8KjHH776JHHn3g. 8lm/%22Nn873R2897ks4JKDJ9JJaza2323!::;09UJRrhDQSKJDKdSJSdskjdslkEdjseze:ze
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.common.AbstractBlackBoxTestCase;
|
||||
|
||||
/**
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class AztecBlackBox1TestCase extends AbstractBlackBoxTestCase {
|
||||
|
||||
public AztecBlackBox1TestCase() {
|
||||
super("test/data/blackbox/aztec-1", new AztecReader(), BarcodeFormat.AZTEC);
|
||||
addTest(6, 6, 0.0f);
|
||||
addTest(6, 6, 90.0f);
|
||||
addTest(6, 6, 180.0f);
|
||||
addTest(6, 6, 270.0f);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.common.AbstractBlackBoxTestCase;
|
||||
|
||||
/**
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class AztecBlackBox2TestCase extends AbstractBlackBoxTestCase {
|
||||
|
||||
public AztecBlackBox2TestCase() {
|
||||
super("test/data/blackbox/aztec-2", new AztecReader(), BarcodeFormat.AZTEC);
|
||||
addTest(1, 1, 0.0f);
|
||||
addTest(1, 1, 90.0f);
|
||||
addTest(1, 1, 180.0f);
|
||||
addTest(1, 1, 270.0f);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue