#ADD: DataMatrix reader.

#CHANGE: MultiFormatReader can read DataMatrix.

git-svn-id: https://zxing.googlecode.com/svn/trunk@1217 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
luizcroc 2010-02-23 23:50:08 +00:00
parent 8090bb441a
commit 2bef6a98d5
20 changed files with 2381 additions and 0 deletions

View file

@ -27,6 +27,7 @@ namespace zxing {
typedef enum BarcodeFormat {
BarcodeFormat_None = 0,
BarcodeFormat_QR_CODE,
BarcodeFormat_DATA_MATRIX,
BarcodeFormat_UPC_E,
BarcodeFormat_UPC_A,
BarcodeFormat_EAN_8,

View file

@ -3,6 +3,7 @@
* ZXing
*
* Created by Lukasz Warchol on 10-01-26.
* Modified by Luiz Silva on 09/02/2010.
* Copyright 2010 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -20,6 +21,7 @@
#include "MultiFormatReader.h"
#include <zxing/qrcode/QRCodeReader.h>
#include <zxing/datamatrix/DataMatrixReader.h>
#include <zxing/oned/MultiFormatUPCEANReader.h>
#include <zxing/oned/MultiFormatOneDReader.h>
#include <zxing/ReaderException.h>
@ -29,6 +31,7 @@ namespace zxing {
readers = new std::vector<Reader*>();
readers->push_back(new zxing::qrcode::QRCodeReader());
readers->push_back(new zxing::datamatrix::DataMatrixReader());
readers->push_back(new zxing::oned::MultiFormatUPCEANReader());
readers->push_back(new zxing::oned::MultiFormatOneDReader());
}

View file

@ -0,0 +1,82 @@
/*
* DataMatrixReader.cpp
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/datamatrix/DataMatrixReader.h>
#include <zxing/datamatrix/detector/Detector.h>
#include <iostream>
namespace zxing {
namespace datamatrix {
using namespace std;
DataMatrixReader::DataMatrixReader() :
decoder_() {
}
Ref<Result> DataMatrixReader::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_DATA_MATRIX));
#ifdef DEBUG
cout << "(5) created result " << result.object_ << ", returning\n" << flush;
#endif
return result;
}
DataMatrixReader::~DataMatrixReader() {
}
}
}

View file

@ -0,0 +1,44 @@
#ifndef __DATA_MATRIX_READER_H__
#define __DATA_MATRIX_READER_H__
/*
* DataMatrixReader.h
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/datamatrix/decoder/Decoder.h>
namespace zxing {
namespace datamatrix {
class DataMatrixReader : public Reader {
private:
Decoder decoder_;
public:
DataMatrixReader();
virtual Ref<Result> decode(Ref<BinaryBitmap> image);
virtual ~DataMatrixReader();
};
}
}
#endif // __DATA_MATRIX_READER_H__

View file

@ -0,0 +1,202 @@
/*
* Version.cpp
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/datamatrix/Version.h>
#include <cstdarg>
#include <limits>
#include <iostream>
namespace zxing {
namespace datamatrix {
using namespace std;
ECB::ECB(int count, int dataCodewords) :
count_(count), dataCodewords_(dataCodewords) {
}
int ECB::getCount() {
return count_;
}
int ECB::getDataCodewords() {
return dataCodewords_;
}
ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks) :
ecCodewords_(ecCodewords) {
ecBlocks_.push_back(ecBlocks);
}
ECBlocks::ECBlocks(int ecCodewords, ECB *ecBlocks1, ECB *ecBlocks2) :
ecCodewords_(ecCodewords) {
ecBlocks_.push_back(ecBlocks1);
ecBlocks_.push_back(ecBlocks2);
}
int ECBlocks::getECCodewords() {
return ecCodewords_;
}
std::vector<ECB*>& ECBlocks::getECBlocks() {
return ecBlocks_;
}
ECBlocks::~ECBlocks() {
for (size_t i = 0; i < ecBlocks_.size(); i++) {
delete ecBlocks_[i];
}
}
vector<Ref<Version> > Version::VERSIONS;
static int N_VERSIONS = Version::buildVersions();
Version::Version(int versionNumber, int symbolSizeRows, int symbolSizeColumns, int dataRegionSizeRows,
int dataRegionSizeColumns, ECBlocks* ecBlocks) : versionNumber_(versionNumber),
symbolSizeRows_(symbolSizeRows), symbolSizeColumns_(symbolSizeColumns),
dataRegionSizeRows_(dataRegionSizeRows), dataRegionSizeColumns_(dataRegionSizeColumns),
ecBlocks_(ecBlocks) {
// Calculate the total number of codewords
int total = 0;
int ecCodewords = ecBlocks_->getECCodewords();
vector<ECB*> &ecbArray = ecBlocks_->getECBlocks();
for (unsigned int i = 0; i < ecbArray.size(); i++) {
ECB *ecBlock = ecbArray[i];
total += ecBlock->getCount() * (ecBlock->getDataCodewords() + ecCodewords);
}
totalCodewords_ = total;
}
Version::~Version() {
delete ecBlocks_;
}
int Version::getVersionNumber() {
return versionNumber_;
}
int Version::getSymbolSizeRows() {
return symbolSizeRows_;
}
int Version::getSymbolSizeColumns() {
return symbolSizeColumns_;
}
int Version::getDataRegionSizeRows() {
return dataRegionSizeRows_;
}
int Version::getDataRegionSizeColumns() {
return dataRegionSizeColumns_;
}
int Version::getTotalCodewords() {
return totalCodewords_;
}
ECBlocks* Version::getECBlocks() {
return ecBlocks_;
}
Version* Version::getVersionForDimensions(int numRows, int numColumns) {
if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {
throw ReaderException("Number of rows and columns must be even");
}
// TODO(bbrown): This is doing a linear search through the array of versions.
// If we interleave the rectangular versions with the square versions we could
// do a binary search.
for (int i = 0; i < N_VERSIONS; ++i){
Version* version = VERSIONS[i];
if (version->getSymbolSizeRows() == numRows && version->getSymbolSizeColumns() == numColumns) {
return version;
}
}
throw ReaderException("Error version not found");
}
/**
* See ISO 16022:2006 5.5.1 Table 7
*/
int Version::buildVersions() {
VERSIONS.push_back(Ref<Version>(new Version(1, 10, 10, 8, 8,
new ECBlocks(5, new ECB(1, 3)))));
VERSIONS.push_back(Ref<Version>(new Version(2, 12, 12, 10, 10,
new ECBlocks(7, new ECB(1, 5)))));
VERSIONS.push_back(Ref<Version>(new Version(3, 14, 14, 12, 12,
new ECBlocks(10, new ECB(1, 8)))));
VERSIONS.push_back(Ref<Version>(new Version(4, 16, 16, 14, 14,
new ECBlocks(12, new ECB(1, 12)))));
VERSIONS.push_back(Ref<Version>(new Version(5, 18, 18, 16, 16,
new ECBlocks(14, new ECB(1, 18)))));
VERSIONS.push_back(Ref<Version>(new Version(6, 20, 20, 18, 18,
new ECBlocks(18, new ECB(1, 22)))));
VERSIONS.push_back(Ref<Version>(new Version(7, 22, 22, 20, 20,
new ECBlocks(20, new ECB(1, 30)))));
VERSIONS.push_back(Ref<Version>(new Version(8, 24, 24, 22, 22,
new ECBlocks(24, new ECB(1, 36)))));
VERSIONS.push_back(Ref<Version>(new Version(9, 26, 26, 24, 24,
new ECBlocks(28, new ECB(1, 44)))));
VERSIONS.push_back(Ref<Version>(new Version(10, 32, 32, 14, 14,
new ECBlocks(36, new ECB(1, 62)))));
VERSIONS.push_back(Ref<Version>(new Version(11, 36, 36, 16, 16,
new ECBlocks(42, new ECB(1, 86)))));
VERSIONS.push_back(Ref<Version>(new Version(12, 40, 40, 18, 18,
new ECBlocks(48, new ECB(1, 114)))));
VERSIONS.push_back(Ref<Version>(new Version(13, 44, 44, 20, 20,
new ECBlocks(56, new ECB(1, 144)))));
VERSIONS.push_back(Ref<Version>(new Version(14, 48, 48, 22, 22,
new ECBlocks(68, new ECB(1, 174)))));
VERSIONS.push_back(Ref<Version>(new Version(15, 52, 52, 24, 24,
new ECBlocks(42, new ECB(2, 102)))));
VERSIONS.push_back(Ref<Version>(new Version(16, 64, 64, 14, 14,
new ECBlocks(56, new ECB(2, 140)))));
VERSIONS.push_back(Ref<Version>(new Version(17, 72, 72, 16, 16,
new ECBlocks(36, new ECB(4, 92)))));
VERSIONS.push_back(Ref<Version>(new Version(18, 80, 80, 18, 18,
new ECBlocks(48, new ECB(4, 114)))));
VERSIONS.push_back(Ref<Version>(new Version(19, 88, 88, 20, 20,
new ECBlocks(56, new ECB(4, 144)))));
VERSIONS.push_back(Ref<Version>(new Version(20, 96, 96, 22, 22,
new ECBlocks(68, new ECB(4, 174)))));
VERSIONS.push_back(Ref<Version>(new Version(21, 104, 104, 24, 24,
new ECBlocks(56, new ECB(6, 136)))));
VERSIONS.push_back(Ref<Version>(new Version(22, 120, 120, 18, 18,
new ECBlocks(68, new ECB(6, 175)))));
VERSIONS.push_back(Ref<Version>(new Version(23, 132, 132, 20, 20,
new ECBlocks(62, new ECB(8, 163)))));
VERSIONS.push_back(Ref<Version>(new Version(24, 144, 144, 22, 22,
new ECBlocks(62, new ECB(8, 156), new ECB(2, 155)))));
VERSIONS.push_back(Ref<Version>(new Version(25, 8, 18, 6, 16,
new ECBlocks(7, new ECB(1, 5)))));
VERSIONS.push_back(Ref<Version>(new Version(26, 8, 32, 6, 14,
new ECBlocks(11, new ECB(1, 10)))));
VERSIONS.push_back(Ref<Version>(new Version(27, 12, 26, 10, 24,
new ECBlocks(14, new ECB(1, 16)))));
VERSIONS.push_back(Ref<Version>(new Version(28, 12, 36, 10, 16,
new ECBlocks(18, new ECB(1, 22)))));
VERSIONS.push_back(Ref<Version>(new Version(29, 16, 36, 10, 16,
new ECBlocks(24, new ECB(1, 32)))));
VERSIONS.push_back(Ref<Version>(new Version(30, 16, 48, 14, 22,
new ECBlocks(28, new ECB(1, 49)))));
return VERSIONS.size();
}
}
}

