From 41daacd233a4e629d931c78cb32ac67bbbacbd6d Mon Sep 17 00:00:00 2001 From: "luizcroc@gmail.com" Date: Fri, 28 Oct 2011 11:24:50 +0000 Subject: [PATCH] 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 --- cpp/core/src/zxing/DecodeHints.cpp | 3 +- cpp/core/src/zxing/NotFoundException.cpp | 29 + cpp/core/src/zxing/NotFoundException.h | 32 + cpp/core/src/zxing/ResultPoint.cpp | 61 ++ cpp/core/src/zxing/ResultPoint.h | 10 + cpp/core/src/zxing/common/BitSource.cpp | 1 - cpp/core/src/zxing/common/BitSource.h | 3 + cpp/core/src/zxing/common/GridSampler.cpp | 21 + cpp/core/src/zxing/common/GridSampler.h | 2 + .../detector/MonochromeRectangleDetector.cpp | 171 +++++ .../detector/MonochromeRectangleDetector.h | 60 ++ .../detector/WhiteRectangleDetector.cpp | 315 +++++++++ .../common/detector/WhiteRectangleDetector.h | 56 ++ cpp/core/src/zxing/datamatrix/Version.cpp | 2 +- .../datamatrix/decoder/BitMatrixParser.cpp | 29 +- .../decoder/DecodedBitStreamParser.cpp | 619 +++++++++--------- .../decoder/DecodedBitStreamParser.h | 7 +- .../src/zxing/datamatrix/decoder/Decoder.cpp | 60 +- .../zxing/datamatrix/detector/Detector.cpp | 552 ++++++++++------ .../src/zxing/datamatrix/detector/Detector.h | 80 ++- .../datamatrix/detector/DetectorException.cpp | 23 + .../datamatrix/detector/DetectorException.h | 23 + cpp/magick/src/main.cpp | 2 +- 23 files changed, 1553 insertions(+), 608 deletions(-) create mode 100644 cpp/core/src/zxing/NotFoundException.cpp create mode 100644 cpp/core/src/zxing/NotFoundException.h create mode 100644 cpp/core/src/zxing/common/detector/MonochromeRectangleDetector.cpp create mode 100644 cpp/core/src/zxing/common/detector/MonochromeRectangleDetector.h create mode 100644 cpp/core/src/zxing/common/detector/WhiteRectangleDetector.cpp create mode 100644 cpp/core/src/zxing/common/detector/WhiteRectangleDetector.h create mode 100644 cpp/core/src/zxing/datamatrix/detector/DetectorException.cpp create mode 100644 cpp/core/src/zxing/datamatrix/detector/DetectorException.h diff --git a/cpp/core/src/zxing/DecodeHints.cpp b/cpp/core/src/zxing/DecodeHints.cpp index 82f9e30bd..578cdc5bb 100644 --- a/cpp/core/src/zxing/DecodeHints.cpp +++ b/cpp/core/src/zxing/DecodeHints.cpp @@ -46,8 +46,7 @@ const DecodeHints DecodeHints::DEFAULT_HINT( BARCODEFORMAT_CODE_128_HINT | BARCODEFORMAT_CODE_39_HINT | BARCODEFORMAT_ITF_HINT | - // TODO: uncomment once this passes QA - // BARCODEFORMAT_DATA_MATRIX_HINT | + BARCODEFORMAT_DATA_MATRIX_HINT | BARCODEFORMAT_QR_CODE_HINT); DecodeHints::DecodeHints() { diff --git a/cpp/core/src/zxing/NotFoundException.cpp b/cpp/core/src/zxing/NotFoundException.cpp new file mode 100644 index 000000000..fb411205b --- /dev/null +++ b/cpp/core/src/zxing/NotFoundException.cpp @@ -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 + +namespace zxing { + +NotFoundException::NotFoundException(const char *msg) : + Exception(msg) { +} + +NotFoundException::~NotFoundException() throw() { +} + +} diff --git a/cpp/core/src/zxing/NotFoundException.h b/cpp/core/src/zxing/NotFoundException.h new file mode 100644 index 000000000..b9a350d2c --- /dev/null +++ b/cpp/core/src/zxing/NotFoundException.h @@ -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 + +namespace zxing { + +class NotFoundException : public Exception { +public: + NotFoundException(const char *msg); + ~NotFoundException() throw(); +}; + +} +#endif // __NOT_FOUND_EXCEPTION_H__ diff --git a/cpp/core/src/zxing/ResultPoint.cpp b/cpp/core/src/zxing/ResultPoint.cpp index ea4a755c6..fa31588a1 100755 --- a/cpp/core/src/zxing/ResultPoint.cpp +++ b/cpp/core/src/zxing/ResultPoint.cpp @@ -19,6 +19,7 @@ */ #include +#include namespace zxing { @@ -36,4 +37,64 @@ float ResultPoint::getY() const { return posY_; } +bool ResultPoint::equals(Ref other) { + return posX_ == other->getX() && posY_ == other->getY(); +} + +/** + *

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 > &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 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 temp = pointA; + pointA = pointC; + pointC = temp; + } + + patterns[0] = pointA; + patterns[1] = pointB; + patterns[2] = pointC; +} + +float ResultPoint::distance(Ref point1, Ref 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 pointA, Ref pointB, Ref pointC) { + float bX = pointB->getX(); + float bY = pointB->getY(); + return ((pointC->getX() - bX) * (pointA->getY() - bY)) - ((pointC->getY() - bY) * (pointA->getX() - bX)); +} } diff --git a/cpp/core/src/zxing/ResultPoint.h b/cpp/core/src/zxing/ResultPoint.h index 6b8b3de1a..ae48b8434 100644 --- a/cpp/core/src/zxing/ResultPoint.h +++ b/cpp/core/src/zxing/ResultPoint.h @@ -22,6 +22,7 @@ */ #include +#include namespace zxing { @@ -37,6 +38,15 @@ public: virtual float getX() const; virtual float getY() const; + + bool equals(Ref other); + + static void orderBestPatterns(std::vector > &patterns); + static float distance(Ref point1, Ref point2); + static float distance(float x1, float x2, float y1, float y2); + +private: + static float crossProductZ(Ref pointA, Ref pointB, Ref pointC); }; } diff --git a/cpp/core/src/zxing/common/BitSource.cpp b/cpp/core/src/zxing/common/BitSource.cpp index 4b53b4da7..d0ff48438 100644 --- a/cpp/core/src/zxing/common/BitSource.cpp +++ b/cpp/core/src/zxing/common/BitSource.cpp @@ -32,7 +32,6 @@ int BitSource::readBits(int numBits) { int result = 0; - // First, read remainder from current byte if (bitOffset_ > 0) { int bitsLeft = 8 - bitOffset_; diff --git a/cpp/core/src/zxing/common/BitSource.h b/cpp/core/src/zxing/common/BitSource.h index bd6c1d50f..4a7dfd599 100644 --- a/cpp/core/src/zxing/common/BitSource.h +++ b/cpp/core/src/zxing/common/BitSource.h @@ -47,6 +47,9 @@ public: bytes_(bytes), byteOffset_(0), bitOffset_(0) { } + int getByteOffset() { + return byteOffset_; + } /** * @param numBits number of bits to read diff --git a/cpp/core/src/zxing/common/GridSampler.cpp b/cpp/core/src/zxing/common/GridSampler.cpp index 03a240ebb..0bdd008fd 100644 --- a/cpp/core/src/zxing/common/GridSampler.cpp +++ b/cpp/core/src/zxing/common/GridSampler.cpp @@ -53,6 +53,27 @@ Ref GridSampler::sampleGrid(Ref image, int dimension, Ref< return bits; } +Ref GridSampler::sampleGrid(Ref image, int dimensionX, int dimensionY, Ref transform) { + Ref bits(new BitMatrix(dimensionX, dimensionY)); + vector 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 GridSampler::sampleGrid(Ref 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 p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY) { diff --git a/cpp/core/src/zxing/common/GridSampler.h b/cpp/core/src/zxing/common/GridSampler.h index 9f47971bc..d7f74e5b2 100644 --- a/cpp/core/src/zxing/common/GridSampler.h +++ b/cpp/core/src/zxing/common/GridSampler.h @@ -32,6 +32,8 @@ private: public: Ref sampleGrid(Ref image, int dimension, Ref transform); + Ref sampleGrid(Ref image, int dimensionX, int dimensionY, Ref transform); + Ref sampleGrid(Ref 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 p2FromY, float p3FromX, float p3FromY, float p4FromX, float p4FromY); diff --git a/cpp/core/src/zxing/common/detector/MonochromeRectangleDetector.cpp b/cpp/core/src/zxing/common/detector/MonochromeRectangleDetector.cpp new file mode 100644 index 000000000..dbff6ebc3 --- /dev/null +++ b/cpp/core/src/zxing/common/detector/MonochromeRectangleDetector.cpp @@ -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 +#include +#include + +namespace zxing { +using namespace std; + +std::vector > 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 pointA(findCornerFromCenter(halfWidth, 0, left, right, + halfHeight, -deltaY, top, bottom, halfWidth >> 1)); + top = (int) pointA->getY() - 1;; + Ref pointB(findCornerFromCenter(halfWidth, -deltaX, left, right, + halfHeight, 0, top, bottom, halfHeight >> 1)); + left = (int) pointB->getX() - 1; + Ref pointC(findCornerFromCenter(halfWidth, deltaX, left, right, + halfHeight, 0, top, bottom, halfHeight >> 1)); + right = (int) pointC->getX() + 1; + Ref 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 > corners(4); + corners[0].reset(pointA); + corners[1].reset(pointB); + corners[2].reset(pointC); + corners[3].reset(pointD); + return corners; + } + +Ref MonochromeRectangleDetector::findCornerFromCenter(int centerX, int deltaX, int left, int right, + int centerY, int deltaY, int top, int bottom, int maxWhiteRun) { + Ref lastRange(NULL); + for (int y = centerY, x = centerX; + y < bottom && y >= top && x < right && x >= left; + y += deltaY, x += deltaX) { + Ref 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 result(new ResultPoint(deltaY > 0 ? lastRange->start : lastRange->end, lastY)); + return result; + } + Ref result(new ResultPoint(lastRange->start, lastY)); + return result; + } else { + Ref result(new ResultPoint(lastRange->end, lastY)); + return result; + } + } else { + int lastX = x - deltaX; + if (lastRange->start < centerY) { + if (lastRange->end > centerY) { + Ref result(new ResultPoint(lastX, deltaX < 0 ? lastRange->start : lastRange->end)); + return result; + } + Ref result(new ResultPoint(lastX, lastRange->start)); + return result; + } else { + Ref result(new ResultPoint(lastX, lastRange->end)); + return result; + } + } + } + } + lastRange = range; + } + throw NotFoundException("Couldn't find corners"); + } + +Ref 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 result(NULL); + if (end > start) { + result = new TwoInts; + result->start = start; + result->end = end; + } + return result; + } +} diff --git a/cpp/core/src/zxing/common/detector/MonochromeRectangleDetector.h b/cpp/core/src/zxing/common/detector/MonochromeRectangleDetector.h new file mode 100644 index 000000000..75d2dd0f3 --- /dev/null +++ b/cpp/core/src/zxing/common/detector/MonochromeRectangleDetector.h @@ -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 +#include +#include +#include +#include +#include + + +namespace zxing { + +struct TwoInts: public Counted { + int start; + int end; +}; + +class MonochromeRectangleDetector : public Counted { +private: + static const int MAX_MODULES = 32; + Ref image_; + +public: + MonochromeRectangleDetector(Ref image) : image_(image) { }; + + std::vector > detect(); + +private: + Ref findCornerFromCenter(int centerX, int deltaX, int left, int right, + int centerY, int deltaY, int top, int bottom, int maxWhiteRun); + + Ref 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__ diff --git a/cpp/core/src/zxing/common/detector/WhiteRectangleDetector.cpp b/cpp/core/src/zxing/common/detector/WhiteRectangleDetector.cpp new file mode 100644 index 000000000..735127b78 --- /dev/null +++ b/cpp/core/src/zxing/common/detector/WhiteRectangleDetector.cpp @@ -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 +#include +#include +#include + +namespace zxing { +using namespace std; + +int WhiteRectangleDetector::INIT_SIZE = 30; +int WhiteRectangleDetector::CORR = 1; + + +WhiteRectangleDetector::WhiteRectangleDetector(Ref image) : image_(image) { + width_ = image->getWidth(); + height_ = image->getHeight(); +}; + +/** + *

