mirror of
https://github.com/zxing/zxing.git
synced 2024-11-10 04:54:04 -08:00
One-D barcodes reader port on c++
git-svn-id: https://zxing.googlecode.com/svn/trunk@1196 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
5ef5f3b02d
commit
76272e8525
|
@ -6,6 +6,7 @@
|
|||
* zxing
|
||||
*
|
||||
* Created by Christian Brunschen on 13/05/2008.
|
||||
* Modified by Lukasz Warchol on 02/02/2010
|
||||
* Copyright 2008 ZXing authors All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -22,10 +23,19 @@
|
|||
*/
|
||||
|
||||
namespace zxing {
|
||||
|
||||
typedef enum BarcodeFormat {
|
||||
BarcodeFormat_None = 0, BarcodeFormat_QR_CODE
|
||||
} BarcodeFormat;
|
||||
|
||||
typedef enum BarcodeFormat {
|
||||
BarcodeFormat_None = 0,
|
||||
BarcodeFormat_QR_CODE,
|
||||
BarcodeFormat_UPC_E,
|
||||
BarcodeFormat_UPC_A,
|
||||
BarcodeFormat_EAN_8,
|
||||
BarcodeFormat_EAN_13,
|
||||
BarcodeFormat_CODE_128,
|
||||
BarcodeFormat_CODE_39,
|
||||
BarcodeFormat_ITF
|
||||
} BarcodeFormat;
|
||||
|
||||
}
|
||||
|
||||
#endif // __BARCODE_FORMAT_H__
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Created by Ralf Kistner on 16/10/2009.
|
||||
* Copyright 2008 ZXing authors All rights reserved.
|
||||
* Modified by Lukasz Warchol on 02/02/2010.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -21,21 +22,27 @@
|
|||
#include <zxing/Binarizer.h>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
Binarizer::Binarizer(Ref<LuminanceSource> source) : source_(source) {
|
||||
}
|
||||
|
||||
Binarizer::~Binarizer() {
|
||||
}
|
||||
|
||||
Ref<BitMatrix> Binarizer::getBlackMatrix() {
|
||||
if (matrix_ == NULL)
|
||||
matrix_ = estimateBlackMatrix();
|
||||
return matrix_;
|
||||
}
|
||||
|
||||
Ref<LuminanceSource> Binarizer::getSource() {
|
||||
return source_;
|
||||
}
|
||||
|
||||
|
||||
Binarizer::Binarizer(Ref<LuminanceSource> source) : source_(source) {
|
||||
}
|
||||
|
||||
Binarizer::~Binarizer() {
|
||||
}
|
||||
|
||||
Ref<BitArray> Binarizer::getBlackRow(int y, Ref<BitArray> row){
|
||||
if (array_ == NULL)
|
||||
array_ = estimateBlackRow(y, row);
|
||||
return array_;
|
||||
}
|
||||
|
||||
Ref<BitMatrix> Binarizer::getBlackMatrix() {
|
||||
if (matrix_ == NULL)
|
||||
matrix_ = estimateBlackMatrix();
|
||||
return matrix_;
|
||||
}
|
||||
|
||||
Ref<LuminanceSource> Binarizer::getSource() {
|
||||
return source_;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Created by Ralf Kistner on 16/10/2009.
|
||||
* Copyright 2008 ZXing authors All rights reserved.
|
||||
* Modified by Lukasz Warchol on 02/02/2010.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -22,24 +23,29 @@
|
|||
#define BINARIZER_H_
|
||||
|
||||
#include <zxing/LuminanceSource.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/common/BitMatrix.h>
|
||||
#include <zxing/common/Counted.h>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class Binarizer : public Counted {
|
||||
private:
|
||||
Ref<LuminanceSource> source_;
|
||||
Ref<BitMatrix> matrix_;
|
||||
|
||||
public:
|
||||
Binarizer(Ref<LuminanceSource> source);
|
||||
virtual ~Binarizer();
|
||||
|
||||
virtual Ref<BitMatrix> estimateBlackMatrix() = 0;
|
||||
Ref<BitMatrix> getBlackMatrix();
|
||||
Ref<LuminanceSource> getSource();
|
||||
};
|
||||
|
||||
|
||||
class Binarizer : public Counted {
|
||||
private:
|
||||
Ref<LuminanceSource> source_;
|
||||
Ref<BitMatrix> matrix_;
|
||||
Ref<BitArray> array_;
|
||||
|
||||
public:
|
||||
Binarizer(Ref<LuminanceSource> source);
|
||||
virtual ~Binarizer();
|
||||
|
||||
virtual Ref<BitArray> estimateBlackRow(int y, Ref<BitArray> row)=0;
|
||||
Ref<BitArray> getBlackRow(int y, Ref<BitArray> row);
|
||||
|
||||
virtual Ref<BitMatrix> estimateBlackMatrix() = 0;
|
||||
Ref<BitMatrix> getBlackMatrix();
|
||||
Ref<LuminanceSource> getSource();
|
||||
};
|
||||
|
||||
}
|
||||
#endif /* BINARIZER_H_ */
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Created by Ralf Kistner on 19/10/2009.
|
||||
* Copyright 2008 ZXing authors All rights reserved.
|
||||
* Modified by Lukasz Warchol on 02/02/2010.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -21,24 +22,36 @@
|
|||
#include <zxing/BinaryBitmap.h>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
BinaryBitmap::BinaryBitmap(Ref<Binarizer> binarizer) : bits_(NULL), binarizer_(binarizer) {
|
||||
|
||||
}
|
||||
|
||||
BinaryBitmap::~BinaryBitmap() {
|
||||
}
|
||||
|
||||
|
||||
Ref<BitMatrix> BinaryBitmap::getBlackMatrix() {
|
||||
if (bits_ == NULL) {
|
||||
bits_ = binarizer_->getBlackMatrix();
|
||||
}
|
||||
return bits_;
|
||||
}
|
||||
|
||||
Ref<LuminanceSource> BinaryBitmap::getSource() {
|
||||
return binarizer_->getSource();
|
||||
}
|
||||
|
||||
|
||||
BinaryBitmap::BinaryBitmap(Ref<Binarizer> binarizer) : bits_(NULL), binarizer_(binarizer) {
|
||||
|
||||
}
|
||||
|
||||
BinaryBitmap::~BinaryBitmap() {
|
||||
}
|
||||
|
||||
Ref<BitArray> BinaryBitmap::getBlackRow(int y, Ref<BitArray> row) {
|
||||
if (array_bits_ == NULL) {
|
||||
array_bits_ = binarizer_->getBlackRow(y, row);
|
||||
}
|
||||
return array_bits_;
|
||||
}
|
||||
|
||||
Ref<BitMatrix> BinaryBitmap::getBlackMatrix() {
|
||||
if (bits_ == NULL) {
|
||||
bits_ = binarizer_->getBlackMatrix();
|
||||
}
|
||||
return bits_;
|
||||
}
|
||||
int BinaryBitmap::getWidth() {
|
||||
return getSource()->getWidth();
|
||||
}
|
||||
int BinaryBitmap::getHeight() {
|
||||
return getSource()->getHeight();
|
||||
}
|
||||
|
||||
Ref<LuminanceSource> BinaryBitmap::getSource() {
|
||||
return binarizer_->getSource();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Created by Ralf Kistner on 19/10/2009.
|
||||
* Copyright 2008 ZXing authors All rights reserved.
|
||||
* Modified by Lukasz Warchol on 02/02/2010.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -22,23 +23,29 @@
|
|||
|
||||
#include <zxing/common/Counted.h>
|
||||
#include <zxing/common/BitMatrix.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Binarizer.h>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class BinaryBitmap : public Counted {
|
||||
private:
|
||||
Ref<BitMatrix> bits_;
|
||||
Ref<Binarizer> binarizer_;
|
||||
|
||||
public:
|
||||
BinaryBitmap(Ref<Binarizer> binarizer);
|
||||
virtual ~BinaryBitmap();
|
||||
|
||||
Ref<BitMatrix> getBlackMatrix();
|
||||
Ref<LuminanceSource> getSource();
|
||||
};
|
||||
|
||||
|
||||
class BinaryBitmap : public Counted {
|
||||
private:
|
||||
Ref<BitMatrix> bits_;
|
||||
Ref<BitArray> array_bits_;
|
||||
Ref<Binarizer> binarizer_;
|
||||
|
||||
public:
|
||||
BinaryBitmap(Ref<Binarizer> binarizer);
|
||||
virtual ~BinaryBitmap();
|
||||
|
||||
Ref<BitArray> getBlackRow(int y, Ref<BitArray> row);
|
||||
Ref<BitMatrix> getBlackMatrix();
|
||||
Ref<LuminanceSource> getSource();
|
||||
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* BINARYBITMAP_H_ */
|
||||
|
|
51
cpp/core/src/zxing/MultiFormatReader.cpp
Normal file
51
cpp/core/src/zxing/MultiFormatReader.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* MultiFormatBarcodeReader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-26.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "MultiFormatReader.h"
|
||||
#include <zxing/qrcode/QRCodeReader.h>
|
||||
#include <zxing/oned/MultiFormatUPCEANReader.h>
|
||||
#include <zxing/oned/MultiFormatOneDReader.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
MultiFormatReader::MultiFormatReader(){
|
||||
readers = new std::vector<Reader*>();
|
||||
|
||||
readers->push_back(new zxing::qrcode::QRCodeReader());
|
||||
readers->push_back(new zxing::oned::MultiFormatUPCEANReader());
|
||||
readers->push_back(new zxing::oned::MultiFormatOneDReader());
|
||||
}
|
||||
|
||||
Ref<Result> MultiFormatReader::decode(Ref<BinaryBitmap> image){
|
||||
int size = readers->size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
Reader* reader = (*readers)[i];
|
||||
try {
|
||||
return reader->decode(image);
|
||||
} catch (ReaderException re) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
throw ReaderException("No code detected");
|
||||
}
|
||||
MultiFormatReader::~MultiFormatReader(){
|
||||
delete readers;
|
||||
}
|
||||
}
|
38
cpp/core/src/zxing/MultiFormatReader.h
Normal file
38
cpp/core/src/zxing/MultiFormatReader.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* MultiFormatBarcodeReader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-26.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#include <zxing/Reader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
class MultiFormatReader : public Reader {
|
||||
|
||||
private:
|
||||
std::vector<Reader*>* readers;
|
||||
public:
|
||||
MultiFormatReader();
|
||||
|
||||
Ref<Result> decode(Ref<BinaryBitmap> image);
|
||||
|
||||
~MultiFormatReader();
|
||||
};
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Created by Ralf Kistner on 16/10/2009.
|
||||
* Copyright 2008 ZXing authors All rights reserved.
|
||||
* Modified by Lukasz Warchol on 02/02/2010.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -23,117 +24,154 @@
|
|||
#include <zxing/common/IllegalArgumentException.h>
|
||||
|
||||
namespace zxing {
|
||||
using namespace std;
|
||||
|
||||
const int LUMINANCE_BITS = 5;
|
||||
const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
|
||||
const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
|
||||
|
||||
GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source) :
|
||||
using namespace std;
|
||||
|
||||
const int LUMINANCE_BITS = 5;
|
||||
const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
|
||||
const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
|
||||
|
||||
GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source) :
|
||||
Binarizer(source) {
|
||||
|
||||
}
|
||||
|
||||
GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {
|
||||
}
|
||||
|
||||
Ref<BitMatrix> GlobalHistogramBinarizer::estimateBlackMatrix() {
|
||||
// Faster than working with the reference
|
||||
LuminanceSource& source = *getSource();
|
||||
int width = source.getWidth();
|
||||
int height = source.getHeight();
|
||||
valarray<int> histogram(0, LUMINANCE_BUCKETS);
|
||||
|
||||
|
||||
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
|
||||
// more robust on the blackbox tests than sampling a diagonal as we used to do.
|
||||
for (int y = 1; y < 5; y++) {
|
||||
int row = height * y / 5;
|
||||
int right = (width << 2) / 5;
|
||||
int sdf;
|
||||
for (int x = width / 5; x < right; x++) {
|
||||
unsigned char pixel = source.getPixel(x, row);
|
||||
histogram[pixel >> LUMINANCE_SHIFT]++;
|
||||
sdf = histogram[pixel >> LUMINANCE_SHIFT];
|
||||
}
|
||||
}
|
||||
|
||||
int blackPoint = estimate(histogram) << LUMINANCE_SHIFT;
|
||||
|
||||
Ref<BitMatrix> matrix_ref(new BitMatrix(width, height));
|
||||
BitMatrix& matrix = *matrix_ref;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (source.getPixel(x, y) <= blackPoint)
|
||||
matrix.set(x, y);
|
||||
}
|
||||
}
|
||||
return matrix_ref;
|
||||
}
|
||||
|
||||
int GlobalHistogramBinarizer::estimate(valarray<int> &histogram) {
|
||||
int numBuckets = histogram.size();
|
||||
int maxBucketCount = 0;
|
||||
|
||||
|
||||
// Find tallest peak in histogram
|
||||
int firstPeak = 0;
|
||||
int firstPeakSize = 0;
|
||||
for (int i = 0; i < numBuckets; i++) {
|
||||
if (histogram[i] > firstPeakSize) {
|
||||
firstPeak = i;
|
||||
firstPeakSize = histogram[i];
|
||||
}
|
||||
if (histogram[i] > maxBucketCount) {
|
||||
maxBucketCount = histogram[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Find second-tallest peak -- well, another peak that is tall and not
|
||||
// so close to the first one
|
||||
int secondPeak = 0;
|
||||
int secondPeakScore = 0;
|
||||
for (int i = 0; i < numBuckets; i++) {
|
||||
int distanceToBiggest = i - firstPeak;
|
||||
// Encourage more distant second peaks by multiplying by square of distance
|
||||
int score = histogram[i] * distanceToBiggest * distanceToBiggest;
|
||||
if (score > secondPeakScore) {
|
||||
secondPeak = i;
|
||||
secondPeakScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
// Put firstPeak first
|
||||
if (firstPeak > secondPeak) {
|
||||
int temp = firstPeak;
|
||||
firstPeak = secondPeak;
|
||||
secondPeak = temp;
|
||||
}
|
||||
|
||||
// Kind of arbitrary; if the two peaks are very close, then we figure there is so little
|
||||
// dynamic range in the image, that discriminating black and white is too error-prone.
|
||||
// Decoding the image/line is either pointless, or may in some cases lead to a false positive
|
||||
// for 1D formats, which are relatively lenient.
|
||||
// We arbitrarily say "close" is "<= 1/16 of the total histogram buckets apart"
|
||||
if (secondPeak - firstPeak <= numBuckets >> 4) {
|
||||
throw IllegalArgumentException("Too little dynamic range in luminance");
|
||||
}
|
||||
|
||||
// Find a valley between them that is low and closer to the white peak
|
||||
int bestValley = secondPeak - 1;
|
||||
int bestValleyScore = -1;
|
||||
for (int i = secondPeak - 1; i > firstPeak; i--) {
|
||||
int fromFirst = i - firstPeak;
|
||||
// Favor a "valley" that is not too close to either peak -- especially not the black peak --
|
||||
// and that has a low value of course
|
||||
int score = fromFirst * fromFirst * (secondPeak - i) * (maxBucketCount - histogram[i]);
|
||||
if (score > bestValleyScore) {
|
||||
bestValley = i;
|
||||
bestValleyScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
return bestValley;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {
|
||||
}
|
||||
|
||||
|
||||
Ref<BitArray> GlobalHistogramBinarizer::estimateBlackRow(int y, Ref<BitArray> row){
|
||||
valarray<int> histogram(0, LUMINANCE_BUCKETS);
|
||||
LuminanceSource& source = *getSource();
|
||||
int width = source.getWidth();
|
||||
if (row == NULL || row->getSize() < width) {
|
||||
row = new BitArray(width);
|
||||
} else {
|
||||
row->clear();
|
||||
}
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
unsigned char pixel = source.getPixel(x, y);
|
||||
histogram[pixel >> LUMINANCE_SHIFT]++;
|
||||
}
|
||||
int blackPoint = estimate(histogram) << LUMINANCE_SHIFT;
|
||||
|
||||
|
||||
Ref<BitArray> array_ref(new BitArray(width));
|
||||
BitArray& array = *array_ref;
|
||||
|
||||
int left = source.getPixel(0, y);
|
||||
int center = source.getPixel(1, y);
|
||||
for (int x = 1; x < width - 1; x++) {
|
||||
int right = source.getPixel(x+1, y);
|
||||
// A simple -1 4 -1 box filter with a weight of 2.
|
||||
int luminance = ((center << 2) - left - right) >> 1;
|
||||
if (luminance < blackPoint) {
|
||||
array.set(x);
|
||||
}
|
||||
left = center;
|
||||
center = right;
|
||||
}
|
||||
|
||||
return array_ref;
|
||||
}
|
||||
|
||||
Ref<BitMatrix> GlobalHistogramBinarizer::estimateBlackMatrix() {
|
||||
// Faster than working with the reference
|
||||
LuminanceSource& source = *getSource();
|
||||
int width = source.getWidth();
|
||||
int height = source.getHeight();
|
||||
valarray<int> histogram(0, LUMINANCE_BUCKETS);
|
||||
|
||||
|
||||
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
|
||||
// more robust on the blackbox tests than sampling a diagonal as we used to do.
|
||||
for (int y = 1; y < 5; y++) {
|
||||
int row = height * y / 5;
|
||||
int right = (width << 2) / 5;
|
||||
int sdf;
|
||||
for (int x = width / 5; x < right; x++) {
|
||||
unsigned char pixel = source.getPixel(x, row);
|
||||
histogram[pixel >> LUMINANCE_SHIFT]++;
|
||||
sdf = histogram[pixel >> LUMINANCE_SHIFT];
|
||||
}
|
||||
}
|
||||
|
||||
int blackPoint = estimate(histogram) << LUMINANCE_SHIFT;
|
||||
|
||||
Ref<BitMatrix> matrix_ref(new BitMatrix(width, height));
|
||||
BitMatrix& matrix = *matrix_ref;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (source.getPixel(x, y) <= blackPoint)
|
||||
matrix.set(x, y);
|
||||
}
|
||||
}
|
||||
return matrix_ref;
|
||||
}
|
||||
|
||||
int GlobalHistogramBinarizer::estimate(valarray<int> &histogram) {
|
||||
int numBuckets = histogram.size();
|
||||
int maxBucketCount = 0;
|
||||
|
||||
|
||||
// Find tallest peak in histogram
|
||||
int firstPeak = 0;
|
||||
int firstPeakSize = 0;
|
||||
for (int i = 0; i < numBuckets; i++) {
|
||||
if (histogram[i] > firstPeakSize) {
|
||||
firstPeak = i;
|
||||
firstPeakSize = histogram[i];
|
||||
}
|
||||
if (histogram[i] > maxBucketCount) {
|
||||
maxBucketCount = histogram[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Find second-tallest peak -- well, another peak that is tall and not
|
||||
// so close to the first one
|
||||
int secondPeak = 0;
|
||||
int secondPeakScore = 0;
|
||||
for (int i = 0; i < numBuckets; i++) {
|
||||
int distanceToBiggest = i - firstPeak;
|
||||
// Encourage more distant second peaks by multiplying by square of distance
|
||||
int score = histogram[i] * distanceToBiggest * distanceToBiggest;
|
||||
if (score > secondPeakScore) {
|
||||
secondPeak = i;
|
||||
secondPeakScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
// Put firstPeak first
|
||||
if (firstPeak > secondPeak) {
|
||||
int temp = firstPeak;
|
||||
firstPeak = secondPeak;
|
||||
secondPeak = temp;
|
||||
}
|
||||
|
||||
// Kind of arbitrary; if the two peaks are very close, then we figure there is so little
|
||||
// dynamic range in the image, that discriminating black and white is too error-prone.
|
||||
// Decoding the image/line is either pointless, or may in some cases lead to a false positive
|
||||
// for 1D formats, which are relatively lenient.
|
||||
// We arbitrarily say "close" is "<= 1/16 of the total histogram buckets apart"
|
||||
if (secondPeak - firstPeak <= numBuckets >> 4) {
|
||||
throw IllegalArgumentException("Too little dynamic range in luminance");
|
||||
}
|
||||
|
||||
// Find a valley between them that is low and closer to the white peak
|
||||
int bestValley = secondPeak - 1;
|
||||
int bestValleyScore = -1;
|
||||
for (int i = secondPeak - 1; i > firstPeak; i--) {
|
||||
int fromFirst = i - firstPeak;
|
||||
// Favor a "valley" that is not too close to either peak -- especially not the black peak --
|
||||
// and that has a low value of course
|
||||
int score = fromFirst * fromFirst * (secondPeak - i) * (maxBucketCount - histogram[i]);
|
||||
if (score > bestValleyScore) {
|
||||
bestValley = i;
|
||||
bestValleyScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
return bestValley;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*
|
||||
* Created by Ralf Kistner on 16/10/2009.
|
||||
* Copyright 2008 ZXing authors All rights reserved.
|
||||
* Modified by Lukasz Warchol on 02/02/2010.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -23,19 +24,21 @@
|
|||
|
||||
#include <valarray>
|
||||
#include <zxing/Binarizer.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/common/BitMatrix.h>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class GlobalHistogramBinarizer : public Binarizer {
|
||||
public:
|
||||
GlobalHistogramBinarizer(Ref<LuminanceSource> source);
|
||||
virtual ~GlobalHistogramBinarizer();
|
||||
|
||||
virtual Ref<BitMatrix> estimateBlackMatrix();
|
||||
static int estimate(std::valarray<int> &histogram);
|
||||
};
|
||||
|
||||
|
||||
class GlobalHistogramBinarizer : public Binarizer {
|
||||
public:
|
||||
GlobalHistogramBinarizer(Ref<LuminanceSource> source);
|
||||
virtual ~GlobalHistogramBinarizer();
|
||||
|
||||
virtual Ref<BitArray> estimateBlackRow(int y, Ref<BitArray> row);
|
||||
virtual Ref<BitMatrix> estimateBlackMatrix();
|
||||
static int estimate(std::valarray<int> &histogram);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* GLOBALHISTOGRAMBINARIZER_H_ */
|
||||
|
|
477
cpp/core/src/zxing/oned/Code128Reader.cpp
Normal file
477
cpp/core/src/zxing/oned/Code128Reader.cpp
Normal file
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* Code128Reader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-15.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "Code128Reader.h"
|
||||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
const int CODE_PATTERNS_LENGHT = 107;
|
||||
const int countersLenght = 6;
|
||||
static const int CODE_PATTERNS[CODE_PATTERNS_LENGHT][countersLenght] = {
|
||||
{2, 1, 2, 2, 2, 2}, /* 0 */
|
||||
{2, 2, 2, 1, 2, 2},
|
||||
{2, 2, 2, 2, 2, 1},
|
||||
{1, 2, 1, 2, 2, 3},
|
||||
{1, 2, 1, 3, 2, 2},
|
||||
{1, 3, 1, 2, 2, 2}, /* 5 */
|
||||
{1, 2, 2, 2, 1, 3},
|
||||
{1, 2, 2, 3, 1, 2},
|
||||
{1, 3, 2, 2, 1, 2},
|
||||
{2, 2, 1, 2, 1, 3},
|
||||
{2, 2, 1, 3, 1, 2}, /* 10 */
|
||||
{2, 3, 1, 2, 1, 2},
|
||||
{1, 1, 2, 2, 3, 2},
|
||||
{1, 2, 2, 1, 3, 2},
|
||||
{1, 2, 2, 2, 3, 1},
|
||||
{1, 1, 3, 2, 2, 2}, /* 15 */
|
||||
{1, 2, 3, 1, 2, 2},
|
||||
{1, 2, 3, 2, 2, 1},
|
||||
{2, 2, 3, 2, 1, 1},
|
||||
{2, 2, 1, 1, 3, 2},
|
||||
{2, 2, 1, 2, 3, 1}, /* 20 */
|
||||
{2, 1, 3, 2, 1, 2},
|
||||
{2, 2, 3, 1, 1, 2},
|
||||
{3, 1, 2, 1, 3, 1},
|
||||
{3, 1, 1, 2, 2, 2},
|
||||
{3, 2, 1, 1, 2, 2}, /* 25 */
|
||||
{3, 2, 1, 2, 2, 1},
|
||||
{3, 1, 2, 2, 1, 2},
|
||||
{3, 2, 2, 1, 1, 2},
|
||||
{3, 2, 2, 2, 1, 1},
|
||||
{2, 1, 2, 1, 2, 3}, /* 30 */
|
||||
{2, 1, 2, 3, 2, 1},
|
||||
{2, 3, 2, 1, 2, 1},
|
||||
{1, 1, 1, 3, 2, 3},
|
||||
{1, 3, 1, 1, 2, 3},
|
||||
{1, 3, 1, 3, 2, 1}, /* 35 */
|
||||
{1, 1, 2, 3, 1, 3},
|
||||
{1, 3, 2, 1, 1, 3},
|
||||
{1, 3, 2, 3, 1, 1},
|
||||
{2, 1, 1, 3, 1, 3},
|
||||
{2, 3, 1, 1, 1, 3}, /* 40 */
|
||||
{2, 3, 1, 3, 1, 1},
|
||||
{1, 1, 2, 1, 3, 3},
|
||||
{1, 1, 2, 3, 3, 1},
|
||||
{1, 3, 2, 1, 3, 1},
|
||||
{1, 1, 3, 1, 2, 3}, /* 45 */
|
||||
{1, 1, 3, 3, 2, 1},
|
||||
{1, 3, 3, 1, 2, 1},
|
||||
{3, 1, 3, 1, 2, 1},
|
||||
{2, 1, 1, 3, 3, 1},
|
||||
{2, 3, 1, 1, 3, 1}, /* 50 */
|
||||
{2, 1, 3, 1, 1, 3},
|
||||
{2, 1, 3, 3, 1, 1},
|
||||
{2, 1, 3, 1, 3, 1},
|
||||
{3, 1, 1, 1, 2, 3},
|
||||
{3, 1, 1, 3, 2, 1}, /* 55 */
|
||||
{3, 3, 1, 1, 2, 1},
|
||||
{3, 1, 2, 1, 1, 3},
|
||||
{3, 1, 2, 3, 1, 1},
|
||||
{3, 3, 2, 1, 1, 1},
|
||||
{3, 1, 4, 1, 1, 1}, /* 60 */
|
||||
{2, 2, 1, 4, 1, 1},
|
||||
{4, 3, 1, 1, 1, 1},
|
||||
{1, 1, 1, 2, 2, 4},
|
||||
{1, 1, 1, 4, 2, 2},
|
||||
{1, 2, 1, 1, 2, 4}, /* 65 */
|
||||
{1, 2, 1, 4, 2, 1},
|
||||
{1, 4, 1, 1, 2, 2},
|
||||
{1, 4, 1, 2, 2, 1},
|
||||
{1, 1, 2, 2, 1, 4},
|
||||
{1, 1, 2, 4, 1, 2}, /* 70 */
|
||||
{1, 2, 2, 1, 1, 4},
|
||||
{1, 2, 2, 4, 1, 1},
|
||||
{1, 4, 2, 1, 1, 2},
|
||||
{1, 4, 2, 2, 1, 1},
|
||||
{2, 4, 1, 2, 1, 1}, /* 75 */
|
||||
{2, 2, 1, 1, 1, 4},
|
||||
{4, 1, 3, 1, 1, 1},
|
||||
{2, 4, 1, 1, 1, 2},
|
||||
{1, 3, 4, 1, 1, 1},
|
||||
{1, 1, 1, 2, 4, 2}, /* 80 */
|
||||
{1, 2, 1, 1, 4, 2},
|
||||
{1, 2, 1, 2, 4, 1},
|
||||
{1, 1, 4, 2, 1, 2},
|
||||
{1, 2, 4, 1, 1, 2},
|
||||
{1, 2, 4, 2, 1, 1}, /* 85 */
|
||||
{4, 1, 1, 2, 1, 2},
|
||||
{4, 2, 1, 1, 1, 2},
|
||||
{4, 2, 1, 2, 1, 1},
|
||||
{2, 1, 2, 1, 4, 1},
|
||||
{2, 1, 4, 1, 2, 1}, /* 90 */
|
||||
{4, 1, 2, 1, 2, 1},
|
||||
{1, 1, 1, 1, 4, 3},
|
||||
{1, 1, 1, 3, 4, 1},
|
||||
{1, 3, 1, 1, 4, 1},
|
||||
{1, 1, 4, 1, 1, 3}, /* 95 */
|
||||
{1, 1, 4, 3, 1, 1},
|
||||
{4, 1, 1, 1, 1, 3},
|
||||
{4, 1, 1, 3, 1, 1},
|
||||
{1, 1, 3, 1, 4, 1},
|
||||
{1, 1, 4, 1, 3, 1}, /* 100 */
|
||||
{3, 1, 1, 1, 4, 1},
|
||||
{4, 1, 1, 1, 3, 1},
|
||||
{2, 1, 1, 4, 1, 2},
|
||||
{2, 1, 1, 2, 1, 4},
|
||||
{2, 1, 1, 2, 3, 2}, /* 105 */
|
||||
{2, 3, 3, 1, 1, 1}
|
||||
};
|
||||
|
||||
|
||||
Code128Reader::Code128Reader(){
|
||||
}
|
||||
|
||||
int* Code128Reader::findStartPattern(Ref<BitArray> row){
|
||||
int width = row->getSize();
|
||||
int rowOffset = 0;
|
||||
while (rowOffset < width) {
|
||||
if (row->get(rowOffset)) {
|
||||
break;
|
||||
}
|
||||
rowOffset++;
|
||||
}
|
||||
|
||||
int counterPosition = 0;
|
||||
int counters[countersLenght] = {0,0,0,0,0,0};
|
||||
int patternStart = rowOffset;
|
||||
bool isWhite = false;
|
||||
int patternLength = sizeof(counters) / sizeof(int);
|
||||
|
||||
for (int i = rowOffset; i < width; i++) {
|
||||
bool pixel = row->get(i);
|
||||
if (pixel ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
int bestVariance = MAX_AVG_VARIANCE;
|
||||
int bestMatch = -1;
|
||||
for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
|
||||
int variance = patternMatchVariance(counters, sizeof(counters)/sizeof(int), CODE_PATTERNS[startCode], MAX_INDIVIDUAL_VARIANCE);
|
||||
if (variance < bestVariance) {
|
||||
bestVariance = variance;
|
||||
bestMatch = startCode;
|
||||
}
|
||||
}
|
||||
if (bestMatch >= 0) {
|
||||
// Look for whitespace before start pattern, >= 50% of width of start pattern
|
||||
if (row->isRange(fmaxl(0, patternStart - (i - patternStart) / 2), patternStart, false)) {
|
||||
int* resultValue = new int[3];
|
||||
resultValue[0] = patternStart;
|
||||
resultValue[1] = i;
|
||||
resultValue[2] = bestMatch;
|
||||
return resultValue;
|
||||
}
|
||||
}
|
||||
patternStart += counters[0] + counters[1];
|
||||
for (int y = 2; y < patternLength; y++) {
|
||||
counters[y - 2] = counters[y];
|
||||
}
|
||||
counters[patternLength - 2] = 0;
|
||||
counters[patternLength - 1] = 0;
|
||||
counterPosition--;
|
||||
} else {
|
||||
counterPosition++;
|
||||
}
|
||||
counters[counterPosition] = 1;
|
||||
isWhite = !isWhite;
|
||||
}
|
||||
}
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
int Code128Reader::decodeCode(Ref<BitArray> row, int counters[], int countersCount, int rowOffset){
|
||||
recordPattern(row, rowOffset, counters, countersCount);
|
||||
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
||||
int bestMatch = -1;
|
||||
for (int d = 0; d < CODE_PATTERNS_LENGHT; d++) {
|
||||
int pattern[countersLenght];
|
||||
|
||||
for(int ind = 0; ind< countersLenght; ind++){
|
||||
pattern[ind] = CODE_PATTERNS[d][ind];
|
||||
}
|
||||
// memcpy(pattern, CODE_PATTERNS[d], countersLenght);
|
||||
int variance = patternMatchVariance(counters, countersCount, pattern, MAX_INDIVIDUAL_VARIANCE);
|
||||
if (variance < bestVariance) {
|
||||
bestVariance = variance;
|
||||
bestMatch = d;
|
||||
}
|
||||
}
|
||||
// TODO We're overlooking the fact that the STOP pattern has 7 values, not 6.
|
||||
if (bestMatch >= 0) {
|
||||
return bestMatch;
|
||||
} else {
|
||||
throw ReaderException("");
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Result> Code128Reader::decodeRow(int rowNumber, Ref<BitArray> row){
|
||||
|
||||
int* startPatternInfo = findStartPattern(row);
|
||||
int startCode = startPatternInfo[2];
|
||||
int codeSet;
|
||||
switch (startCode) {
|
||||
case CODE_START_A:
|
||||
codeSet = CODE_CODE_A;
|
||||
break;
|
||||
case CODE_START_B:
|
||||
codeSet = CODE_CODE_B;
|
||||
break;
|
||||
case CODE_START_C:
|
||||
codeSet = CODE_CODE_C;
|
||||
break;
|
||||
default:
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
bool isNextShifted = false;
|
||||
|
||||
std::string tmpResultString;
|
||||
|
||||
|
||||
int lastStart = startPatternInfo[0];
|
||||
int nextStart = startPatternInfo[1];
|
||||
int counters[countersLenght] = {0,0,0,0,0,0};
|
||||
|
||||
int lastCode = 0;
|
||||
int code = 0;
|
||||
int checksumTotal = startCode;
|
||||
int multiplier = 0;
|
||||
bool lastCharacterWasPrintable = true;
|
||||
|
||||
while (!done) {
|
||||
|
||||
bool unshift = isNextShifted;
|
||||
isNextShifted = false;
|
||||
|
||||
// Save off last code
|
||||
lastCode = code;
|
||||
|
||||
// Decode another code from image
|
||||
code = decodeCode(row, counters, sizeof(counters)/sizeof(int), nextStart);
|
||||
|
||||
// Remember whether the last code was printable or not (excluding CODE_STOP)
|
||||
if (code != CODE_STOP) {
|
||||
lastCharacterWasPrintable = true;
|
||||
}
|
||||
|
||||
// Add to checksum computation (if not CODE_STOP of course)
|
||||
if (code != CODE_STOP) {
|
||||
multiplier++;
|
||||
checksumTotal += multiplier * code;
|
||||
}
|
||||
|
||||
// Advance to where the next code will to start
|
||||
lastStart = nextStart;
|
||||
int _countersLenght = sizeof(counters) / sizeof(int);
|
||||
for (int i = 0; i < _countersLenght; i++) {
|
||||
nextStart += counters[i];
|
||||
}
|
||||
|
||||
// Take care of illegal start codes
|
||||
switch (code) {
|
||||
case CODE_START_A:
|
||||
case CODE_START_B:
|
||||
case CODE_START_C:
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
switch (codeSet) {
|
||||
|
||||
case CODE_CODE_A:
|
||||
if (code < 64) {
|
||||
tmpResultString.append(1, (char) (' ' + code));
|
||||
} else if (code < 96) {
|
||||
tmpResultString.append(1, (char) (code - 64));
|
||||
} else {
|
||||
// Don't let CODE_STOP, which always appears, affect whether whether we think the last
|
||||
// code was printable or not.
|
||||
if (code != CODE_STOP) {
|
||||
lastCharacterWasPrintable = false;
|
||||
}
|
||||
switch (code) {
|
||||
case CODE_FNC_1:
|
||||
case CODE_FNC_2:
|
||||
case CODE_FNC_3:
|
||||
case CODE_FNC_4_A:
|
||||
// do nothing?
|
||||
break;
|
||||
case CODE_SHIFT:
|
||||
isNextShifted = true;
|
||||
codeSet = CODE_CODE_B;
|
||||
break;
|
||||
case CODE_CODE_B:
|
||||
codeSet = CODE_CODE_B;
|
||||
break;
|
||||
case CODE_CODE_C:
|
||||
codeSet = CODE_CODE_C;
|
||||
break;
|
||||
case CODE_STOP:
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CODE_CODE_B:
|
||||
if (code < 96) {
|
||||
tmpResultString.append(1, (char) (' ' + code));
|
||||
} else {
|
||||
if (code != CODE_STOP) {
|
||||
lastCharacterWasPrintable = false;
|
||||
}
|
||||
switch (code) {
|
||||
case CODE_FNC_1:
|
||||
case CODE_FNC_2:
|
||||
case CODE_FNC_3:
|
||||
case CODE_FNC_4_B:
|
||||
// do nothing?
|
||||
break;
|
||||
case CODE_SHIFT:
|
||||
isNextShifted = true;
|
||||
codeSet = CODE_CODE_C;
|
||||
break;
|
||||
case CODE_CODE_A:
|
||||
codeSet = CODE_CODE_A;
|
||||
break;
|
||||
case CODE_CODE_C:
|
||||
codeSet = CODE_CODE_C;
|
||||
break;
|
||||
case CODE_STOP:
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CODE_CODE_C:
|
||||
if (code < 100) {
|
||||
if (code < 10) {
|
||||
tmpResultString.append(1, '0');
|
||||
}
|
||||
tmpResultString.append(1, code);
|
||||
} else {
|
||||
if (code != CODE_STOP) {
|
||||
lastCharacterWasPrintable = false;
|
||||
}
|
||||
switch (code) {
|
||||
case CODE_FNC_1:
|
||||
// do nothing?
|
||||
break;
|
||||
case CODE_CODE_A:
|
||||
codeSet = CODE_CODE_A;
|
||||
break;
|
||||
case CODE_CODE_B:
|
||||
codeSet = CODE_CODE_B;
|
||||
break;
|
||||
case CODE_STOP:
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Unshift back to another code set if we were shifted
|
||||
if (unshift) {
|
||||
switch (codeSet) {
|
||||
case CODE_CODE_A:
|
||||
codeSet = CODE_CODE_C;
|
||||
break;
|
||||
case CODE_CODE_B:
|
||||
codeSet = CODE_CODE_A;
|
||||
break;
|
||||
case CODE_CODE_C:
|
||||
codeSet = CODE_CODE_B;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check for ample whitespace following pattern, but, to do this we first need to remember that
|
||||
// we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left
|
||||
// to read off. Would be slightly better to properly read. Here we just skip it:
|
||||
int width = row->getSize();
|
||||
while (nextStart < width && row->get(nextStart)) {
|
||||
nextStart++;
|
||||
}
|
||||
if (!row->isRange(nextStart, fminl(width, nextStart + (nextStart - lastStart) / 2), false)) {
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
// Pull out from sum the value of the penultimate check code
|
||||
checksumTotal -= multiplier * lastCode;
|
||||
// lastCode is the checksum then:
|
||||
if (checksumTotal % 103 != lastCode) {
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
// Need to pull out the check digits from string
|
||||
int resultLength = tmpResultString.length();
|
||||
// Only bother if the result had at least one character, and if the checksum digit happened to
|
||||
// be a printable character. If it was just interpreted as a control code, nothing to remove.
|
||||
if (resultLength > 0 && lastCharacterWasPrintable) {
|
||||
if (codeSet == CODE_CODE_C) {
|
||||
tmpResultString.erase(resultLength - 2, resultLength);
|
||||
} else {
|
||||
tmpResultString.erase(resultLength - 1, resultLength);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<String> resultString(new String(tmpResultString));
|
||||
// String resultString(tmpResultString);
|
||||
|
||||
if (tmpResultString.length() == 0) {
|
||||
// Almost surely a false positive
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
float left = (float) (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;
|
||||
float right = (float) (nextStart + lastStart) / 2.0f;
|
||||
|
||||
std::vector< Ref<ResultPoint> > resultPoints(2);
|
||||
Ref<OneDResultPoint> resultPoint1(new OneDResultPoint(left, (float) rowNumber));
|
||||
Ref<OneDResultPoint> resultPoint2(new OneDResultPoint(right, (float) rowNumber));
|
||||
resultPoints[0] = resultPoint1;
|
||||
resultPoints[1] = resultPoint2;
|
||||
|
||||
ArrayRef<unsigned char> resultBytes(1);
|
||||
|
||||
delete [] startPatternInfo;
|
||||
startPatternInfo = NULL;
|
||||
|
||||
Ref<Result> res(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_CODE_128));
|
||||
return res;
|
||||
}
|
||||
|
||||
void Code128Reader::append(char* s, char c){
|
||||
int len = strlen(s);
|
||||
s[len] = c;
|
||||
s[len + 1] = '\0';
|
||||
}
|
||||
|
||||
Code128Reader::~Code128Reader(){
|
||||
}
|
||||
}
|
||||
}
|
61
cpp/core/src/zxing/oned/Code128Reader.h
Normal file
61
cpp/core/src/zxing/oned/Code128Reader.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Code128Reader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-15.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <zxing/oned/OneDReader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class Code128Reader : public OneDReader {
|
||||
|
||||
private:
|
||||
static const int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.25f);
|
||||
static const int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f);
|
||||
|
||||
static const int CODE_SHIFT = 98;
|
||||
|
||||
static const int CODE_CODE_C = 99;
|
||||
static const int CODE_CODE_B = 100;
|
||||
static const int CODE_CODE_A = 101;
|
||||
|
||||
static const int CODE_FNC_1 = 102;
|
||||
static const int CODE_FNC_2 = 97;
|
||||
static const int CODE_FNC_3 = 96;
|
||||
static const int CODE_FNC_4_A = 101;
|
||||
static const int CODE_FNC_4_B = 100;
|
||||
|
||||
static const int CODE_START_A = 103;
|
||||
static const int CODE_START_B = 104;
|
||||
static const int CODE_START_C = 105;
|
||||
static const int CODE_STOP = 106;
|
||||
|
||||
static int* findStartPattern(Ref<BitArray> row);
|
||||
static int decodeCode(Ref<BitArray> row, int counters[], int countersCount, int rowOffset);
|
||||
|
||||
void append(char* s, char c);
|
||||
public:
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
Code128Reader();
|
||||
~Code128Reader();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
339
cpp/core/src/zxing/oned/Code39Reader.cpp
Normal file
339
cpp/core/src/zxing/oned/Code39Reader.cpp
Normal file
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
* Code39Reader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-26.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "Code39Reader.h"
|
||||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
static const char* ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";
|
||||
|
||||
|
||||
/**
|
||||
* These represent the encodings of characters, as patterns of wide and narrow bars.
|
||||
* The 9 least-significant bits of each int correspond to the pattern of wide and narrow,
|
||||
* with 1s representing "wide" and 0s representing narrow.
|
||||
*/
|
||||
const int CHARACTER_ENCODINGS_LEN = 44;
|
||||
static int CHARACTER_ENCODINGS[CHARACTER_ENCODINGS_LEN] = {
|
||||
0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9
|
||||
0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J
|
||||
0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T
|
||||
0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, // U-*
|
||||
0x0A8, 0x0A2, 0x08A, 0x02A // $-%
|
||||
};
|
||||
|
||||
static int ASTERISK_ENCODING = 0x094;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a reader that assumes all encoded data is data, and does not treat the final
|
||||
* character as a check digit. It will not decoded "extended Code 39" sequences.
|
||||
*/
|
||||
Code39Reader::Code39Reader(){
|
||||
ALPHABET_STRING = new std::string("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%");
|
||||
usingCheckDigit = false;
|
||||
extendedMode = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reader that can be configured to check the last character as a check digit.
|
||||
* It will not decoded "extended Code 39" sequences.
|
||||
*
|
||||
* @param usingCheckDigit if true, treat the last data character as a check digit, not
|
||||
* data, and verify that the checksum passes.
|
||||
*/
|
||||
Code39Reader::Code39Reader(bool usingCheckDigit_){
|
||||
ALPHABET_STRING = new std::string("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%");
|
||||
usingCheckDigit = usingCheckDigit_;
|
||||
extendedMode = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Ref<Result> Code39Reader::decodeRow(int rowNumber, Ref<BitArray> row){
|
||||
int* start = findAsteriskPattern(row);
|
||||
int nextStart = start[1];
|
||||
int end = row->getSize();
|
||||
|
||||
// Read off white space
|
||||
while (nextStart < end && !row->get(nextStart)) {
|
||||
nextStart++;
|
||||
}
|
||||
|
||||
std::string tmpResultString;
|
||||
|
||||
int countersLen = 9;
|
||||
int* counters = new int[countersLen];
|
||||
for (int i=0; i<countersLen; i++) {
|
||||
counters[i] = 0;
|
||||
}
|
||||
char decodedChar;
|
||||
int lastStart;
|
||||
do {
|
||||
recordPattern(row, nextStart, counters, countersLen);
|
||||
int pattern = toNarrowWidePattern(counters, countersLen);
|
||||
if (pattern < 0) {
|
||||
throw ReaderException("pattern < 0");
|
||||
}
|
||||
decodedChar = patternToChar(pattern);
|
||||
tmpResultString.append(1, decodedChar);
|
||||
lastStart = nextStart;
|
||||
for (int i = 0; i < countersLen; i++) {
|
||||
nextStart += counters[i];
|
||||
}
|
||||
// Read off white space
|
||||
while (nextStart < end && !row->get(nextStart)) {
|
||||
nextStart++;
|
||||
}
|
||||
} while (decodedChar != '*');
|
||||
tmpResultString.erase(tmpResultString.length()-1, 1);// remove asterisk
|
||||
|
||||
// Look for whitespace after pattern:
|
||||
int lastPatternSize = 0;
|
||||
for (int i = 0; i < countersLen; i++) {
|
||||
lastPatternSize += counters[i];
|
||||
}
|
||||
int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;
|
||||
// If 50% of last pattern size, following last pattern, is not whitespace, fail
|
||||
// (but if it's whitespace to the very end of the image, that's OK)
|
||||
if (nextStart != end && whiteSpaceAfterEnd / 2 < lastPatternSize) {
|
||||
throw ReaderException("too short end white space");
|
||||
}
|
||||
|
||||
if (usingCheckDigit) {
|
||||
int max = tmpResultString.length() - 1;
|
||||
int total = 0;
|
||||
for (int i = 0; i < max; i++) {
|
||||
total += ALPHABET_STRING->find_first_of(tmpResultString[i], 0);
|
||||
}
|
||||
if (total % 43 != ALPHABET_STRING->find_first_of(tmpResultString[max], 0)) {
|
||||
throw ReaderException("");
|
||||
}
|
||||
tmpResultString.erase(max, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Ref<String> resultString(new String(tmpResultString));
|
||||
if (extendedMode) {
|
||||
delete resultString;
|
||||
resultString = decodeExtended(tmpResultString);
|
||||
}
|
||||
|
||||
if (tmpResultString.length() == 0) {
|
||||
// Almost surely a false positive
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
float left = (float) (start[1] + start[0]) / 2.0f;
|
||||
float right = (float) (nextStart + lastStart) / 2.0f;
|
||||
|
||||
std::vector< Ref<ResultPoint> > resultPoints(2);
|
||||
Ref<OneDResultPoint> resultPoint1(new OneDResultPoint(left, (float) rowNumber));
|
||||
Ref<OneDResultPoint> resultPoint2(new OneDResultPoint(right, (float) rowNumber));
|
||||
resultPoints[0] = resultPoint1;
|
||||
resultPoints[1] = resultPoint2;
|
||||
|
||||
ArrayRef<unsigned char> resultBytes(1);
|
||||
|
||||
delete [] start;
|
||||
|
||||
Ref<Result> res(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_CODE_39));
|
||||
return res;
|
||||
}
|
||||
|
||||
int* Code39Reader::findAsteriskPattern(Ref<BitArray> row){
|
||||
int width = row->getSize();
|
||||
int rowOffset = 0;
|
||||
while (rowOffset < width) {
|
||||
if (row->get(rowOffset)) {
|
||||
break;
|
||||
}
|
||||
rowOffset++;
|
||||
}
|
||||
|
||||
int counterPosition = 0;
|
||||
int countersLen = 9;
|
||||
int* counters = new int[countersLen];
|
||||
for (int i=0; i<countersLen; i++) {
|
||||
counters[i] = 0;
|
||||
}
|
||||
int patternStart = rowOffset;
|
||||
bool isWhite = false;
|
||||
int patternLength = countersLen;
|
||||
|
||||
for (int i = rowOffset; i < width; i++) {
|
||||
bool pixel = row->get(i);
|
||||
if (pixel ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
if (toNarrowWidePattern(counters, countersLen) == ASTERISK_ENCODING) {
|
||||
// Look for whitespace before start pattern, >= 50% of width of start pattern
|
||||
if (row->isRange(fmaxl(0, patternStart - (i - patternStart) / 2), patternStart, false)) {
|
||||
int* resultValue = new int[2];
|
||||
resultValue[0] = patternStart;
|
||||
resultValue[1] = i;
|
||||
return resultValue;
|
||||
}
|
||||
}
|
||||
patternStart += counters[0] + counters[1];
|
||||
for (int y = 2; y < patternLength; y++) {
|
||||
counters[y - 2] = counters[y];
|
||||
}
|
||||
counters[patternLength - 2] = 0;
|
||||
counters[patternLength - 1] = 0;
|
||||
counterPosition--;
|
||||
} else {
|
||||
counterPosition++;
|
||||
}
|
||||
counters[counterPosition] = 1;
|
||||
isWhite = !isWhite;
|
||||
}
|
||||
}
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
// For efficiency, returns -1 on failure. Not throwing here saved as many as 700 exceptions
|
||||
// per image when using some of our blackbox images.
|
||||
int Code39Reader::toNarrowWidePattern(int counters[], int countersLen){
|
||||
int numCounters = countersLen;
|
||||
int maxNarrowCounter = 0;
|
||||
int wideCounters;
|
||||
do {
|
||||
int minCounter = INT_MAX;
|
||||
for (int i = 0; i < numCounters; i++) {
|
||||
int counter = counters[i];
|
||||
if (counter < minCounter && counter > maxNarrowCounter) {
|
||||
minCounter = counter;
|
||||
}
|
||||
}
|
||||
maxNarrowCounter = minCounter;
|
||||
wideCounters = 0;
|
||||
int totalWideCountersWidth = 0;
|
||||
int pattern = 0;
|
||||
for (int i = 0; i < numCounters; i++) {
|
||||
int counter = counters[i];
|
||||
if (counters[i] > maxNarrowCounter) {
|
||||
pattern |= 1 << (numCounters - 1 - i);
|
||||
wideCounters++;
|
||||
totalWideCountersWidth += counter;
|
||||
}
|
||||
}
|
||||
if (wideCounters == 3) {
|
||||
// Found 3 wide counters, but are they close enough in width?
|
||||
// We can perform a cheap, conservative check to see if any individual
|
||||
// counter is more than 1.5 times the average:
|
||||
for (int i = 0; i < numCounters && wideCounters > 0; i++) {
|
||||
int counter = counters[i];
|
||||
if (counters[i] > maxNarrowCounter) {
|
||||
wideCounters--;
|
||||
// totalWideCountersWidth = 3 * average, so this checks if counter >= 3/2 * average
|
||||
if ((counter << 1) >= totalWideCountersWidth) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
} while (wideCounters > 3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char Code39Reader::patternToChar(int pattern){
|
||||
for (int i = 0; i < CHARACTER_ENCODINGS_LEN; i++) {
|
||||
if (CHARACTER_ENCODINGS[i] == pattern) {
|
||||
return ALPHABET[i];
|
||||
}
|
||||
}
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
Ref<String> Code39Reader::decodeExtended(std::string encoded){
|
||||
int length = encoded.length();
|
||||
std::string tmpDecoded;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = encoded[i];
|
||||
if (c == '+' || c == '$' || c == '%' || c == '/') {
|
||||
char next = encoded[i + 1];
|
||||
char decodedChar = '\0';
|
||||
switch (c) {
|
||||
case '+':
|
||||
// +A to +Z map to a to z
|
||||
if (next >= 'A' && next <= 'Z') {
|
||||
decodedChar = (char) (next + 32);
|
||||
} else {
|
||||
throw ReaderException("");
|
||||
}
|
||||
break;
|
||||
case '$':
|
||||
// $A to $Z map to control codes SH to SB
|
||||
if (next >= 'A' && next <= 'Z') {
|
||||
decodedChar = (char) (next - 64);
|
||||
} else {
|
||||
throw ReaderException("");
|
||||
}
|
||||
break;
|
||||
case '%':
|
||||
// %A to %E map to control codes ESC to US
|
||||
if (next >= 'A' && next <= 'E') {
|
||||
decodedChar = (char) (next - 38);
|
||||
} else if (next >= 'F' && next <= 'W') {
|
||||
decodedChar = (char) (next - 11);
|
||||
} else {
|
||||
throw ReaderException("");
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
// /A to /O map to ! to , and /Z maps to :
|
||||
if (next >= 'A' && next <= 'O') {
|
||||
decodedChar = (char) (next - 32);
|
||||
} else if (next == 'Z') {
|
||||
decodedChar = ':';
|
||||
} else {
|
||||
throw ReaderException("");
|
||||
}
|
||||
break;
|
||||
}
|
||||
tmpDecoded.append(1, decodedChar);
|
||||
// bump up i again since we read two characters
|
||||
i++;
|
||||
} else {
|
||||
tmpDecoded.append(1, c);
|
||||
}
|
||||
}
|
||||
Ref<String> decoded(new String(tmpDecoded));
|
||||
return decoded;
|
||||
}
|
||||
|
||||
|
||||
Code39Reader::~Code39Reader(){
|
||||
delete ALPHABET_STRING;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
57
cpp/core/src/zxing/oned/Code39Reader.h
Normal file
57
cpp/core/src/zxing/oned/Code39Reader.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Code39Reader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-26.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <zxing/oned/OneDReader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
/**
|
||||
* <p>Decodes Code 39 barcodes. This does not support "Full ASCII Code 39" yet.</p>
|
||||
* Ported form Java (author Sean Owen)
|
||||
* @author Lukasz Warchol
|
||||
*/
|
||||
class Code39Reader : public OneDReader {
|
||||
|
||||
private:
|
||||
std::string* ALPHABET_STRING;
|
||||
|
||||
bool usingCheckDigit;
|
||||
bool extendedMode;
|
||||
|
||||
static int* findAsteriskPattern(Ref<BitArray> row); //throws ReaderException
|
||||
static int toNarrowWidePattern(int counters[], int countersLen);
|
||||
static char patternToChar(int pattern); //throws ReaderException
|
||||
static Ref<String> decodeExtended(std::string encoded); //throws ReaderException
|
||||
|
||||
void append(char* s, char c);
|
||||
public:
|
||||
Code39Reader();
|
||||
Code39Reader(bool usingCheckDigit_);
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
|
||||
~Code39Reader();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
95
cpp/core/src/zxing/oned/EAN13Reader.cpp
Normal file
95
cpp/core/src/zxing/oned/EAN13Reader.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* EAN13Reader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-22.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "EAN13Reader.h"
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
static const int FIRST_DIGIT_ENCODINGS[10] = {0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A};
|
||||
|
||||
|
||||
EAN13Reader::EAN13Reader(){
|
||||
decodeMiddleCounters = new int[4];
|
||||
for (int i=0; i<4; i++) {
|
||||
decodeMiddleCounters[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int EAN13Reader::decodeMiddle(Ref<BitArray> row, int startRange[], int startRangeLen, std::string& resultString){
|
||||
int countersLen = 4;
|
||||
int* counters = decodeMiddleCounters;
|
||||
counters[0] = 0;
|
||||
counters[1] = 0;
|
||||
counters[2] = 0;
|
||||
counters[3] = 0;
|
||||
|
||||
|
||||
int end = row->getSize();
|
||||
int rowOffset = startRange[1];
|
||||
|
||||
int lgPatternFound = 0;
|
||||
|
||||
for (int x = 0; x < 6 && rowOffset < end; x++) {
|
||||
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_AND_G_PATTERNS);
|
||||
resultString.append(1, (char) ('0' + bestMatch % 10));
|
||||
for (int i = 0; i < countersLen; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
if (bestMatch >= 10) {
|
||||
lgPatternFound |= 1 << (5 - x);
|
||||
}
|
||||
}
|
||||
|
||||
determineFirstDigit(resultString, lgPatternFound);
|
||||
|
||||
int* middleRange = findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), getMIDDLE_PATTERN_LEN());
|
||||
rowOffset = middleRange[1];
|
||||
|
||||
for (int x = 0; x < 6 && rowOffset < end; x++) {
|
||||
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_PATTERNS);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
for (int i = 0; i < countersLen; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
}
|
||||
|
||||
return rowOffset;
|
||||
}
|
||||
|
||||
void EAN13Reader::determineFirstDigit(std::string& resultString, int lgPatternFound){
|
||||
for (int d = 0; d < 10; d++) {
|
||||
if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) {
|
||||
resultString.insert(0, 1, (char) ('0' + d));
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw ReaderException("determineFirstDigit");
|
||||
}
|
||||
|
||||
BarcodeFormat EAN13Reader::getBarcodeFormat(){
|
||||
return BarcodeFormat_EAN_13;
|
||||
}
|
||||
EAN13Reader::~EAN13Reader(){
|
||||
delete [] decodeMiddleCounters;
|
||||
}
|
||||
}
|
||||
}
|
41
cpp/core/src/zxing/oned/EAN13Reader.h
Normal file
41
cpp/core/src/zxing/oned/EAN13Reader.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* EAN13Reader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-22.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <zxing/oned/UPCEANReader.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class EAN13Reader : public UPCEANReader {
|
||||
|
||||
private:
|
||||
int* decodeMiddleCounters;
|
||||
static void determineFirstDigit(std::string& resultString, int lgPatternFound); //throws ReaderException
|
||||
|
||||
public:
|
||||
EAN13Reader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, int startRange[], int startRangeLen, std::string& resultString); //throws ReaderException
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
~EAN13Reader();
|
||||
};
|
||||
}
|
||||
}
|
75
cpp/core/src/zxing/oned/EAN8Reader.cpp
Normal file
75
cpp/core/src/zxing/oned/EAN8Reader.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* EAN8Reader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-25.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "EAN8Reader.h"
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
EAN8Reader::EAN8Reader(){
|
||||
decodeMiddleCounters = new int[4];
|
||||
for (int i=0; i<4; i++) {
|
||||
decodeMiddleCounters[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int EAN8Reader::decodeMiddle(Ref<BitArray> row, int startRange[], int startRangeLen, std::string& resultString){
|
||||
int countersLen = 4;
|
||||
int* counters = decodeMiddleCounters;
|
||||
counters[0] = 0;
|
||||
counters[1] = 0;
|
||||
counters[2] = 0;
|
||||
counters[3] = 0;
|
||||
|
||||
|
||||
int end = row->getSize();
|
||||
int rowOffset = startRange[1];
|
||||
|
||||
for (int x = 0; x < 4 && rowOffset < end; x++) {
|
||||
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_PATTERNS);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
for (int i = 0; i < countersLen; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
}
|
||||
|
||||
int* middleRange = findGuardPattern(row, rowOffset, true, (int*)getMIDDLE_PATTERN(), getMIDDLE_PATTERN_LEN());
|
||||
rowOffset = middleRange[1];
|
||||
|
||||
for (int x = 0; x < 4 && rowOffset < end; x++) {
|
||||
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_PATTERNS);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
for (int i = 0; i < countersLen; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
}
|
||||
|
||||
return rowOffset;
|
||||
}
|
||||
|
||||
BarcodeFormat EAN8Reader::getBarcodeFormat(){
|
||||
return BarcodeFormat_EAN_8;
|
||||
}
|
||||
EAN8Reader::~EAN8Reader(){
|
||||
delete [] decodeMiddleCounters;
|
||||
}
|
||||
}
|
||||
}
|
40
cpp/core/src/zxing/oned/EAN8Reader.h
Normal file
40
cpp/core/src/zxing/oned/EAN8Reader.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* EAN8Reader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-25.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <zxing/oned/UPCEANReader.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class EAN8Reader : public UPCEANReader {
|
||||
|
||||
private:
|
||||
int* decodeMiddleCounters;
|
||||
|
||||
public:
|
||||
EAN8Reader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, int startRange[], int startRangeLen, std::string& resultString); //throws ReaderException
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
~EAN8Reader();
|
||||
};
|
||||
}
|
||||
}
|
353
cpp/core/src/zxing/oned/ITFReader.cpp
Normal file
353
cpp/core/src/zxing/oned/ITFReader.cpp
Normal file
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
* ITFReader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-26.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "ITFReader.h"
|
||||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
static const int W = 3; // Pixel width of a wide line
|
||||
static const int N = 1; // Pixed width of a narrow line
|
||||
|
||||
const int DEFAULT_ALLOWED_LENGTHS[4] = { 6, 10, 14, 44 };
|
||||
|
||||
/**
|
||||
* Start/end guard pattern.
|
||||
*
|
||||
* Note: The end pattern is reversed because the row is reversed before
|
||||
* searching for the END_PATTERN
|
||||
*/
|
||||
static const int START_PATTERN_LEN = 4;
|
||||
static const int START_PATTERN[START_PATTERN_LEN] = {N, N, N, N};
|
||||
|
||||
static const int END_PATTERN_REVERSED_LEN = 3;
|
||||
static const int END_PATTERN_REVERSED[END_PATTERN_REVERSED_LEN] = {N, N, W};
|
||||
|
||||
/**
|
||||
* Patterns of Wide / Narrow lines to indicate each digit
|
||||
*/
|
||||
static const int PATTERNS_LEN = 10;
|
||||
static const int PATTERNS[PATTERNS_LEN][5] = {
|
||||
{N, N, W, W, N}, // 0
|
||||
{W, N, N, N, W}, // 1
|
||||
{N, W, N, N, W}, // 2
|
||||
{W, W, N, N, N}, // 3
|
||||
{N, N, W, N, W}, // 4
|
||||
{W, N, W, N, N}, // 5
|
||||
{N, W, W, N, N}, // 6
|
||||
{N, N, N, W, W}, // 7
|
||||
{W, N, N, W, N}, // 8
|
||||
{N, W, N, W, N} // 9
|
||||
};
|
||||
|
||||
|
||||
ITFReader::ITFReader(){
|
||||
narrowLineWidth = -1;
|
||||
}
|
||||
|
||||
|
||||
Ref<Result> ITFReader::decodeRow(int rowNumber, Ref<BitArray> row){
|
||||
// Find out where the Middle section (payload) starts & ends
|
||||
int* startRange = decodeStart(row);
|
||||
int* endRange = decodeEnd(row);
|
||||
|
||||
std::string tmpResult;
|
||||
decodeMiddle(row, startRange[1], endRange[0], tmpResult);
|
||||
|
||||
// To avoid false positives with 2D barcodes (and other patterns), make
|
||||
// an assumption that the decoded string must be 6, 10 or 14 digits.
|
||||
int length = tmpResult.length();
|
||||
bool lengthOK = false;
|
||||
if (length == 6 || length == 10 || length == 14) {
|
||||
lengthOK = true;
|
||||
}
|
||||
if (!lengthOK) {
|
||||
throw ReaderException("not enought characters count");
|
||||
}
|
||||
|
||||
Ref<String> resultString(new String(tmpResult));
|
||||
|
||||
std::vector< Ref<ResultPoint> > resultPoints(2);
|
||||
Ref<OneDResultPoint> resultPoint1(new OneDResultPoint(startRange[1], (float) rowNumber));
|
||||
Ref<OneDResultPoint> resultPoint2(new OneDResultPoint(endRange[0], (float) rowNumber));
|
||||
resultPoints[0] = resultPoint1;
|
||||
resultPoints[1] = resultPoint2;
|
||||
|
||||
ArrayRef<unsigned char> resultBytes(1);
|
||||
|
||||
delete [] startRange;
|
||||
delete [] endRange;
|
||||
|
||||
Ref<Result> res(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF));
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param row row of black/white values to search
|
||||
* @param payloadStart offset of start pattern
|
||||
* @param resultString {@link StringBuffer} to append decoded chars to
|
||||
* @throws ReaderException if decoding could not complete successfully
|
||||
*/
|
||||
void ITFReader::decodeMiddle(Ref<BitArray> row, int payloadStart, int payloadEnd, std::string& resultString){
|
||||
// Digits are interleaved in pairs - 5 black lines for one digit, and the
|
||||
// 5
|
||||
// interleaved white lines for the second digit.
|
||||
// Therefore, need to scan 10 lines and then
|
||||
// split these into two arrays
|
||||
int counterDigitPairLen = 10;
|
||||
int* counterDigitPair = new int[counterDigitPairLen];
|
||||
for (int i=0; i<counterDigitPairLen; i++) {
|
||||
counterDigitPair[i] = 0;
|
||||
}
|
||||
|
||||
int* counterBlack = new int[5];
|
||||
int* counterWhite = new int[5];
|
||||
for (int i=0; i<5; i++) {
|
||||
counterBlack[i] = 0;
|
||||
counterWhite[i] = 0;
|
||||
}
|
||||
|
||||
while (payloadStart < payloadEnd) {
|
||||
// Get 10 runs of black/white.
|
||||
recordPattern(row, payloadStart, counterDigitPair, counterDigitPairLen);
|
||||
// Split them into each array
|
||||
for (int k = 0; k < 5; k++) {
|
||||
int twoK = k << 1;
|
||||
counterBlack[k] = counterDigitPair[twoK];
|
||||
counterWhite[k] = counterDigitPair[twoK + 1];
|
||||
}
|
||||
|
||||
int bestMatch = decodeDigit(counterBlack, 5);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
bestMatch = decodeDigit(counterWhite, 5);
|
||||
resultString.append(1, (char) ('0' + bestMatch));
|
||||
|
||||
for (int i = 0; i < counterDigitPairLen; i++) {
|
||||
payloadStart += counterDigitPair[i];
|
||||
}
|
||||
}
|
||||
delete [] counterDigitPair;
|
||||
delete [] counterBlack;
|
||||
delete [] counterWhite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify where the start of the middle / payload section starts.
|
||||
*
|
||||
* @param row row of black/white values to search
|
||||
* @return Array, containing index of start of 'start block' and end of
|
||||
* 'start block'
|
||||
* @throws ReaderException
|
||||
*/
|
||||
int* ITFReader::decodeStart(Ref<BitArray> row){
|
||||
int endStart = skipWhiteSpace(row);
|
||||
/// static int* findGuardPattern(Ref<BitArray> row, int rowOffset, bool whiteFirst, const int pattern[], int patternLen);
|
||||
int* startPattern = findGuardPattern(row, endStart, START_PATTERN, START_PATTERN_LEN);
|
||||
|
||||
// Determine the width of a narrow line in pixels. We can do this by
|
||||
// getting the width of the start pattern and dividing by 4 because its
|
||||
// made up of 4 narrow lines.
|
||||
narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2;
|
||||
|
||||
validateQuietZone(row, startPattern[0]);
|
||||
|
||||
return startPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify where the end of the middle / payload section ends.
|
||||
*
|
||||
* @param row row of black/white values to search
|
||||
* @return Array, containing index of start of 'end block' and end of 'end
|
||||
* block'
|
||||
* @throws ReaderException
|
||||
*/
|
||||
|
||||
int* ITFReader::decodeEnd(Ref<BitArray> row){
|
||||
// For convenience, reverse the row and then
|
||||
// search from 'the start' for the end block
|
||||
row->reverse();
|
||||
try {
|
||||
int endStart = skipWhiteSpace(row);
|
||||
int* endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED, END_PATTERN_REVERSED_LEN);
|
||||
|
||||
// The start & end patterns must be pre/post fixed by a quiet zone. This
|
||||
// zone must be at least 10 times the width of a narrow line.
|
||||
// ref: http://www.barcode-1.net/i25code.html
|
||||
validateQuietZone(row, endPattern[0]);
|
||||
|
||||
// Now recalculate the indices of where the 'endblock' starts & stops to
|
||||
// accommodate
|
||||
// the reversed nature of the search
|
||||
int temp = endPattern[0];
|
||||
endPattern[0] = row->getSize() - endPattern[1];
|
||||
endPattern[1] = row->getSize() - temp;
|
||||
|
||||
return endPattern;
|
||||
}catch (Exception e) {
|
||||
row->reverse();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The start & end patterns must be pre/post fixed by a quiet zone. This
|
||||
* zone must be at least 10 times the width of a narrow line. Scan back until
|
||||
* we either get to the start of the barcode or match the necessary number of
|
||||
* quiet zone pixels.
|
||||
*
|
||||
* Note: Its assumed the row is reversed when using this method to find
|
||||
* quiet zone after the end pattern.
|
||||
*
|
||||
* ref: http://www.barcode-1.net/i25code.html
|
||||
*
|
||||
* @param row bit array representing the scanned barcode.
|
||||
* @param startPattern index into row of the start or end pattern.
|
||||
* @throws ReaderException if the quiet zone cannot be found, a ReaderException is thrown.
|
||||
*/
|
||||
void ITFReader::validateQuietZone(Ref<BitArray> row, int startPattern){
|
||||
#pragma mark needs some corrections
|
||||
// int quietCount = narrowLineWidth * 10; // expect to find this many pixels of quiet zone
|
||||
//
|
||||
// for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {
|
||||
// if (row->get(i)) {
|
||||
// break;
|
||||
// }
|
||||
// quietCount--;
|
||||
// }
|
||||
// if (quietCount != 0) {
|
||||
// // Unable to find the necessary number of quiet zone pixels.
|
||||
// throw ReaderException("Unable to find the necessary number of quiet zone pixels");
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip all whitespace until we get to the first black line.
|
||||
*
|
||||
* @param row row of black/white values to search
|
||||
* @return index of the first black line.
|
||||
* @throws ReaderException Throws exception if no black lines are found in the row
|
||||
*/
|
||||
int ITFReader::skipWhiteSpace(Ref<BitArray> row){
|
||||
int width = row->getSize();
|
||||
int endStart = 0;
|
||||
while (endStart < width) {
|
||||
if (row->get(endStart)) {
|
||||
break;
|
||||
}
|
||||
endStart++;
|
||||
}
|
||||
if (endStart == width) {
|
||||
throw ReaderException("");
|
||||
}
|
||||
return endStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param row row of black/white values to search
|
||||
* @param rowOffset position to start search
|
||||
* @param pattern pattern of counts of number of black and white pixels that are
|
||||
* being searched for as a pattern
|
||||
* @return start/end horizontal offset of guard pattern, as an array of two
|
||||
* ints
|
||||
* @throws ReaderException if pattern is not found
|
||||
*/
|
||||
|
||||
int* ITFReader::findGuardPattern(Ref<BitArray> row, int rowOffset, const int pattern[], int patternLen){
|
||||
// TODO: This is very similar to implementation in UPCEANReader. Consider if they can be
|
||||
// merged to a single method.
|
||||
int patternLength = patternLen;
|
||||
int* counters = new int[patternLength];
|
||||
for (int i=0; i<patternLength; i++) {
|
||||
counters[i] = 0;
|
||||
}
|
||||
int width = row->getSize();
|
||||
bool isWhite = false;
|
||||
|
||||
int counterPosition = 0;
|
||||
int patternStart = rowOffset;
|
||||
for (int x = rowOffset; x < width; x++) {
|
||||
bool pixel = row->get(x);
|
||||
if (pixel ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
if (patternMatchVariance(counters, patternLength, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
|
||||
int* resultValue = new int[2];
|
||||
resultValue[0] = patternStart;
|
||||
resultValue[1] = x;
|
||||
return resultValue;
|
||||
}
|
||||
patternStart += counters[0] + counters[1];
|
||||
for (int y = 2; y < patternLength; y++) {
|
||||
counters[y - 2] = counters[y];
|
||||
}
|
||||
counters[patternLength - 2] = 0;
|
||||
counters[patternLength - 1] = 0;
|
||||
counterPosition--;
|
||||
} else {
|
||||
counterPosition++;
|
||||
}
|
||||
counters[counterPosition] = 1;
|
||||
isWhite = !isWhite;
|
||||
}
|
||||
}
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to decode a sequence of ITF black/white lines into single
|
||||
* digit.
|
||||
*
|
||||
* @param counters the counts of runs of observed black/white/black/... values
|
||||
* @return The decoded digit
|
||||
* @throws ReaderException if digit cannot be decoded
|
||||
*/
|
||||
int ITFReader::decodeDigit(int counters[], int countersLen){
|
||||
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
||||
int bestMatch = -1;
|
||||
int max = PATTERNS_LEN;
|
||||
for (int i = 0; i < max; i++) {
|
||||
int pattern[countersLen];
|
||||
for(int ind = 0; ind<countersLen; ind++){
|
||||
pattern[ind] = PATTERNS[i][ind];
|
||||
}
|
||||
int variance = patternMatchVariance(counters, countersLen, pattern, MAX_INDIVIDUAL_VARIANCE);
|
||||
if (variance < bestVariance) {
|
||||
bestVariance = variance;
|
||||
bestMatch = i;
|
||||
}
|
||||
}
|
||||
if (bestMatch >= 0) {
|
||||
return bestMatch;
|
||||
} else {
|
||||
throw ReaderException("digit didint found");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ITFReader::~ITFReader(){
|
||||
}
|
||||
}
|
||||
}
|
53
cpp/core/src/zxing/oned/ITFReader.h
Normal file
53
cpp/core/src/zxing/oned/ITFReader.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* ITFReader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-26.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <zxing/oned/OneDReader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class ITFReader : public OneDReader {
|
||||
|
||||
private:
|
||||
static const int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f);
|
||||
static const int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.8f);
|
||||
|
||||
// Stores the actual narrow line width of the image being decoded.
|
||||
int narrowLineWidth;
|
||||
|
||||
int* decodeStart(Ref<BitArray> row); //throws ReaderException
|
||||
int* decodeEnd(Ref<BitArray> row); //throws ReaderException
|
||||
static void decodeMiddle(Ref<BitArray> row, int payloadStart, int payloadEnd, std::string& resultString); //throws ReaderException
|
||||
void validateQuietZone(Ref<BitArray> row, int startPattern); //throws ReaderException
|
||||
static int skipWhiteSpace(Ref<BitArray> row); //throws ReaderException
|
||||
|
||||
static int* findGuardPattern(Ref<BitArray> row, int rowOffset, const int pattern[], int patternLen); //throws ReaderException
|
||||
static int decodeDigit(int counters[], int countersLen); //throws ReaderException
|
||||
|
||||
void append(char* s, char c);
|
||||
public:
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row); ///throws ReaderException
|
||||
ITFReader();
|
||||
~ITFReader();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
55
cpp/core/src/zxing/oned/MultiFormatOneDReader.cpp
Normal file
55
cpp/core/src/zxing/oned/MultiFormatOneDReader.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* MultiFormatOneDReader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-25.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "MultiFormatOneDReader.h"
|
||||
|
||||
#include <zxing/oned/MultiFormatUPCEANReader.h>
|
||||
#include <zxing/oned/Code39Reader.h>
|
||||
#include <zxing/oned/Code128Reader.h>
|
||||
#include <zxing/oned/ITFReader.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
MultiFormatOneDReader::MultiFormatOneDReader(){
|
||||
readers = new std::vector<OneDReader*>();
|
||||
readers->push_back(new MultiFormatUPCEANReader());
|
||||
readers->push_back(new Code39Reader());
|
||||
readers->push_back(new Code128Reader());
|
||||
readers->push_back(new ITFReader());
|
||||
}
|
||||
|
||||
Ref<Result> MultiFormatOneDReader::decodeRow(int rowNumber, Ref<BitArray> row){
|
||||
int size = readers->size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
OneDReader* reader = (*readers)[i];
|
||||
try {
|
||||
return reader->decodeRow(rowNumber, row);
|
||||
} catch (ReaderException re) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
throw ReaderException("No code detected");
|
||||
}
|
||||
MultiFormatOneDReader::~MultiFormatOneDReader(){
|
||||
delete readers;
|
||||
}
|
||||
}
|
||||
}
|
39
cpp/core/src/zxing/oned/MultiFormatOneDReader.h
Normal file
39
cpp/core/src/zxing/oned/MultiFormatOneDReader.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* MultiFormatOneDReader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-25.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <zxing/oned/OneDReader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class MultiFormatOneDReader : public OneDReader {
|
||||
|
||||
private:
|
||||
std::vector<OneDReader*>* readers;
|
||||
public:
|
||||
MultiFormatOneDReader();
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
|
||||
~MultiFormatOneDReader();
|
||||
};
|
||||
}
|
||||
}
|
79
cpp/core/src/zxing/oned/MultiFormatUPCEANReader.cpp
Normal file
79
cpp/core/src/zxing/oned/MultiFormatUPCEANReader.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* MultiFormatUPCEANReader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-25.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include "MultiFormatUPCEANReader.h"
|
||||
|
||||
#include <zxing/oned/EAN13Reader.h>
|
||||
#include <zxing/oned/EAN8Reader.h>
|
||||
#include <zxing/oned/UPCEReader.h>
|
||||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/common/Array.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
MultiFormatUPCEANReader::MultiFormatUPCEANReader(){
|
||||
readers = new std::vector<OneDReader*>();
|
||||
readers->push_back(new EAN13Reader());
|
||||
// UPC-A is covered by EAN-13
|
||||
readers->push_back(new EAN8Reader());
|
||||
readers->push_back(new UPCEReader());
|
||||
}
|
||||
|
||||
Ref<Result> MultiFormatUPCEANReader::decodeRow(int rowNumber, Ref<BitArray> row){
|
||||
// Compute this location once and reuse it on multiple implementations
|
||||
int size = readers->size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
OneDReader* reader = (*readers)[i];
|
||||
Ref<Result> result;
|
||||
try {
|
||||
result = reader->decodeRow(rowNumber, row);//decodeRow(rowNumber, row, startGuardPattern);
|
||||
} catch (ReaderException re) {
|
||||
continue;
|
||||
}
|
||||
// Special case: a 12-digit code encoded in UPC-A is identical to a "0"
|
||||
// followed by those 12 digits encoded as EAN-13. Each will recognize such a code,
|
||||
// UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with "0".
|
||||
// Individually these are correct and their readers will both read such a code
|
||||
// and correctly call it EAN-13, or UPC-A, respectively.
|
||||
//
|
||||
// In this case, if we've been looking for both types, we'd like to call it
|
||||
// a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read
|
||||
// UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A
|
||||
// result if appropriate.
|
||||
if (result->getBarcodeFormat() == BarcodeFormat_EAN_13) {
|
||||
std::string& text = (result->getText())->getText();
|
||||
if (text[0] == '0') {
|
||||
Ref<String> resultString(new String(text.substr(1)));
|
||||
Ref<Result> res(new Result(resultString, result->getRawBytes(), result->getResultPoints(), BarcodeFormat_UPC_A));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
throw ReaderException("No EAN code detected");
|
||||
}
|
||||
|
||||
MultiFormatUPCEANReader::~MultiFormatUPCEANReader(){
|
||||
delete readers;
|
||||
}
|
||||
}
|
||||
}
|
41
cpp/core/src/zxing/oned/MultiFormatUPCEANReader.h
Normal file
41
cpp/core/src/zxing/oned/MultiFormatUPCEANReader.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* MultiFormatUPCEANReader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-25.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <zxing/oned/OneDReader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class MultiFormatUPCEANReader : public OneDReader {
|
||||
|
||||
private:
|
||||
std::vector<OneDReader*>* readers;
|
||||
public:
|
||||
MultiFormatUPCEANReader();
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
|
||||
~MultiFormatUPCEANReader();
|
||||
};
|
||||
}
|
||||
}
|
193
cpp/core/src/zxing/oned/OneDReader.cpp
Normal file
193
cpp/core/src/zxing/oned/OneDReader.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* OneDReader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-15.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "OneDReader.h"
|
||||
#include <zxing/ReaderException.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
using namespace std;
|
||||
|
||||
OneDReader::OneDReader() {
|
||||
}
|
||||
|
||||
Ref<Result> OneDReader::decode(Ref<BinaryBitmap> image) {
|
||||
try {
|
||||
return doDecode(image);
|
||||
}catch (ReaderException re) {
|
||||
if (false /*tryHarder && image.isRotateSupported()*/) {
|
||||
/*
|
||||
BinaryBitmap rotatedImage = image.rotateCounterClockwise();
|
||||
Result result = doDecode(rotatedImage, hints);
|
||||
// Record that we found it rotated 90 degrees CCW / 270 degrees CW
|
||||
Hashtable metadata = result.getResultMetadata();
|
||||
int orientation = 270;
|
||||
if (metadata != null && metadata.containsKey(ResultMetadataType.ORIENTATION)) {
|
||||
// But if we found it reversed in doDecode(), add in that result here:
|
||||
orientation = (orientation +
|
||||
((Integer) metadata.get(ResultMetadataType.ORIENTATION)).intValue()) % 360;
|
||||
}
|
||||
result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(orientation));
|
||||
// Update result points
|
||||
ResultPoint[] points = result.getResultPoints();
|
||||
int height = rotatedImage.getHeight();
|
||||
for (int i = 0; i < points.length; i++) {
|
||||
points[i] = new ResultPoint(height - points[i].getY() - 1, points[i].getX());
|
||||
}
|
||||
return result;
|
||||
*/
|
||||
} else {
|
||||
throw re;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Result> OneDReader::doDecode(Ref<BinaryBitmap> image){
|
||||
int width = image->getWidth();
|
||||
int height = image->getHeight();
|
||||
Ref<BitArray> row(new BitArray(width));
|
||||
// BitArray row = new BitArray(width);
|
||||
|
||||
int middle = height >> 1;
|
||||
bool tryHarder = true;//hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
|
||||
int rowStep = (int)fmax(1, height >> (tryHarder ? 7 : 4));
|
||||
int maxLines;
|
||||
if (tryHarder) {
|
||||
maxLines = height; // Look at the whole image, not just the center
|
||||
} else {
|
||||
maxLines = 9; // Nine rows spaced 1/16 apart is roughly the middle half of the image
|
||||
}
|
||||
|
||||
for (int x = 0; x < maxLines; x++) {
|
||||
|
||||
// Scanning from the middle out. Determine which row we're looking at next:
|
||||
int rowStepsAboveOrBelow = (x + 1) >> 1;
|
||||
bool isAbove = (x & 0x01) == 0; // i.e. is x even?
|
||||
int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);
|
||||
if (rowNumber < 0 || rowNumber >= height) {
|
||||
// Oops, if we run off the top or bottom, stop
|
||||
break;
|
||||
}
|
||||
|
||||
// Estimate black point for this row and load it:
|
||||
try {
|
||||
row = image->getBlackRow(rowNumber, row);
|
||||
}catch (ReaderException re) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// While we have the image data in a BitArray, it's fairly cheap to reverse it in place to
|
||||
// handle decoding upside down barcodes.
|
||||
for (int attempt = 0; attempt < 2; attempt++) {
|
||||
if (attempt == 1) { // trying again?
|
||||
row->reverse(); // reverse the row and continue
|
||||
}
|
||||
try {
|
||||
// Look for a barcode
|
||||
Ref<Result> result = decodeRow(rowNumber, row);
|
||||
// We found our barcode
|
||||
if (attempt == 1) {
|
||||
// // But it was upside down, so note that
|
||||
// result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(180));
|
||||
// // And remember to flip the result points horizontally.
|
||||
// ResultPoint[] points = result.getResultPoints();
|
||||
// points[0] = new ResultPoint(width - points[0].getX() - 1, points[0].getY());
|
||||
// points[1] = new ResultPoint(width - points[1].getX() - 1, points[1].getY());
|
||||
}
|
||||
return result;
|
||||
} catch (ReaderException re) {
|
||||
// continue -- just couldn't decode this row
|
||||
}
|
||||
}
|
||||
}
|
||||
throw ReaderException("");
|
||||
}
|
||||
|
||||
int OneDReader::patternMatchVariance(int counters[], int countersSize, const int pattern[], int maxIndividualVariance) {
|
||||
int numCounters = countersSize;
|
||||
int total = 0;
|
||||
int patternLength = 0;
|
||||
for (int i = 0; i < numCounters; i++) {
|
||||
total += counters[i];
|
||||
patternLength += pattern[i];
|
||||
}
|
||||
if (total < patternLength) {
|
||||
// If we don't even have one pixel per unit of bar width, assume this is too small
|
||||
// to reliably match, so fail:
|
||||
return INT_MAX;
|
||||
}
|
||||
// We're going to fake floating-point math in integers. We just need to use more bits.
|
||||
// Scale up patternLength so that intermediate values below like scaledCounter will have
|
||||
// more "significant digits"
|
||||
int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
|
||||
maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
|
||||
|
||||
int totalVariance = 0;
|
||||
for (int x = 0; x < numCounters; x++) {
|
||||
int counter = counters[x] << INTEGER_MATH_SHIFT;
|
||||
int scaledPattern = pattern[x] * unitBarWidth;
|
||||
int variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
|
||||
if (variance > maxIndividualVariance) {
|
||||
return INT_MAX;
|
||||
}
|
||||
totalVariance += variance;
|
||||
}
|
||||
return totalVariance / total;
|
||||
}
|
||||
|
||||
void OneDReader::recordPattern(Ref<BitArray> row, int start, int counters[], int countersCount){
|
||||
int numCounters = countersCount;//sizeof(counters) / sizeof(int);
|
||||
for (int i = 0; i < numCounters; i++) {
|
||||
counters[i] = 0;
|
||||
}
|
||||
int end = row->getSize();
|
||||
if (start >= end) {
|
||||
throw ReaderException("recordPattern: start >= end");
|
||||
}
|
||||
bool isWhite = !row->get(start);
|
||||
int counterPosition = 0;
|
||||
int i = start;
|
||||
while (i < end) {
|
||||
bool pixel = row->get(i);
|
||||
if (pixel ^ isWhite) { // that is, exactly one is true
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
counterPosition++;
|
||||
if (counterPosition == numCounters) {
|
||||
break;
|
||||
} else {
|
||||
counters[counterPosition] = 1;
|
||||
isWhite ^= true; // isWhite = !isWhite;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// If we read fully the last section of pixels and filled up our counters -- or filled
|
||||
// the last counter but ran off the side of the image, OK. Otherwise, a problem.
|
||||
if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) {
|
||||
throw ReaderException("recordPattern");
|
||||
}
|
||||
}
|
||||
|
||||
OneDReader::~OneDReader() {
|
||||
}
|
||||
}
|
||||
}
|
46
cpp/core/src/zxing/oned/OneDReader.h
Normal file
46
cpp/core/src/zxing/oned/OneDReader.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* OneDReader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-15.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zxing/Reader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/BinaryBitmap.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class OneDReader : public Reader {
|
||||
private:
|
||||
static const int INTEGER_MATH_SHIFT = 8;
|
||||
|
||||
Ref<Result> doDecode(Ref<BinaryBitmap> image);
|
||||
public:
|
||||
static const int PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT;
|
||||
|
||||
OneDReader();
|
||||
virtual Ref<Result> decode(Ref<BinaryBitmap> image);
|
||||
virtual Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row) = 0;
|
||||
|
||||
static int patternMatchVariance(int counters[], int countersSize, const int pattern[], int maxIndividualVariance);
|
||||
static void recordPattern(Ref<BitArray> row, int start, int counters[], int countersCount);
|
||||
virtual ~OneDReader();
|
||||
};
|
||||
}
|
||||
}
|
39
cpp/core/src/zxing/oned/OneDResultPoint.cpp
Normal file
39
cpp/core/src/zxing/oned/OneDResultPoint.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* OneDResultPoint.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-20.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "OneDResultPoint.h"
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
using namespace std;
|
||||
|
||||
OneDResultPoint::OneDResultPoint(float posX, float posY) : posX_(posX), posY_(posY){
|
||||
}
|
||||
|
||||
float OneDResultPoint::getX() {
|
||||
return posX_;
|
||||
}
|
||||
|
||||
float OneDResultPoint::getY() {
|
||||
return posY_;
|
||||
}
|
||||
}
|
||||
}
|
37
cpp/core/src/zxing/oned/OneDResultPoint.h
Normal file
37
cpp/core/src/zxing/oned/OneDResultPoint.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* OneDResultPoint.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-20.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <zxing/ResultPoint.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
class OneDResultPoint : public ResultPoint {
|
||||
private:
|
||||
float posX_;
|
||||
float posY_;
|
||||
|
||||
public:
|
||||
OneDResultPoint(float posX, float posY);
|
||||
float getX();
|
||||
float getY();
|
||||
};
|
||||
}
|
||||
}
|
64
cpp/core/src/zxing/oned/UPCAReader.cpp
Normal file
64
cpp/core/src/zxing/oned/UPCAReader.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* UPCAReader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-25.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "UPCAReader.h"
|
||||
#include <zxing/oned/EAN13Reader.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
UPCAReader::UPCAReader(){
|
||||
ean13Reader = new EAN13Reader();
|
||||
}
|
||||
|
||||
Ref<Result> UPCAReader::decodeRow(int rowNumber, Ref<BitArray> row){
|
||||
return maybeReturnResult(ean13Reader->decodeRow(rowNumber, row));
|
||||
}
|
||||
Ref<Result> UPCAReader::decodeRow(int rowNumber, Ref<BitArray> row, int startGuardRange[]){
|
||||
return maybeReturnResult(ean13Reader->decodeRow(rowNumber, row, startGuardRange));
|
||||
}
|
||||
Ref<Result> UPCAReader::decode(Ref<BinaryBitmap> image){
|
||||
return maybeReturnResult(ean13Reader->decode(image));
|
||||
}
|
||||
|
||||
int UPCAReader::decodeMiddle(Ref<BitArray> row, int startRange[], int startRangeLen, std::string& resultString){
|
||||
return ean13Reader->decodeMiddle(row, startRange, startRangeLen, resultString);
|
||||
}
|
||||
|
||||
Ref<Result> UPCAReader::maybeReturnResult(Ref<Result> result){
|
||||
std::string& text = (result->getText())->getText();
|
||||
if (text[0] == '0') {
|
||||
Ref<String> resultString(new String(text.substr(1)));
|
||||
Ref<Result> res(new Result(resultString, result->getRawBytes(), result->getResultPoints(), BarcodeFormat_UPC_A));
|
||||
return res;
|
||||
} else {
|
||||
throw ReaderException("Not UPC-A barcode.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BarcodeFormat UPCAReader::getBarcodeFormat(){
|
||||
return BarcodeFormat_UPC_A;
|
||||
}
|
||||
UPCAReader::~UPCAReader(){
|
||||
delete ean13Reader;
|
||||
}
|
||||
}
|
||||
}
|
45
cpp/core/src/zxing/oned/UPCAReader.h
Normal file
45
cpp/core/src/zxing/oned/UPCAReader.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* UPCAReader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-25.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <zxing/oned/UPCEANReader.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class UPCAReader : public UPCEANReader {
|
||||
|
||||
private:
|
||||
UPCEANReader* ean13Reader;
|
||||
static Ref<Result> maybeReturnResult(Ref<Result> result); //throws ReaderException
|
||||
|
||||
public:
|
||||
UPCAReader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, int startRange[], int startRangeLen, std::string& resultString); //throws ReaderException
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row); //throws ReaderException
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row, int startGuardRange[]); //throws ReaderException
|
||||
Ref<Result> decode(Ref<BinaryBitmap> image);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
~UPCAReader();
|
||||
};
|
||||
}
|
||||
}
|
310
cpp/core/src/zxing/oned/UPCEANReader.cpp
Normal file
310
cpp/core/src/zxing/oned/UPCEANReader.cpp
Normal file
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* UPCEANReader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-21.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "UPCEANReader.h"
|
||||
#include <zxing/oned/OneDResultPoint.h>
|
||||
#include <zxing/ReaderException.h>
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
/**
|
||||
* Start/end guard pattern.
|
||||
*/
|
||||
static const int START_END_PATTERN[3] = {1, 1, 1};
|
||||
|
||||
/**
|
||||
* Pattern marking the middle of a UPC/EAN pattern, separating the two halves.
|
||||
*/
|
||||
static const int MIDDLE_PATTERN_LEN = 5;
|
||||
static const int MIDDLE_PATTERN[MIDDLE_PATTERN_LEN] = {1, 1, 1, 1, 1};
|
||||
|
||||
/**
|
||||
* "Odd", or "L" patterns used to encode UPC/EAN digits.
|
||||
*/
|
||||
const int L_PATTERNS_LEN = 10;
|
||||
const int L_PATTERNS_SUB_LEN = 4;
|
||||
const int L_PATTERNS[10][4] = {
|
||||
{3, 2, 1, 1}, // 0
|
||||
{2, 2, 2, 1}, // 1
|
||||
{2, 1, 2, 2}, // 2
|
||||
{1, 4, 1, 1}, // 3
|
||||
{1, 1, 3, 2}, // 4
|
||||
{1, 2, 3, 1}, // 5
|
||||
{1, 1, 1, 4}, // 6
|
||||
{1, 3, 1, 2}, // 7
|
||||
{1, 2, 1, 3}, // 8
|
||||
{3, 1, 1, 2} // 9
|
||||
};
|
||||
|
||||
/**
|
||||
* As above but also including the "even", or "G" patterns used to encode UPC/EAN digits.
|
||||
*/
|
||||
const int L_AND_G_PATTERNS_LEN = 20;
|
||||
const int L_AND_G_PATTERNS_SUB_LEN = 4;
|
||||
const int L_AND_G_PATTERNS[L_AND_G_PATTERNS_LEN][L_AND_G_PATTERNS_SUB_LEN] = {
|
||||
{3, 2, 1, 1}, // 0
|
||||
{2, 2, 2, 1}, // 1
|
||||
{2, 1, 2, 2}, // 2
|
||||
{1, 4, 1, 1}, // 3
|
||||
{1, 1, 3, 2}, // 4
|
||||
{1, 2, 3, 1}, // 5
|
||||
{1, 1, 1, 4}, // 6
|
||||
{1, 3, 1, 2}, // 7
|
||||
{1, 2, 1, 3}, // 8
|
||||
{3, 1, 1, 2}, // 9
|
||||
{1, 1, 2, 3}, // 10 reversed 0
|
||||
{1, 2, 2, 2}, // 11 reversed 1
|
||||
{2, 2, 1, 2}, // 12 reversed 2
|
||||
{1, 1, 4, 1}, // 13 reversed 3
|
||||
{2, 3, 1, 1}, // 14 reversed 4
|
||||
{1, 3, 2, 1}, // 15 reversed 5
|
||||
{4, 1, 1, 1}, // 16 reversed 6
|
||||
{2, 1, 3, 1}, // 17 reversed 7
|
||||
{3, 1, 2, 1}, // 18 reversed 8
|
||||
{2, 1, 1, 3} // 19 reversed 9
|
||||
};
|
||||
|
||||
|
||||
const int UPCEANReader::getMIDDLE_PATTERN_LEN(){
|
||||
return MIDDLE_PATTERN_LEN;
|
||||
}
|
||||
const int* UPCEANReader::getMIDDLE_PATTERN(){
|
||||
return MIDDLE_PATTERN;
|
||||
}
|
||||
|
||||
UPCEANReader::UPCEANReader(){
|
||||
}
|
||||
|
||||
|
||||
Ref<Result> UPCEANReader::decodeRow(int rowNumber, Ref<BitArray> row){
|
||||
return decodeRow(rowNumber, row, findStartGuardPattern(row));
|
||||
}
|
||||
Ref<Result> UPCEANReader::decodeRow(int rowNumber, Ref<BitArray> row, int startGuardRange[]){
|
||||
|
||||
std::string tmpResultString;
|
||||
std::string& tmpResultStringRef = tmpResultString;
|
||||
int endStart = decodeMiddle(row, startGuardRange, 2 /*reference findGuardPattern*/ , tmpResultStringRef);
|
||||
|
||||
int* endRange = decodeEnd(row, endStart);
|
||||
|
||||
#pragma mark QuietZone needs some change
|
||||
// Make sure there is a quiet zone at least as big as the end pattern after the barcode. The
|
||||
// spec might want more whitespace, but in practice this is the maximum we can count on.
|
||||
// int end = endRange[1];
|
||||
// int quietEnd = end + (end - endRange[0]);
|
||||
// if (quietEnd >= row->getSize() || !row->isRange(end, quietEnd, false)) {
|
||||
// throw ReaderException("Quiet zone asserrt fail.");
|
||||
// }
|
||||
|
||||
if (!checkChecksum(tmpResultString)) {
|
||||
throw ReaderException("Checksum fail.");
|
||||
}
|
||||
|
||||
Ref<String> resultString(new String(tmpResultString));
|
||||
|
||||
float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f;
|
||||
float right = (float) (endRange[1] + endRange[0]) / 2.0f;
|
||||
|
||||
std::vector< Ref<ResultPoint> > resultPoints(2);
|
||||
Ref<OneDResultPoint> resultPoint1(new OneDResultPoint(left, (float) rowNumber));
|
||||
Ref<OneDResultPoint> resultPoint2(new OneDResultPoint(right, (float) rowNumber));
|
||||
resultPoints[0] = resultPoint1;
|
||||
resultPoints[1] = resultPoint2;
|
||||
|
||||
ArrayRef<unsigned char> resultBytes(1);
|
||||
|
||||
if (startGuardRange!=NULL) {
|
||||
delete [] startGuardRange;
|
||||
startGuardRange = NULL;
|
||||
}
|
||||
if (endRange!=NULL) {
|
||||
delete [] endRange;
|
||||
endRange = NULL;
|
||||
}
|
||||
|
||||
Ref<Result> res(new Result(resultString, resultBytes, resultPoints, getBarcodeFormat()));
|
||||
return res;
|
||||
}
|
||||
|
||||
int* UPCEANReader::findStartGuardPattern(Ref<BitArray> row){
|
||||
bool foundStart = false;
|
||||
|
||||
int* startRange = NULL;
|
||||
int nextStart = 0;
|
||||
while (!foundStart) {
|
||||
startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN, sizeof(START_END_PATTERN)/sizeof(int));
|
||||
int start = startRange[0];
|
||||
nextStart = startRange[1];
|
||||
// Make sure there is a quiet zone at least as big as the start pattern before the barcode.
|
||||
// If this check would run off the left edge of the image, do not accept this barcode,
|
||||
// as it is very likely to be a false positive.
|
||||
int quietStart = start - (nextStart - start);
|
||||
if (quietStart >= 0) {
|
||||
foundStart = row->isRange(quietStart, start, false);
|
||||
}
|
||||
}
|
||||
return startRange;
|
||||
}
|
||||
|
||||
int* UPCEANReader::findGuardPattern(Ref<BitArray> row, int rowOffset, bool whiteFirst, const int pattern[], int patternLen){
|
||||
int patternLength = patternLen;
|
||||
|
||||
int counters[patternLength];
|
||||
int countersCount = sizeof(counters)/sizeof(int);
|
||||
for (int i=0; i<countersCount ; i++) {
|
||||
counters[i]=0;
|
||||
}
|
||||
int width = row->getSize();
|
||||
bool isWhite = false;
|
||||
while (rowOffset < width) {
|
||||
isWhite = !row->get(rowOffset);
|
||||
if (whiteFirst == isWhite) {
|
||||
break;
|
||||
}
|
||||
rowOffset++;
|
||||
}
|
||||
|
||||
int counterPosition = 0;
|
||||
int patternStart = rowOffset;
|
||||
for (int x = rowOffset; x < width; x++) {
|
||||
bool pixel = row->get(x);
|
||||
if (pixel ^ isWhite) {
|
||||
counters[counterPosition]++;
|
||||
} else {
|
||||
if (counterPosition == patternLength - 1) {
|
||||
if (patternMatchVariance(counters, countersCount, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
|
||||
int* resultValue = new int[2];
|
||||
resultValue[0] = patternStart;
|
||||
resultValue[1] = x;
|
||||
return resultValue;
|
||||
}
|
||||
patternStart += counters[0] + counters[1];
|
||||
for (int y = 2; y < patternLength; y++) {
|
||||
counters[y - 2] = counters[y];
|
||||
}
|
||||
counters[patternLength - 2] = 0;
|
||||
counters[patternLength - 1] = 0;
|
||||
counterPosition--;
|
||||
} else {
|
||||
counterPosition++;
|
||||
}
|
||||
counters[counterPosition] = 1;
|
||||
isWhite = !isWhite;
|
||||
}
|
||||
}
|
||||
throw ReaderException("findGuardPattern");
|
||||
}
|
||||
|
||||
int* UPCEANReader::decodeEnd(Ref<BitArray> row, int endStart){
|
||||
return findGuardPattern(row, endStart, false, START_END_PATTERN, sizeof(START_END_PATTERN)/sizeof(int));
|
||||
}
|
||||
|
||||
// int UPCEANReader::decodeDigit(Ref<BitArray> row, int counters[], int countersLen, int rowOffset, int** patterns/*[][]*/, int paterns1Len, int paterns2Len)
|
||||
int UPCEANReader::decodeDigit(Ref<BitArray> row, int counters[], int countersLen, int rowOffset, UPC_EAN_PATTERNS paternType){
|
||||
recordPattern(row, rowOffset, counters, countersLen);
|
||||
int bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept
|
||||
int bestMatch = -1;
|
||||
|
||||
int max = 0;
|
||||
switch (paternType) {
|
||||
case UPC_EAN_PATTERNS_L_PATTERNS:
|
||||
max = L_PATTERNS_LEN;
|
||||
for (int i = 0; i < max; i++) {
|
||||
int pattern[countersLen];
|
||||
for(int j = 0; j< countersLen; j++){
|
||||
pattern[j] = L_PATTERNS[i][j];
|
||||
}
|
||||
|
||||
int variance = patternMatchVariance(counters, countersLen, pattern, MAX_INDIVIDUAL_VARIANCE);
|
||||
if (variance < bestVariance) {
|
||||
bestVariance = variance;
|
||||
bestMatch = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UPC_EAN_PATTERNS_L_AND_G_PATTERNS:
|
||||
max = L_AND_G_PATTERNS_LEN;
|
||||
for (int i = 0; i < max; i++) {
|
||||
int pattern[countersLen];
|
||||
for(int j = 0; j< countersLen; j++){
|
||||
pattern[j] = L_AND_G_PATTERNS[i][j];
|
||||
}
|
||||
|
||||
int variance = patternMatchVariance(counters, countersLen, pattern, MAX_INDIVIDUAL_VARIANCE);
|
||||
if (variance < bestVariance) {
|
||||
bestVariance = variance;
|
||||
bestMatch = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (bestMatch >= 0) {
|
||||
return bestMatch;
|
||||
} else {
|
||||
throw ReaderException("UPCEANReader::decodeDigit: No best mach");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return {@link #checkStandardUPCEANChecksum(String)}
|
||||
*/
|
||||
bool UPCEANReader::checkChecksum(std::string s){
|
||||
return checkStandardUPCEANChecksum(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the UPC/EAN checksum on a string of digits, and reports
|
||||
* whether the checksum is correct or not.
|
||||
*
|
||||
* @param s string of digits to check
|
||||
* @return true iff string of digits passes the UPC/EAN checksum algorithm
|
||||
* @throws ReaderException if the string does not contain only digits
|
||||
*/
|
||||
bool UPCEANReader::checkStandardUPCEANChecksum(std::string s){
|
||||
int length = s.length();
|
||||
if (length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int sum = 0;
|
||||
for (int i = length - 2; i >= 0; i -= 2) {
|
||||
int digit = (int) s[i] - (int) '0';
|
||||
if (digit < 0 || digit > 9) {
|
||||
throw ReaderException("checkStandardUPCEANChecksum");
|
||||
}
|
||||
sum += digit;
|
||||
}
|
||||
sum *= 3;
|
||||
for (int i = length - 1; i >= 0; i -= 2) {
|
||||
int digit = (int) s[i] - (int) '0';
|
||||
if (digit < 0 || digit > 9) {
|
||||
throw ReaderException("checkStandardUPCEANChecksum");
|
||||
}
|
||||
sum += digit;
|
||||
}
|
||||
return sum % 10 == 0;
|
||||
}
|
||||
UPCEANReader::~UPCEANReader(){
|
||||
}
|
||||
}
|
||||
}
|
65
cpp/core/src/zxing/oned/UPCEANReader.h
Normal file
65
cpp/core/src/zxing/oned/UPCEANReader.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* UPCEANReader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-21.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zxing/oned/OneDReader.h>
|
||||
#include <zxing/common/BitArray.h>
|
||||
#include <zxing/Result.h>
|
||||
typedef enum UPC_EAN_PATTERNS {
|
||||
UPC_EAN_PATTERNS_L_PATTERNS = 0,
|
||||
UPC_EAN_PATTERNS_L_AND_G_PATTERNS
|
||||
} UPC_EAN_PATTERNS;
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class UPCEANReader : public OneDReader {
|
||||
|
||||
private:
|
||||
static const int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f);
|
||||
static const int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f);
|
||||
|
||||
static int* findStartGuardPattern(Ref<BitArray> row); //throws ReaderException
|
||||
|
||||
int* decodeEnd(Ref<BitArray> row, int endStart); //throws ReaderException
|
||||
|
||||
static bool checkStandardUPCEANChecksum(std::string s); //throws ReaderException
|
||||
protected:
|
||||
static int* findGuardPattern(Ref<BitArray> row, int rowOffset, bool whiteFirst, const int pattern[], int patternLen); //throws ReaderException
|
||||
|
||||
virtual const int getMIDDLE_PATTERN_LEN();
|
||||
virtual const int* getMIDDLE_PATTERN();
|
||||
|
||||
public:
|
||||
UPCEANReader();
|
||||
|
||||
virtual int decodeMiddle(Ref<BitArray> row, int startRange[], int startRangeLen, std::string& resultString) = 0; //throws ReaderException
|
||||
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row);
|
||||
Ref<Result> decodeRow(int rowNumber, Ref<BitArray> row, int startGuardRange[]);
|
||||
|
||||
static int decodeDigit(Ref<BitArray> row, int counters[], int countersLen, int rowOffset, UPC_EAN_PATTERNS paternType); //throws ReaderException
|
||||
|
||||
bool checkChecksum(std::string s); //throws ReaderException
|
||||
|
||||
virtual BarcodeFormat getBarcodeFormat() = 0;
|
||||
virtual ~UPCEANReader();
|
||||
};
|
||||
}
|
||||
}
|
150
cpp/core/src/zxing/oned/UPCEReader.cpp
Normal file
150
cpp/core/src/zxing/oned/UPCEReader.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* UPCEReader.cpp
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-26.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "UPCEReader.h"
|
||||
#include <zxing/ReaderException.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
|
||||
/**
|
||||
* The pattern that marks the middle, and end, of a UPC-E pattern.
|
||||
* There is no "second half" to a UPC-E barcode.
|
||||
*/
|
||||
static const int MIDDLE_END_PATTERN[6] = {1, 1, 1, 1, 1, 1};
|
||||
|
||||
/**
|
||||
* See {@link #L_AND_G_PATTERNS}; these values similarly represent patterns of
|
||||
* even-odd parity encodings of digits that imply both the number system (0 or 1)
|
||||
* used, and the check digit.
|
||||
*/
|
||||
static const int NUMSYS_AND_CHECK_DIGIT_PATTERNS[2][10] = {
|
||||
{0x38, 0x34, 0x32, 0x31, 0x2C, 0x26, 0x23, 0x2A, 0x29, 0x25},
|
||||
{0x07, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A}
|
||||
};
|
||||
|
||||
UPCEReader::UPCEReader(){
|
||||
decodeMiddleCounters = new int[4];
|
||||
for (int i=0; i<4; i++) {
|
||||
decodeMiddleCounters[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int UPCEReader::decodeMiddle(Ref<BitArray> row, int startRange[], int startRangeLen, std::string& resultString){
|
||||
int countersLen = 4;
|
||||
int* counters = decodeMiddleCounters;
|
||||
counters[0] = 0;
|
||||
counters[1] = 0;
|
||||
counters[2] = 0;
|
||||
counters[3] = 0;
|
||||
|
||||
|
||||
int end = row->getSize();
|
||||
int rowOffset = startRange[1];
|
||||
|
||||
int lgPatternFound = 0;
|
||||
|
||||
for (int x = 0; x < 6 && rowOffset < end; x++) {
|
||||
int bestMatch = decodeDigit(row, counters, countersLen, rowOffset, UPC_EAN_PATTERNS_L_AND_G_PATTERNS);
|
||||
resultString.append(1, (char) ('0' + bestMatch % 10));
|
||||
for (int i = 0; i < countersLen; i++) {
|
||||
rowOffset += counters[i];
|
||||
}
|
||||
if (bestMatch >= 10) {
|
||||
lgPatternFound |= 1 << (5 - x);
|
||||
}
|
||||
}
|
||||
|
||||
determineNumSysAndCheckDigit(resultString, lgPatternFound);
|
||||
|
||||
return rowOffset;
|
||||
}
|
||||
|
||||
int* UPCEReader::decodeEnd(Ref<BitArray> row, int endStart){
|
||||
return findGuardPattern(row, endStart, true, MIDDLE_END_PATTERN, sizeof(MIDDLE_END_PATTERN)/sizeof(int));
|
||||
}
|
||||
|
||||
bool UPCEReader::checkChecksum(std::string s){
|
||||
return UPCEANReader::checkChecksum(convertUPCEtoUPCA(s));
|
||||
}
|
||||
|
||||
|
||||
void UPCEReader::determineNumSysAndCheckDigit(std::string& resultString, int lgPatternFound){
|
||||
for (int numSys = 0; numSys <= 1; numSys++) {
|
||||
for (int d = 0; d < 10; d++) {
|
||||
if (lgPatternFound == NUMSYS_AND_CHECK_DIGIT_PATTERNS[numSys][d]) {
|
||||
resultString.insert(0, 1, (char) ('0' + numSys));
|
||||
resultString.append(1, (char) ('0' + d));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw ReaderException("determineNumSysAndCheckDigit exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands a UPC-E value back into its full, equivalent UPC-A code value.
|
||||
*
|
||||
* @param upce UPC-E code as string of digits
|
||||
* @return equivalent UPC-A code as string of digits
|
||||
*/
|
||||
std::string& UPCEReader::convertUPCEtoUPCA(std::string upce) {
|
||||
std::string result;
|
||||
result.append(1, upce[0]);
|
||||
char lastChar = upce[6];
|
||||
switch (lastChar) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
result.append(upce.substr(1,2));
|
||||
result.append(1, lastChar);
|
||||
result.append("0000");
|
||||
result.append(upce.substr(3,3));
|
||||
break;
|
||||
case '3':
|
||||
result.append(upce.substr(1,3));
|
||||
result.append("00000");
|
||||
result.append(upce.substr(4,2));
|
||||
break;
|
||||
case '4':
|
||||
result.append(upce.substr(1,4));
|
||||
result.append("00000");
|
||||
result.append(1, upce[5]);
|
||||
break;
|
||||
default:
|
||||
result.append(upce.substr(1,5));
|
||||
result.append("0000");
|
||||
result.append(1, lastChar);
|
||||
break;
|
||||
}
|
||||
result.append(1, upce[7]);
|
||||
std::string& returnResult = result;
|
||||
return returnResult;
|
||||
}
|
||||
|
||||
|
||||
BarcodeFormat UPCEReader::getBarcodeFormat(){
|
||||
return BarcodeFormat_UPC_E;
|
||||
}
|
||||
UPCEReader::~UPCEReader(){
|
||||
delete [] decodeMiddleCounters;
|
||||
}
|
||||
}
|
||||
}
|
45
cpp/core/src/zxing/oned/UPCEReader.h
Normal file
45
cpp/core/src/zxing/oned/UPCEReader.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* UPCEReader.h
|
||||
* ZXing
|
||||
*
|
||||
* Created by Lukasz Warchol on 10-01-26.
|
||||
* Copyright 2010 ZXing authors All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <zxing/oned/UPCEANReader.h>
|
||||
#include <zxing/Result.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace oned {
|
||||
class UPCEReader : public UPCEANReader {
|
||||
|
||||
private:
|
||||
int* decodeMiddleCounters;
|
||||
static void determineFirstDigit(std::string& resultString, int lgPatternFound); //throws ReaderException
|
||||
static void determineNumSysAndCheckDigit(std::string& resultString, int lgPatternFound); //throws ReaderException
|
||||
protected:
|
||||
int* decodeEnd(Ref<BitArray> row, int endStart); //throws ReaderException
|
||||
bool checkChecksum(std::string s); //throws ReaderException
|
||||
public:
|
||||
UPCEReader();
|
||||
|
||||
int decodeMiddle(Ref<BitArray> row, int startRange[], int startRangeLen, std::string& resultString); //throws ReaderException
|
||||
static std::string& convertUPCEtoUPCA(std::string upce);
|
||||
|
||||
BarcodeFormat getBarcodeFormat();
|
||||
~UPCEReader();
|
||||
};
|
||||
}
|
||||
}
|
|
@ -24,60 +24,59 @@
|
|||
#include <iostream>
|
||||
|
||||
namespace zxing {
|
||||
namespace qrcode {
|
||||
|
||||
using namespace std;
|
||||
|
||||
QRCodeReader::QRCodeReader() :
|
||||
decoder_() {
|
||||
}
|
||||
|
||||
Ref<Result> QRCodeReader::decode(Ref<BinaryBitmap> image) {
|
||||
#ifdef DEBUG
|
||||
cout << "decoding image " << image.object_ << ":\n" << flush;
|
||||
#endif
|
||||
|
||||
Detector detector(image->getBlackMatrix());
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
cout << "(1) created detector " << &detector << "\n" << flush;
|
||||
#endif
|
||||
|
||||
Ref<DetectorResult> detectorResult(detector.detect());
|
||||
#ifdef DEBUG
|
||||
cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush;
|
||||
#endif
|
||||
|
||||
std::vector<Ref<ResultPoint> > points(detectorResult->getPoints());
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
cout << "(3) extracted points " << &points << "\n" << flush;
|
||||
cout << "found " << points->size() << " points:\n";
|
||||
for (size_t i = 0; i < points->size(); i++) {
|
||||
cout << " " << points[i]->getX() << "," << points[i]->getY() << "\n";
|
||||
}
|
||||
cout << "bits:\n";
|
||||
cout << *(detectorResult->getBits()) << "\n";
|
||||
#endif
|
||||
|
||||
Ref<DecoderResult> decoderResult(decoder_.decode(detectorResult->getBits()));
|
||||
#ifdef DEBUG
|
||||
cout << "(4) decoded, have decoderResult " << decoderResult.object_ << "\n" << flush;
|
||||
#endif
|
||||
|
||||
Ref<Result> result(
|
||||
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_QR_CODE));
|
||||
#ifdef DEBUG
|
||||
cout << "(5) created result " << result.object_ << ", returning\n" << flush;
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QRCodeReader::~QRCodeReader() {
|
||||
}
|
||||
|
||||
}
|
||||
namespace qrcode {
|
||||
|
||||
using namespace std;
|
||||
|
||||
QRCodeReader::QRCodeReader() :decoder_() {
|
||||
}
|
||||
|
||||
Ref<Result> QRCodeReader::decode(Ref<BinaryBitmap> image) {
|
||||
#ifdef DEBUG
|
||||
cout << "decoding image " << image.object_ << ":\n" << flush;
|
||||
#endif
|
||||
|
||||
Detector detector(image->getBlackMatrix());
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
cout << "(1) created detector " << &detector << "\n" << flush;
|
||||
#endif
|
||||
|
||||
Ref<DetectorResult> detectorResult(detector.detect());
|
||||
#ifdef DEBUG
|
||||
cout << "(2) detected, have detectorResult " << detectorResult.object_ << "\n" << flush;
|
||||
#endif
|
||||
|
||||
std::vector<Ref<ResultPoint> > points(detectorResult->getPoints());
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
cout << "(3) extracted points " << &points << "\n" << flush;
|
||||
cout << "found " << points->size() << " points:\n";
|
||||
for (size_t i = 0; i < points->size(); i++) {
|
||||
cout << " " << points[i]->getX() << "," << points[i]->getY() << "\n";
|
||||
}
|
||||
cout << "bits:\n";
|
||||
cout << *(detectorResult->getBits()) << "\n";
|
||||
#endif
|
||||
|
||||
Ref<DecoderResult> decoderResult(decoder_.decode(detectorResult->getBits()));
|
||||
#ifdef DEBUG
|
||||
cout << "(4) decoded, have decoderResult " << decoderResult.object_ << "\n" << flush;
|
||||
#endif
|
||||
|
||||
Ref<Result> result(
|
||||
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat_QR_CODE));
|
||||
#ifdef DEBUG
|
||||
cout << "(5) created result " << result.object_ << ", returning\n" << flush;
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QRCodeReader::~QRCodeReader() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,20 +25,19 @@
|
|||
#include <zxing/qrcode/decoder/Decoder.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace qrcode {
|
||||
|
||||
class QRCodeReader : public Reader {
|
||||
private:
|
||||
Decoder decoder_;
|
||||
|
||||
public:
|
||||
QRCodeReader();
|
||||
virtual Ref<Result> decode(Ref<BinaryBitmap> image);
|
||||
virtual ~QRCodeReader();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
namespace qrcode {
|
||||
|
||||
class QRCodeReader : public Reader {
|
||||
private:
|
||||
Decoder decoder_;
|
||||
|
||||
public:
|
||||
QRCodeReader();
|
||||
virtual Ref<Result> decode(Ref<BinaryBitmap> image);
|
||||
virtual ~QRCodeReader();
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __QR_CODE_READER_H__
|
||||
|
|
|
@ -25,22 +25,22 @@
|
|||
#include <cmath>
|
||||
|
||||
namespace zxing {
|
||||
namespace qrcode {
|
||||
|
||||
class AlignmentPattern : public ResultPoint {
|
||||
private:
|
||||
float posX_;
|
||||
float posY_;
|
||||
float estimatedModuleSize_;
|
||||
|
||||
public:
|
||||
AlignmentPattern(float posX, float posY, float estimatedModuleSize);
|
||||
float getX();
|
||||
float getY();
|
||||
bool aboutEquals(float moduleSize, float i, float j);
|
||||
};
|
||||
|
||||
}
|
||||
namespace qrcode {
|
||||
|
||||
class AlignmentPattern : public ResultPoint {
|
||||
private:
|
||||
float posX_;
|
||||
float posY_;
|
||||
float estimatedModuleSize_;
|
||||
|
||||
public:
|
||||
AlignmentPattern(float posX, float posY, float estimatedModuleSize);
|
||||
float getX();
|
||||
float getY();
|
||||
bool aboutEquals(float moduleSize, float i, float j);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __ALIGNMENT_PATTERN_H__
|
||||
|
|
|
@ -21,38 +21,38 @@
|
|||
#include <zxing/qrcode/detector/FinderPattern.h>
|
||||
|
||||
namespace zxing {
|
||||
namespace qrcode {
|
||||
|
||||
using namespace std;
|
||||
|
||||
FinderPattern::FinderPattern(float posX, float posY, float estimatedModuleSize) :
|
||||
posX_(posX), posY_(posY), estimatedModuleSize_(estimatedModuleSize), counter_(1) {
|
||||
}
|
||||
|
||||
float FinderPattern::getX() {
|
||||
return posX_;
|
||||
}
|
||||
|
||||
float FinderPattern::getY() {
|
||||
return posY_;
|
||||
}
|
||||
|
||||
int FinderPattern::getCount() {
|
||||
return counter_;
|
||||
}
|
||||
|
||||
float FinderPattern::getEstimatedModuleSize() {
|
||||
return estimatedModuleSize_;
|
||||
}
|
||||
|
||||
void FinderPattern::incrementCount() {
|
||||
counter_++;
|
||||
}
|
||||
|
||||
bool FinderPattern::aboutEquals(float moduleSize, float i, float j) {
|
||||
return abs(i - posY_) <= moduleSize && abs(j - posX_) <= moduleSize && (abs(moduleSize - estimatedModuleSize_)
|
||||
<= 1.0f || abs(moduleSize - estimatedModuleSize_) / estimatedModuleSize_ <= 0.1f);
|
||||
}
|
||||
|
||||
}
|
||||
namespace qrcode {
|
||||
|
||||
using namespace std;
|
||||
|
||||
FinderPattern::FinderPattern(float posX, float posY, float estimatedModuleSize) :
|
||||
posX_(posX), posY_(posY), estimatedModuleSize_(estimatedModuleSize), counter_(1) {
|
||||
}
|
||||
|
||||
float FinderPattern::getX() {
|
||||
return posX_;
|
||||
}
|
||||
|
||||
float FinderPattern::getY() {
|
||||
return posY_;
|
||||
}
|
||||
|
||||
int FinderPattern::getCount() {
|
||||
return counter_;
|
||||
}
|
||||
|
||||
float FinderPattern::getEstimatedModuleSize() {
|
||||
return estimatedModuleSize_;
|
||||
}
|
||||
|
||||
void FinderPattern::incrementCount() {
|
||||
counter_++;
|
||||
}
|
||||
|
||||
bool FinderPattern::aboutEquals(float moduleSize, float i, float j) {
|
||||
return abs(i - posY_) <= moduleSize && abs(j - posX_) <= moduleSize && (abs(moduleSize - estimatedModuleSize_)
|
||||
<= 1.0f || abs(moduleSize - estimatedModuleSize_) / estimatedModuleSize_ <= 0.1f);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,25 +25,25 @@
|
|||
#include <cmath>
|
||||
|
||||
namespace zxing {
|
||||
namespace qrcode {
|
||||
|
||||
class FinderPattern : public ResultPoint {
|
||||
private:
|
||||
float posX_;
|
||||
float posY_;
|
||||
float estimatedModuleSize_;
|
||||
int counter_;
|
||||
|
||||
public:
|
||||
FinderPattern(float posX, float posY, float estimatedModuleSize);
|
||||
float getX();
|
||||
float getY();
|
||||
int getCount();
|
||||
float getEstimatedModuleSize();
|
||||
void incrementCount();
|
||||
bool aboutEquals(float moduleSize, float i, float j);
|
||||
};
|
||||
}
|
||||
namespace qrcode {
|
||||
|
||||
class FinderPattern : public ResultPoint {
|
||||
private:
|
||||
float posX_;
|
||||
float posY_;
|
||||
float estimatedModuleSize_;
|
||||
int counter_;
|
||||
|
||||
public:
|
||||
FinderPattern(float posX, float posY, float estimatedModuleSize);
|
||||
float getX();
|
||||
float getY();
|
||||
int getCount();
|
||||
float getEstimatedModuleSize();
|
||||
void incrementCount();
|
||||
bool aboutEquals(float moduleSize, float i, float j);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __FINDER_PATTERN_H__
|
||||
|
|
Loading…
Reference in a new issue