View file

@ -0,0 +1,85 @@
#ifndef __VERSION_H__
#define __VERSION_H__
/*
* Version.h
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/ReaderException.h>
#include <zxing/common/BitMatrix.h>
#include <zxing/common/Counted.h>
#include <vector>
#include <valarray>
namespace zxing {
namespace datamatrix {
using namespace std;
class ECB {
private:
int count_;
int dataCodewords_;
public:
ECB(int count, int dataCodewords);
int getCount();
int getDataCodewords();
};
class ECBlocks {
private:
int ecCodewords_;
std::vector<ECB*> ecBlocks_;
public:
ECBlocks(int ecCodewords, ECB *ecBlocks);
ECBlocks(int ecCodewords, ECB *ecBlocks1, ECB *ecBlocks2);
int getECCodewords();
std::vector<ECB*>& getECBlocks();
~ECBlocks();
};
class Version : public Counted {
private:
int versionNumber_;
int symbolSizeRows_;
int symbolSizeColumns_;
int dataRegionSizeRows_;
int dataRegionSizeColumns_;
ECBlocks* ecBlocks_;
int totalCodewords_;
Version(int versionNumber, int symbolSizeRows, int symbolSizeColumns, int dataRegionSizeRows,
int dataRegionSizeColumns, ECBlocks *ecBlocks);
public:
static std::vector<Ref<Version> > VERSIONS;
~Version();
int getVersionNumber();
int getSymbolSizeRows();
int getSymbolSizeColumns();
int getDataRegionSizeRows();
int getDataRegionSizeColumns();
int getTotalCodewords();
ECBlocks* getECBlocks();
static int buildVersions();
Version* getVersionForDimensions(int numRows, int numColumns);
};
}
}
#endif // __VERSION_H__

View file

@ -0,0 +1,362 @@
/*
* BitMatrixParser.cpp
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/datamatrix/decoder/BitMatrixParser.h>
#include <zxing/common/IllegalArgumentException.h>
namespace zxing {
namespace datamatrix {
int BitMatrixParser::copyBit(size_t x, size_t y, int versionBits) {
return bitMatrix_->get(x, y) ? (versionBits << 1) | 0x1 : versionBits << 1;
}
BitMatrixParser::BitMatrixParser(Ref<BitMatrix> bitMatrix) : parsedVersion_(0) {
size_t dimension = bitMatrix->getDimension();
if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0)
throw ReaderException("Dimension must be even, > 10 < 144");
parsedVersion_ = readVersion(bitMatrix);
bitMatrix_ = extractDataRegion(bitMatrix);
// TODO(bbrown): Make this work for rectangular symbols
readBitMatrix_ = new BitMatrix(bitMatrix_->getDimension());
}
Version *BitMatrixParser::readVersion(Ref<BitMatrix> bitMatrix) {
if (parsedVersion_ != 0) {
return parsedVersion_;
}
// TODO(bbrown): make this work for rectangular dimensions as well.
int numRows = bitMatrix->getDimension();
int numColumns = numRows;
Version* version = parsedVersion_->getVersionForDimensions(numRows, numColumns);
if (version != 0) {
return version;
}
throw ReaderException("Couldn't decode version");
}
ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
ArrayRef<unsigned char> result(parsedVersion_->getTotalCodewords());
int resultOffset = 0;
int row = 4;
int column = 0;
// TODO(bbrown): Data Matrix can be rectangular, assuming square for now
int numRows = bitMatrix_->getDimension();
int numColumns = numRows;
bool corner1Read = false;
bool corner2Read = false;
bool corner3Read = false;
bool corner4Read = false;
// Read all of the codewords
do {
// Check the four corner cases
if ((row == numRows) && (column == 0) && !corner1Read) {
result[resultOffset++] = (unsigned char) readCorner1(numRows, numColumns);
row -= 2;
column +=2;
corner1Read = true;
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) {
result[resultOffset++] = (unsigned char) readCorner2(numRows, numColumns);
row -= 2;
column +=2;
corner2Read = true;
} else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) {
result[resultOffset++] = (unsigned char) readCorner3(numRows, numColumns);
row -= 2;
column +=2;
corner3Read = true;
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) {
result[resultOffset++] = (unsigned char) readCorner4(numRows, numColumns);
row -= 2;
column +=2;
corner4Read = true;
} else {
// Sweep upward diagonally to the right
do {
if ((row < numRows) && (column >= 0) && !readBitMatrix_->get(column, row)) {
result[resultOffset++] = (unsigned char) readUtah(row, column, numRows, numColumns);
}
row -= 2;
column +=2;
} while ((row >= 0) && (column < numColumns));
row += 1;
column +=3;
// Sweep downward diagonally to the left
do {
if ((row >= 0) && (column < numColumns) && !readBitMatrix_->get(column, row)) {
result[resultOffset++] = (unsigned char) readUtah(row, column, numRows, numColumns);
}
row += 2;
column -=2;
} while ((row < numRows) && (column >= 0));
row += 3;
column +=1;
}
} while ((row < numRows) || (column < numColumns));
if (resultOffset != parsedVersion_->getTotalCodewords()) {
throw ReaderException("Did not read all codewords");
}
return result;
}
bool BitMatrixParser::readModule(int row, int column, int numRows, int numColumns) {
// Adjust the row and column indices based on boundary wrapping
if (row < 0) {
row += numRows;
column += 4 - ((numRows + 4) & 0x07);
}
if (column < 0) {
column += numColumns;
row += 4 - ((numColumns + 4) & 0x07);
}
readBitMatrix_->set(column, row);
return bitMatrix_->get(column, row);
}
int BitMatrixParser::readUtah(int row, int column, int numRows, int numColumns) {
int currentByte = 0;
if (readModule(row - 2, column - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 2, column - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
int BitMatrixParser::readCorner1(int numRows, int numColumns) {
int currentByte = 0;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(2, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(3, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
int BitMatrixParser::readCorner2(int numRows, int numColumns) {
int currentByte = 0;
if (readModule(numRows - 3, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 2, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 4, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 3, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
int BitMatrixParser::readCorner3(int numRows, int numColumns) {
int currentByte = 0;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 3, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 3, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
int BitMatrixParser::readCorner4(int numRows, int numColumns) {
int currentByte = 0;
if (readModule(numRows - 3, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 2, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(2, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(3, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
Ref<BitMatrix> BitMatrixParser::extractDataRegion(Ref<BitMatrix> bitMatrix) {
int symbolSizeRows = parsedVersion_->getSymbolSizeRows();
int symbolSizeColumns = parsedVersion_->getSymbolSizeColumns();
// TODO(bbrown): Make this work with rectangular codes
if ((int)bitMatrix->getDimension() != symbolSizeRows) {
throw IllegalArgumentException("Dimension of bitMarix must match the version size");
}
int dataRegionSizeRows = parsedVersion_->getDataRegionSizeRows();
int dataRegionSizeColumns = parsedVersion_->getDataRegionSizeColumns();
int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows;
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
//int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
// TODO(bbrown): Make this work with rectangular codes
Ref<BitMatrix> bitMatrixWithoutAlignment(new BitMatrix(sizeDataRegionRow));
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns;
for (int i = 0; i < dataRegionSizeRows; ++i) {
int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;
int writeRowOffset = dataRegionRowOffset + i;
for (int j = 0; j < dataRegionSizeColumns; ++j) {
int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;
if (bitMatrix->get(readColumnOffset, readRowOffset)) {
int writeColumnOffset = dataRegionColumnOffset + j;
bitMatrixWithoutAlignment->set(writeColumnOffset, writeRowOffset);
}
}
}
}
}
return bitMatrixWithoutAlignment;
}
}
}

View file

@ -0,0 +1,59 @@
#ifndef __BIT_MATRIX_PARSER_DM_H__
#define __BIT_MATRIX_PARSER_DM_H__
/*
* BitMatrixParser.h
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/ReaderException.h>
#include <zxing/common/BitMatrix.h>
#include <zxing/common/Counted.h>
#include <zxing/common/Array.h>
#include <zxing/datamatrix/Version.h>
namespace zxing {
namespace datamatrix {
class BitMatrixParser : public Counted {
private:
Ref<BitMatrix> bitMatrix_;
Ref<BitMatrix> readBitMatrix_;
Version *parsedVersion_;
int copyBit(size_t x, size_t y, int versionBits);
public:
BitMatrixParser(Ref<BitMatrix> bitMatrix);
Version *readVersion(Ref<BitMatrix> bitMatrix);
ArrayRef<unsigned char> readCodewords();
bool readModule(int row, int column, int numRows, int numColumns);
private:
int readUtah(int row, int column, int numRows, int numColumns);
int readCorner1(int numRows, int numColumns);
int readCorner2(int numRows, int numColumns);
int readCorner3(int numRows, int numColumns);
int readCorner4(int numRows, int numColumns);
Ref<BitMatrix> extractDataRegion(Ref<BitMatrix> bitMatrix);
};
}
}
#endif // __BIT_MATRIX_PARSER_DM_H__

View file

@ -0,0 +1,113 @@
/*
* DataBlock.cpp
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/datamatrix/decoder/DataBlock.h>
#include <zxing/common/IllegalArgumentException.h>
namespace zxing {
namespace datamatrix {
using namespace std;
DataBlock::DataBlock(int numDataCodewords, ArrayRef<unsigned char> codewords) :
numDataCodewords_(numDataCodewords), codewords_(codewords) {
}
int DataBlock::getNumDataCodewords() {
return numDataCodewords_;
}
ArrayRef<unsigned char> DataBlock::getCodewords() {
return codewords_;
}
std::vector<Ref<DataBlock> > DataBlock::getDataBlocks(ArrayRef<unsigned char> rawCodewords, Version *version) {
// Figure out the number and size of data blocks used by this version and
// error correction level
ECBlocks* ecBlocks = version->getECBlocks();
// First count the total number of data blocks
int totalBlocks = 0;
vector<ECB*> ecBlockArray = ecBlocks->getECBlocks();
for (size_t i = 0; i < ecBlockArray.size(); i++) {
totalBlocks += ecBlockArray[i]->getCount();
}
// Now establish DataBlocks of the appropriate size and number of data codewords
std::vector<Ref<DataBlock> > result(totalBlocks);
int numResultBlocks = 0;
for (size_t j = 0; j < ecBlockArray.size(); j++) {
ECB *ecBlock = ecBlockArray[j];
for (int i = 0; i < ecBlock->getCount(); i++) {
int numDataCodewords = ecBlock->getDataCodewords();
int numBlockCodewords = ecBlocks->getECCodewords() + numDataCodewords;
ArrayRef<unsigned char> buffer(numBlockCodewords);
Ref<DataBlock> blockRef(new DataBlock(numDataCodewords, buffer));
result[numResultBlocks++] = blockRef;
}
}
// All blocks have the same amount of data, except that the last n
// (where n may be 0) have 1 more byte. Figure out where these start.
int shorterBlocksTotalCodewords = result[0]->codewords_.size();
int longerBlocksStartAt = result.size() - 1;
while (longerBlocksStartAt >= 0) {
int numCodewords = result[longerBlocksStartAt]->codewords_.size();
if (numCodewords == shorterBlocksTotalCodewords) {
break;
}
if (numCodewords != shorterBlocksTotalCodewords + 1) {
throw IllegalArgumentException("Data block sizes differ by more than 1");
}
longerBlocksStartAt--;
}
longerBlocksStartAt++;
int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks->getECCodewords();
// The last elements of result may be 1 element longer;
// first fill out as many elements as all of them have
int rawCodewordsOffset = 0;
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
for (int j = 0; j < numResultBlocks; j++) {
result[j]->codewords_[i] = rawCodewords[rawCodewordsOffset++];
}
}
// Fill out the last data block in the longer ones
for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
result[j]->codewords_[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
}
// Now add in error correction blocks
int max = result[0]->codewords_.size();
for (int i = shorterBlocksNumDataCodewords; i < max; i++) {
for (int j = 0; j < numResultBlocks; j++) {
int iOffset = j < longerBlocksStartAt ? i : i + 1;
result[j]->codewords_[iOffset] = rawCodewords[rawCodewordsOffset++];
}
}
if ((size_t)rawCodewordsOffset != rawCodewords.size()) {
throw IllegalArgumentException("rawCodewordsOffset != rawCodewords.length");
}
return result;
}
}
}

View file

@ -0,0 +1,50 @@
#ifndef __DATA_BLOCK_DM_H__
#define __DATA_BLOCK_DM_H__
/*
* DataBlock.h
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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 <valarray>
#include <vector>
#include <zxing/common/Counted.h>
#include <zxing/common/Array.h>
#include <zxing/datamatrix/Version.h>
namespace zxing {
namespace datamatrix {
class DataBlock : public Counted {
private:
int numDataCodewords_;
ArrayRef<unsigned char> codewords_;
DataBlock(int numDataCodewords, ArrayRef<unsigned char> codewords);
public:
static std::vector<Ref<DataBlock> > getDataBlocks(ArrayRef<unsigned char> rawCodewords, Version *version);
int getNumDataCodewords();
ArrayRef<unsigned char> getCodewords();
};
}
}
#endif // __DATA_BLOCK_DM_H__

View file

@ -0,0 +1,404 @@
/*
* DecodedBitStreamParser.cpp
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/ReaderException.h>
#include <zxing/datamatrix/decoder/DecodedBitStreamParser.h>
#include <iostream>
namespace zxing {
namespace datamatrix {
using namespace std;
const char DecodedBitStreamParser::C40_BASIC_SET_CHARS[] = {
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
const char DecodedBitStreamParser::C40_SHIFT2_SET_CHARS[] = {
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_'
};
const char DecodedBitStreamParser::TEXT_BASIC_SET_CHARS[] = {
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
};
const char DecodedBitStreamParser::TEXT_SHIFT3_SET_CHARS[] = {
'\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127
};
std::string DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes) {
Ref<BitSource> bits(new BitSource(bytes));
ostringstream result;
ostringstream resultTrailer;
// bool trailer = false;
int mode = ASCII_ENCODE;
do {
if (mode == ASCII_ENCODE) {
mode = decodeAsciiSegment(bits, result, resultTrailer);
} else {
switch (mode) {
case C40_ENCODE:
decodeC40Segment(bits, result);
break;
case TEXT_ENCODE:
decodeTextSegment(bits, result);
break;
case ANSIX12_ENCODE:
decodeAnsiX12Segment(bits, result);
break;
case EDIFACT_ENCODE:
decodeEdifactSegment(bits, result);
break;
case BASE256_ENCODE:
decodeBase256Segment(bits, result);
break;
default:
throw ReaderException("Unsupported mode indicator");
}
mode = ASCII_ENCODE;
}
} while (mode != PAD_ENCODE && bits->available() > 0);
/* if (trailer) {
result << resultTrailer;
}
*/
return result.str();
}
int DecodedBitStreamParser::decodeAsciiSegment(Ref<BitSource> bits, ostringstream & result,
ostringstream & resultTrailer) {
bool upperShift = false;
do {
int oneByte = bits->readBits(8);
if (oneByte == 0) {
throw ReaderException("Not enough bits to decode");
} else if (oneByte <= 128) { // ASCII data (ASCII value + 1)
oneByte = upperShift ? (oneByte + 128) : oneByte;
upperShift = false;
result << (char) (oneByte - 1);
return ASCII_ENCODE;
} else if (oneByte == 129) { // Pad
return PAD_ENCODE;
} else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130)
int value = oneByte - 130;
if (value < 10) { // padd with '0' for single digit values
result << '0';
}
result << value;
} else if (oneByte == 230) { // Latch to C40 encodation
return C40_ENCODE;
} else if (oneByte == 231) { // Latch to Base 256 encodation
return BASE256_ENCODE;
} else if (oneByte == 232) { // FNC1
//throw ReaderException.getInstance();
// Ignore this symbol for now
} else if (oneByte == 233) { // Structured Append
//throw ReaderException.getInstance();
// Ignore this symbol for now
} else if (oneByte == 234) { // Reader Programming
//throw ReaderException.getInstance();
// Ignore this symbol for now
} else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII)
upperShift = true;
} else if (oneByte == 236) { // 05 Macro
/* trailer = false;
result << "[)>\u001E05\u001D";
resultTrailer << "\u001E\u0004";
// Ignore this symbol for now
*/ } else if (oneByte == 237) { // 06 Macro
/* trailer = false;
result << "[)>\u001E06\u001D";
resultTrailer << "\u001E\u0004";
// Ignore this symbol for now
*/ } else if (oneByte == 238) { // Latch to ANSI X12 encodation
return ANSIX12_ENCODE;
} else if (oneByte == 239) { // Latch to Text encodation
return TEXT_ENCODE;
} else if (oneByte == 240) { // Latch to EDIFACT encodation
return EDIFACT_ENCODE;
} else if (oneByte == 241) { // ECI Character
// TODO(bbrown): I think we need to support ECI
//throw ReaderException.getInstance();
// Ignore this symbol for now
} else if (oneByte >= 242) { // Not to be used in ASCII encodation
throw ReaderException("Not to be used in ASCII encodation");
}
} while (bits->available() > 0);
return ASCII_ENCODE;
}
void DecodedBitStreamParser::decodeC40Segment(Ref<BitSource> bits, ostringstream & result) {
// Three C40 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
bool upperShift = false;
int* cValues = new int[3];
do {
// If there is only one byte left then it will be encoded as ASCII
if (bits->available() == 8) {
return;
}
int firstByte = bits->readBits(8);
if (firstByte == 254) { // Unlatch codeword
return;
}
parseTwoBytes(firstByte, bits->readBits(8), cValues);
int shift = 0;
for (int i = 0; i < 3; i++) {
int cValue = cValues[i];
switch (shift) {
case 0:
if (cValue < 3) {
shift = cValue + 1;
} else {
if (upperShift) {
result << (char) (C40_BASIC_SET_CHARS[cValue] + 128);
upperShift = false;
} else {
result << C40_BASIC_SET_CHARS[cValue];
}
}
break;
case 1:
if (upperShift) {
result << cValue + 128;
upperShift = false;
} else {
result << cValue;
}
shift = 0;
break;
case 2:
if (cValue < 27) {
if (upperShift) {
result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128);
upperShift = false;
} else {
result << C40_SHIFT2_SET_CHARS[cValue];
}
} else if (cValue == 27) { // FNC1
throw ReaderException("FNC1");
} else if (cValue == 30) { // Upper Shift
upperShift = true;
} else {
throw ReaderException("Upper Shift");
}
shift = 0;
break;
case 3:
if (upperShift) {
result << (char) (cValue + 224);
upperShift = false;
} else {
result << (char) (cValue + 96);
}
shift = 0;
break;
default:
throw ReaderException("");
}
}
} while (bits->available() > 0);
}
void DecodedBitStreamParser::decodeTextSegment(Ref<BitSource> bits, ostringstream & result) {
// Three Text values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
bool upperShift = false;
int* cValues = new int[3];
do {
// If there is only one byte left then it will be encoded as ASCII
if (bits->available() == 8) {
return;
}
int firstByte = bits->readBits(8);
if (firstByte == 254) { // Unlatch codeword
return;
}
parseTwoBytes(firstByte, bits->readBits(8), cValues);
int shift = 0;
for (int i = 0; i < 3; i++) {
int cValue = cValues[i];
switch (shift) {
case 0:
if (cValue < 3) {
shift = cValue + 1;
} else {
if (upperShift) {
result << (char) (TEXT_BASIC_SET_CHARS[cValue] + 128);
upperShift = false;
} else {
result << (TEXT_BASIC_SET_CHARS[cValue]);
}
}
break;
case 1:
if (upperShift) {
result << (char) (cValue + 128);
upperShift = false;
} else {
result << (cValue);
}
shift = 0;
break;
case 2:
// Shift 2 for Text is the same encoding as C40
if (cValue < 27) {
if (upperShift) {
result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128);
upperShift = false;
} else {
result << (C40_SHIFT2_SET_CHARS[cValue]);
}
} else if (cValue == 27) { // FNC1
throw ReaderException("FNC1");
} else if (cValue == 30) { // Upper Shift
upperShift = true;
} else {
throw ReaderException("Upper Shift");
}
shift = 0;
break;
case 3:
if (upperShift) {
result << (char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128);
upperShift = false;
} else {
result << (TEXT_SHIFT3_SET_CHARS[cValue]);
}
shift = 0;
break;
default:
throw ReaderException("");
}
}
} while (bits->available() > 0);
}
void DecodedBitStreamParser::decodeAnsiX12Segment(Ref<BitSource> bits, ostringstream & result) {
// Three ANSI X12 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
int* cValues = new int[3];
do {
// If there is only one byte left then it will be encoded as ASCII
if (bits->available() == 8) {
return;
}
int firstByte = bits->readBits(8);
if (firstByte == 254) { // Unlatch codeword
return;
}
parseTwoBytes(firstByte, bits->readBits(8), cValues);
for (int i = 0; i < 3; i++) {
int cValue = cValues[i];
if (cValue == 0) { // X12 segment terminator <CR>
result << '\r';
} else if (cValue == 1) { // X12 segment separator *
result << '*';
} else if (cValue == 2) { // X12 sub-element separator >
result << '>';
} else if (cValue == 3) { // space
result << ' ';
} else if (cValue < 14) { // 0 - 9
result << (char) (cValue + 44);
} else if (cValue < 40) { // A - Z
result << (char) (cValue + 51);
} else {
throw ReaderException("");
}
}
} while (bits->available() > 0);
}
void DecodedBitStreamParser::parseTwoBytes(int firstByte, int secondByte, int*& result) {
int fullBitValue = (firstByte << 8) + secondByte - 1;
int temp = fullBitValue / 1600;
result[0] = temp;
fullBitValue -= temp * 1600;
temp = fullBitValue / 40;
result[1] = temp;
result[2] = fullBitValue - temp * 40;
}
void DecodedBitStreamParser::decodeEdifactSegment(Ref<BitSource> bits, ostringstream & result) {
bool unlatch = false;
do {
// If there is only two or less bytes left then it will be encoded as ASCII
if (bits->available() <= 16) {
return;
}
for (int i = 0; i < 4; i++) {
int edifactValue = bits->readBits(6);
// Check for the unlatch character
if (edifactValue == 0x2B67) { // 011111
unlatch = true;
// If we encounter the unlatch code then continue reading because the Codeword triple
// is padded with 0's
}
if (!unlatch) {
if ((edifactValue & 32) == 0) { // no 1 in the leading (6th) bit
edifactValue |= 64; // Add a leading 01 to the 6 bit binary value
}
result << (edifactValue);
}
}
} while (!unlatch && bits->available() > 0);
}
void DecodedBitStreamParser::decodeBase256Segment(Ref<BitSource> bits, ostringstream & result){//, vector<unsigned char> byteSegments)
// Figure out how long the Base 256 Segment is.
int d1 = bits->readBits(8);
int count;
if (d1 == 0) { // Read the remainder of the symbol
count = bits->available() / 8;
} else if (d1 < 250) {
count = d1;
} else {
count = 250 * (d1 - 249) + bits->readBits(8);
}
unsigned char* bytes = new unsigned char[count];
for (int i = 0; i < count; i++) {
bytes[i] = unrandomize255State(bits->readBits(8), i);
}
//byteSegments.push_back(bytes);
result << bytes;
}
}
}

