mirror of
https://github.com/zxing/zxing.git
synced 2024-11-09 20:44:03 -08:00
Style changes to Aztec, enable in web app and command line
git-svn-id: https://zxing.googlecode.com/svn/trunk@1669 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
e1c047c54b
commit
c4dede3689
|
@ -1,32 +1,48 @@
|
|||
/*
|
||||
* Copyright 2010 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.ResultPoint;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.DetectorResult;
|
||||
|
||||
public class AztecDetectorResult extends DetectorResult {
|
||||
public final 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;
|
||||
}
|
||||
private final boolean compact;
|
||||
private final int nbDatablocks;
|
||||
private final int nbLayers;
|
||||
|
||||
public int getNbLayers() {
|
||||
return 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 getNbDatablocks() {
|
||||
return nbDatablocks;
|
||||
}
|
||||
public int getNbLayers() {
|
||||
return nbLayers;
|
||||
}
|
||||
|
||||
public boolean isCompact() {
|
||||
return compact;
|
||||
}
|
||||
public int getNbDatablocks() {
|
||||
return nbDatablocks;
|
||||
}
|
||||
|
||||
public boolean isCompact() {
|
||||
return compact;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
* Copyright 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -28,7 +28,6 @@ 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;
|
||||
|
||||
|
@ -41,8 +40,6 @@ import java.util.Hashtable;
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -51,28 +48,26 @@ public final class AztecReader implements Reader {
|
|||
* @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 {
|
||||
public Result decode(BinaryBitmap image) throws NotFoundException, FormatException {
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
public Result decode(BinaryBitmap image, Hashtable hints)
|
||||
throws NotFoundException, ChecksumException, FormatException {
|
||||
DecoderResult decoderResult;
|
||||
ResultPoint[] points;
|
||||
throws NotFoundException, FormatException {
|
||||
|
||||
AztecDetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect();
|
||||
points = detectorResult.getPoints();
|
||||
ResultPoint[] 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]);
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
DecoderResult decoderResult = new Decoder().decode(detectorResult);
|
||||
|
||||
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.AZTEC);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
* Copyright 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,7 +16,6 @@
|
|||
|
||||
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;
|
||||
|
@ -33,435 +32,421 @@ import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
|||
*/
|
||||
public final class Decoder {
|
||||
|
||||
int numCodewords;
|
||||
int codewordSize;
|
||||
AztecDetectorResult ddata;
|
||||
|
||||
public Decoder() {}
|
||||
private static final int UPPER = 0;
|
||||
private static final int LOWER = 1;
|
||||
private static final int MIXED = 2;
|
||||
private static final int DIGIT = 3;
|
||||
private static final int PUNCT = 4;
|
||||
private static final int BINARY = 5;
|
||||
|
||||
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);
|
||||
private static final int[] NB_BITS_COMPACT = {
|
||||
0, 104, 240, 408, 608
|
||||
};
|
||||
|
||||
private static final int[] NB_BITS = {
|
||||
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
|
||||
};
|
||||
|
||||
private static final int[] NB_DATABLOCK_COMPACT = {
|
||||
0, 17, 40, 51, 76
|
||||
};
|
||||
|
||||
private static final int[] NB_DATABLOCK = {
|
||||
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
|
||||
};
|
||||
|
||||
private 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"
|
||||
};
|
||||
|
||||
private 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"
|
||||
};
|
||||
|
||||
private static final String[] MIXED_TABLE = {
|
||||
"CTRL_PS", " ", "\1", "\2", "\3", "\4", "\5", "\6", "\7", "\b", "\t", "\n",
|
||||
"\13", "\f", "\r", "\33", "\34", "\35", "\36", "\37", "@", "\\", "^", "_",
|
||||
"`", "|", "~", "\177", "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS"
|
||||
};
|
||||
|
||||
private static final String[] PUNCT_TABLE = {
|
||||
"", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", "'", "(", ")",
|
||||
"*", "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL"
|
||||
};
|
||||
|
||||
private static final String[] DIGIT_TABLE = {
|
||||
"CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ".", "CTRL_UL", "CTRL_US"
|
||||
};
|
||||
|
||||
private int numCodewords;
|
||||
private int codewordSize;
|
||||
private AztecDetectorResult ddata;
|
||||
|
||||
public DecoderResult decode(AztecDetectorResult detectorResult) throws FormatException {
|
||||
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
|
||||
*
|
||||
* Gets the string encoded in the aztec code bits
|
||||
*
|
||||
* @return the decoded string
|
||||
* @throws FormatException if the input is not valid
|
||||
*/
|
||||
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;
|
||||
}
|
||||
private String getEncodedData(boolean[] correctedBits) throws FormatException {
|
||||
|
||||
/**
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
int endIndex = codewordSize * ddata.getNbDatablocks();
|
||||
if (endIndex > correctedBits.length) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
int lastTable = UPPER;
|
||||
int table = UPPER;
|
||||
int startIndex = 0;
|
||||
StringBuilder result = new StringBuilder(20);
|
||||
boolean end = false;
|
||||
boolean shift = false;
|
||||
boolean switchShift = false;
|
||||
|
||||
static final int NbBitsCompact[] = {
|
||||
0,104, 240, 408, 608
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
int code;
|
||||
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_")) {
|
||||
// Table changes
|
||||
table = getTable(str.charAt(5));
|
||||
|
||||
if (str.charAt(6) == 'S') {
|
||||
shift = true;
|
||||
}
|
||||
} else {
|
||||
result.append(str);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (switchShift) {
|
||||
table = lastTable;
|
||||
shift = false;
|
||||
switchShift = false;
|
||||
}
|
||||
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gets the table corresponding to the char passed
|
||||
*/
|
||||
private static 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;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
/**
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
private static 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>
|
||||
*
|
||||
* @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;
|
||||
int offset;
|
||||
|
||||
if (ddata.isCompact()) {
|
||||
offset = NB_BITS_COMPACT[ddata.getNbLayers()] - numCodewords*codewordSize;
|
||||
numECCodewords = NB_DATABLOCK_COMPACT[ddata.getNbLayers()] - numDataCodewords;
|
||||
} else {
|
||||
offset = NB_BITS[ddata.getNbLayers()] - numCodewords*codewordSize;
|
||||
numECCodewords = NB_DATABLOCK[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++;
|
||||
//}
|
||||
}
|
||||
|
||||
try {
|
||||
ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(gf);
|
||||
rsDecoder.decode(dataWords, numECCodewords);
|
||||
} catch (ReedSolomonException rse) {
|
||||
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 (color == seriesColor) {
|
||||
//bit must be inverted
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
|
||||
seriesColor = false;
|
||||
seriesCount = 0;
|
||||
offset++;
|
||||
} else {
|
||||
|
||||
if (seriesColor == color) {
|
||||
seriesCount++;
|
||||
} else {
|
||||
seriesCount = 1;
|
||||
seriesColor = color;
|
||||
}
|
||||
|
||||
correctedBits[i * codewordSize + j - offset] = color;
|
||||
|
||||
}
|
||||
|
||||
flag >>>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return correctedBits;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Gets the array of bits from an Aztec Code 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() > NB_BITS_COMPACT.length) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
rawbits = new boolean[NB_BITS_COMPACT[ddata.getNbLayers()]];
|
||||
numCodewords = NB_DATABLOCK_COMPACT[ddata.getNbLayers()];
|
||||
} else {
|
||||
if (ddata.getNbLayers() > NB_BITS.length) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
rawbits = new boolean[NB_BITS[ddata.getNbLayers()]];
|
||||
numCodewords = NB_DATABLOCK[ddata.getNbLayers()];
|
||||
}
|
||||
|
||||
int layer = ddata.getNbLayers();
|
||||
int size = matrix.height;
|
||||
int rawbitsOffset = 0;
|
||||
int matrixOffset = 0;
|
||||
|
||||
while (layer != 0) {
|
||||
|
||||
int 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;
|
||||
}
|
||||
|
||||
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"
|
||||
};
|
||||
|
||||
/**
|
||||
* Transforms an Aztec code matrix by removing the control dashed lines
|
||||
*/
|
||||
private static 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;
|
||||
|
||||
for (int x = 0; x < matrix.width; x++) {
|
||||
|
||||
if ((matrix.width / 2 - x)%16 == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int ny = 0;
|
||||
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
|
||||
*/
|
||||
private static int readCode(boolean[] rawbits, int startIndex, int length) {
|
||||
int res = 0;
|
||||
|
||||
for (int i = startIndex; i < startIndex + length; i++) {
|
||||
res <<= 1;
|
||||
if (rawbits[i]) {
|
||||
res++;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
* Copyright 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -39,8 +39,8 @@ public final class Detector {
|
|||
private boolean compact;
|
||||
private int nbLayers;
|
||||
private int nbDataBlocks;
|
||||
private int nbCenterLayers;
|
||||
private int shift;
|
||||
private int nbCenterLayers;
|
||||
private int shift;
|
||||
|
||||
public Detector(BitMatrix image) {
|
||||
this.image = image;
|
||||
|
@ -53,14 +53,14 @@ public final class Detector {
|
|||
* @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
|
||||
|
||||
// 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
|
||||
|
@ -72,75 +72,74 @@ public final class Detector {
|
|||
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);
|
||||
/**
|
||||
* <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;
|
||||
if (resab[0] && resab[2 * nbCenterLayers]) {
|
||||
shift = 0;
|
||||
} else if (resbc[0] && resbc[2 * nbCenterLayers]) {
|
||||
shift = 1;
|
||||
} else if (rescd[0] && rescd[2 * nbCenterLayers]) {
|
||||
shift = 2;
|
||||
} else if (resda[0] && resda[2 * nbCenterLayers]) {
|
||||
shift = 3;
|
||||
} else {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
//d a
|
||||
//
|
||||
//c b
|
||||
//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];
|
||||
}
|
||||
boolean[] parameterData;
|
||||
boolean[] shiftedParameterData;
|
||||
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];
|
||||
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];
|
||||
}
|
||||
}
|
||||
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];
|
||||
parameterData = new boolean[40];
|
||||
for (int i = 0; i < 40; i++) {
|
||||
parameterData[i] = shiftedParameterData[(i+shift*10)%40];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,20 +148,20 @@ public final class Detector {
|
|||
|
||||
// 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 {
|
||||
|
||||
/**
|
||||
*
|
||||
* <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;
|
||||
|
@ -184,63 +183,63 @@ public final class Detector {
|
|||
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();
|
||||
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 static void correctParameterData(boolean[] parameterData, boolean compact) throws NotFoundException {
|
||||
|
||||
/**
|
||||
*
|
||||
* <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){
|
||||
|
||||
if (compact) {
|
||||
numCodewords = 7;
|
||||
numDataCodewords = 2;
|
||||
} else {
|
||||
numCodewords = 10;
|
||||
numDataCodewords = 4;
|
||||
}
|
||||
|
||||
int numECCodewords = numCodewords - numDataCodewords;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int codewordSize = 4;
|
||||
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);
|
||||
rsDecoder.decode(parameterWords, numECCodewords);
|
||||
} catch (ReedSolomonException rse) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -256,40 +255,36 @@ public final class Detector {
|
|||
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;
|
||||
for (nbCenterLayers = 1; nbCenterLayers < 9; nbCenterLayers++) {
|
||||
Point pouta = getFirstDifferent(pina, color, 1, -1);
|
||||
Point poutb = getFirstDifferent(pinb, color, 1, 1);
|
||||
Point poutc = getFirstDifferent(pinc, color, -1, 1);
|
||||
Point poutd = getFirstDifferent(pind, color, -1, -1);
|
||||
|
||||
//d a
|
||||
//
|
||||
//c b
|
||||
|
||||
if (nbCenterLayers>2) {
|
||||
float q = 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();
|
||||
if (nbCenterLayers != 5 && nbCenterLayers != 7) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
compact = nbCenterLayers==5;
|
||||
|
@ -311,8 +306,9 @@ public final class Detector {
|
|||
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();
|
||||
if (!isValid(targetax, targetay) || !isValid(targetbx, targetby)
|
||||
|| !isValid(targetcx, targetcy) || !isValid(targetdx, targetdy)) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
Point pa = new Point(targetax,targetay);
|
||||
|
@ -321,41 +317,41 @@ public final class Detector {
|
|||
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() {
|
||||
/**
|
||||
*
|
||||
* 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){
|
||||
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();
|
||||
|
||||
// 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
|
||||
|
@ -365,288 +361,255 @@ public final class Detector {
|
|||
// 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();
|
||||
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();
|
||||
|
||||
} 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;
|
||||
}
|
||||
return new Point(cx, cy);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 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,
|
||||
/**
|
||||
* Samples an Aztec matrix from an image
|
||||
*/
|
||||
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();
|
||||
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 ;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
int nbBitsForNbLayers;
|
||||
int nbBitsForNbDatablocks;
|
||||
|
||||
nbLayers ++;
|
||||
nbDataBlocks ++;
|
||||
if (compact) {
|
||||
nbBitsForNbLayers = 2;
|
||||
nbBitsForNbDatablocks = 6;
|
||||
} else {
|
||||
nbBitsForNbLayers = 5;
|
||||
nbBitsForNbDatablocks = 11;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nbBitsForNbLayers; i++) {
|
||||
nbLayers <<= 1;
|
||||
if (parameterData[i]) {
|
||||
nbLayers += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = nbBitsForNbLayers; i < nbBitsForNbLayers + nbBitsForNbDatablocks; i++) {
|
||||
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;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 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) {
|
||||
|
||||
/**
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
boolean[] res = new boolean[size];
|
||||
float d = distance(p1,p2);
|
||||
float moduleSize = d/(size-1);
|
||||
float dx = moduleSize*(p2.x - p1.x)/d;
|
||||
float dy = moduleSize*(p2.y - p1.y)/d;
|
||||
|
||||
/**
|
||||
*
|
||||
* 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);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the border of the rectangle passed in parameter is compound of white points only
|
||||
* or black points only
|
||||
*/
|
||||
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);
|
||||
|
||||
return c == cInit && c != 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of a segment
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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 static class Point {
|
||||
public final int x;
|
||||
public final int y;
|
||||
|
||||
public ResultPoint toResultPoint() {
|
||||
return new ResultPoint(x, y);
|
||||
}
|
||||
|
||||
private 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);
|
||||
return (x >= 0 && x < image.width && y > 0 && y < image.height);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -657,7 +620,7 @@ private Point getFirstDifferent(Point init, boolean color, int dx, int dy){
|
|||
return (int) (d + 0.5f);
|
||||
}
|
||||
|
||||
// L2 distance
|
||||
// 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)
|
||||
|
|
|
@ -32,25 +32,25 @@ import com.google.zxing.common.BitMatrix;
|
|||
*/
|
||||
public final class WhiteRectangleDetector {
|
||||
|
||||
private final int INIT_SIZE = 30;
|
||||
private final int CORR = 1;
|
||||
private static final int INIT_SIZE = 30;
|
||||
private static 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;
|
||||
private final int leftInit;
|
||||
private final int rightInit;
|
||||
private final int downInit;
|
||||
private final int upInit;
|
||||
|
||||
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;
|
||||
leftInit = (width - INIT_SIZE) >> 1;
|
||||
rightInit = (width + INIT_SIZE) >> 1;
|
||||
upInit = (height - INIT_SIZE) >> 1;
|
||||
downInit = (height + INIT_SIZE) >> 1;
|
||||
}
|
||||
|
||||
public WhiteRectangleDetector(BitMatrix image, int INIT_SIZE, int x, int y) {
|
||||
|
@ -58,10 +58,10 @@ public final class WhiteRectangleDetector {
|
|||
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;
|
||||
leftInit = x - halfsize;
|
||||
rightInit = x + halfsize;
|
||||
upInit = y - halfsize;
|
||||
downInit = y + halfsize;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,10 +80,10 @@ public final class WhiteRectangleDetector {
|
|||
*/
|
||||
public ResultPoint[] detect() throws NotFoundException {
|
||||
|
||||
int left = left_init;
|
||||
int right = right_init;
|
||||
int up = up_init;
|
||||
int down = down_init;
|
||||
int left = leftInit;
|
||||
int right = rightInit;
|
||||
int up = upInit;
|
||||
int down = downInit;
|
||||
boolean sizeExceeded = false;
|
||||
boolean aBlackPointFoundOnBorder = true;
|
||||
boolean atLeastOneBlackPointFoundOnBorder = false;
|
||||
|
|
|
@ -85,7 +85,9 @@ public final class GenericGF {
|
|||
}
|
||||
|
||||
private void checkInit(){
|
||||
if (!initialized) initialize();
|
||||
if (!initialized) {
|
||||
initialize();
|
||||
}
|
||||
}
|
||||
|
||||
GenericGFPoly getZero() {
|
||||
|
|
|
@ -122,8 +122,9 @@ public final class CommandLineRunner {
|
|||
vector.addElement(BarcodeFormat.ITF);
|
||||
vector.addElement(BarcodeFormat.QR_CODE);
|
||||
vector.addElement(BarcodeFormat.DATA_MATRIX);
|
||||
vector.addElement(BarcodeFormat.AZTEC);
|
||||
vector.addElement(BarcodeFormat.PDF417);
|
||||
//vector.addElement(BarcodeFormat.CODABAR);
|
||||
vector.addElement(BarcodeFormat.CODABAR);
|
||||
}
|
||||
hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
|
||||
if (tryHarder) {
|
||||
|
|
|
@ -109,11 +109,12 @@ public final class DecodeServlet extends HttpServlet {
|
|||
possibleFormats.add(BarcodeFormat.CODE_39);
|
||||
possibleFormats.add(BarcodeFormat.CODE_93);
|
||||
possibleFormats.add(BarcodeFormat.CODE_128);
|
||||
//possibleFormats.add(BarcodeFormat.CODABAR);
|
||||
possibleFormats.add(BarcodeFormat.CODABAR);
|
||||
possibleFormats.add(BarcodeFormat.ITF);
|
||||
possibleFormats.add(BarcodeFormat.RSS14);
|
||||
possibleFormats.add(BarcodeFormat.QR_CODE);
|
||||
possibleFormats.add(BarcodeFormat.DATA_MATRIX);
|
||||
possibleFormats.add(BarcodeFormat.AZTEC);
|
||||
possibleFormats.add(BarcodeFormat.PDF417);
|
||||
HINTS.put(DecodeHintType.POSSIBLE_FORMATS, possibleFormats);
|
||||
HINTS_PURE = new Hashtable<DecodeHintType, Object>(HINTS);
|
||||
|
|
Loading…
Reference in a new issue