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:
luizcroc@gmail.com 2011-10-28 11:24:50 +00:00
parent d65dd54e03
commit 41daacd233
23 changed files with 1553 additions and 608 deletions

View file

@ -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() {

View 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() {
}
}

View 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__

View file

@ -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));
}
} }

View file

@ -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);
}; };
} }

View file

@ -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_;

View file

@ -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

View file

@ -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) {

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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__

View 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;
}
}

View 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

View file

@ -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)))));

View file

@ -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) {

View file

@ -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;
} }
} }
} }

View file

@ -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);
}; };
} }

View file

@ -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;
} }
} }
} }

View file

@ -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));
}
} }
} }

View file

@ -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);
}; };
} }

View 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 */

View 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_ */

View file

@ -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);