View file

@ -0,0 +1,103 @@
#ifndef __DECODED_BIT_STREAM_PARSER_DM_H__
#define __DECODED_BIT_STREAM_PARSER_DM_H__
/*
* DecodedBitStreamParser.h
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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 <string>
#include <sstream>
#include <zxing/common/BitSource.h>
#include <zxing/common/Counted.h>
#include <zxing/common/Array.h>
namespace zxing {
namespace datamatrix {
class DecodedBitStreamParser {
private:
static const int PAD_ENCODE = 0; // Not really an encoding
static const int ASCII_ENCODE = 1;
static const int C40_ENCODE = 2;
static const int TEXT_ENCODE = 3;
static const int ANSIX12_ENCODE = 4;
static const int EDIFACT_ENCODE = 5;
static const int BASE256_ENCODE = 6;
/**
* See ISO 16022:2006, Annex C Table C.1
* The C40 Basic Character Set (*'s used for placeholders for the shift values)
*/
static const char C40_BASIC_SET_CHARS[];
static const char C40_SHIFT2_SET_CHARS[];
/**
* See ISO 16022:2006, Annex C Table C.2
* The Text Basic Character Set (*'s used for placeholders for the shift values)
*/
static const char TEXT_BASIC_SET_CHARS[];
static const char TEXT_SHIFT3_SET_CHARS[];
/**
* See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
*/
int decodeAsciiSegment(Ref<BitSource> bits, std::ostringstream &result, std::ostringstream &resultTrailer);
/**
* See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
*/
void decodeC40Segment(Ref<BitSource> bits, std::ostringstream &result);
/**
* See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
*/
void decodeTextSegment(Ref<BitSource> bits, std::ostringstream &result);
/**
* See ISO 16022:2006, 5.2.7
*/
void decodeAnsiX12Segment(Ref<BitSource> bits, std::ostringstream &result);
/**
* See ISO 16022:2006, 5.2.8 and Annex C Table C.3
*/
void decodeEdifactSegment(Ref<BitSource> bits, std::ostringstream &result);
/**
* See ISO 16022:2006, 5.2.9 and Annex B, B.2
*/
void decodeBase256Segment(Ref<BitSource> bits, std::ostringstream &result);//,std::vector<unsigned char> byteSegments);
void parseTwoBytes(int firstByte, int secondByte, int*& result);
/**
* See ISO 16022:2006, Annex B, B.2
*/
unsigned char unrandomize255State(int randomizedBase256Codeword,
int base256CodewordPosition) {
int pseudoRandomNumber = ((149 * base256CodewordPosition) % 255) + 1;
int tempVariable = randomizedBase256Codeword - pseudoRandomNumber;
return (unsigned char) (tempVariable >= 0 ? tempVariable : (tempVariable + 256));
};
void append(std::ostream &ost, const unsigned char *bufIn, size_t nIn, const char *src);
public:
DecodedBitStreamParser() { };
std::string decode(ArrayRef<unsigned char> bytes);
};
}
}
#endif // __DECODED_BIT_STREAM_PARSER_DM_H__

