mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Issue 1022: updating cpp port and making DataMatrixReader almost as good as java
git-svn-id: https://zxing.googlecode.com/svn/trunk@1992 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
d65dd54e03
commit
41daacd233
|
@ -46,8 +46,7 @@ const DecodeHints DecodeHints::DEFAULT_HINT(
|
||||||
BARCODEFORMAT_CODE_128_HINT |
|
BARCODEFORMAT_CODE_128_HINT |
|
||||||
BARCODEFORMAT_CODE_39_HINT |
|
BARCODEFORMAT_CODE_39_HINT |
|
||||||
BARCODEFORMAT_ITF_HINT |
|
BARCODEFORMAT_ITF_HINT |
|
||||||
// TODO: uncomment once this passes QA
|
BARCODEFORMAT_DATA_MATRIX_HINT |
|
||||||
// BARCODEFORMAT_DATA_MATRIX_HINT |
|
|
||||||
BARCODEFORMAT_QR_CODE_HINT);
|
BARCODEFORMAT_QR_CODE_HINT);
|
||||||
|
|
||||||
DecodeHints::DecodeHints() {
|
DecodeHints::DecodeHints() {
|
||||||
|
|
29
cpp/core/src/zxing/NotFoundException.cpp
Normal file
29
cpp/core/src/zxing/NotFoundException.cpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* NotFoundException.cpp
|
||||||
|
* zxing
|
||||||
|
*
|
||||||
|
* 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/NotFoundException.h>
|
||||||
|
|
||||||
|
namespace zxing {
|
||||||
|
|
||||||
|
NotFoundException::NotFoundException(const char *msg) :
|
||||||
|
Exception(msg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
NotFoundException::~NotFoundException() throw() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
cpp/core/src/zxing/NotFoundException.h
Normal file
32
cpp/core/src/zxing/NotFoundException.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef __NOT_FOUND_EXCEPTION_H__
|
||||||
|
#define __NOT_FOUND_EXCEPTION_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NotFoundException.h
|
||||||
|
* zxing
|
||||||
|
*
|
||||||
|
* 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/Exception.h>
|
||||||
|
|
||||||
|
namespace zxing {
|
||||||
|
|
||||||
|
class NotFoundException : public Exception {
|
||||||
|
public:
|
||||||
|
NotFoundException(const char *msg);
|
||||||
|
~NotFoundException() throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // __NOT_FOUND_EXCEPTION_H__
|
|
@ -19,6 +19,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <zxing/ResultPoint.h>
|
#include <zxing/ResultPoint.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
namespace zxing {
|
namespace zxing {
|
||||||
|
|
||||||
|
@ -36,4 +37,64 @@ float ResultPoint::getY() const {
|
||||||
return posY_;
|
return posY_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ResultPoint::equals(Ref<ResultPoint> other) {
|
||||||
|
return posX_ == other->getX() && posY_ == other->getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and
|
||||||
|
* BC < AC and the angle between BC and BA is less than 180 degrees.
|
||||||
|
*/
|
||||||
|
void ResultPoint::orderBestPatterns(std::vector<Ref<ResultPoint> > &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<ResultPoint> 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<ResultPoint> temp = pointA;
|
||||||
|
pointA = pointC;
|
||||||
|
pointC = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
patterns[0] = pointA;
|
||||||
|
patterns[1] = pointB;
|
||||||
|
patterns[2] = pointC;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ResultPoint::distance(Ref<ResultPoint> point1, Ref<ResultPoint> point2) {
|
||||||
|
return distance(point1->getX(), point1->getY(), point2->getX(), point2->getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
float ResultPoint::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));
|
||||||
|
}
|
||||||
|
|
||||||
|
float ResultPoint::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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <zxing/common/Counted.h>
|
#include <zxing/common/Counted.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace zxing {
|
namespace zxing {
|
||||||
|
|
||||||
|
@ -37,6 +38,15 @@ public:
|
||||||
|
|
||||||
virtual float getX() const;
|
virtual float getX() const;
|
||||||
virtual float getY() const;
|
virtual float getY() const;
|
||||||
|
|
||||||
|
bool equals(Ref<ResultPoint> other);
|
||||||
|
|
||||||
|
static void orderBestPatterns(std::vector<Ref<ResultPoint> > &patterns);
|
||||||
|
static float distance(Ref<ResultPoint> point1, Ref<ResultPoint> point2);
|
||||||
|
static float distance(float x1, float x2, float y1, float y2);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static float crossProductZ(Ref<ResultPoint> pointA, Ref<ResultPoint> pointB, Ref<ResultPoint> pointC);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ int BitSource::readBits(int numBits) {
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
|
|
||||||
// First, read remainder from current byte
|
// First, read remainder from current byte
|
||||||
if (bitOffset_ > 0) {
|
if (bitOffset_ > 0) {
|
||||||
int bitsLeft = 8 - bitOffset_;
|
int bitsLeft = 8 - bitOffset_;
|
||||||
|
|
|
@ -47,6 +47,9 @@ public:
|
||||||
bytes_(bytes), byteOffset_(0), bitOffset_(0) {
|
bytes_(bytes), byteOffset_(0), bitOffset_(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getByteOffset() {
|
||||||
|
return byteOffset_;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param numBits number of bits to read
|
* @param numBits number of bits to read
|
||||||
|
|
|
@ -53,6 +53,27 @@ Ref<BitMatrix> GridSampler::sampleGrid(Ref<BitMatrix> image, int dimension, Ref<
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ref<BitMatrix> GridSampler::sampleGrid(Ref<BitMatrix> image, int dimensionX, int dimensionY, Ref<PerspectiveTransform> transform) {
|
||||||
|
Ref<BitMatrix> bits(new BitMatrix(dimensionX, dimensionY));
|
||||||
|
vector<float> points(dimensionX << 1, (const float)0.0f);
|
||||||
|
for (int y = 0; y < dimensionY; y++) {
|
||||||
|
int max = points.size();
|
||||||
|
float yValue = (float)y + 0.5f;
|
||||||
|
for (int x = 0; x < max; x += 2) {
|
||||||
|
points[x] = (float)(x >> 1) + 0.5f;
|
||||||
|
points[x + 1] = yValue;
|
||||||
|
}
|
||||||
|
transform->transformPoints(points);
|
||||||
|
checkAndNudgePoints(image, points);
|
||||||
|
for (int x = 0; x < max; x += 2) {
|
||||||
|
if (image->get((int)points[x], (int)points[x + 1])) {
|
||||||
|
bits->set(x >> 1, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
Ref<BitMatrix> GridSampler::sampleGrid(Ref<BitMatrix> image, int dimension, float p1ToX, float p1ToY, float p2ToX,
|
Ref<BitMatrix> GridSampler::sampleGrid(Ref<BitMatrix> image, int dimension, float p1ToX, float p1ToY, float p2ToX,
|
||||||
float p2ToY, float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX,
|
float p2ToY, float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX,
|
||||||
float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY) {
|
float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY) {
|
||||||
|
|
|
@ -32,6 +32,8 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform> transform);
|
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform> transform);
|
||||||
|
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimensionX, int dimensionY, Ref<PerspectiveTransform> transform);
|
||||||
|
|
||||||
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimension, float p1ToX, float p1ToY, float p2ToX, float p2ToY,
|
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimension, float p1ToX, float p1ToY, float p2ToX, float p2ToY,
|
||||||
float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX,
|
float p3ToX, float p3ToY, float p4ToX, float p4ToY, float p1FromX, float p1FromY, float p2FromX,
|
||||||
float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY);
|
float p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY);
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* MonochromeRectangleDetector.cpp
|
||||||
|
* y_wmk
|
||||||
|
*
|
||||||
|
* Created by Luiz Silva on 09/02/2010.
|
||||||
|
* Copyright 2010 y_wmk 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/NotFoundException.h>
|
||||||
|
#include <zxing/common/detector/MonochromeRectangleDetector.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace zxing {
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
std::vector<Ref<ResultPoint> > 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<ResultPoint> pointA(findCornerFromCenter(halfWidth, 0, left, right,
|
||||||
|
halfHeight, -deltaY, top, bottom, halfWidth >> 1));
|
||||||
|
top = (int) pointA->getY() - 1;;
|
||||||
|
Ref<ResultPoint> pointB(findCornerFromCenter(halfWidth, -deltaX, left, right,
|
||||||
|
halfHeight, 0, top, bottom, halfHeight >> 1));
|
||||||
|
left = (int) pointB->getX() - 1;
|
||||||
|
Ref<ResultPoint> pointC(findCornerFromCenter(halfWidth, deltaX, left, right,
|
||||||
|
halfHeight, 0, top, bottom, halfHeight >> 1));
|
||||||
|
right = (int) pointC->getX() + 1;
|
||||||
|
Ref<ResultPoint> 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<ResultPoint> > corners(4);
|
||||||
|
corners[0].reset(pointA);
|
||||||
|
corners[1].reset(pointB);
|
||||||
|
corners[2].reset(pointC);
|
||||||
|
corners[3].reset(pointD);
|
||||||
|
return corners;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ResultPoint> MonochromeRectangleDetector::findCornerFromCenter(int centerX, int deltaX, int left, int right,
|
||||||
|
int centerY, int deltaY, int top, int bottom, int maxWhiteRun) {
|
||||||
|
Ref<TwoInts> lastRange(NULL);
|
||||||
|
for (int y = centerY, x = centerX;
|
||||||
|
y < bottom && y >= top && x < right && x >= left;
|
||||||
|
y += deltaY, x += deltaX) {
|
||||||
|
Ref<TwoInts> range(NULL);
|
||||||
|
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 NotFoundException("Couldn't find corners (lastRange = NULL) ");
|
||||||
|
} else {
|
||||||
|
// lastRange was found
|
||||||
|
if (deltaX == 0) {
|
||||||
|
int lastY = y - deltaY;
|
||||||
|
if (lastRange->start < centerX) {
|
||||||
|
if (lastRange->end > centerX) {
|
||||||
|
// straddle, choose one or the other based on direction
|
||||||
|
Ref<ResultPoint> result(new ResultPoint(deltaY > 0 ? lastRange->start : lastRange->end, lastY));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
Ref<ResultPoint> result(new ResultPoint(lastRange->start, lastY));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
Ref<ResultPoint> result(new ResultPoint(lastRange->end, lastY));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int lastX = x - deltaX;
|
||||||
|
if (lastRange->start < centerY) {
|
||||||
|
if (lastRange->end > centerY) {
|
||||||
|
Ref<ResultPoint> result(new ResultPoint(lastX, deltaX < 0 ? lastRange->start : lastRange->end));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
Ref<ResultPoint> result(new ResultPoint(lastX, lastRange->start));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
Ref<ResultPoint> result(new ResultPoint(lastX, lastRange->end));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastRange = range;
|
||||||
|
}
|
||||||
|
throw NotFoundException("Couldn't find corners");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<TwoInts> 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--;
|
||||||
|
Ref<TwoInts> result(NULL);
|
||||||
|
if (end > start) {
|
||||||
|
result = new TwoInts;
|
||||||
|
result->start = start;
|
||||||
|
result->end = end;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
#ifndef __MONOCHROMERECTANGLEDETECTOR_H__
|
||||||
|
#define __MONOCHROMERECTANGLEDETECTOR_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MonochromeRectangleDetector.h
|
||||||
|
* y_wmk
|
||||||
|
*
|
||||||
|
* Created by Luiz Silva on 09/02/2010.
|
||||||
|
* Copyright 2010 y_wmk 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/NotFoundException.h>
|
||||||
|
#include <zxing/ResultPoint.h>
|
||||||
|
#include <zxing/common/BitMatrix.h>
|
||||||
|
#include <zxing/common/Counted.h>
|
||||||
|
#include <zxing/ResultPoint.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace zxing {
|
||||||
|
|
||||||
|
struct TwoInts: public Counted {
|
||||||
|
int start;
|
||||||
|
int end;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MonochromeRectangleDetector : public Counted {
|
||||||
|
private:
|
||||||
|
static const int MAX_MODULES = 32;
|
||||||
|
Ref<BitMatrix> image_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MonochromeRectangleDetector(Ref<BitMatrix> image) : image_(image) { };
|
||||||
|
|
||||||
|
std::vector<Ref<ResultPoint> > detect();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ref<ResultPoint> findCornerFromCenter(int centerX, int deltaX, int left, int right,
|
||||||
|
int centerY, int deltaY, int top, int bottom, int maxWhiteRun);
|
||||||
|
|
||||||
|
Ref<TwoInts> 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__
|
315
cpp/core/src/zxing/common/detector/WhiteRectangleDetector.cpp
Normal file
315
cpp/core/src/zxing/common/detector/WhiteRectangleDetector.cpp
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
/*
|
||||||
|
* WhiteRectangleDetector.cpp
|
||||||
|
* y_wmk
|
||||||
|
*
|
||||||
|
* Created by Luiz Silva on 09/02/2010.
|
||||||
|
* Copyright 2010 y_wmk 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/NotFoundException.h>
|
||||||
|
#include <zxing/common/detector/WhiteRectangleDetector.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace zxing {
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int WhiteRectangleDetector::INIT_SIZE = 30;
|
||||||
|
int WhiteRectangleDetector::CORR = 1;
|
||||||
|
|
||||||
|
|
||||||
|
WhiteRectangleDetector::WhiteRectangleDetector(Ref<BitMatrix> image) : image_(image) {
|
||||||
|
width_ = image->getWidth();
|
||||||
|
height_ = image->getHeight();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Detects a candidate barcode-like rectangular region within an image. It
|
||||||
|
* starts around the center of the image, increases the size of the candidate
|
||||||
|
* region until it finds a white rectangular region.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return {@link vector<Ref<ResultPoint> >} describing the corners of the rectangular
|
||||||
|
* region. The first and last points are opposed on the diagonal, as
|
||||||
|
* are the second and third. The first point will be the topmost
|
||||||
|
* point and the last, the bottommost. The second point will be
|
||||||
|
* leftmost and the third, the rightmost
|
||||||
|
* @throws NotFoundException if no Data Matrix Code can be found
|
||||||
|
*/
|
||||||
|
std::vector<Ref<ResultPoint> > WhiteRectangleDetector::detect() {
|
||||||
|
int left = (width_ - INIT_SIZE) >> 1;
|
||||||
|
int right = (width_ + INIT_SIZE) >> 1;
|
||||||
|
int up = (height_ - INIT_SIZE) >> 1;
|
||||||
|
int down = (height_ + INIT_SIZE) >> 1;
|
||||||
|
if (up < 0 || left < 0 || down >= height_ || right >= width_) {
|
||||||
|
throw NotFoundException("Invalid dimensions WhiteRectangleDetector");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sizeExceeded = false;
|
||||||
|
bool aBlackPointFoundOnBorder = true;
|
||||||
|
bool atLeastOneBlackPointFoundOnBorder = false;
|
||||||
|
|
||||||
|
while (aBlackPointFoundOnBorder) {
|
||||||
|
aBlackPointFoundOnBorder = false;
|
||||||
|
|
||||||
|
// .....
|
||||||
|
// . |
|
||||||
|
// .....
|
||||||
|
bool rightBorderNotWhite = true;
|
||||||
|
while (rightBorderNotWhite && right < width_) {
|
||||||
|
rightBorderNotWhite = containsBlackPoint(up, down, right, false);
|
||||||
|
if (rightBorderNotWhite) {
|
||||||
|
right++;
|
||||||
|
aBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (right >= width_) {
|
||||||
|
sizeExceeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .....
|
||||||
|
// . .
|
||||||
|
// .___.
|
||||||
|
bool bottomBorderNotWhite = true;
|
||||||
|
while (bottomBorderNotWhite && down < height_) {
|
||||||
|
bottomBorderNotWhite = containsBlackPoint(left, right, down, true);
|
||||||
|
if (bottomBorderNotWhite) {
|
||||||
|
down++;
|
||||||
|
aBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (down >= height_) {
|
||||||
|
sizeExceeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .....
|
||||||
|
// | .
|
||||||
|
// .....
|
||||||
|
bool leftBorderNotWhite = true;
|
||||||
|
while (leftBorderNotWhite && left >= 0) {
|
||||||
|
leftBorderNotWhite = containsBlackPoint(up, down, left, false);
|
||||||
|
if (leftBorderNotWhite) {
|
||||||
|
left--;
|
||||||
|
aBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left < 0) {
|
||||||
|
sizeExceeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .___.
|
||||||
|
// . .
|
||||||
|
// .....
|
||||||
|
bool topBorderNotWhite = true;
|
||||||
|
while (topBorderNotWhite && up >= 0) {
|
||||||
|
topBorderNotWhite = containsBlackPoint(left, right, up, true);
|
||||||
|
if (topBorderNotWhite) {
|
||||||
|
up--;
|
||||||
|
aBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (up < 0) {
|
||||||
|
sizeExceeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aBlackPointFoundOnBorder) {
|
||||||
|
atLeastOneBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) {
|
||||||
|
|
||||||
|
int maxSize = right - left;
|
||||||
|
|
||||||
|
Ref<ResultPoint> z(NULL);
|
||||||
|
//go up right
|
||||||
|
for (int i = 1; i < maxSize; i++) {
|
||||||
|
z = getBlackPointOnSegment(left, down - i, left + i, down);
|
||||||
|
if (z != NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z == NULL) {
|
||||||
|
throw NotFoundException("z == NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ResultPoint> t(NULL);
|
||||||
|
//go down right
|
||||||
|
for (int i = 1; i < maxSize; i++) {
|
||||||
|
t = getBlackPointOnSegment(left, up + i, left + i, up);
|
||||||
|
if (t != NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == NULL) {
|
||||||
|
throw NotFoundException("t == NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ResultPoint> x(NULL);
|
||||||
|
//go down left
|
||||||
|
for (int i = 1; i < maxSize; i++) {
|
||||||
|
x = getBlackPointOnSegment(right, up + i, right - i, up);
|
||||||
|
if (x != NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x == NULL) {
|
||||||
|
throw NotFoundException("x == NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ResultPoint> y(NULL);
|
||||||
|
//go up left
|
||||||
|
for (int i = 1; i < maxSize; i++) {
|
||||||
|
y = getBlackPointOnSegment(right, down - i, right - i, down);
|
||||||
|
if (y != NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y == NULL) {
|
||||||
|
throw NotFoundException("y == NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
return centerEdges(y, z, x, t);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw NotFoundException("No black point found on border");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends up being a bit faster than Math.round(). This merely rounds its
|
||||||
|
* argument to the nearest int, where x.5 rounds up.
|
||||||
|
*/
|
||||||
|
int WhiteRectangleDetector::round(float d) {
|
||||||
|
return (int) (d + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ResultPoint> WhiteRectangleDetector::getBlackPointOnSegment(float aX, float aY, float bX, float bY) {
|
||||||
|
int dist = distanceL2(aX, aY, bX, bY);
|
||||||
|
float xStep = (bX - aX) / dist;
|
||||||
|
float yStep = (bY - aY) / dist;
|
||||||
|
for (int i = 0; i < dist; i++) {
|
||||||
|
int x = round(aX + i * xStep);
|
||||||
|
int y = round(aY + i * yStep);
|
||||||
|
if (image_->get(x, y)) {
|
||||||
|
Ref<ResultPoint> point(new ResultPoint(x, y));
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ref<ResultPoint> point(NULL);
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WhiteRectangleDetector::distanceL2(float aX, float aY, float bX, float bY) {
|
||||||
|
float xDiff = aX - bX;
|
||||||
|
float yDiff = aY - bY;
|
||||||
|
return round((float)sqrt(xDiff * xDiff + yDiff * yDiff));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* recenters the points of a constant distance towards the center
|
||||||
|
*
|
||||||
|
* @param y bottom most point
|
||||||
|
* @param z left most point
|
||||||
|
* @param x right most point
|
||||||
|
* @param t top most point
|
||||||
|
* @return {@link vector<Ref<ResultPoint> >} describing the corners of the rectangular
|
||||||
|
* region. The first and last points are opposed on the diagonal, as
|
||||||
|
* are the second and third. The first point will be the topmost
|
||||||
|
* point and the last, the bottommost. The second point will be
|
||||||
|
* leftmost and the third, the rightmost
|
||||||
|
*/
|
||||||
|
vector<Ref<ResultPoint> > WhiteRectangleDetector::centerEdges(Ref<ResultPoint> y, Ref<ResultPoint> z,
|
||||||
|
Ref<ResultPoint> x, Ref<ResultPoint> t) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// t t
|
||||||
|
// z x
|
||||||
|
// x OR z
|
||||||
|
// y y
|
||||||
|
//
|
||||||
|
|
||||||
|
float yi = y->getX();
|
||||||
|
float yj = y->getY();
|
||||||
|
float zi = z->getX();
|
||||||
|
float zj = z->getY();
|
||||||
|
float xi = x->getX();
|
||||||
|
float xj = x->getY();
|
||||||
|
float ti = t->getX();
|
||||||
|
float tj = t->getY();
|
||||||
|
|
||||||
|
std::vector<Ref<ResultPoint> > corners(4);
|
||||||
|
if (yi < (float)width_/2) {
|
||||||
|
Ref<ResultPoint> pointA(new ResultPoint(ti - CORR, tj + CORR));
|
||||||
|
Ref<ResultPoint> pointB(new ResultPoint(zi + CORR, zj + CORR));
|
||||||
|
Ref<ResultPoint> pointC(new ResultPoint(xi - CORR, xj - CORR));
|
||||||
|
Ref<ResultPoint> pointD(new ResultPoint(yi + CORR, yj - CORR));
|
||||||
|
corners[0].reset(pointA);
|
||||||
|
corners[1].reset(pointB);
|
||||||
|
corners[2].reset(pointC);
|
||||||
|
corners[3].reset(pointD);
|
||||||
|
} else {
|
||||||
|
Ref<ResultPoint> pointA(new ResultPoint(ti + CORR, tj + CORR));
|
||||||
|
Ref<ResultPoint> pointB(new ResultPoint(zi + CORR, zj - CORR));
|
||||||
|
Ref<ResultPoint> pointC(new ResultPoint(xi - CORR, xj + CORR));
|
||||||
|
Ref<ResultPoint> pointD(new ResultPoint(yi - CORR, yj - CORR));
|
||||||
|
corners[0].reset(pointA);
|
||||||
|
corners[1].reset(pointB);
|
||||||
|
corners[2].reset(pointC);
|
||||||
|
corners[3].reset(pointD);
|
||||||
|
}
|
||||||
|
return corners;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a segment contains a black point
|
||||||
|
*
|
||||||
|
* @param a min value of the scanned coordinate
|
||||||
|
* @param b max value of the scanned coordinate
|
||||||
|
* @param fixed value of fixed coordinate
|
||||||
|
* @param horizontal set to true if scan must be horizontal, false if vertical
|
||||||
|
* @return true if a black point has been found, else false.
|
||||||
|
*/
|
||||||
|
bool WhiteRectangleDetector::containsBlackPoint(int a, int b, int fixed, bool horizontal) {
|
||||||
|
if (horizontal) {
|
||||||
|
for (int x = a; x <= b; x++) {
|
||||||
|
if (image_->get(x, fixed)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int y = a; y <= b; y++) {
|
||||||
|
if (image_->get(fixed, y)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
56
cpp/core/src/zxing/common/detector/WhiteRectangleDetector.h
Normal file
56
cpp/core/src/zxing/common/detector/WhiteRectangleDetector.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef __WHITERECTANGLEDETECTOR_H__
|
||||||
|
#define __WHITERECTANGLEDETECTOR_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WhiteRectangleDetector.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Created by Luiz Silva on 09/02/2010.
|
||||||
|
* Copyright 2010 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/ResultPoint.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace zxing {
|
||||||
|
|
||||||
|
class WhiteRectangleDetector : public Counted {
|
||||||
|
private:
|
||||||
|
static int INIT_SIZE;
|
||||||
|
static int CORR;
|
||||||
|
Ref<BitMatrix> image_;
|
||||||
|
int width_;
|
||||||
|
int height_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WhiteRectangleDetector(Ref<BitMatrix> image);
|
||||||
|
std::vector<Ref<ResultPoint> > detect();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int round(float a);
|
||||||
|
Ref<ResultPoint> getBlackPointOnSegment(float aX, float aY, float bX, float bY);
|
||||||
|
int distanceL2(float aX, float aY, float bX, float bY);
|
||||||
|
std::vector<Ref<ResultPoint> > centerEdges(Ref<ResultPoint> y, Ref<ResultPoint> z,
|
||||||
|
Ref<ResultPoint> x, Ref<ResultPoint> t);
|
||||||
|
bool containsBlackPoint(int a, int b, int fixed, bool horizontal);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -189,7 +189,7 @@ int Version::buildVersions() {
|
||||||
new ECBlocks(14, new ECB(1, 16)))));
|
new ECBlocks(14, new ECB(1, 16)))));
|
||||||
VERSIONS.push_back(Ref<Version>(new Version(28, 12, 36, 10, 16,
|
VERSIONS.push_back(Ref<Version>(new Version(28, 12, 36, 10, 16,
|
||||||
new ECBlocks(18, new ECB(1, 22)))));
|
new ECBlocks(18, new ECB(1, 22)))));
|
||||||
VERSIONS.push_back(Ref<Version>(new Version(29, 16, 36, 10, 16,
|
VERSIONS.push_back(Ref<Version>(new Version(29, 16, 36, 14, 16,
|
||||||
new ECBlocks(24, new ECB(1, 32)))));
|
new ECBlocks(24, new ECB(1, 32)))));
|
||||||
VERSIONS.push_back(Ref<Version>(new Version(30, 16, 48, 14, 22,
|
VERSIONS.push_back(Ref<Version>(new Version(30, 16, 48, 14, 22,
|
||||||
new ECBlocks(28, new ECB(1, 49)))));
|
new ECBlocks(28, new ECB(1, 49)))));
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include <zxing/datamatrix/decoder/BitMatrixParser.h>
|
#include <zxing/datamatrix/decoder/BitMatrixParser.h>
|
||||||
#include <zxing/common/IllegalArgumentException.h>
|
#include <zxing/common/IllegalArgumentException.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace zxing {
|
namespace zxing {
|
||||||
namespace datamatrix {
|
namespace datamatrix {
|
||||||
|
|
||||||
|
@ -32,13 +34,12 @@ BitMatrixParser::BitMatrixParser(Ref<BitMatrix> bitMatrix) : bitMatrix_(NULL),
|
||||||
parsedVersion_(NULL),
|
parsedVersion_(NULL),
|
||||||
readBitMatrix_(NULL) {
|
readBitMatrix_(NULL) {
|
||||||
size_t dimension = bitMatrix->getDimension();
|
size_t dimension = bitMatrix->getDimension();
|
||||||
if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0)
|
if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0)
|
||||||
throw ReaderException("Dimension must be even, > 10 < 144");
|
throw ReaderException("Dimension must be even, > 8 < 144");
|
||||||
|
|
||||||
parsedVersion_ = readVersion(bitMatrix);
|
parsedVersion_ = readVersion(bitMatrix);
|
||||||
bitMatrix_ = extractDataRegion(bitMatrix);
|
bitMatrix_ = extractDataRegion(bitMatrix);
|
||||||
// TODO(bbrown): Make this work for rectangular symbols
|
readBitMatrix_ = new BitMatrix(bitMatrix_->getWidth(), bitMatrix_->getHeight());
|
||||||
readBitMatrix_ = new BitMatrix(bitMatrix_->getDimension());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Version> BitMatrixParser::readVersion(Ref<BitMatrix> bitMatrix) {
|
Ref<Version> BitMatrixParser::readVersion(Ref<BitMatrix> bitMatrix) {
|
||||||
|
@ -46,9 +47,8 @@ Ref<Version> BitMatrixParser::readVersion(Ref<BitMatrix> bitMatrix) {
|
||||||
return parsedVersion_;
|
return parsedVersion_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bbrown): make this work for rectangular dimensions as well.
|
int numRows = bitMatrix->getHeight();//getDimension();
|
||||||
int numRows = bitMatrix->getDimension();
|
int numColumns = bitMatrix->getWidth();//numRows;
|
||||||
int numColumns = numRows;
|
|
||||||
|
|
||||||
Ref<Version> version = parsedVersion_->getVersionForDimensions(numRows, numColumns);
|
Ref<Version> version = parsedVersion_->getVersionForDimensions(numRows, numColumns);
|
||||||
if (version != 0) {
|
if (version != 0) {
|
||||||
|
@ -63,9 +63,8 @@ ArrayRef<unsigned char> BitMatrixParser::readCodewords() {
|
||||||
int row = 4;
|
int row = 4;
|
||||||
int column = 0;
|
int column = 0;
|
||||||
|
|
||||||
// TODO(bbrown): Data Matrix can be rectangular, assuming square for now
|
int numRows = bitMatrix_->getHeight();
|
||||||
int numRows = bitMatrix_->getDimension();
|
int numColumns = bitMatrix_->getWidth();
|
||||||
int numColumns = numRows;
|
|
||||||
|
|
||||||
bool corner1Read = false;
|
bool corner1Read = false;
|
||||||
bool corner2Read = false;
|
bool corner2Read = false;
|
||||||
|
@ -324,9 +323,8 @@ Ref<BitMatrix> BitMatrixParser::extractDataRegion(Ref<BitMatrix> bitMatrix) {
|
||||||
int symbolSizeRows = parsedVersion_->getSymbolSizeRows();
|
int symbolSizeRows = parsedVersion_->getSymbolSizeRows();
|
||||||
int symbolSizeColumns = parsedVersion_->getSymbolSizeColumns();
|
int symbolSizeColumns = parsedVersion_->getSymbolSizeColumns();
|
||||||
|
|
||||||
// TODO(bbrown): Make this work with rectangular codes
|
if ((int)bitMatrix->getHeight() != symbolSizeRows) {
|
||||||
if ((int)bitMatrix->getDimension() != symbolSizeRows) {
|
throw IllegalArgumentException("Dimension of bitMatrix must match the version size");
|
||||||
throw IllegalArgumentException("Dimension of bitMarix must match the version size");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int dataRegionSizeRows = parsedVersion_->getDataRegionSizeRows();
|
int dataRegionSizeRows = parsedVersion_->getDataRegionSizeRows();
|
||||||
|
@ -336,10 +334,9 @@ Ref<BitMatrix> BitMatrixParser::extractDataRegion(Ref<BitMatrix> bitMatrix) {
|
||||||
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
|
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
|
||||||
|
|
||||||
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
|
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
|
||||||
//int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
|
int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
|
||||||
|
|
||||||
// TODO(bbrown): Make this work with rectangular codes
|
Ref<BitMatrix> bitMatrixWithoutAlignment(new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow));
|
||||||
Ref<BitMatrix> bitMatrixWithoutAlignment(new BitMatrix(sizeDataRegionRow));
|
|
||||||
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
|
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
|
||||||
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
|
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
|
||||||
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
|
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <zxing/ReaderException.h>
|
#include <zxing/FormatException.h>
|
||||||
#include <zxing/datamatrix/decoder/DecodedBitStreamParser.h>
|
#include <zxing/datamatrix/decoder/DecodedBitStreamParser.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <zxing/common/DecoderResult.h>
|
||||||
|
|
||||||
namespace zxing {
|
namespace zxing {
|
||||||
namespace datamatrix {
|
namespace datamatrix {
|
||||||
|
@ -28,10 +29,10 @@ namespace datamatrix {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const char DecodedBitStreamParser::C40_BASIC_SET_CHARS[] = {
|
const char DecodedBitStreamParser::C40_BASIC_SET_CHARS[] = {
|
||||||
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
'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'
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
|
||||||
};
|
};
|
||||||
|
|
||||||
const char DecodedBitStreamParser::C40_SHIFT2_SET_CHARS[] = {
|
const char DecodedBitStreamParser::C40_SHIFT2_SET_CHARS[] = {
|
||||||
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
|
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
|
||||||
|
@ -42,362 +43,374 @@ const char DecodedBitStreamParser::TEXT_BASIC_SET_CHARS[] = {
|
||||||
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
'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'
|
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
|
||||||
};
|
};
|
||||||
|
|
||||||
const char DecodedBitStreamParser::TEXT_SHIFT3_SET_CHARS[] = {
|
const char DecodedBitStreamParser::TEXT_SHIFT3_SET_CHARS[] = {
|
||||||
'\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
'\'', '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
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes) {
|
Ref<DecoderResult> DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes) {
|
||||||
Ref<BitSource> bits(new BitSource(bytes));
|
Ref<BitSource> bits(new BitSource(bytes));
|
||||||
ostringstream result;
|
ostringstream result;
|
||||||
ostringstream resultTrailer;
|
ostringstream resultTrailer;
|
||||||
// bool trailer = false;
|
vector<unsigned char> byteSegments;
|
||||||
int mode = ASCII_ENCODE;
|
int mode = ASCII_ENCODE;
|
||||||
do {
|
do {
|
||||||
if (mode == ASCII_ENCODE) {
|
if (mode == ASCII_ENCODE) {
|
||||||
mode = decodeAsciiSegment(bits, result, resultTrailer);
|
mode = decodeAsciiSegment(bits, result, resultTrailer);
|
||||||
} else {
|
} else {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case C40_ENCODE:
|
case C40_ENCODE:
|
||||||
decodeC40Segment(bits, result);
|
decodeC40Segment(bits, result);
|
||||||
break;
|
break;
|
||||||
case TEXT_ENCODE:
|
case TEXT_ENCODE:
|
||||||
decodeTextSegment(bits, result);
|
decodeTextSegment(bits, result);
|
||||||
break;
|
break;
|
||||||
case ANSIX12_ENCODE:
|
case ANSIX12_ENCODE:
|
||||||
decodeAnsiX12Segment(bits, result);
|
decodeAnsiX12Segment(bits, result);
|
||||||
break;
|
break;
|
||||||
case EDIFACT_ENCODE:
|
case EDIFACT_ENCODE:
|
||||||
decodeEdifactSegment(bits, result);
|
decodeEdifactSegment(bits, result);
|
||||||
break;
|
break;
|
||||||
case BASE256_ENCODE:
|
case BASE256_ENCODE:
|
||||||
decodeBase256Segment(bits, result);
|
decodeBase256Segment(bits, result, byteSegments);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw ReaderException("Unsupported mode indicator");
|
throw FormatException("Unsupported mode indicator");
|
||||||
}
|
|
||||||
mode = ASCII_ENCODE;
|
|
||||||
}
|
}
|
||||||
} while (mode != PAD_ENCODE && bits->available() > 0);
|
mode = ASCII_ENCODE;
|
||||||
/* if (trailer) {
|
|
||||||
result << resultTrailer;
|
|
||||||
}
|
}
|
||||||
*/
|
} while (mode != PAD_ENCODE && bits->available() > 0);
|
||||||
return result.str();
|
|
||||||
|
if (resultTrailer.str().size() > 0) {
|
||||||
|
result << resultTrailer.str();
|
||||||
|
}
|
||||||
|
ArrayRef<unsigned char> rawBytes(bytes);
|
||||||
|
Ref<String> text(new String(result.str()));
|
||||||
|
return Ref<DecoderResult>(new DecoderResult(rawBytes, text));
|
||||||
}
|
}
|
||||||
|
|
||||||
int DecodedBitStreamParser::decodeAsciiSegment(Ref<BitSource> bits, ostringstream & result,
|
int DecodedBitStreamParser::decodeAsciiSegment(Ref<BitSource> bits, ostringstream & result,
|
||||||
ostringstream & resultTrailer) {
|
ostringstream & resultTrailer) {
|
||||||
bool upperShift = false;
|
bool upperShift = false;
|
||||||
do {
|
do {
|
||||||
int oneByte = bits->readBits(8);
|
int oneByte = bits->readBits(8);
|
||||||
if (oneByte == 0) {
|
if (oneByte == 0) {
|
||||||
throw ReaderException("Not enough bits to decode");
|
throw FormatException("Not enough bits to decode");
|
||||||
} else if (oneByte <= 128) { // ASCII data (ASCII value + 1)
|
} else if (oneByte <= 128) { // ASCII data (ASCII value + 1)
|
||||||
oneByte = upperShift ? (oneByte + 128) : oneByte;
|
oneByte = upperShift ? (oneByte + 128) : oneByte;
|
||||||
// upperShift = false;
|
// upperShift = false;
|
||||||
result << (char) (oneByte - 1);
|
result << (char) (oneByte - 1);
|
||||||
return ASCII_ENCODE;
|
return ASCII_ENCODE;
|
||||||
} else if (oneByte == 129) { // Pad
|
} else if (oneByte == 129) { // Pad
|
||||||
return PAD_ENCODE;
|
return PAD_ENCODE;
|
||||||
} else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130)
|
} else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130)
|
||||||
int value = oneByte - 130;
|
int value = oneByte - 130;
|
||||||
if (value < 10) { // padd with '0' for single digit values
|
if (value < 10) { // padd with '0' for single digit values
|
||||||
result << '0';
|
result << '0';
|
||||||
}
|
}
|
||||||
result << value;
|
result << value;
|
||||||
} else if (oneByte == 230) { // Latch to C40 encodation
|
} else if (oneByte == 230) { // Latch to C40 encodation
|
||||||
return C40_ENCODE;
|
return C40_ENCODE;
|
||||||
} else if (oneByte == 231) { // Latch to Base 256 encodation
|
} else if (oneByte == 231) { // Latch to Base 256 encodation
|
||||||
return BASE256_ENCODE;
|
return BASE256_ENCODE;
|
||||||
} else if (oneByte == 232) { // FNC1
|
} else if (oneByte == 232) { // FNC1
|
||||||
//throw ReaderException.getInstance();
|
result << ((char) 29); // translate as ASCII 29
|
||||||
// Ignore this symbol for now
|
} else if (oneByte == 233 || oneByte == 234) {
|
||||||
} else if (oneByte == 233) { // Structured Append
|
// Structured Append, Reader Programming
|
||||||
//throw ReaderException.getInstance();
|
// Ignore these symbols for now
|
||||||
// Ignore this symbol for now
|
// throw FormatException.getInstance();
|
||||||
} else if (oneByte == 234) { // Reader Programming
|
} else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII)
|
||||||
//throw ReaderException.getInstance();
|
upperShift = true;
|
||||||
// Ignore this symbol for now
|
} else if (oneByte == 236) { // 05 Macro
|
||||||
} else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII)
|
result << ("[)>RS05GS");
|
||||||
upperShift = true;
|
resultTrailer << ("RSEOT");
|
||||||
} else if (oneByte == 236) { // 05 Macro
|
} else if (oneByte == 237) { // 06 Macro
|
||||||
/* trailer = false;
|
result << ("[)>RS06GS");
|
||||||
result << "[)>\u001E05\u001D";
|
resultTrailer << ("RSEOT");
|
||||||
resultTrailer << "\u001E\u0004";
|
} else if (oneByte == 238) { // Latch to ANSI X12 encodation
|
||||||
// Ignore this symbol for now
|
return ANSIX12_ENCODE;
|
||||||
*/ } else if (oneByte == 237) { // 06 Macro
|
} else if (oneByte == 239) { // Latch to Text encodation
|
||||||
/* trailer = false;
|
return TEXT_ENCODE;
|
||||||
result << "[)>\u001E06\u001D";
|
} else if (oneByte == 240) { // Latch to EDIFACT encodation
|
||||||
resultTrailer << "\u001E\u0004";
|
return EDIFACT_ENCODE;
|
||||||
// Ignore this symbol for now
|
} else if (oneByte == 241) { // ECI Character
|
||||||
*/ } else if (oneByte == 238) { // Latch to ANSI X12 encodation
|
// TODO(bbrown): I think we need to support ECI
|
||||||
return ANSIX12_ENCODE;
|
// throw FormatException.getInstance();
|
||||||
} else if (oneByte == 239) { // Latch to Text encodation
|
// Ignore this symbol for now
|
||||||
return TEXT_ENCODE;
|
} else if (oneByte >= 242) { // Not to be used in ASCII encodation
|
||||||
} else if (oneByte == 240) { // Latch to EDIFACT encodation
|
// ... but work around encoders that end with 254, latch back to ASCII
|
||||||
return EDIFACT_ENCODE;
|
if (oneByte == 254 && bits->available() == 0) {
|
||||||
} else if (oneByte == 241) { // ECI Character
|
// Ignore
|
||||||
// TODO(bbrown): I think we need to support ECI
|
} else {
|
||||||
//throw ReaderException.getInstance();
|
throw FormatException("Not to be used in ASCII encodation");
|
||||||
// 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;
|
||||||
} while (bits->available() > 0);
|
|
||||||
return ASCII_ENCODE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeC40Segment(Ref<BitSource> bits, ostringstream & result) {
|
void DecodedBitStreamParser::decodeC40Segment(Ref<BitSource> bits, ostringstream & result) {
|
||||||
// Three C40 values are encoded in a 16-bit value as
|
// Three C40 values are encoded in a 16-bit value as
|
||||||
// (1600 * C1) + (40 * C2) + C3 + 1
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||||
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
|
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
|
||||||
bool upperShift = false;
|
bool upperShift = false;
|
||||||
|
|
||||||
int* cValues = new int[3];
|
int* cValues = new int[3];
|
||||||
do {
|
int shift = 0;
|
||||||
// If there is only one byte left then it will be encoded as ASCII
|
do {
|
||||||
if (bits->available() == 8) {
|
// If there is only one byte left then it will be encoded as ASCII
|
||||||
return;
|
if (bits->available() == 8) {
|
||||||
}
|
return;
|
||||||
int firstByte = bits->readBits(8);
|
}
|
||||||
if (firstByte == 254) { // Unlatch codeword
|
int firstByte = bits->readBits(8);
|
||||||
return;
|
if (firstByte == 254) { // Unlatch codeword
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
parseTwoBytes(firstByte, bits->readBits(8), cValues);
|
parseTwoBytes(firstByte, bits->readBits(8), cValues);
|
||||||
|
|
||||||
int shift = 0;
|
for (int i = 0; i < 3; i++) {
|
||||||
for (int i = 0; i < 3; i++) {
|
int cValue = cValues[i];
|
||||||
int cValue = cValues[i];
|
switch (shift) {
|
||||||
switch (shift) {
|
case 0:
|
||||||
case 0:
|
if (cValue < 3) {
|
||||||
if (cValue < 3) {
|
shift = cValue + 1;
|
||||||
shift = cValue + 1;
|
} else {
|
||||||
} 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) {
|
if (upperShift) {
|
||||||
result << cValue + 128;
|
result << (char) (C40_BASIC_SET_CHARS[cValue] + 128);
|
||||||
upperShift = false;
|
upperShift = false;
|
||||||
} else {
|
} else {
|
||||||
result << cValue;
|
result << C40_BASIC_SET_CHARS[cValue];
|
||||||
}
|
}
|
||||||
shift = 0;
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
if (cValue < 27) {
|
if (upperShift) {
|
||||||
if (upperShift) {
|
result << (char) (cValue + 128);
|
||||||
result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128);
|
upperShift = false;
|
||||||
upperShift = false;
|
} else {
|
||||||
} else {
|
result << (char) cValue;
|
||||||
result << C40_SHIFT2_SET_CHARS[cValue];
|
}
|
||||||
}
|
shift = 0;
|
||||||
} else if (cValue == 27) { // FNC1
|
break;
|
||||||
throw ReaderException("FNC1");
|
case 2:
|
||||||
} else if (cValue == 30) { // Upper Shift
|
if (cValue < 27) {
|
||||||
upperShift = true;
|
|
||||||
} else {
|
|
||||||
throw ReaderException("Upper Shift");
|
|
||||||
}
|
|
||||||
shift = 0;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
if (upperShift) {
|
if (upperShift) {
|
||||||
result << (char) (cValue + 224);
|
result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128);
|
||||||
upperShift = false;
|
upperShift = false;
|
||||||
} else {
|
} else {
|
||||||
result << (char) (cValue + 96);
|
result << C40_SHIFT2_SET_CHARS[cValue];
|
||||||
}
|
}
|
||||||
shift = 0;
|
} else if (cValue == 27) { // FNC1
|
||||||
break;
|
result << ((char) 29); // translate as ASCII 29
|
||||||
default:
|
} else if (cValue == 30) { // Upper Shift
|
||||||
throw ReaderException("");
|
upperShift = true;
|
||||||
}
|
} else {
|
||||||
|
throw FormatException("decodeC40Segment: 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 FormatException("decodeC40Segment: no case");
|
||||||
}
|
}
|
||||||
} while (bits->available() > 0);
|
}
|
||||||
|
} while (bits->available() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeTextSegment(Ref<BitSource> bits, ostringstream & result) {
|
void DecodedBitStreamParser::decodeTextSegment(Ref<BitSource> bits, ostringstream & result) {
|
||||||
// Three Text values are encoded in a 16-bit value as
|
// Three Text values are encoded in a 16-bit value as
|
||||||
// (1600 * C1) + (40 * C2) + C3 + 1
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||||
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
|
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
|
||||||
bool upperShift = false;
|
bool upperShift = false;
|
||||||
|
|
||||||
int* cValues = new int[3];
|
int* cValues = new int[3];
|
||||||
do {
|
int shift = 0;
|
||||||
// If there is only one byte left then it will be encoded as ASCII
|
do {
|
||||||
if (bits->available() == 8) {
|
// If there is only one byte left then it will be encoded as ASCII
|
||||||
return;
|
if (bits->available() == 8) {
|
||||||
}
|
return;
|
||||||
int firstByte = bits->readBits(8);
|
}
|
||||||
if (firstByte == 254) { // Unlatch codeword
|
int firstByte = bits->readBits(8);
|
||||||
return;
|
if (firstByte == 254) { // Unlatch codeword
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
parseTwoBytes(firstByte, bits->readBits(8), cValues);
|
parseTwoBytes(firstByte, bits->readBits(8), cValues);
|
||||||
|
|
||||||
int shift = 0;
|
for (int i = 0; i < 3; i++) {
|
||||||
for (int i = 0; i < 3; i++) {
|
int cValue = cValues[i];
|
||||||
int cValue = cValues[i];
|
switch (shift) {
|
||||||
switch (shift) {
|
case 0:
|
||||||
case 0:
|
if (cValue < 3) {
|
||||||
if (cValue < 3) {
|
shift = cValue + 1;
|
||||||
shift = cValue + 1;
|
} else {
|
||||||
} 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) {
|
if (upperShift) {
|
||||||
result << (char) (cValue + 128);
|
result << (char) (TEXT_BASIC_SET_CHARS[cValue] + 128);
|
||||||
upperShift = false;
|
upperShift = false;
|
||||||
} else {
|
} else {
|
||||||
result << (cValue);
|
result << (TEXT_BASIC_SET_CHARS[cValue]);
|
||||||
}
|
}
|
||||||
shift = 0;
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
// Shift 2 for Text is the same encoding as C40
|
if (upperShift) {
|
||||||
if (cValue < 27) {
|
result << (char) (cValue + 128);
|
||||||
if (upperShift) {
|
upperShift = false;
|
||||||
result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128);
|
} else {
|
||||||
upperShift = false;
|
result << (char) (cValue);
|
||||||
} else {
|
}
|
||||||
result << (C40_SHIFT2_SET_CHARS[cValue]);
|
shift = 0;
|
||||||
}
|
break;
|
||||||
} else if (cValue == 27) { // FNC1
|
case 2:
|
||||||
throw ReaderException("FNC1");
|
// Shift 2 for Text is the same encoding as C40
|
||||||
} else if (cValue == 30) { // Upper Shift
|
if (cValue < 27) {
|
||||||
upperShift = true;
|
|
||||||
} else {
|
|
||||||
throw ReaderException("Upper Shift");
|
|
||||||
}
|
|
||||||
shift = 0;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
if (upperShift) {
|
if (upperShift) {
|
||||||
result << (char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128);
|
result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128);
|
||||||
upperShift = false;
|
upperShift = false;
|
||||||
} else {
|
} else {
|
||||||
result << (TEXT_SHIFT3_SET_CHARS[cValue]);
|
result << (C40_SHIFT2_SET_CHARS[cValue]);
|
||||||
}
|
}
|
||||||
shift = 0;
|
} else if (cValue == 27) { // FNC1
|
||||||
break;
|
result << ((char) 29); // translate as ASCII 29
|
||||||
default:
|
} else if (cValue == 30) { // Upper Shift
|
||||||
throw ReaderException("");
|
upperShift = true;
|
||||||
}
|
} else {
|
||||||
|
throw FormatException("decodeTextSegment: 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 FormatException("decodeTextSegment: no case");
|
||||||
}
|
}
|
||||||
} while (bits->available() > 0);
|
}
|
||||||
|
} while (bits->available() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeAnsiX12Segment(Ref<BitSource> bits, ostringstream & result) {
|
void DecodedBitStreamParser::decodeAnsiX12Segment(Ref<BitSource> bits, ostringstream & result) {
|
||||||
// Three ANSI X12 values are encoded in a 16-bit value as
|
// Three ANSI X12 values are encoded in a 16-bit value as
|
||||||
// (1600 * C1) + (40 * C2) + C3 + 1
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||||
|
|
||||||
int* cValues = new int[3];
|
int* cValues = new int[3];
|
||||||
do {
|
do {
|
||||||
// If there is only one byte left then it will be encoded as ASCII
|
// If there is only one byte left then it will be encoded as ASCII
|
||||||
if (bits->available() == 8) {
|
if (bits->available() == 8) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int firstByte = bits->readBits(8);
|
int firstByte = bits->readBits(8);
|
||||||
if (firstByte == 254) { // Unlatch codeword
|
if (firstByte == 254) { // Unlatch codeword
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseTwoBytes(firstByte, bits->readBits(8), cValues);
|
parseTwoBytes(firstByte, bits->readBits(8), cValues);
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
int cValue = cValues[i];
|
int cValue = cValues[i];
|
||||||
if (cValue == 0) { // X12 segment terminator <CR>
|
if (cValue == 0) { // X12 segment terminator <CR>
|
||||||
result << '\r';
|
result << '\r';
|
||||||
} else if (cValue == 1) { // X12 segment separator *
|
} else if (cValue == 1) { // X12 segment separator *
|
||||||
result << '*';
|
result << '*';
|
||||||
} else if (cValue == 2) { // X12 sub-element separator >
|
} else if (cValue == 2) { // X12 sub-element separator >
|
||||||
result << '>';
|
result << '>';
|
||||||
} else if (cValue == 3) { // space
|
} else if (cValue == 3) { // space
|
||||||
result << ' ';
|
result << ' ';
|
||||||
} else if (cValue < 14) { // 0 - 9
|
} else if (cValue < 14) { // 0 - 9
|
||||||
result << (char) (cValue + 44);
|
result << (char) (cValue + 44);
|
||||||
} else if (cValue < 40) { // A - Z
|
} else if (cValue < 40) { // A - Z
|
||||||
result << (char) (cValue + 51);
|
result << (char) (cValue + 51);
|
||||||
} else {
|
} else {
|
||||||
throw ReaderException("");
|
throw FormatException("decodeAnsiX12Segment: no case");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} while (bits->available() > 0);
|
}
|
||||||
|
} while (bits->available() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::parseTwoBytes(int firstByte, int secondByte, int*& result) {
|
void DecodedBitStreamParser::parseTwoBytes(int firstByte, int secondByte, int*& result) {
|
||||||
int fullBitValue = (firstByte << 8) + secondByte - 1;
|
int fullBitValue = (firstByte << 8) + secondByte - 1;
|
||||||
int temp = fullBitValue / 1600;
|
int temp = fullBitValue / 1600;
|
||||||
result[0] = temp;
|
result[0] = temp;
|
||||||
fullBitValue -= temp * 1600;
|
fullBitValue -= temp * 1600;
|
||||||
temp = fullBitValue / 40;
|
temp = fullBitValue / 40;
|
||||||
result[1] = temp;
|
result[1] = temp;
|
||||||
result[2] = fullBitValue - temp * 40;
|
result[2] = fullBitValue - temp * 40;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeEdifactSegment(Ref<BitSource> bits, ostringstream & result) {
|
void DecodedBitStreamParser::decodeEdifactSegment(Ref<BitSource> bits, ostringstream & result) {
|
||||||
bool unlatch = false;
|
bool unlatch = false;
|
||||||
do {
|
do {
|
||||||
// If there is only two or less bytes left then it will be encoded as ASCII
|
// If there is only two or less bytes left then it will be encoded as ASCII
|
||||||
if (bits->available() <= 16) {
|
if (bits->available() <= 16) {
|
||||||
return;
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
if (!unlatch) {
|
||||||
int edifactValue = bits->readBits(6);
|
if ((edifactValue & 0x20) == 0) { // no 1 in the leading (6th) bit
|
||||||
|
edifactValue |= 0x40; // Add a leading 01 to the 6 bit binary value
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
result << (char)(edifactValue);
|
||||||
}
|
}
|
||||||
} while (!unlatch && bits->available() > 0);
|
}
|
||||||
|
} while (!unlatch && bits->available() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeBase256Segment(Ref<BitSource> bits, ostringstream & result){//, vector<unsigned char> byteSegments)
|
void DecodedBitStreamParser::decodeBase256Segment(Ref<BitSource> bits, ostringstream& result, vector<unsigned char> byteSegments) {
|
||||||
// Figure out how long the Base 256 Segment is.
|
// Figure out how long the Base 256 Segment is.
|
||||||
int d1 = bits->readBits(8);
|
int codewordPosition = 1 + bits->getByteOffset(); // position is 1-indexed
|
||||||
int count;
|
int d1 = unrandomize255State(bits->readBits(8), codewordPosition++);
|
||||||
if (d1 == 0) { // Read the remainder of the symbol
|
int count;
|
||||||
count = bits->available() / 8;
|
if (d1 == 0) { // Read the remainder of the symbol
|
||||||
} else if (d1 < 250) {
|
count = bits->available() / 8;
|
||||||
count = d1;
|
} else if (d1 < 250) {
|
||||||
} else {
|
count = d1;
|
||||||
count = 250 * (d1 - 249) + bits->readBits(8);
|
} else {
|
||||||
|
count = 250 * (d1 - 249) + unrandomize255State(bits->readBits(8), codewordPosition++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're seeing NegativeArraySizeException errors from users.
|
||||||
|
if (count < 0) {
|
||||||
|
throw FormatException("NegativeArraySizeException");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* bytes = new unsigned char[count];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// Have seen this particular error in the wild, such as at
|
||||||
|
// http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2
|
||||||
|
if (bits->available() < 8) {
|
||||||
|
throw FormatException("byteSegments");
|
||||||
}
|
}
|
||||||
unsigned char* bytes = new unsigned char[count];
|
bytes[i] = unrandomize255State(bits->readBits(8), codewordPosition++);
|
||||||
for (int i = 0; i < count; i++) {
|
byteSegments.push_back(bytes[i]);
|
||||||
bytes[i] = unrandomize255State(bits->readBits(8), i);
|
result << (char)bytes[i];
|
||||||
}
|
}
|
||||||
//byteSegments.push_back(bytes);
|
|
||||||
result << bytes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,10 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <zxing/common/Array.h>
|
||||||
#include <zxing/common/BitSource.h>
|
#include <zxing/common/BitSource.h>
|
||||||
#include <zxing/common/Counted.h>
|
#include <zxing/common/Counted.h>
|
||||||
#include <zxing/common/Array.h>
|
#include <zxing/common/DecoderResult.h>
|
||||||
|
|
||||||
|
|
||||||
namespace zxing {
|
namespace zxing {
|
||||||
|
@ -78,7 +79,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* See ISO 16022:2006, 5.2.9 and Annex B, B.2
|
* 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 decodeBase256Segment(Ref<BitSource> bits, std::ostringstream &result, std::vector<unsigned char> byteSegments);
|
||||||
|
|
||||||
void parseTwoBytes(int firstByte, int secondByte, int*& result);
|
void parseTwoBytes(int firstByte, int secondByte, int*& result);
|
||||||
/**
|
/**
|
||||||
|
@ -94,7 +95,7 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DecodedBitStreamParser() { };
|
DecodedBitStreamParser() { };
|
||||||
std::string decode(ArrayRef<unsigned char> bytes);
|
Ref<DecoderResult> decode(ArrayRef<unsigned char> bytes);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,54 +43,52 @@ void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCo
|
||||||
codewordInts[i] = codewordBytes[i] & 0xff;
|
codewordInts[i] = codewordBytes[i] & 0xff;
|
||||||
}
|
}
|
||||||
int numECCodewords = numCodewords - numDataCodewords;
|
int numECCodewords = numCodewords - numDataCodewords;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rsDecoder_.decode(codewordInts, numECCodewords);
|
rsDecoder_.decode(codewordInts, numECCodewords);
|
||||||
} catch (ReedSolomonException const& ex) {
|
} catch (ReedSolomonException const& ex) {
|
||||||
ReaderException rex(ex.what());
|
ReaderException rex(ex.what());
|
||||||
throw rex;
|
throw rex;
|
||||||
}
|
}
|
||||||
|
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||||
|
// We don't care about errors in the error-correction codewords
|
||||||
for (int i = 0; i < numDataCodewords; i++) {
|
for (int i = 0; i < numDataCodewords; i++) {
|
||||||
codewordBytes[i] = (unsigned char)codewordInts[i];
|
codewordBytes[i] = (unsigned char)codewordInts[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
|
Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
|
||||||
// Construct a parser and read version, error-correction level
|
// Construct a parser and read version, error-correction level
|
||||||
BitMatrixParser parser(bits);
|
BitMatrixParser parser(bits);
|
||||||
Version *version = parser.readVersion(bits);
|
Version *version = parser.readVersion(bits);
|
||||||
|
|
||||||
// Read codewords
|
// Read codewords
|
||||||
ArrayRef<unsigned char> codewords(parser.readCodewords());
|
ArrayRef<unsigned char> codewords(parser.readCodewords());
|
||||||
// Separate into data blocks
|
// Separate into data blocks
|
||||||
std::vector<Ref<DataBlock> > dataBlocks = DataBlock::getDataBlocks(codewords, version);
|
std::vector<Ref<DataBlock> > dataBlocks = DataBlock::getDataBlocks(codewords, version);
|
||||||
|
|
||||||
// Count total number of data bytes
|
int dataBlocksCount = dataBlocks.size();
|
||||||
int totalBytes = 0;
|
|
||||||
for (unsigned int i = 0; i < dataBlocks.size(); i++) {
|
// Count total number of data bytes
|
||||||
totalBytes += dataBlocks[i]->getNumDataCodewords();
|
int totalBytes = 0;
|
||||||
|
for (int i = 0; i < dataBlocksCount; i++) {
|
||||||
|
totalBytes += dataBlocks[i]->getNumDataCodewords();
|
||||||
|
}
|
||||||
|
ArrayRef<unsigned char> resultBytes(totalBytes);
|
||||||
|
|
||||||
|
// Error-correct and copy data blocks together into a stream of bytes
|
||||||
|
for (int j = 0; j < dataBlocksCount; 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++) {
|
||||||
|
// De-interlace data blocks.
|
||||||
|
resultBytes[i * dataBlocksCount + j] = codewordBytes[i];
|
||||||
}
|
}
|
||||||
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
|
// Decode the contents of that stream of bytes
|
||||||
DecodedBitStreamParser decodedBSParser;
|
DecodedBitStreamParser decodedBSParser;
|
||||||
Ref<String> text(new String(decodedBSParser.decode(resultBytes)));
|
return Ref<DecoderResult> (decodedBSParser.decode(resultBytes));
|
||||||
|
|
||||||
Ref<DecoderResult> result(new DecoderResult(resultBytes, text));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <zxing/ResultPoint.h>
|
||||||
#include <zxing/common/GridSampler.h>
|
#include <zxing/common/GridSampler.h>
|
||||||
#include <zxing/datamatrix/detector/Detector.h>
|
#include <zxing/datamatrix/detector/Detector.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -30,287 +31,404 @@ namespace datamatrix {
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
ResultPointsAndTransitions::ResultPointsAndTransitions() : to_(), from_(), transitions_(0) {
|
ResultPointsAndTransitions::ResultPointsAndTransitions() {
|
||||||
Ref<CornerPoint> ref(new CornerPoint(0,0));
|
Ref<ResultPoint> ref(new ResultPoint(0, 0));
|
||||||
from_ = ref;
|
from_ = ref;
|
||||||
to_ = ref;
|
to_ = ref;
|
||||||
|
transitions_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultPointsAndTransitions::ResultPointsAndTransitions(Ref<CornerPoint> from, Ref<CornerPoint> to, int transitions) :
|
ResultPointsAndTransitions::ResultPointsAndTransitions(Ref<ResultPoint> from, Ref<ResultPoint> to,
|
||||||
to_(to), from_(from), transitions_(transitions) {
|
int transitions)
|
||||||
|
: to_(to), from_(from), transitions_(transitions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<CornerPoint> ResultPointsAndTransitions::getFrom() {
|
Ref<ResultPoint> ResultPointsAndTransitions::getFrom() {
|
||||||
return from_;
|
return from_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<CornerPoint> ResultPointsAndTransitions::getTo() {
|
Ref<ResultPoint> ResultPointsAndTransitions::getTo() {
|
||||||
return to_;
|
return to_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ResultPointsAndTransitions::getTransitions() {
|
int ResultPointsAndTransitions::getTransitions() {
|
||||||
return transitions_;
|
return transitions_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Detector::Detector(Ref<BitMatrix> image) : image_(image) { }
|
Detector::Detector(Ref<BitMatrix> image)
|
||||||
|
: image_(image) {
|
||||||
|
}
|
||||||
|
|
||||||
Ref<BitMatrix> Detector::getImage() {
|
Ref<BitMatrix> Detector::getImage() {
|
||||||
return image_;
|
return image_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<DetectorResult> Detector::detect() {
|
Ref<DetectorResult> Detector::detect() {
|
||||||
Ref<MonochromeRectangleDetector> rectangleDetector_(new MonochromeRectangleDetector(image_));
|
Ref<WhiteRectangleDetector> rectangleDetector_(new WhiteRectangleDetector(image_));
|
||||||
std::vector<Ref<CornerPoint> > cornerPoints = rectangleDetector_->detect();
|
std::vector<Ref<ResultPoint> > ResultPoints = rectangleDetector_->detect();
|
||||||
Ref<CornerPoint> pointA = cornerPoints[0];
|
Ref<ResultPoint> pointA = ResultPoints[0];
|
||||||
Ref<CornerPoint> pointB = cornerPoints[1];
|
Ref<ResultPoint> pointB = ResultPoints[1];
|
||||||
Ref<CornerPoint> pointC = cornerPoints[2];
|
Ref<ResultPoint> pointC = ResultPoints[2];
|
||||||
Ref<CornerPoint> pointD = cornerPoints[3];
|
Ref<ResultPoint> pointD = ResultPoints[3];
|
||||||
|
|
||||||
// Point A and D are across the diagonal from one another,
|
// Point A and D are across the diagonal from one another,
|
||||||
// as are B and C. Figure out which are the solid black lines
|
// as are B and C. Figure out which are the solid black lines
|
||||||
// by counting transitions
|
// by counting transitions
|
||||||
std::vector<Ref<ResultPointsAndTransitions> > transitions(4);
|
std::vector<Ref<ResultPointsAndTransitions> > transitions(4);
|
||||||
transitions[0].reset(transitionsBetween(pointA, pointB));
|
transitions[0].reset(transitionsBetween(pointA, pointB));
|
||||||
transitions[1].reset(transitionsBetween(pointA, pointC));
|
transitions[1].reset(transitionsBetween(pointA, pointC));
|
||||||
transitions[2].reset(transitionsBetween(pointB, pointD));
|
transitions[2].reset(transitionsBetween(pointB, pointD));
|
||||||
transitions[3].reset(transitionsBetween(pointC, pointD));
|
transitions[3].reset(transitionsBetween(pointC, pointD));
|
||||||
insertionSort(transitions);
|
insertionSort(transitions);
|
||||||
|
|
||||||
// Sort by number of transitions. First two will be the two solid sides; last two
|
// Sort by number of transitions. First two will be the two solid sides; last two
|
||||||
// will be the two alternating black/white sides
|
// will be the two alternating black/white sides
|
||||||
Ref<ResultPointsAndTransitions> lSideOne(transitions[0]);
|
Ref<ResultPointsAndTransitions> lSideOne(transitions[0]);
|
||||||
Ref<ResultPointsAndTransitions> lSideTwo(transitions[1]);
|
Ref<ResultPointsAndTransitions> lSideTwo(transitions[1]);
|
||||||
|
|
||||||
// Figure out which point is their intersection by tallying up the number of times we see the
|
// 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.
|
// endpoints in the four endpoints. One will show up twice.
|
||||||
Ref<CornerPoint> maybeTopLeft;
|
Ref<ResultPoint> maybeTopLeft;
|
||||||
Ref<CornerPoint> bottomLeft;
|
Ref<ResultPoint> bottomLeft;
|
||||||
Ref<CornerPoint> maybeBottomRight;
|
Ref<ResultPoint> maybeBottomRight;
|
||||||
if (lSideOne->getFrom()->equals(lSideOne->getTo())) {
|
if (lSideOne->getFrom()->equals(lSideOne->getTo())) {
|
||||||
bottomLeft = lSideOne->getFrom();
|
bottomLeft = lSideOne->getFrom();
|
||||||
maybeTopLeft = lSideTwo->getFrom();
|
maybeTopLeft = lSideTwo->getFrom();
|
||||||
maybeBottomRight = lSideTwo->getTo();
|
maybeBottomRight = lSideTwo->getTo();
|
||||||
}
|
} else if (lSideOne->getFrom()->equals(lSideTwo->getFrom())) {
|
||||||
else if (lSideOne->getFrom()->equals(lSideTwo->getFrom())) {
|
|
||||||
bottomLeft = lSideOne->getFrom();
|
bottomLeft = lSideOne->getFrom();
|
||||||
maybeTopLeft = lSideOne->getTo();
|
maybeTopLeft = lSideOne->getTo();
|
||||||
maybeBottomRight = lSideTwo->getTo();
|
maybeBottomRight = lSideTwo->getTo();
|
||||||
}
|
} else if (lSideOne->getFrom()->equals(lSideTwo->getTo())) {
|
||||||
else if (lSideOne->getFrom()->equals(lSideTwo->getTo())) {
|
|
||||||
bottomLeft = lSideOne->getFrom();
|
bottomLeft = lSideOne->getFrom();
|
||||||
maybeTopLeft = lSideOne->getTo();
|
maybeTopLeft = lSideOne->getTo();
|
||||||
maybeBottomRight = lSideTwo->getFrom();
|
maybeBottomRight = lSideTwo->getFrom();
|
||||||
}
|
} else if (lSideOne->getTo()->equals(lSideTwo->getFrom())) {
|
||||||
else if (lSideOne->getTo()->equals(lSideTwo->getFrom())) {
|
|
||||||
bottomLeft = lSideOne->getTo();
|
bottomLeft = lSideOne->getTo();
|
||||||
maybeTopLeft = lSideOne->getFrom();
|
maybeTopLeft = lSideOne->getFrom();
|
||||||
maybeBottomRight = lSideTwo->getTo();
|
maybeBottomRight = lSideTwo->getTo();
|
||||||
}
|
} else if (lSideOne->getTo()->equals(lSideTwo->getTo())) {
|
||||||
else if (lSideOne->getTo()->equals(lSideTwo->getTo())) {
|
|
||||||
bottomLeft = lSideOne->getTo();
|
bottomLeft = lSideOne->getTo();
|
||||||
maybeTopLeft = lSideOne->getFrom();
|
maybeTopLeft = lSideOne->getFrom();
|
||||||
maybeBottomRight = lSideTwo->getFrom();
|
maybeBottomRight = lSideTwo->getFrom();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
bottomLeft = lSideTwo->getFrom();
|
bottomLeft = lSideTwo->getFrom();
|
||||||
maybeTopLeft = lSideOne->getTo();
|
maybeTopLeft = lSideOne->getTo();
|
||||||
maybeBottomRight = lSideOne->getFrom();
|
maybeBottomRight = lSideOne->getFrom();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bottom left is correct but top left and bottom right might be switched
|
// Bottom left is correct but top left and bottom right might be switched
|
||||||
std::vector<Ref<CornerPoint> > corners(3);
|
std::vector<Ref<ResultPoint> > corners(3);
|
||||||
corners[0].reset(maybeTopLeft);
|
corners[0].reset(maybeTopLeft);
|
||||||
corners[1].reset(bottomLeft);
|
corners[1].reset(bottomLeft);
|
||||||
corners[2].reset(maybeBottomRight);
|
corners[2].reset(maybeBottomRight);
|
||||||
// Use the dot product trick to sort them out
|
|
||||||
orderBestPatterns(corners);
|
|
||||||
|
|
||||||
// Now we know which is which:
|
// Use the dot product trick to sort them out
|
||||||
Ref<CornerPoint> bottomRight(corners[0]);
|
ResultPoint::orderBestPatterns(corners);
|
||||||
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
|
// Now we know which is which:
|
||||||
Ref<CornerPoint> topRight;
|
Ref<ResultPoint> bottomRight(corners[0]);
|
||||||
if (!(pointA->equals(bottomRight) || pointA->equals(bottomLeft) || pointA->equals(topLeft))) {
|
bottomLeft = corners[1];
|
||||||
topRight = pointA;
|
Ref<ResultPoint> topLeft(corners[2]);
|
||||||
} 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();
|
// Which point didn't we find in relation to the "L" sides? that's the top right corner
|
||||||
float topRightY = (bottomRight->getY() - bottomLeft->getY()) + topLeft->getY();
|
Ref<ResultPoint> topRight;
|
||||||
Ref<CornerPoint> topR(new CornerPoint(topRightX,topRightY));
|
if (!(pointA->equals(bottomRight) || pointA->equals(bottomLeft) || pointA->equals(topLeft))) {
|
||||||
|
topRight = pointA;
|
||||||
// Next determine the dimension by tracing along the top or right side and counting black/white
|
} else if (!(pointB->equals(bottomRight) || pointB->equals(bottomLeft)
|
||||||
// transitions. Since we start inside a black module, we should see a number of transitions
|
|| pointB->equals(topLeft))) {
|
||||||
// equal to 1 less than the code dimension. Well, actually 2 less, because we are going to
|
topRight = pointB;
|
||||||
// end on a black module:
|
} else if (!(pointC->equals(bottomRight) || pointC->equals(bottomLeft)
|
||||||
// The top right point is actually the corner of a module, which is one of the two black modules
|
|| pointC->equals(topLeft))) {
|
||||||
// adjacent to the white module at the top right. Tracing to that corner from either the top left
|
topRight = pointC;
|
||||||
// or bottom right should work here. The number of transitions could be higher than it should be
|
} else {
|
||||||
// due to noise. So we try both and take the min.
|
topRight = pointD;
|
||||||
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 <
|
// Next determine the dimension by tracing along the top or right side and counting black/white
|
||||||
ResultPoint > bottomLeft, Ref<ResultPoint> bottomRight, int dimension) {
|
// 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:
|
||||||
|
|
||||||
Ref<PerspectiveTransform> transform(PerspectiveTransform::quadrilateralToQuadrilateral(
|
// The top right point is actually the corner of a module, which is one of the two black modules
|
||||||
0.0f,
|
// adjacent to the white module at the top right. Tracing to that corner from either the top left
|
||||||
0.0f,
|
// or bottom right should work here.
|
||||||
dimension,
|
|
||||||
0.0f,
|
int dimensionTop = transitionsBetween(topLeft, topRight)->getTransitions();
|
||||||
dimension,
|
int dimensionRight = transitionsBetween(bottomRight, topRight)->getTransitions();
|
||||||
dimension,
|
|
||||||
0.0f,
|
//dimensionTop++;
|
||||||
dimension,
|
if ((dimensionTop & 0x01) == 1) {
|
||||||
topLeft->getX(),
|
// it can't be odd, so, round... up?
|
||||||
topLeft->getY(),
|
dimensionTop++;
|
||||||
topRight->getX(),
|
}
|
||||||
topRight->getY(),
|
dimensionTop += 2;
|
||||||
bottomRight->getX(),
|
|
||||||
bottomRight->getY(),
|
//dimensionRight++;
|
||||||
bottomLeft->getX(),
|
if ((dimensionRight & 0x01) == 1) {
|
||||||
bottomLeft->getY()));
|
// it can't be odd, so, round... up?
|
||||||
|
dimensionRight++;
|
||||||
|
}
|
||||||
|
dimensionRight += 2;
|
||||||
|
|
||||||
|
Ref<BitMatrix> bits;
|
||||||
|
Ref<PerspectiveTransform> transform;
|
||||||
|
Ref<ResultPoint> correctedTopRight;
|
||||||
|
|
||||||
|
|
||||||
|
// Rectanguar symbols are 6x16, 6x28, 10x24, 10x32, 14x32, or 14x44. If one dimension is more
|
||||||
|
// than twice the other, it's certainly rectangular, but to cut a bit more slack we accept it as
|
||||||
|
// rectangular if the bigger side is at least 7/4 times the other:
|
||||||
|
if (4 * dimensionTop >= 7 * dimensionRight || 4 * dimensionRight >= 7 * dimensionTop) {
|
||||||
|
// The matrix is rectangular
|
||||||
|
correctedTopRight = correctTopRightRectangular(bottomLeft, bottomRight, topLeft, topRight,
|
||||||
|
dimensionTop, dimensionRight);
|
||||||
|
if (correctedTopRight == NULL) {
|
||||||
|
correctedTopRight = topRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
dimensionTop = transitionsBetween(topLeft, correctedTopRight)->getTransitions();
|
||||||
|
dimensionRight = transitionsBetween(bottomRight, correctedTopRight)->getTransitions();
|
||||||
|
|
||||||
|
if ((dimensionTop & 0x01) == 1) {
|
||||||
|
// it can't be odd, so, round... up?
|
||||||
|
dimensionTop++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((dimensionRight & 0x01) == 1) {
|
||||||
|
// it can't be odd, so, round... up?
|
||||||
|
dimensionRight++;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform = createTransform(topLeft, correctedTopRight, bottomLeft, bottomRight, dimensionTop,
|
||||||
|
dimensionRight);
|
||||||
|
bits = sampleGrid(image_, dimensionTop, dimensionRight, transform);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// The matrix is square
|
||||||
|
int dimension = min(dimensionRight, dimensionTop);
|
||||||
|
|
||||||
|
// correct top right point to match the white module
|
||||||
|
correctedTopRight = correctTopRight(bottomLeft, bottomRight, topLeft, topRight, dimension);
|
||||||
|
if (correctedTopRight == NULL) {
|
||||||
|
correctedTopRight = topRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redetermine the dimension using the corrected top right point
|
||||||
|
int dimensionCorrected = max(transitionsBetween(topLeft, correctedTopRight)->getTransitions(),
|
||||||
|
transitionsBetween(bottomRight, correctedTopRight)->getTransitions());
|
||||||
|
dimensionCorrected++;
|
||||||
|
if ((dimensionCorrected & 0x01) == 1) {
|
||||||
|
dimensionCorrected++;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform = createTransform(topLeft, correctedTopRight, bottomLeft, bottomRight,
|
||||||
|
dimensionCorrected, dimensionCorrected);
|
||||||
|
bits = sampleGrid(image_, dimensionCorrected, dimensionCorrected, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Ref<ResultPoint> > points(4);
|
||||||
|
points[0].reset(topLeft);
|
||||||
|
points[1].reset(bottomLeft);
|
||||||
|
points[2].reset(correctedTopRight);
|
||||||
|
points[3].reset(bottomRight);
|
||||||
|
Ref<DetectorResult> detectorResult(new DetectorResult(bits, points, transform));
|
||||||
|
return detectorResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the position of the white top right module using the output of the rectangle detector
|
||||||
|
* for a rectangular matrix
|
||||||
|
*/
|
||||||
|
Ref<ResultPoint> Detector::correctTopRightRectangular(Ref<ResultPoint> bottomLeft,
|
||||||
|
Ref<ResultPoint> bottomRight, Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight,
|
||||||
|
int dimensionTop, int dimensionRight) {
|
||||||
|
|
||||||
|
float corr = distance(bottomLeft, bottomRight) / (float) dimensionTop;
|
||||||
|
int norm = distance(topLeft, topRight);
|
||||||
|
float cos = (topRight->getX() - topLeft->getX()) / norm;
|
||||||
|
float sin = (topRight->getY() - topLeft->getY()) / norm;
|
||||||
|
|
||||||
|
Ref<ResultPoint> c1(
|
||||||
|
new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin));
|
||||||
|
|
||||||
|
corr = distance(bottomLeft, topLeft) / (float) dimensionRight;
|
||||||
|
norm = distance(bottomRight, topRight);
|
||||||
|
cos = (topRight->getX() - bottomRight->getX()) / norm;
|
||||||
|
sin = (topRight->getY() - bottomRight->getY()) / norm;
|
||||||
|
|
||||||
|
Ref<ResultPoint> c2(
|
||||||
|
new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin));
|
||||||
|
|
||||||
|
if (!isValid(c1)) {
|
||||||
|
if (isValid(c2)) {
|
||||||
|
return c2;
|
||||||
|
}
|
||||||
|
return Ref<ResultPoint>(NULL);
|
||||||
|
}
|
||||||
|
if (!isValid(c2)) {
|
||||||
|
return c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l1 = abs(dimensionTop - transitionsBetween(topLeft, c1)->getTransitions())
|
||||||
|
+ abs(dimensionRight - transitionsBetween(bottomRight, c1)->getTransitions());
|
||||||
|
int l2 = abs(dimensionTop - transitionsBetween(topLeft, c2)->getTransitions())
|
||||||
|
+ abs(dimensionRight - transitionsBetween(bottomRight, c2)->getTransitions());
|
||||||
|
|
||||||
|
return l1 <= l2 ? c1 : c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the position of the white top right module using the output of the rectangle detector
|
||||||
|
* for a square matrix
|
||||||
|
*/
|
||||||
|
Ref<ResultPoint> Detector::correctTopRight(Ref<ResultPoint> bottomLeft,
|
||||||
|
Ref<ResultPoint> bottomRight, Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight,
|
||||||
|
int dimension) {
|
||||||
|
|
||||||
|
float corr = distance(bottomLeft, bottomRight) / (float) dimension;
|
||||||
|
int norm = distance(topLeft, topRight);
|
||||||
|
float cos = (topRight->getX() - topLeft->getX()) / norm;
|
||||||
|
float sin = (topRight->getY() - topLeft->getY()) / norm;
|
||||||
|
|
||||||
|
Ref<ResultPoint> c1(
|
||||||
|
new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin));
|
||||||
|
|
||||||
|
corr = distance(bottomLeft, bottomRight) / (float) dimension;
|
||||||
|
norm = distance(bottomRight, topRight);
|
||||||
|
cos = (topRight->getX() - bottomRight->getX()) / norm;
|
||||||
|
sin = (topRight->getY() - bottomRight->getY()) / norm;
|
||||||
|
|
||||||
|
Ref<ResultPoint> c2(
|
||||||
|
new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin));
|
||||||
|
|
||||||
|
if (!isValid(c1)) {
|
||||||
|
if (isValid(c2)) {
|
||||||
|
return c2;
|
||||||
|
}
|
||||||
|
return Ref<ResultPoint>(NULL);
|
||||||
|
}
|
||||||
|
if (!isValid(c2)) {
|
||||||
|
return c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l1 = abs(
|
||||||
|
transitionsBetween(topLeft, c1)->getTransitions()
|
||||||
|
- transitionsBetween(bottomRight, c1)->getTransitions());
|
||||||
|
int l2 = abs(
|
||||||
|
transitionsBetween(topLeft, c2)->getTransitions()
|
||||||
|
- transitionsBetween(bottomRight, c2)->getTransitions());
|
||||||
|
|
||||||
|
return l1 <= l2 ? c1 : c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Detector::isValid(Ref<ResultPoint> p) {
|
||||||
|
return p->getX() >= 0 && p->getX() < image_->getWidth() && p->getY() > 0
|
||||||
|
&& p->getY() < image_->getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// L2 distance
|
||||||
|
int Detector::distance(Ref<ResultPoint> a, Ref<ResultPoint> b) {
|
||||||
|
return round(
|
||||||
|
(float) sqrt(
|
||||||
|
(double) (a->getX() - b->getX()) * (a->getX() - b->getX())
|
||||||
|
+ (a->getY() - b->getY()) * (a->getY() - b->getY())));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<ResultPointsAndTransitions> Detector::transitionsBetween(Ref<ResultPoint> from,
|
||||||
|
Ref<ResultPoint> 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 dimensionX, int dimensionY) {
|
||||||
|
|
||||||
|
Ref<PerspectiveTransform> transform(
|
||||||
|
PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||||
|
0.5f,
|
||||||
|
0.5f,
|
||||||
|
dimensionX - 0.5f,
|
||||||
|
0.5f,
|
||||||
|
dimensionX - 0.5f,
|
||||||
|
dimensionY - 0.5f,
|
||||||
|
0.5f,
|
||||||
|
dimensionY - 0.5f,
|
||||||
|
topLeft->getX(),
|
||||||
|
topLeft->getY(),
|
||||||
|
topRight->getX(),
|
||||||
|
topRight->getY(),
|
||||||
|
bottomRight->getX(),
|
||||||
|
bottomRight->getY(),
|
||||||
|
bottomLeft->getX(),
|
||||||
|
bottomLeft->getY()));
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<BitMatrix> Detector::sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform> transform) {
|
Ref<BitMatrix> Detector::sampleGrid(Ref<BitMatrix> image, int dimensionX, int dimensionY,
|
||||||
|
Ref<PerspectiveTransform> transform) {
|
||||||
GridSampler &sampler = GridSampler::getInstance();
|
GridSampler &sampler = GridSampler::getInstance();
|
||||||
return sampler.sampleGrid(image, dimension, transform);
|
return sampler.sampleGrid(image, dimensionX, dimensionY, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Detector::insertionSort(std::vector<Ref<ResultPointsAndTransitions> > &vector) {
|
void Detector::insertionSort(std::vector<Ref<ResultPointsAndTransitions> > &vector) {
|
||||||
int max = vector.size();
|
int max = vector.size();
|
||||||
bool swapped = true;
|
bool swapped = true;
|
||||||
Ref<ResultPointsAndTransitions> value;
|
Ref<ResultPointsAndTransitions> value;
|
||||||
Ref<ResultPointsAndTransitions> valueB;
|
Ref<ResultPointsAndTransitions> valueB;
|
||||||
do {
|
do {
|
||||||
swapped = false;
|
swapped = false;
|
||||||
for (int i = 1; i < max; i++) {
|
for (int i = 1; i < max; i++) {
|
||||||
value = vector[i-1];
|
value = vector[i - 1];
|
||||||
if (compare(value, (valueB = vector[i])) > 0) {
|
if (compare(value, (valueB = vector[i])) > 0){
|
||||||
swapped = true;
|
swapped = true;
|
||||||
vector[i-1].reset(valueB);
|
vector[i - 1].reset(valueB);
|
||||||
vector[i].reset(value);
|
vector[i].reset(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (swapped);
|
} 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) {
|
int Detector::compare(Ref<ResultPointsAndTransitions> a, Ref<ResultPointsAndTransitions> b) {
|
||||||
return a->getTransitions() - b->getTransitions();
|
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,56 +22,70 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <zxing/common/Counted.h>
|
#include <zxing/common/Counted.h>
|
||||||
#include <zxing/common/DetectorResult.h>
|
#include <zxing/common/DetectorResult.h>
|
||||||
#include <zxing/common/BitMatrix.h>
|
#include <zxing/common/BitMatrix.h>
|
||||||
#include <zxing/common/PerspectiveTransform.h>
|
#include <zxing/common/PerspectiveTransform.h>
|
||||||
#include <zxing/datamatrix/detector/MonochromeRectangleDetector.h>
|
#include <zxing/common/detector/WhiteRectangleDetector.h>
|
||||||
|
|
||||||
|
|
||||||
namespace zxing {
|
namespace zxing {
|
||||||
namespace datamatrix {
|
namespace datamatrix {
|
||||||
|
|
||||||
class ResultPointsAndTransitions : public Counted {
|
class ResultPointsAndTransitions: public Counted {
|
||||||
private:
|
private:
|
||||||
Ref<CornerPoint> to_;
|
Ref<ResultPoint> to_;
|
||||||
Ref<CornerPoint> from_;
|
Ref<ResultPoint> from_;
|
||||||
int transitions_;
|
int transitions_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ResultPointsAndTransitions();
|
ResultPointsAndTransitions();
|
||||||
ResultPointsAndTransitions(Ref<CornerPoint> from, Ref<CornerPoint> to, int transitions);
|
ResultPointsAndTransitions(Ref<ResultPoint> from, Ref<ResultPoint> to, int transitions);
|
||||||
Ref<CornerPoint> getFrom();
|
Ref<ResultPoint> getFrom();
|
||||||
Ref<CornerPoint> getTo();
|
Ref<ResultPoint> getTo();
|
||||||
int getTransitions();
|
int getTransitions();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Detector : public Counted {
|
class Detector: public Counted {
|
||||||
private:
|
private:
|
||||||
Ref<BitMatrix> image_;
|
Ref<BitMatrix> image_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimension, Ref<PerspectiveTransform> transform);
|
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimensionX, int dimensionY,
|
||||||
|
Ref<PerspectiveTransform> transform);
|
||||||
|
|
||||||
void insertionSort(std::vector<Ref<ResultPointsAndTransitions> >& vector);
|
void insertionSort(std::vector<Ref<ResultPointsAndTransitions> >& vector);
|
||||||
|
|
||||||
Ref<ResultPointsAndTransitions> transitionsBetween(Ref<CornerPoint> from, Ref<CornerPoint> to);
|
Ref<ResultPoint> correctTopRightRectangular(Ref<ResultPoint> bottomLeft,
|
||||||
int min(int a, int b) { return a > b ? b : a; };
|
Ref<ResultPoint> bottomRight, Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight,
|
||||||
|
int dimensionTop, int dimensionRight);
|
||||||
|
Ref<ResultPoint> correctTopRight(Ref<ResultPoint> bottomLeft, Ref<ResultPoint> bottomRight,
|
||||||
|
Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, int dimension);
|
||||||
|
bool isValid(Ref<ResultPoint> p);
|
||||||
|
int distance(Ref<ResultPoint> a, Ref<ResultPoint> b);
|
||||||
|
Ref<ResultPointsAndTransitions> transitionsBetween(Ref<ResultPoint> from, Ref<ResultPoint> to);
|
||||||
|
int min(int a, int b) {
|
||||||
|
return a > b ? b : a;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Ends up being a bit faster than round(). This merely rounds its
|
||||||
|
* argument to the nearest int, where x.5 rounds up.
|
||||||
|
*/
|
||||||
|
int round(float d) {
|
||||||
|
return (int) (d + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Ref<BitMatrix> getImage();
|
Ref<BitMatrix> getImage();
|
||||||
Detector(Ref<BitMatrix> image);
|
Detector(Ref<BitMatrix> image);
|
||||||
|
|
||||||
virtual Ref<PerspectiveTransform> createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <
|
virtual Ref<PerspectiveTransform> createTransform(Ref<ResultPoint> topLeft,
|
||||||
ResultPoint > bottomLeft, Ref<ResultPoint> bottomRight, int dimension);
|
Ref<ResultPoint> topRight, Ref<ResultPoint> bottomLeft, Ref<ResultPoint> bottomRight,
|
||||||
|
int dimensionX, int dimensionY);
|
||||||
|
|
||||||
Ref<DetectorResult> detect();
|
Ref<DetectorResult> detect();
|
||||||
void orderBestPatterns(std::vector<Ref<CornerPoint> > &patterns);
|
|
||||||
float distance(float x1, float x2, float y1, float y2);
|
private:
|
||||||
private:
|
int compare(Ref<ResultPointsAndTransitions> a, Ref<ResultPointsAndTransitions> b);
|
||||||
int compare(Ref<ResultPointsAndTransitions> a, Ref<ResultPointsAndTransitions> b);
|
|
||||||
float crossProductZ(Ref<ResultPoint> pointA, Ref<ResultPoint> pointB, Ref<ResultPoint> pointC);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
23
cpp/core/src/zxing/datamatrix/detector/DetectorException.cpp
Normal file
23
cpp/core/src/zxing/datamatrix/detector/DetectorException.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* DetectorException.cpp
|
||||||
|
*
|
||||||
|
* Created on: Aug 26, 2011
|
||||||
|
* Author: luiz
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DetectorException.h"
|
||||||
|
|
||||||
|
namespace zxing {
|
||||||
|
namespace datamatrix {
|
||||||
|
|
||||||
|
DetectorException::DetectorException(const char *msg) :
|
||||||
|
Exception(msg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DetectorException::~DetectorException() throw () {
|
||||||
|
// TODO Auto-generated destructor stub
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} /* namespace zxing */
|
23
cpp/core/src/zxing/datamatrix/detector/DetectorException.h
Normal file
23
cpp/core/src/zxing/datamatrix/detector/DetectorException.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* DetectorException.h
|
||||||
|
*
|
||||||
|
* Created on: Aug 26, 2011
|
||||||
|
* Author: luiz
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DETECTOREXCEPTION_H_
|
||||||
|
#define DETECTOREXCEPTION_H_
|
||||||
|
|
||||||
|
#include <zxing/Exception.h>
|
||||||
|
|
||||||
|
namespace zxing {
|
||||||
|
namespace datamatrix {
|
||||||
|
|
||||||
|
class DetectorException : public Exception {
|
||||||
|
public:
|
||||||
|
DetectorException(const char *msg);
|
||||||
|
virtual ~DetectorException() throw();
|
||||||
|
};
|
||||||
|
} /* namespace nexxera */
|
||||||
|
} /* namespace zxing */
|
||||||
|
#endif /* DETECTOREXCEPTION_H_ */
|
|
@ -55,7 +55,7 @@ static bool tryHarder = false;
|
||||||
static bool show_filename = false;
|
static bool show_filename = false;
|
||||||
static bool search_multi = false;
|
static bool search_multi = false;
|
||||||
|
|
||||||
static const int MAX_EXPECTED = 1024;
|
static const int MAX_EXPECTED = 4096;
|
||||||
|
|
||||||
Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
||||||
Ref<Reader> reader(new MultiFormatReader);
|
Ref<Reader> reader(new MultiFormatReader);
|
||||||
|
|
Loading…
Reference in a new issue