+ * 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. + *

+ * + * @return {@link vector >} 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 > 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 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 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 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 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 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 point(new ResultPoint(x, y)); + return point; + } + } + Ref 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 >} 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 > WhiteRectangleDetector::centerEdges(Ref y, Ref z, + Ref x, Ref 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 > corners(4); + if (yi < (float)width_/2) { + Ref pointA(new ResultPoint(ti - CORR, tj + CORR)); + Ref pointB(new ResultPoint(zi + CORR, zj + CORR)); + Ref pointC(new ResultPoint(xi - CORR, xj - CORR)); + Ref 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 pointA(new ResultPoint(ti + CORR, tj + CORR)); + Ref pointB(new ResultPoint(zi + CORR, zj - CORR)); + Ref pointC(new ResultPoint(xi - CORR, xj + CORR)); + Ref 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; +} +} diff --git a/cpp/core/src/zxing/common/detector/WhiteRectangleDetector.h b/cpp/core/src/zxing/common/detector/WhiteRectangleDetector.h new file mode 100644 index 000000000..87de26a6b --- /dev/null +++ b/cpp/core/src/zxing/common/detector/WhiteRectangleDetector.h @@ -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 +#include +#include +#include +#include +#include + + +namespace zxing { + +class WhiteRectangleDetector : public Counted { + private: + static int INIT_SIZE; + static int CORR; + Ref image_; + int width_; + int height_; + + public: + WhiteRectangleDetector(Ref image); + std::vector > detect(); + + private: + int round(float a); + Ref getBlackPointOnSegment(float aX, float aY, float bX, float bY); + int distanceL2(float aX, float aY, float bX, float bY); + std::vector > centerEdges(Ref y, Ref z, + Ref x, Ref t); + bool containsBlackPoint(int a, int b, int fixed, bool horizontal); +}; +} + +#endif diff --git a/cpp/core/src/zxing/datamatrix/Version.cpp b/cpp/core/src/zxing/datamatrix/Version.cpp index e48d88ef9..f94ca65ac 100644 --- a/cpp/core/src/zxing/datamatrix/Version.cpp +++ b/cpp/core/src/zxing/datamatrix/Version.cpp @@ -189,7 +189,7 @@ int Version::buildVersions() { new ECBlocks(14, new ECB(1, 16))))); VERSIONS.push_back(Ref(new Version(28, 12, 36, 10, 16, new ECBlocks(18, new ECB(1, 22))))); - VERSIONS.push_back(Ref(new Version(29, 16, 36, 10, 16, + VERSIONS.push_back(Ref(new Version(29, 16, 36, 14, 16, new ECBlocks(24, new ECB(1, 32))))); VERSIONS.push_back(Ref(new Version(30, 16, 48, 14, 22, new ECBlocks(28, new ECB(1, 49))))); diff --git a/cpp/core/src/zxing/datamatrix/decoder/BitMatrixParser.cpp b/cpp/core/src/zxing/datamatrix/decoder/BitMatrixParser.cpp index eb5c06f48..2e61da7a2 100644 --- a/cpp/core/src/zxing/datamatrix/decoder/BitMatrixParser.cpp +++ b/cpp/core/src/zxing/datamatrix/decoder/BitMatrixParser.cpp @@ -21,6 +21,8 @@ #include #include +#include + namespace zxing { namespace datamatrix { @@ -32,13 +34,12 @@ BitMatrixParser::BitMatrixParser(Ref bitMatrix) : bitMatrix_(NULL), parsedVersion_(NULL), readBitMatrix_(NULL) { size_t dimension = bitMatrix->getDimension(); - if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) - throw ReaderException("Dimension must be even, > 10 < 144"); + if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0) + throw ReaderException("Dimension must be even, > 8 < 144"); parsedVersion_ = readVersion(bitMatrix); bitMatrix_ = extractDataRegion(bitMatrix); - // TODO(bbrown): Make this work for rectangular symbols - readBitMatrix_ = new BitMatrix(bitMatrix_->getDimension()); + readBitMatrix_ = new BitMatrix(bitMatrix_->getWidth(), bitMatrix_->getHeight()); } Ref BitMatrixParser::readVersion(Ref bitMatrix) { @@ -46,9 +47,8 @@ Ref BitMatrixParser::readVersion(Ref bitMatrix) { return parsedVersion_; } - // TODO(bbrown): make this work for rectangular dimensions as well. - int numRows = bitMatrix->getDimension(); - int numColumns = numRows; + int numRows = bitMatrix->getHeight();//getDimension(); + int numColumns = bitMatrix->getWidth();//numRows; Ref version = parsedVersion_->getVersionForDimensions(numRows, numColumns); if (version != 0) { @@ -63,9 +63,8 @@ ArrayRef BitMatrixParser::readCodewords() { int row = 4; int column = 0; - // TODO(bbrown): Data Matrix can be rectangular, assuming square for now - int numRows = bitMatrix_->getDimension(); - int numColumns = numRows; + int numRows = bitMatrix_->getHeight(); + int numColumns = bitMatrix_->getWidth(); bool corner1Read = false; bool corner2Read = false; @@ -324,9 +323,8 @@ Ref BitMatrixParser::extractDataRegion(Ref bitMatrix) { int symbolSizeRows = parsedVersion_->getSymbolSizeRows(); int symbolSizeColumns = parsedVersion_->getSymbolSizeColumns(); - // TODO(bbrown): Make this work with rectangular codes - if ((int)bitMatrix->getDimension() != symbolSizeRows) { - throw IllegalArgumentException("Dimension of bitMarix must match the version size"); + if ((int)bitMatrix->getHeight() != symbolSizeRows) { + throw IllegalArgumentException("Dimension of bitMatrix must match the version size"); } int dataRegionSizeRows = parsedVersion_->getDataRegionSizeRows(); @@ -336,10 +334,9 @@ Ref BitMatrixParser::extractDataRegion(Ref bitMatrix) { int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns; int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows; - //int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; + int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; - // TODO(bbrown): Make this work with rectangular codes - Ref bitMatrixWithoutAlignment(new BitMatrix(sizeDataRegionRow)); + Ref bitMatrixWithoutAlignment(new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow)); for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) { int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows; for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) { diff --git a/cpp/core/src/zxing/datamatrix/decoder/DecodedBitStreamParser.cpp b/cpp/core/src/zxing/datamatrix/decoder/DecodedBitStreamParser.cpp index 5b96003f4..6df3a8bce 100644 --- a/cpp/core/src/zxing/datamatrix/decoder/DecodedBitStreamParser.cpp +++ b/cpp/core/src/zxing/datamatrix/decoder/DecodedBitStreamParser.cpp @@ -18,9 +18,10 @@ * limitations under the License. */ -#include +#include #include #include +#include namespace zxing { namespace datamatrix { @@ -28,10 +29,10 @@ namespace datamatrix { using namespace std; const char DecodedBitStreamParser::C40_BASIC_SET_CHARS[] = { - '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' - }; + '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' +}; const char DecodedBitStreamParser::C40_SHIFT2_SET_CHARS[] = { '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', @@ -42,362 +43,374 @@ const char DecodedBitStreamParser::TEXT_BASIC_SET_CHARS[] = { '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' - }; +}; const char DecodedBitStreamParser::TEXT_SHIFT3_SET_CHARS[] = { '\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127 }; -std::string DecodedBitStreamParser::decode(ArrayRef bytes) { - Ref bits(new BitSource(bytes)); - ostringstream result; - ostringstream resultTrailer; -// bool trailer = false; - int mode = ASCII_ENCODE; - do { - if (mode == ASCII_ENCODE) { - mode = decodeAsciiSegment(bits, result, resultTrailer); - } else { - switch (mode) { - case C40_ENCODE: - decodeC40Segment(bits, result); - break; - case TEXT_ENCODE: - decodeTextSegment(bits, result); - break; - case ANSIX12_ENCODE: - decodeAnsiX12Segment(bits, result); - break; - case EDIFACT_ENCODE: - decodeEdifactSegment(bits, result); - break; - case BASE256_ENCODE: - decodeBase256Segment(bits, result); - break; - default: - throw ReaderException("Unsupported mode indicator"); - } - mode = ASCII_ENCODE; +Ref DecodedBitStreamParser::decode(ArrayRef bytes) { + Ref bits(new BitSource(bytes)); + ostringstream result; + ostringstream resultTrailer; + vector byteSegments; + int mode = ASCII_ENCODE; + do { + if (mode == ASCII_ENCODE) { + mode = decodeAsciiSegment(bits, result, resultTrailer); + } else { + switch (mode) { + case C40_ENCODE: + decodeC40Segment(bits, result); + break; + case TEXT_ENCODE: + decodeTextSegment(bits, result); + break; + case ANSIX12_ENCODE: + decodeAnsiX12Segment(bits, result); + break; + case EDIFACT_ENCODE: + decodeEdifactSegment(bits, result); + break; + case BASE256_ENCODE: + decodeBase256Segment(bits, result, byteSegments); + break; + default: + throw FormatException("Unsupported mode indicator"); } - } while (mode != PAD_ENCODE && bits->available() > 0); -/* if (trailer) { - result << resultTrailer; + mode = ASCII_ENCODE; } -*/ - return result.str(); + } while (mode != PAD_ENCODE && bits->available() > 0); + + if (resultTrailer.str().size() > 0) { + result << resultTrailer.str(); + } + ArrayRef rawBytes(bytes); + Ref text(new String(result.str())); + return Ref(new DecoderResult(rawBytes, text)); } - + int DecodedBitStreamParser::decodeAsciiSegment(Ref bits, ostringstream & result, - ostringstream & resultTrailer) { - bool upperShift = false; - do { - int oneByte = bits->readBits(8); - if (oneByte == 0) { - throw ReaderException("Not enough bits to decode"); - } else if (oneByte <= 128) { // ASCII data (ASCII value + 1) - oneByte = upperShift ? (oneByte + 128) : oneByte; - // upperShift = false; - result << (char) (oneByte - 1); - return ASCII_ENCODE; - } else if (oneByte == 129) { // Pad - return PAD_ENCODE; - } else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130) - int value = oneByte - 130; - if (value < 10) { // padd with '0' for single digit values - result << '0'; - } - result << value; - } else if (oneByte == 230) { // Latch to C40 encodation - return C40_ENCODE; - } else if (oneByte == 231) { // Latch to Base 256 encodation - return BASE256_ENCODE; - } else if (oneByte == 232) { // FNC1 - //throw ReaderException.getInstance(); - // Ignore this symbol for now - } else if (oneByte == 233) { // Structured Append - //throw ReaderException.getInstance(); - // Ignore this symbol for now - } else if (oneByte == 234) { // Reader Programming - //throw ReaderException.getInstance(); - // Ignore this symbol for now - } else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII) - upperShift = true; - } else if (oneByte == 236) { // 05 Macro - /* trailer = false; - result << "[)>\u001E05\u001D"; - resultTrailer << "\u001E\u0004"; - // Ignore this symbol for now - */ } else if (oneByte == 237) { // 06 Macro - /* trailer = false; - result << "[)>\u001E06\u001D"; - resultTrailer << "\u001E\u0004"; - // Ignore this symbol for now - */ } else if (oneByte == 238) { // Latch to ANSI X12 encodation - return ANSIX12_ENCODE; - } else if (oneByte == 239) { // Latch to Text encodation - return TEXT_ENCODE; - } else if (oneByte == 240) { // Latch to EDIFACT encodation - return EDIFACT_ENCODE; - } else if (oneByte == 241) { // ECI Character - // TODO(bbrown): I think we need to support ECI - //throw ReaderException.getInstance(); - // Ignore this symbol for now - } else if (oneByte >= 242) { // Not to be used in ASCII encodation - throw ReaderException("Not to be used in ASCII encodation"); - } - } while (bits->available() > 0); - return ASCII_ENCODE; + ostringstream & resultTrailer) { + bool upperShift = false; + do { + int oneByte = bits->readBits(8); + if (oneByte == 0) { + throw FormatException("Not enough bits to decode"); + } else if (oneByte <= 128) { // ASCII data (ASCII value + 1) + oneByte = upperShift ? (oneByte + 128) : oneByte; + // upperShift = false; + result << (char) (oneByte - 1); + return ASCII_ENCODE; + } else if (oneByte == 129) { // Pad + return PAD_ENCODE; + } else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130) + int value = oneByte - 130; + if (value < 10) { // padd with '0' for single digit values + result << '0'; + } + result << value; + } else if (oneByte == 230) { // Latch to C40 encodation + return C40_ENCODE; + } else if (oneByte == 231) { // Latch to Base 256 encodation + return BASE256_ENCODE; + } else if (oneByte == 232) { // FNC1 + result << ((char) 29); // translate as ASCII 29 + } else if (oneByte == 233 || oneByte == 234) { + // Structured Append, Reader Programming + // Ignore these symbols for now + // throw FormatException.getInstance(); + } else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII) + upperShift = true; + } else if (oneByte == 236) { // 05 Macro + result << ("[)>RS05GS"); + resultTrailer << ("RSEOT"); + } else if (oneByte == 237) { // 06 Macro + result << ("[)>RS06GS"); + resultTrailer << ("RSEOT"); + } else if (oneByte == 238) { // Latch to ANSI X12 encodation + return ANSIX12_ENCODE; + } else if (oneByte == 239) { // Latch to Text encodation + return TEXT_ENCODE; + } else if (oneByte == 240) { // Latch to EDIFACT encodation + return EDIFACT_ENCODE; + } else if (oneByte == 241) { // ECI Character + // TODO(bbrown): I think we need to support ECI + // throw FormatException.getInstance(); + // Ignore this symbol for now + } else if (oneByte >= 242) { // Not to be used in ASCII encodation + // ... but work around encoders that end with 254, latch back to ASCII + if (oneByte == 254 && bits->available() == 0) { + // Ignore + } else { + throw FormatException("Not to be used in ASCII encodation"); + } + } + } while (bits->available() > 0); + return ASCII_ENCODE; } void DecodedBitStreamParser::decodeC40Segment(Ref bits, ostringstream & result) { - // Three C40 values are encoded in a 16-bit value as - // (1600 * C1) + (40 * C2) + C3 + 1 - // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time - bool upperShift = false; + // Three C40 values are encoded in a 16-bit value as + // (1600 * C1) + (40 * C2) + C3 + 1 + // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time + bool upperShift = false; - int* cValues = new int[3]; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits->available() == 8) { - return; - } - int firstByte = bits->readBits(8); - if (firstByte == 254) { // Unlatch codeword - return; - } + int* cValues = new int[3]; + int shift = 0; + do { + // If there is only one byte left then it will be encoded as ASCII + if (bits->available() == 8) { + return; + } + int firstByte = bits->readBits(8); + if (firstByte == 254) { // Unlatch codeword + return; + } - parseTwoBytes(firstByte, bits->readBits(8), cValues); + parseTwoBytes(firstByte, bits->readBits(8), cValues); - int shift = 0; - for (int i = 0; i < 3; i++) { - int cValue = cValues[i]; - switch (shift) { - case 0: - if (cValue < 3) { - shift = cValue + 1; - } else { - if (upperShift) { - result << (char) (C40_BASIC_SET_CHARS[cValue] + 128); - upperShift = false; - } else { - result << C40_BASIC_SET_CHARS[cValue]; - } - } - break; - case 1: + for (int i = 0; i < 3; i++) { + int cValue = cValues[i]; + switch (shift) { + case 0: + if (cValue < 3) { + shift = cValue + 1; + } else { if (upperShift) { - result << cValue + 128; + result << (char) (C40_BASIC_SET_CHARS[cValue] + 128); upperShift = false; } else { - result << cValue; + result << C40_BASIC_SET_CHARS[cValue]; } - shift = 0; - break; - case 2: - if (cValue < 27) { - if (upperShift) { - result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128); - upperShift = false; - } else { - result << C40_SHIFT2_SET_CHARS[cValue]; - } - } else if (cValue == 27) { // FNC1 - throw ReaderException("FNC1"); - } else if (cValue == 30) { // Upper Shift - upperShift = true; - } else { - throw ReaderException("Upper Shift"); - } - shift = 0; - break; - case 3: + } + break; + case 1: + if (upperShift) { + result << (char) (cValue + 128); + upperShift = false; + } else { + result << (char) cValue; + } + shift = 0; + break; + case 2: + if (cValue < 27) { if (upperShift) { - result << (char) (cValue + 224); + result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128); upperShift = false; } else { - result << (char) (cValue + 96); + result << C40_SHIFT2_SET_CHARS[cValue]; } - shift = 0; - break; - default: - throw ReaderException(""); - } + } else if (cValue == 27) { // FNC1 + result << ((char) 29); // translate as ASCII 29 + } else if (cValue == 30) { // Upper Shift + 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 bits, ostringstream & result) { - // Three Text values are encoded in a 16-bit value as - // (1600 * C1) + (40 * C2) + C3 + 1 - // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time - bool upperShift = false; + // Three Text values are encoded in a 16-bit value as + // (1600 * C1) + (40 * C2) + C3 + 1 + // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time + bool upperShift = false; - int* cValues = new int[3]; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits->available() == 8) { - return; - } - int firstByte = bits->readBits(8); - if (firstByte == 254) { // Unlatch codeword - return; - } + int* cValues = new int[3]; + int shift = 0; + do { + // If there is only one byte left then it will be encoded as ASCII + if (bits->available() == 8) { + return; + } + int firstByte = bits->readBits(8); + if (firstByte == 254) { // Unlatch codeword + return; + } - parseTwoBytes(firstByte, bits->readBits(8), cValues); + parseTwoBytes(firstByte, bits->readBits(8), cValues); - int shift = 0; - for (int i = 0; i < 3; i++) { - int cValue = cValues[i]; - switch (shift) { - case 0: - if (cValue < 3) { - shift = cValue + 1; - } else { - if (upperShift) { - result << (char) (TEXT_BASIC_SET_CHARS[cValue] + 128); - upperShift = false; - } else { - result << (TEXT_BASIC_SET_CHARS[cValue]); - } - } - break; - case 1: + for (int i = 0; i < 3; i++) { + int cValue = cValues[i]; + switch (shift) { + case 0: + if (cValue < 3) { + shift = cValue + 1; + } else { if (upperShift) { - result << (char) (cValue + 128); + result << (char) (TEXT_BASIC_SET_CHARS[cValue] + 128); upperShift = false; } else { - result << (cValue); + result << (TEXT_BASIC_SET_CHARS[cValue]); } - shift = 0; - break; - case 2: - // Shift 2 for Text is the same encoding as C40 - if (cValue < 27) { - if (upperShift) { - result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128); - upperShift = false; - } else { - result << (C40_SHIFT2_SET_CHARS[cValue]); - } - } else if (cValue == 27) { // FNC1 - throw ReaderException("FNC1"); - } else if (cValue == 30) { // Upper Shift - upperShift = true; - } else { - throw ReaderException("Upper Shift"); - } - shift = 0; - break; - case 3: + } + break; + case 1: + if (upperShift) { + result << (char) (cValue + 128); + upperShift = false; + } else { + result << (char) (cValue); + } + shift = 0; + break; + case 2: + // Shift 2 for Text is the same encoding as C40 + if (cValue < 27) { if (upperShift) { - result << (char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128); + result << (char) (C40_SHIFT2_SET_CHARS[cValue] + 128); upperShift = false; } else { - result << (TEXT_SHIFT3_SET_CHARS[cValue]); + result << (C40_SHIFT2_SET_CHARS[cValue]); } - shift = 0; - break; - default: - throw ReaderException(""); - } + } else if (cValue == 27) { // FNC1 + result << ((char) 29); // translate as ASCII 29 + } else if (cValue == 30) { // Upper Shift + 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 bits, ostringstream & result) { - // Three ANSI X12 values are encoded in a 16-bit value as - // (1600 * C1) + (40 * C2) + C3 + 1 + // Three ANSI X12 values are encoded in a 16-bit value as + // (1600 * C1) + (40 * C2) + C3 + 1 - int* cValues = new int[3]; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits->available() == 8) { - return; - } - int firstByte = bits->readBits(8); - if (firstByte == 254) { // Unlatch codeword - return; - } + int* cValues = new int[3]; + do { + // If there is only one byte left then it will be encoded as ASCII + if (bits->available() == 8) { + return; + } + int firstByte = bits->readBits(8); + if (firstByte == 254) { // Unlatch codeword + return; + } - parseTwoBytes(firstByte, bits->readBits(8), cValues); + parseTwoBytes(firstByte, bits->readBits(8), cValues); - for (int i = 0; i < 3; i++) { - int cValue = cValues[i]; - if (cValue == 0) { // X12 segment terminator - result << '\r'; - } else if (cValue == 1) { // X12 segment separator * - result << '*'; - } else if (cValue == 2) { // X12 sub-element separator > - result << '>'; - } else if (cValue == 3) { // space - result << ' '; - } else if (cValue < 14) { // 0 - 9 - result << (char) (cValue + 44); - } else if (cValue < 40) { // A - Z - result << (char) (cValue + 51); - } else { - throw ReaderException(""); - } + for (int i = 0; i < 3; i++) { + int cValue = cValues[i]; + if (cValue == 0) { // X12 segment terminator + result << '\r'; + } else if (cValue == 1) { // X12 segment separator * + result << '*'; + } else if (cValue == 2) { // X12 sub-element separator > + result << '>'; + } else if (cValue == 3) { // space + result << ' '; + } else if (cValue < 14) { // 0 - 9 + result << (char) (cValue + 44); + } else if (cValue < 40) { // A - Z + result << (char) (cValue + 51); + } else { + throw FormatException("decodeAnsiX12Segment: no case"); } - } while (bits->available() > 0); + } + } while (bits->available() > 0); } void DecodedBitStreamParser::parseTwoBytes(int firstByte, int secondByte, int*& result) { - int fullBitValue = (firstByte << 8) + secondByte - 1; - int temp = fullBitValue / 1600; - result[0] = temp; - fullBitValue -= temp * 1600; - temp = fullBitValue / 40; - result[1] = temp; - result[2] = fullBitValue - temp * 40; + int fullBitValue = (firstByte << 8) + secondByte - 1; + int temp = fullBitValue / 1600; + result[0] = temp; + fullBitValue -= temp * 1600; + temp = fullBitValue / 40; + result[1] = temp; + result[2] = fullBitValue - temp * 40; } void DecodedBitStreamParser::decodeEdifactSegment(Ref bits, ostringstream & result) { - bool unlatch = false; - do { - // If there is only two or less bytes left then it will be encoded as ASCII - if (bits->available() <= 16) { - return; + bool unlatch = false; + do { + // If there is only two or less bytes left then it will be encoded as ASCII + if (bits->available() <= 16) { + return; + } + + for (int i = 0; i < 4; i++) { + int edifactValue = bits->readBits(6); + + // Check for the unlatch character + if (edifactValue == 0x2B67) { // 011111 + unlatch = true; + // If we encounter the unlatch code then continue reading because the Codeword triple + // is padded with 0's } - for (int i = 0; i < 4; i++) { - int edifactValue = bits->readBits(6); - - // Check for the unlatch character - if (edifactValue == 0x2B67) { // 011111 - unlatch = true; - // If we encounter the unlatch code then continue reading because the Codeword triple - // is padded with 0's - } - - if (!unlatch) { - if ((edifactValue & 32) == 0) { // no 1 in the leading (6th) bit - edifactValue |= 64; // Add a leading 01 to the 6 bit binary value - } - result << (edifactValue); + if (!unlatch) { + if ((edifactValue & 0x20) == 0) { // no 1 in the leading (6th) bit + edifactValue |= 0x40; // Add a leading 01 to the 6 bit binary value } + result << (char)(edifactValue); } - } while (!unlatch && bits->available() > 0); + } + } while (!unlatch && bits->available() > 0); } -void DecodedBitStreamParser::decodeBase256Segment(Ref bits, ostringstream & result){//, vector byteSegments) - // Figure out how long the Base 256 Segment is. - int d1 = bits->readBits(8); - int count; - if (d1 == 0) { // Read the remainder of the symbol - count = bits->available() / 8; - } else if (d1 < 250) { - count = d1; - } else { - count = 250 * (d1 - 249) + bits->readBits(8); +void DecodedBitStreamParser::decodeBase256Segment(Ref bits, ostringstream& result, vector byteSegments) { + // Figure out how long the Base 256 Segment is. + int codewordPosition = 1 + bits->getByteOffset(); // position is 1-indexed + int d1 = unrandomize255State(bits->readBits(8), codewordPosition++); + int count; + if (d1 == 0) { // Read the remainder of the symbol + count = bits->available() / 8; + } else if (d1 < 250) { + count = d1; + } else { + count = 250 * (d1 - 249) + 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]; - for (int i = 0; i < count; i++) { - bytes[i] = unrandomize255State(bits->readBits(8), i); - } - //byteSegments.push_back(bytes); - result << bytes; + bytes[i] = unrandomize255State(bits->readBits(8), codewordPosition++); + byteSegments.push_back(bytes[i]); + result << (char)bytes[i]; + } } } } diff --git a/cpp/core/src/zxing/datamatrix/decoder/DecodedBitStreamParser.h b/cpp/core/src/zxing/datamatrix/decoder/DecodedBitStreamParser.h index 6a0b26030..20414c084 100644 --- a/cpp/core/src/zxing/datamatrix/decoder/DecodedBitStreamParser.h +++ b/cpp/core/src/zxing/datamatrix/decoder/DecodedBitStreamParser.h @@ -23,9 +23,10 @@ #include #include +#include #include #include -#include +#include namespace zxing { @@ -78,7 +79,7 @@ private: /** * See ISO 16022:2006, 5.2.9 and Annex B, B.2 */ - void decodeBase256Segment(Ref bits, std::ostringstream &result);//,std::vector byteSegments); + void decodeBase256Segment(Ref bits, std::ostringstream &result, std::vector byteSegments); void parseTwoBytes(int firstByte, int secondByte, int*& result); /** @@ -94,7 +95,7 @@ private: public: DecodedBitStreamParser() { }; - std::string decode(ArrayRef bytes); + Ref decode(ArrayRef bytes); }; } diff --git a/cpp/core/src/zxing/datamatrix/decoder/Decoder.cpp b/cpp/core/src/zxing/datamatrix/decoder/Decoder.cpp index f841d3a95..6fc042156 100644 --- a/cpp/core/src/zxing/datamatrix/decoder/Decoder.cpp +++ b/cpp/core/src/zxing/datamatrix/decoder/Decoder.cpp @@ -43,54 +43,52 @@ void Decoder::correctErrors(ArrayRef codewordBytes, int numDataCo codewordInts[i] = codewordBytes[i] & 0xff; } int numECCodewords = numCodewords - numDataCodewords; - try { rsDecoder_.decode(codewordInts, numECCodewords); } catch (ReedSolomonException const& ex) { ReaderException rex(ex.what()); 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++) { codewordBytes[i] = (unsigned char)codewordInts[i]; } } Ref Decoder::decode(Ref bits) { - // Construct a parser and read version, error-correction level - BitMatrixParser parser(bits); - Version *version = parser.readVersion(bits); + // Construct a parser and read version, error-correction level + BitMatrixParser parser(bits); + Version *version = parser.readVersion(bits); - // Read codewords - ArrayRef codewords(parser.readCodewords()); - // Separate into data blocks - std::vector > dataBlocks = DataBlock::getDataBlocks(codewords, version); + // Read codewords + ArrayRef codewords(parser.readCodewords()); + // Separate into data blocks + std::vector > dataBlocks = DataBlock::getDataBlocks(codewords, version); - // Count total number of data bytes - int totalBytes = 0; - for (unsigned int i = 0; i < dataBlocks.size(); i++) { - totalBytes += dataBlocks[i]->getNumDataCodewords(); + int dataBlocksCount = dataBlocks.size(); + + // Count total number of data bytes + int totalBytes = 0; + for (int i = 0; i < dataBlocksCount; i++) { + totalBytes += dataBlocks[i]->getNumDataCodewords(); + } + ArrayRef resultBytes(totalBytes); + + // Error-correct and copy data blocks together into a stream of bytes + for (int j = 0; j < dataBlocksCount; j++) { + Ref dataBlock(dataBlocks[j]); + ArrayRef 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 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(dataBlocks[j]); - ArrayRef codewordBytes = dataBlock->getCodewords(); - int numDataCodewords = dataBlock->getNumDataCodewords(); - correctErrors(codewordBytes, numDataCodewords); - for (int i = 0; i < numDataCodewords; i++) { - resultBytes[resultOffset++] = codewordBytes[i]; - } - } - + } // Decode the contents of that stream of bytes DecodedBitStreamParser decodedBSParser; - Ref text(new String(decodedBSParser.decode(resultBytes))); - - Ref result(new DecoderResult(resultBytes, text)); - return result; + return Ref (decodedBSParser.decode(resultBytes)); } } } diff --git a/cpp/core/src/zxing/datamatrix/detector/Detector.cpp b/cpp/core/src/zxing/datamatrix/detector/Detector.cpp index b46100964..72d9ebf9a 100644 --- a/cpp/core/src/zxing/datamatrix/detector/Detector.cpp +++ b/cpp/core/src/zxing/datamatrix/detector/Detector.cpp @@ -19,6 +19,7 @@ * limitations under the License. */ +#include #include #include #include @@ -30,287 +31,404 @@ namespace datamatrix { using namespace std; -ResultPointsAndTransitions::ResultPointsAndTransitions() : to_(), from_(), transitions_(0) { - Ref ref(new CornerPoint(0,0)); +ResultPointsAndTransitions::ResultPointsAndTransitions() { + Ref ref(new ResultPoint(0, 0)); from_ = ref; to_ = ref; + transitions_ = 0; } -ResultPointsAndTransitions::ResultPointsAndTransitions(Ref from, Ref to, int transitions) : - to_(to), from_(from), transitions_(transitions) { +ResultPointsAndTransitions::ResultPointsAndTransitions(Ref from, Ref to, + int transitions) + : to_(to), from_(from), transitions_(transitions) { } -Ref ResultPointsAndTransitions::getFrom() { - return from_; +Ref ResultPointsAndTransitions::getFrom() { + return from_; } -Ref ResultPointsAndTransitions::getTo() { - return to_; +Ref ResultPointsAndTransitions::getTo() { + return to_; } int ResultPointsAndTransitions::getTransitions() { - return transitions_; + return transitions_; } -Detector::Detector(Ref image) : image_(image) { } +Detector::Detector(Ref image) + : image_(image) { +} Ref Detector::getImage() { - return image_; + return image_; } Ref Detector::detect() { - Ref rectangleDetector_(new MonochromeRectangleDetector(image_)); - std::vector > cornerPoints = rectangleDetector_->detect(); - Ref pointA = cornerPoints[0]; - Ref pointB = cornerPoints[1]; - Ref pointC = cornerPoints[2]; - Ref pointD = cornerPoints[3]; + Ref rectangleDetector_(new WhiteRectangleDetector(image_)); + std::vector > ResultPoints = rectangleDetector_->detect(); + Ref pointA = ResultPoints[0]; + Ref pointB = ResultPoints[1]; + Ref pointC = ResultPoints[2]; + Ref pointD = ResultPoints[3]; - // Point A and D are across the diagonal from one another, - // as are B and C. Figure out which are the solid black lines - // by counting transitions - std::vector > transitions(4); - transitions[0].reset(transitionsBetween(pointA, pointB)); - transitions[1].reset(transitionsBetween(pointA, pointC)); - transitions[2].reset(transitionsBetween(pointB, pointD)); - transitions[3].reset(transitionsBetween(pointC, pointD)); - insertionSort(transitions); + // Point A and D are across the diagonal from one another, + // as are B and C. Figure out which are the solid black lines + // by counting transitions + std::vector > transitions(4); + transitions[0].reset(transitionsBetween(pointA, pointB)); + transitions[1].reset(transitionsBetween(pointA, pointC)); + transitions[2].reset(transitionsBetween(pointB, pointD)); + transitions[3].reset(transitionsBetween(pointC, pointD)); + insertionSort(transitions); - // Sort by number of transitions. First two will be the two solid sides; last two - // will be the two alternating black/white sides - Ref lSideOne(transitions[0]); - Ref lSideTwo(transitions[1]); + // Sort by number of transitions. First two will be the two solid sides; last two + // will be the two alternating black/white sides + Ref lSideOne(transitions[0]); + Ref lSideTwo(transitions[1]); - // Figure out which point is their intersection by tallying up the number of times we see the - // endpoints in the four endpoints. One will show up twice. - Ref maybeTopLeft; - Ref bottomLeft; - Ref maybeBottomRight; + // Figure out which point is their intersection by tallying up the number of times we see the + // endpoints in the four endpoints. One will show up twice. + Ref maybeTopLeft; + Ref bottomLeft; + Ref maybeBottomRight; if (lSideOne->getFrom()->equals(lSideOne->getTo())) { bottomLeft = lSideOne->getFrom(); maybeTopLeft = lSideTwo->getFrom(); maybeBottomRight = lSideTwo->getTo(); - } - else if (lSideOne->getFrom()->equals(lSideTwo->getFrom())) { + } else if (lSideOne->getFrom()->equals(lSideTwo->getFrom())) { bottomLeft = lSideOne->getFrom(); maybeTopLeft = lSideOne->getTo(); maybeBottomRight = lSideTwo->getTo(); - } - else if (lSideOne->getFrom()->equals(lSideTwo->getTo())) { + } else if (lSideOne->getFrom()->equals(lSideTwo->getTo())) { bottomLeft = lSideOne->getFrom(); maybeTopLeft = lSideOne->getTo(); maybeBottomRight = lSideTwo->getFrom(); - } - else if (lSideOne->getTo()->equals(lSideTwo->getFrom())) { + } else if (lSideOne->getTo()->equals(lSideTwo->getFrom())) { bottomLeft = lSideOne->getTo(); maybeTopLeft = lSideOne->getFrom(); maybeBottomRight = lSideTwo->getTo(); - } - else if (lSideOne->getTo()->equals(lSideTwo->getTo())) { + } else if (lSideOne->getTo()->equals(lSideTwo->getTo())) { bottomLeft = lSideOne->getTo(); maybeTopLeft = lSideOne->getFrom(); maybeBottomRight = lSideTwo->getFrom(); - } - else { + } else { bottomLeft = lSideTwo->getFrom(); maybeTopLeft = lSideOne->getTo(); maybeBottomRight = lSideOne->getFrom(); } - // Bottom left is correct but top left and bottom right might be switched - std::vector > corners(3); - corners[0].reset(maybeTopLeft); - corners[1].reset(bottomLeft); - corners[2].reset(maybeBottomRight); - // Use the dot product trick to sort them out - orderBestPatterns(corners); + // Bottom left is correct but top left and bottom right might be switched + std::vector > corners(3); + corners[0].reset(maybeTopLeft); + corners[1].reset(bottomLeft); + corners[2].reset(maybeBottomRight); - // Now we know which is which: - Ref bottomRight(corners[0]); - bottomLeft = corners[1]; - Ref topLeft(corners[2]); + // Use the dot product trick to sort them out + ResultPoint::orderBestPatterns(corners); - // Which point didn't we find in relation to the "L" sides? that's the top right corner - Ref topRight; - if (!(pointA->equals(bottomRight) || pointA->equals(bottomLeft) || pointA->equals(topLeft))) { - topRight = pointA; - } else if (!(pointB->equals(bottomRight) || pointB->equals(bottomLeft) || pointB->equals(topLeft))) { - topRight = pointB; - } else if (!(pointC->equals(bottomRight) || pointC->equals(bottomLeft) || pointC->equals(topLeft))) { - topRight = pointC; - } else { - topRight = pointD; - } + // Now we know which is which: + Ref bottomRight(corners[0]); + bottomLeft = corners[1]; + Ref topLeft(corners[2]); - float topRightX = (bottomRight->getX() - bottomLeft->getX()) + topLeft->getX(); - float topRightY = (bottomRight->getY() - bottomLeft->getY()) + topLeft->getY(); - Ref topR(new CornerPoint(topRightX,topRightY)); - - // Next determine the dimension by tracing along the top or right side and counting black/white - // transitions. Since we start inside a black module, we should see a number of transitions - // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to - // end on a black module: - // The top right point is actually the corner of a module, which is one of the two black modules - // adjacent to the white module at the top right. Tracing to that corner from either the top left - // or bottom right should work here. The number of transitions could be higher than it should be - // due to noise. So we try both and take the min. - int dimension = min(transitionsBetween(topLeft, topRight)->getTransitions(), - transitionsBetween(bottomRight, topRight)->getTransitions()); - if ((dimension & 0x01) == 1) { - // it can't be odd, so, round... up? - dimension++; - } - dimension += 2; - - Ref transform = createTransform(topLeft, topR, bottomLeft, bottomRight, dimension); - Ref bits(sampleGrid(image_, dimension, transform)); - std::vector > points(4); - points[0].reset(pointA); - points[1].reset(pointB); - points[2].reset(pointC); - points[3].reset(pointD); - Ref detectorResult(new DetectorResult(bits, points, transform)); - return detectorResult; -} - -Ref Detector::transitionsBetween(Ref from, Ref 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 result(new ResultPointsAndTransitions(from, to, transitions)); - return result; + // Which point didn't we find in relation to the "L" sides? that's the top right corner + Ref topRight; + if (!(pointA->equals(bottomRight) || pointA->equals(bottomLeft) || pointA->equals(topLeft))) { + topRight = pointA; + } else if (!(pointB->equals(bottomRight) || pointB->equals(bottomLeft) + || pointB->equals(topLeft))) { + topRight = pointB; + } else if (!(pointC->equals(bottomRight) || pointC->equals(bottomLeft) + || pointC->equals(topLeft))) { + topRight = pointC; + } else { + topRight = pointD; } -Ref Detector::createTransform(Ref topLeft, Ref topRight, Ref < - ResultPoint > bottomLeft, Ref bottomRight, int dimension) { + // Next determine the dimension by tracing along the top or right side and counting black/white + // transitions. Since we start inside a black module, we should see a number of transitions + // equal to 1 less than the code dimension. Well, actually 2 less, because we are going to + // end on a black module: - Ref transform(PerspectiveTransform::quadrilateralToQuadrilateral( - 0.0f, - 0.0f, - dimension, - 0.0f, - dimension, - dimension, - 0.0f, - dimension, - topLeft->getX(), - topLeft->getY(), - topRight->getX(), - topRight->getY(), - bottomRight->getX(), - bottomRight->getY(), - bottomLeft->getX(), - bottomLeft->getY())); + // The top right point is actually the corner of a module, which is one of the two black modules + // adjacent to the white module at the top right. Tracing to that corner from either the top left + // or bottom right should work here. + + int dimensionTop = transitionsBetween(topLeft, topRight)->getTransitions(); + int dimensionRight = transitionsBetween(bottomRight, topRight)->getTransitions(); + + //dimensionTop++; + if ((dimensionTop & 0x01) == 1) { + // it can't be odd, so, round... up? + dimensionTop++; + } + dimensionTop += 2; + + //dimensionRight++; + if ((dimensionRight & 0x01) == 1) { + // it can't be odd, so, round... up? + dimensionRight++; + } + dimensionRight += 2; + + Ref bits; + Ref transform; + Ref 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 > points(4); + points[0].reset(topLeft); + points[1].reset(bottomLeft); + points[2].reset(correctedTopRight); + points[3].reset(bottomRight); + Ref 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 Detector::correctTopRightRectangular(Ref bottomLeft, + Ref bottomRight, Ref topLeft, Ref 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 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 c2( + new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin)); + + if (!isValid(c1)) { + if (isValid(c2)) { + return c2; + } + return Ref(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 Detector::correctTopRight(Ref bottomLeft, + Ref bottomRight, Ref topLeft, Ref 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 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 c2( + new ResultPoint(topRight->getX() + corr * cos, topRight->getY() + corr * sin)); + + if (!isValid(c1)) { + if (isValid(c2)) { + return c2; + } + return Ref(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 p) { + return p->getX() >= 0 && p->getX() < image_->getWidth() && p->getY() > 0 + && p->getY() < image_->getHeight(); +} + +// L2 distance +int Detector::distance(Ref a, Ref b) { + return round( + (float) sqrt( + (double) (a->getX() - b->getX()) * (a->getX() - b->getX()) + + (a->getY() - b->getY()) * (a->getY() - b->getY()))); +} + +Ref Detector::transitionsBetween(Ref from, + Ref 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 result(new ResultPointsAndTransitions(from, to, transitions)); + return result; +} + +Ref Detector::createTransform(Ref topLeft, + Ref topRight, Ref bottomLeft, Ref bottomRight, + int dimensionX, int dimensionY) { + + Ref 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; } -Ref Detector::sampleGrid(Ref image, int dimension, Ref transform) { +Ref Detector::sampleGrid(Ref image, int dimensionX, int dimensionY, + Ref transform) { GridSampler &sampler = GridSampler::getInstance(); - return sampler.sampleGrid(image, dimension, transform); + return sampler.sampleGrid(image, dimensionX, dimensionY, transform); } void Detector::insertionSort(std::vector > &vector) { - int max = vector.size(); + int max = vector.size(); bool swapped = true; - Ref value; - Ref valueB; + Ref value; + Ref valueB; do { swapped = false; - for (int i = 1; i < max; i++) { - value = vector[i-1]; - if (compare(value, (valueB = vector[i])) > 0) { - swapped = true; - vector[i-1].reset(valueB); + for (int i = 1; i < max; i++) { + value = vector[i - 1]; + if (compare(value, (valueB = vector[i])) > 0){ + swapped = true; + vector[i - 1].reset(valueB); vector[i].reset(value); } } } while (swapped); } -void Detector::orderBestPatterns(std::vector > &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 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 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 a, Ref b) { - return a->getTransitions() - b->getTransitions(); - } - -float Detector::crossProductZ(Ref pointA, Ref pointB, Ref pointC) { - float bX = pointB->getX(); - float bY = pointB->getY(); - return ((pointC->getX() - bX) * (pointA->getY() - bY)) - ((pointC->getY() - bY) * (pointA->getX() - bX)); - } + return a->getTransitions() - b->getTransitions(); +} } } diff --git a/cpp/core/src/zxing/datamatrix/detector/Detector.h b/cpp/core/src/zxing/datamatrix/detector/Detector.h index 4258d5c86..8e0bf063b 100644 --- a/cpp/core/src/zxing/datamatrix/detector/Detector.h +++ b/cpp/core/src/zxing/datamatrix/detector/Detector.h @@ -22,56 +22,70 @@ * limitations under the License. */ - #include #include #include #include -#include - +#include namespace zxing { namespace datamatrix { -class ResultPointsAndTransitions : public Counted { -private: - Ref to_; - Ref from_; - int transitions_; +class ResultPointsAndTransitions: public Counted { + private: + Ref to_; + Ref from_; + int transitions_; -public: - ResultPointsAndTransitions(); - ResultPointsAndTransitions(Ref from, Ref to, int transitions); - Ref getFrom(); - Ref getTo(); - int getTransitions(); + public: + ResultPointsAndTransitions(); + ResultPointsAndTransitions(Ref from, Ref to, int transitions); + Ref getFrom(); + Ref getTo(); + int getTransitions(); }; -class Detector : public Counted { -private: - Ref image_; +class Detector: public Counted { + private: + Ref image_; -protected: - Ref sampleGrid(Ref image, int dimension, Ref transform); + protected: + Ref sampleGrid(Ref image, int dimensionX, int dimensionY, + Ref transform); - void insertionSort(std::vector >& vector); + void insertionSort(std::vector >& vector); - Ref transitionsBetween(Ref from, Ref to); - int min(int a, int b) { return a > b ? b : a; }; + Ref correctTopRightRectangular(Ref bottomLeft, + Ref bottomRight, Ref topLeft, Ref topRight, + int dimensionTop, int dimensionRight); + Ref correctTopRight(Ref bottomLeft, Ref bottomRight, + Ref topLeft, Ref topRight, int dimension); + bool isValid(Ref p); + int distance(Ref a, Ref b); + Ref transitionsBetween(Ref from, Ref 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: - Ref getImage(); - Detector(Ref image); + public: + Ref getImage(); + Detector(Ref image); - virtual Ref createTransform(Ref topLeft, Ref topRight, Ref < - ResultPoint > bottomLeft, Ref bottomRight, int dimension); + virtual Ref createTransform(Ref topLeft, + Ref topRight, Ref bottomLeft, Ref bottomRight, + int dimensionX, int dimensionY); - Ref detect(); - void orderBestPatterns(std::vector > &patterns); - float distance(float x1, float x2, float y1, float y2); -private: - int compare(Ref a, Ref b); - float crossProductZ(Ref pointA, Ref pointB, Ref pointC); + Ref detect(); + + private: + int compare(Ref a, Ref b); }; } diff --git a/cpp/core/src/zxing/datamatrix/detector/DetectorException.cpp b/cpp/core/src/zxing/datamatrix/detector/DetectorException.cpp new file mode 100644 index 000000000..a1ba77bb5 --- /dev/null +++ b/cpp/core/src/zxing/datamatrix/detector/DetectorException.cpp @@ -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 */ diff --git a/cpp/core/src/zxing/datamatrix/detector/DetectorException.h b/cpp/core/src/zxing/datamatrix/detector/DetectorException.h new file mode 100644 index 000000000..8002ac9d1 --- /dev/null +++ b/cpp/core/src/zxing/datamatrix/detector/DetectorException.h @@ -0,0 +1,23 @@ +/* + * DetectorException.h + * + * Created on: Aug 26, 2011 + * Author: luiz + */ + +#ifndef DETECTOREXCEPTION_H_ +#define DETECTOREXCEPTION_H_ + +#include + +namespace zxing { +namespace datamatrix { + +class DetectorException : public Exception { + public: + DetectorException(const char *msg); + virtual ~DetectorException() throw(); +}; +} /* namespace nexxera */ +} /* namespace zxing */ +#endif /* DETECTOREXCEPTION_H_ */ diff --git a/cpp/magick/src/main.cpp b/cpp/magick/src/main.cpp index 403f0faca..d3b6da8a1 100644 --- a/cpp/magick/src/main.cpp +++ b/cpp/magick/src/main.cpp @@ -55,7 +55,7 @@ static bool tryHarder = false; static bool show_filename = false; static bool search_multi = false; -static const int MAX_EXPECTED = 1024; +static const int MAX_EXPECTED = 4096; Ref decode(Ref image, DecodeHints hints) { Ref reader(new MultiFormatReader);