View file

@ -0,0 +1,96 @@
/*
* Decoder.cpp
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/datamatrix/decoder/Decoder.h>
#include <zxing/datamatrix/decoder/BitMatrixParser.h>
#include <zxing/datamatrix/decoder/DataBlock.h>
#include <zxing/datamatrix/decoder/DecodedBitStreamParser.h>
#include <zxing/datamatrix/Version.h>
#include <zxing/ReaderException.h>
#include <zxing/common/reedsolomon/ReedSolomonException.h>
namespace zxing {
namespace datamatrix {
using namespace std;
Decoder::Decoder() :
rsDecoder_(GF256::DATA_MATRIX_FIELD) {
}
void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCodewords) {
int numCodewords = codewordBytes->size();
ArrayRef<int> codewordInts(numCodewords);
for (int i = 0; i < numCodewords; i++) {
codewordInts[i] = codewordBytes[i] & 0xff;
}
int numECCodewords = numCodewords - numDataCodewords;
try {
rsDecoder_.decode(codewordInts, numECCodewords);
} catch (ReedSolomonException ex) {
ReaderException rex(ex.what());
throw rex;
}
for (int i = 0; i < numDataCodewords; i++) {
codewordBytes[i] = (unsigned char)codewordInts[i];
}
}
Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
// Construct a parser and read version, error-correction level
BitMatrixParser parser(bits);
Version *version = parser.readVersion(bits);
// Read codewords
ArrayRef<unsigned char> codewords(parser.readCodewords());
// Separate into data blocks
std::vector<Ref<DataBlock> > dataBlocks = DataBlock::getDataBlocks(codewords, version);
// Count total number of data bytes
int totalBytes = 0;
for (unsigned int i = 0; i < dataBlocks.size(); i++) {
totalBytes += dataBlocks[i]->getNumDataCodewords();
}
ArrayRef<unsigned char> resultBytes(totalBytes);
int resultOffset = 0;
// Error-correct and copy data blocks together into a stream of bytes
for (unsigned int j = 0; j < dataBlocks.size(); j++) {
Ref<DataBlock> dataBlock(dataBlocks[j]);
ArrayRef<unsigned char> codewordBytes = dataBlock->getCodewords();
int numDataCodewords = dataBlock->getNumDataCodewords();
correctErrors(codewordBytes, numDataCodewords);
for (int i = 0; i < numDataCodewords; i++) {
resultBytes[resultOffset++] = codewordBytes[i];
}
}
// Decode the contents of that stream of bytes
DecodedBitStreamParser decodedBSParser;
Ref<String> text(new String(decodedBSParser.decode(resultBytes)));
Ref<DecoderResult> result(new DecoderResult(resultBytes, text));
return result;
}
}
}

View file

@ -0,0 +1,51 @@
#ifndef __DECODER_DM_H__
#define __DECODER_DM_H__
/*
* Decoder.h
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/common/reedsolomon/ReedSolomonDecoder.h>
#include <zxing/common/reedsolomon/GF256.h>
#include <zxing/common/Counted.h>
#include <zxing/common/Array.h>
#include <zxing/common/DecoderResult.h>
#include <zxing/common/BitMatrix.h>
#include <valarray>
namespace zxing {
namespace datamatrix {
class Decoder {
private:
ReedSolomonDecoder rsDecoder_;
void correctErrors(ArrayRef<unsigned char> bytes, int numDataCodewords);
public:
Decoder();
Ref<DecoderResult> decode(Ref<BitMatrix> bits);
};
}
}
#endif // __DECODER_DM_H__

View file

@ -0,0 +1,54 @@
/*
* CornerPoint.cpp
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/datamatrix/detector/CornerPoint.h>
namespace zxing {
namespace datamatrix {
using namespace std;
CornerPoint::CornerPoint(float posX, float posY) :
posX_(posX), posY_(posY) {
}
float CornerPoint::getX() {
return posX_;
}
float CornerPoint::getY() {
return posY_;
}
int CornerPoint::getCount() {
return counter_;
}
void CornerPoint::incrementCount() {
counter_++;
}
bool CornerPoint::equals(Ref<CornerPoint> other) {
return posX_ == other->getX() && posY_ == other->getY();
}
}
}

View file

@ -0,0 +1,47 @@
#ifndef __CORNER_FINDER_H__
#define __CORNER_FINDER_H__
/*
* CornerPoint.h
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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 datamatrix {
class CornerPoint : public ResultPoint {
private:
float posX_;
float posY_;
int counter_;
public:
CornerPoint(float posX, float posY);
float getX();
float getY();
int getCount();
void incrementCount();
bool equals(Ref<CornerPoint> other);
};
}
}
#endif // __CORNER_FINDER_H__

View file

@ -0,0 +1,317 @@
/*
* Detector.cpp
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/common/GridSampler.h>
#include <zxing/datamatrix/detector/Detector.h>
#include <cmath>
#include <sstream>
namespace zxing {
namespace datamatrix {
using namespace std;
ResultPointsAndTransitions::ResultPointsAndTransitions() {
Ref<CornerPoint> ref(new CornerPoint(0,0));
from_ = ref;
to_ = ref;
transitions_ = 0;
}
ResultPointsAndTransitions::ResultPointsAndTransitions(Ref<CornerPoint> from, Ref<CornerPoint> to, int transitions) {
from_ = from;
to_ = to;
transitions_ = transitions;
}
Ref<CornerPoint> ResultPointsAndTransitions::getFrom() {
return from_;
}
Ref<CornerPoint> ResultPointsAndTransitions::getTo() {
return to_;
}
int ResultPointsAndTransitions::getTransitions() {
return transitions_;
}
Detector::Detector(Ref<BitMatrix> image) : image_(image) { }
Ref<BitMatrix> Detector::getImage() {
return image_;
}
Ref<DetectorResult> Detector::detect() {
Ref<MonochromeRectangleDetector> rectangleDetector_(new MonochromeRectangleDetector(image_));
std::vector<Ref<CornerPoint> > cornerPoints = rectangleDetector_->detect();
Ref<CornerPoint> pointA = cornerPoints[0];
Ref<CornerPoint> pointB = cornerPoints[1];
Ref<CornerPoint> pointC = cornerPoints[2];
Ref<CornerPoint> pointD = cornerPoints[3];
// Point A and D are across the diagonal from one another,
// as are B and C. Figure out which are the solid black lines
// by counting transitions
std::vector<Ref<ResultPointsAndTransitions> > transitions(4);
transitions[0].reset(transitionsBetween(pointA, pointB));
transitions[1].reset(transitionsBetween(pointA, pointC));
transitions[2].reset(transitionsBetween(pointB, pointD));
transitions[3].reset(transitionsBetween(pointC, pointD));
insertionSort(transitions);
// Sort by number of transitions. First two will be the two solid sides; last two
// will be the two alternating black/white sides
Ref<ResultPointsAndTransitions> lSideOne(transitions[0]);
Ref<ResultPointsAndTransitions> lSideTwo(transitions[1]);
// Figure out which point is their intersection by tallying up the number of times we see the
// endpoints in the four endpoints. One will show up twice.
Ref<CornerPoint> maybeTopLeft;
Ref<CornerPoint> bottomLeft;
Ref<CornerPoint> maybeBottomRight;
if (lSideOne->getFrom()->equals(lSideOne->getTo())) {
bottomLeft = lSideOne->getFrom();
maybeTopLeft = lSideTwo->getFrom();
maybeBottomRight = lSideTwo->getTo();
}
else if (lSideOne->getFrom()->equals(lSideTwo->getFrom())) {
bottomLeft = lSideOne->getFrom();
maybeTopLeft = lSideOne->getTo();
maybeBottomRight = lSideTwo->getTo();
}
else if (lSideOne->getFrom()->equals(lSideTwo->getTo())) {
bottomLeft = lSideOne->getFrom();
maybeTopLeft = lSideOne->getTo();
maybeBottomRight = lSideTwo->getFrom();
}
else if (lSideOne->getTo()->equals(lSideTwo->getFrom())) {
bottomLeft = lSideOne->getTo();
maybeTopLeft = lSideOne->getFrom();
maybeBottomRight = lSideTwo->getTo();
}
else if (lSideOne->getTo()->equals(lSideTwo->getTo())) {
bottomLeft = lSideOne->getTo();
maybeTopLeft = lSideOne->getFrom();
maybeBottomRight = lSideTwo->getFrom();
}
else {
bottomLeft = lSideTwo->getFrom();
maybeTopLeft = lSideOne->getTo();
maybeBottomRight = lSideOne->getFrom();
}
// Bottom left is correct but top left and bottom right might be switched
std::vector<Ref<CornerPoint> > corners(3);
corners[0].reset(maybeTopLeft);
corners[1].reset(bottomLeft);
corners[2].reset(maybeBottomRight);
// Use the dot product trick to sort them out
orderBestPatterns(corners);
// Now we know which is which:
Ref<CornerPoint> bottomRight(corners[0]);
bottomLeft = corners[1];
Ref<CornerPoint> topLeft(corners[2]);
// Which point didn't we find in relation to the "L" sides? that's the top right corner
Ref<CornerPoint> topRight;
if (!(pointA->equals(bottomRight) || pointA->equals(bottomLeft) || pointA->equals(topLeft))) {
topRight = pointA;
} else if (!(pointB->equals(bottomRight) || pointB->equals(bottomLeft) || pointB->equals(topLeft))) {
topRight = pointB;
} else if (!(pointC->equals(bottomRight) || pointC->equals(bottomLeft) || pointC->equals(topLeft))) {
topRight = pointC;
} else {
topRight = pointD;
}
float topRightX = (bottomRight->getX() - bottomLeft->getX()) + topLeft->getX();
float topRightY = (bottomRight->getY() - bottomLeft->getY()) + topLeft->getY();
Ref<CornerPoint> topR(new CornerPoint(topRightX,topRightY));
// Next determine the dimension by tracing along the top or right side and counting black/white
// transitions. Since we start inside a black module, we should see a number of transitions
// equal to 1 less than the code dimension. Well, actually 2 less, because we are going to
// end on a black module:
// The top right point is actually the corner of a module, which is one of the two black modules
// adjacent to the white module at the top right. Tracing to that corner from either the top left
// or bottom right should work here. The number of transitions could be higher than it should be
// due to noise. So we try both and take the min.
int dimension = min(transitionsBetween(topLeft, topRight)->getTransitions(),
transitionsBetween(bottomRight, topRight)->getTransitions());
if ((dimension & 0x01) == 1) {
// it can't be odd, so, round... up?
dimension++;
}
dimension += 2;
Ref<PerspectiveTransform> transform = createTransform(topLeft, topR, bottomLeft, bottomRight, dimension);
Ref<BitMatrix> bits(sampleGrid(image_, dimension, transform));
std::vector<Ref<ResultPoint> > points(4);
points[0].reset(pointA);
points[1].reset(pointB);
points[2].reset(pointC);
points[3].reset(pointD);
Ref<DetectorResult> detectorResult(new DetectorResult(bits, points, transform));
return detectorResult;
}
Ref<ResultPointsAndTransitions> Detector::transitionsBetween(Ref<CornerPoint> from, Ref<CornerPoint> to) {
// See QR Code Detector, sizeOfBlackWhiteBlackRun()
int fromX = (int) from->getX();
int fromY = (int) from->getY();
int toX = (int) to->getX();
int toY = (int) to->getY();
bool steep = abs(toY - fromY) > abs(toX - fromX);
if (steep) {
int temp = fromX;
fromX = fromY;
fromY = temp;
temp = toX;
toX = toY;
toY = temp;
}
int dx = abs(toX - fromX);
int dy = abs(toY - fromY);
int error = -dx >> 1;
int ystep = fromY < toY ? 1 : -1;
int xstep = fromX < toX ? 1 : -1;
int transitions = 0;
bool inBlack = image_->get(steep ? fromY : fromX, steep ? fromX : fromY);
for (int x = fromX, y = fromY; x != toX; x += xstep) {
bool isBlack = image_->get(steep ? y : x, steep ? x : y);
if (isBlack != inBlack) {
transitions++;
inBlack = isBlack;
}
error += dy;
if (error > 0) {
if (y == toY) {
break;
}
y += ystep;
error -= dx;
}
}
Ref<ResultPointsAndTransitions> result(new ResultPointsAndTransitions(from, to, transitions));
return result;
}
Ref<PerspectiveTransform> Detector::createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
ResultPoint > bottomLeft, Ref<ResultPoint> bottomRight, int dimension) {
Ref<PerspectiveTransform> transform(PerspectiveTransform::quadrilateralToQuadrilateral(
0.0f,
0.0f,
dimension,
0.0f,
dimension,
dimension,
0.0f,
dimension,
topLeft->getX(),
topLeft->getY(),
topRight->getX(),
topRight->getY(),
bottomRight->getX(),
bottomRight->getY(),
bottomLeft->getX(),
bottomLeft->getY()));
return transform;
}
Ref<BitMatrix> Detector::sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform> transform) {
GridSampler &sampler = GridSampler::getInstance();
return sampler.sampleGrid(image, dimension, transform);
}
void Detector::insertionSort(std::vector<Ref<ResultPointsAndTransitions> > &vector) {
int max = vector.size();
bool swapped = true;
Ref<ResultPointsAndTransitions> value;
Ref<ResultPointsAndTransitions> valueB;
do {
swapped = false;
for (int i = 1; i < max; i++) {
value = vector[i-1];
if (compare(value, (valueB = vector[i])) > 0) {
swapped = true;
vector[i-1].reset(valueB);
vector[i].reset(value);
}
}
} while (swapped);
}
void Detector::orderBestPatterns(std::vector<Ref<CornerPoint> > &patterns) {
// Find distances between pattern centers
float zeroOneDistance = distance(patterns[0]->getX(), patterns[1]->getX(),patterns[0]->getY(), patterns[1]->getY());
float oneTwoDistance = distance(patterns[1]->getX(), patterns[2]->getX(),patterns[1]->getY(), patterns[2]->getY());
float zeroTwoDistance = distance(patterns[0]->getX(), patterns[2]->getX(),patterns[0]->getY(), patterns[2]->getY());
Ref<CornerPoint> pointA, pointB, pointC;
// Assume one closest to other two is B; A and C will just be guesses at first
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
pointB = patterns[0];
pointA = patterns[1];
pointC = patterns[2];
} else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
pointB = patterns[1];
pointA = patterns[0];
pointC = patterns[2];
} else {
pointB = patterns[2];
pointA = patterns[0];
pointC = patterns[1];
}
// Use cross product to figure out whether A and C are correct or flipped.
// This asks whether BC x BA has a positive z component, which is the arrangement
// we want for A, B, C. If it's negative, then we've got it flipped around and
// should swap A and C.
if (crossProductZ(pointA, pointB, pointC) < 0.0f) {
Ref<CornerPoint> temp = pointA;
pointA = pointC;
pointC = temp;
}
patterns[0] = pointA;
patterns[1] = pointB;
patterns[2] = pointC;
}
float Detector::distance(float x1, float x2, float y1, float y2) {
float xDiff = x1 - x2;
float yDiff = y1 - y2;
return (float) sqrt((double) (xDiff * xDiff + yDiff * yDiff));
}
int Detector::compare(Ref<ResultPointsAndTransitions> a, Ref<ResultPointsAndTransitions> b) {
return a->getTransitions() - b->getTransitions();
}
float Detector::crossProductZ(Ref<ResultPoint> pointA, Ref<ResultPoint> pointB, Ref<ResultPoint> pointC) {
float bX = pointB->getX();
float bY = pointB->getY();
return ((pointC->getX() - bX) * (pointA->getY() - bY)) - ((pointC->getY() - bY) * (pointA->getX() - bX));
}
}
}

View file

@ -0,0 +1,79 @@
#ifndef __DETECTOR_H__
#define __DETECTOR_H__
/*
* Detector.h
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/common/Counted.h>
#include <zxing/common/DetectorResult.h>
#include <zxing/common/BitMatrix.h>
#include <zxing/common/PerspectiveTransform.h>
#include <zxing/datamatrix/detector/MonochromeRectangleDetector.h>
namespace zxing {
namespace datamatrix {
class ResultPointsAndTransitions : public Counted {
private:
Ref<CornerPoint> to_;
Ref<CornerPoint> from_;
int transitions_;
public:
ResultPointsAndTransitions();
ResultPointsAndTransitions(Ref<CornerPoint> from, Ref<CornerPoint> to, int transitions);
Ref<CornerPoint> getFrom();
Ref<CornerPoint> getTo();
int getTransitions();
};
class Detector : public Counted {
private:
Ref<BitMatrix> image_;
protected:
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform> transform);
void insertionSort(std::vector<Ref<ResultPointsAndTransitions> >& vector);
Ref<ResultPointsAndTransitions> transitionsBetween(Ref<CornerPoint> from, Ref<CornerPoint> to);
int min(int a, int b) { return a > b ? b : a; };
public:
Ref<BitMatrix> getImage();
Detector(Ref<BitMatrix> image);
virtual Ref<PerspectiveTransform> createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
ResultPoint > bottomLeft, Ref<ResultPoint> bottomRight, int dimension);
Ref<DetectorResult> detect();
void orderBestPatterns(std::vector<Ref<CornerPoint> > &patterns);
float distance(float x1, float x2, float y1, float y2);
private:
int compare(Ref<ResultPointsAndTransitions> a, Ref<ResultPointsAndTransitions> b);
float crossProductZ(Ref<ResultPoint> pointA, Ref<ResultPoint> pointB, Ref<ResultPoint> pointC);
};
}
}
#endif // __DETECTOR_H__

View file

@ -0,0 +1,173 @@
/*
* MonochromeRectangleDetector.cpp
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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/ReaderException.h>
#include <zxing/datamatrix/detector/MonochromeRectangleDetector.h>
#include <sstream>
namespace zxing {
namespace datamatrix {
std::vector<Ref<CornerPoint> > MonochromeRectangleDetector::detect() {
int height = image_->getHeight();
int width = image_->getWidth();
int halfHeight = height >> 1;
int halfWidth = width >> 1;
int deltaY = max(1, height / (MAX_MODULES << 3));
int deltaX = max(1, width / (MAX_MODULES << 3));
int top = 0;
int bottom = height;
int left = 0;
int right = width;
Ref<CornerPoint> pointA(findCornerFromCenter(halfWidth, 0, left, right,
halfHeight, -deltaY, top, bottom, halfWidth >> 1));
top = (int) pointA->getY() - 1;
Ref<CornerPoint> pointB(findCornerFromCenter(halfWidth, -deltaX, left, right,
halfHeight, 0, top, bottom, halfHeight >> 1));
left = (int) pointB->getX() - 1;
Ref<CornerPoint> pointC(findCornerFromCenter(halfWidth, deltaX, left, right,
halfHeight, 0, top, bottom, halfHeight >> 1));
right = (int) pointC->getX() + 1;
Ref<CornerPoint> pointD(findCornerFromCenter(halfWidth, 0, left, right,
halfHeight, deltaY, top, bottom, halfWidth >> 1));
bottom = (int) pointD->getY() + 1;
// Go try to find point A again with better information -- might have been off at first.
pointA.reset(findCornerFromCenter(halfWidth, 0, left, right,
halfHeight, -deltaY, top, bottom, halfWidth >> 2));
std::vector<Ref<CornerPoint> > corners(4);
corners[0].reset(pointA);
corners[1].reset(pointB);
corners[2].reset(pointC);
corners[3].reset(pointD);
return corners;
}
Ref<CornerPoint> MonochromeRectangleDetector::findCornerFromCenter(int centerX, int deltaX, int left, int right,
int centerY, int deltaY, int top, int bottom, int maxWhiteRun) {
int* lastRange = NULL;
for (int y = centerY, x = centerX;
y < bottom && y >= top && x < right && x >= left;
y += deltaY, x += deltaX) {
int* range;
if (deltaX == 0) {
// horizontal slices, up and down
range = blackWhiteRange(y, maxWhiteRun, left, right, true);
} else {
// vertical slices, left and right
range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);
}
if (range == NULL) {
if (lastRange == NULL) {
throw ReaderException("Couldn't find corners (lastRange = NULL) ");
} else {
// lastRange was found
if (deltaX == 0) {
int lastY = y - deltaY;
if (lastRange[0] < centerX) {
if (lastRange[1] > centerX) {
// straddle, choose one or the other based on direction
Ref<CornerPoint> result(new CornerPoint(deltaY > 0 ? lastRange[0] : lastRange[1], lastY));
return result;
}
Ref<CornerPoint> result(new CornerPoint(lastRange[0], lastY));
return result;
} else {
Ref<CornerPoint> result(new CornerPoint(lastRange[1], lastY));
return result;
}
} else {
int lastX = x - deltaX;
if (lastRange[0] < centerY) {
if (lastRange[1] > centerY) {
Ref<CornerPoint> result(new CornerPoint(lastX, deltaX < 0 ? lastRange[0] : lastRange[1]));
return result;
}
Ref<CornerPoint> result(new CornerPoint(lastX, lastRange[0]));
return result;
} else {
Ref<CornerPoint> result(new CornerPoint(lastX, lastRange[1]));
return result;
}
}
}
}
lastRange = range;
}
throw ReaderException("Couldn't find corners");
}
int* MonochromeRectangleDetector::blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim,
bool horizontal) {
int center = (minDim + maxDim) >> 1;
// Scan left/up first
int start = center;
while (start >= minDim) {
if (horizontal ? image_->get(start, fixedDimension) : image_->get(fixedDimension, start)) {
start--;
} else {
int whiteRunStart = start;
do {
start--;
} while (start >= minDim && !(horizontal ? image_->get(start, fixedDimension) :
image_->get(fixedDimension, start)));
int whiteRunSize = whiteRunStart - start;
if (start < minDim || whiteRunSize > maxWhiteRun) {
start = whiteRunStart;
break;
}
}
}
start++;
// Then try right/down
int end = center;
while (end < maxDim) {
if (horizontal ? image_->get(end, fixedDimension) : image_->get(fixedDimension, end)) {
end++;
} else {
int whiteRunStart = end;
do {
end++;
} while (end < maxDim && !(horizontal ? image_->get(end, fixedDimension) :
image_->get(fixedDimension, end)));
int whiteRunSize = end - whiteRunStart;
if (end >= maxDim || whiteRunSize > maxWhiteRun) {
end = whiteRunStart;
break;
}
}
}
end--;
int* result;
if (end > start) {
result = new int [2];
result[0] = start;
result[1] = end;
}
else
result = NULL;
return result;
}
}
}

View file

@ -0,0 +1,56 @@
#ifndef __MONOCHROMERECTANGLEDETECTOR_H__
#define __MONOCHROMERECTANGLEDETECTOR_H__
/*
* MonochromeRectangleDetector.h
* zxing
*
* Created by Luiz Silva on 09/02/2010.
* 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 <vector>
#include <zxing/ReaderException.h>
#include <zxing/ResultPoint.h>
#include <zxing/common/BitMatrix.h>
#include <zxing/common/Counted.h>
#include <zxing/datamatrix/detector/CornerPoint.h>
namespace zxing {
namespace datamatrix {
class MonochromeRectangleDetector : public Counted {
private:
static const int MAX_MODULES = 32;
Ref<BitMatrix> image_;
public:
MonochromeRectangleDetector(Ref<BitMatrix> image) : image_(image) { };
std::vector<Ref<CornerPoint> > detect();
private:
Ref<CornerPoint> findCornerFromCenter(int centerX, int deltaX, int left, int right,
int centerY, int deltaY, int top, int bottom, int maxWhiteRun);
int* blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim,
bool horizontal);
int max(int a, float b) { return (float) a > b ? a : (int) b;};
};
}
}
#endif // __MONOCHROMERECTANGLEDETECTOR_H__