Port multi reader from Java. Closes Issue 1027.

I tweaked things a little, small formatting (to help me diff) and
made some of the protection levels match the Java code. Nothing
semantic.

Great work. Thanks!

git-svn-id: https://zxing.googlecode.com/svn/trunk@1986 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
smparkes@smparkes.net 2011-10-20 19:11:33 +00:00
parent 0e6aad1092
commit 709f7f060a
31 changed files with 983 additions and 113 deletions

View file

@ -1,3 +1,5 @@
# -*- python -*-
Decider('MD5')
vars = Variables()
@ -20,7 +22,7 @@ if env['PIC']:
compile_options['CXXFLAGS'] = ' '.join(flags)
compile_options['LINKFLAGS'] = "-ldl"
def all_files(dir, ext='.cpp', level=5):
def all_files(dir, ext='.cpp', level=6):
files = []
for i in range(1, level):
files += Glob(dir + ('/*' * i) + ext)

View file

@ -22,6 +22,18 @@
namespace zxing {
ResultPoint::ResultPoint() : posX_(0), posY_(0) {}
ResultPoint::ResultPoint(float x, float y) : posX_(x), posY_(y) {}
ResultPoint::~ResultPoint() {}
float ResultPoint::getX() const {
return posX_;
}
float ResultPoint::getY() const {
return posY_;
}
}

View file

@ -1,3 +1,4 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
#ifndef __RESULT_POINT_H__
#define __RESULT_POINT_H__
@ -26,12 +27,16 @@ namespace zxing {
class ResultPoint : public Counted {
protected:
ResultPoint() {}
float posX_;
float posY_;
public:
ResultPoint();
ResultPoint(float x, float y);
virtual ~ResultPoint();
virtual float getX() const = 0;
virtual float getY() const = 0;
virtual float getX() const;
virtual float getY() const;
};
}

View file

@ -27,15 +27,7 @@ namespace zxing {
using namespace std;
CornerPoint::CornerPoint(float posX, float posY) :
posX_(posX), posY_(posY), counter_(0) {
}
float CornerPoint::getX() const {
return posX_;
}
float CornerPoint::getY() const {
return posY_;
ResultPoint(posX,posY), counter_(0) {
}
int CornerPoint::getCount() const {

View file

@ -29,14 +29,10 @@ namespace zxing {
class CornerPoint : public ResultPoint {
private:
float posX_;
float posY_;
int counter_;
public:
CornerPoint(float posX, float posY);
float getX() const;
float getY() const;
int getCount() const;
void incrementCount();
bool equals(Ref<CornerPoint> other) const;

View file

@ -0,0 +1,71 @@
/*
* Copyright 2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/multi/ByQuadrantReader.h>
#include <zxing/ReaderException.h>
namespace zxing {
namespace multi {
ByQuadrantReader::ByQuadrantReader(Reader& delegate) : delegate_(delegate) {}
ByQuadrantReader::~ByQuadrantReader(){}
Ref<Result> ByQuadrantReader::decode(Ref<BinaryBitmap> image){
return decode(image, DecodeHints::DEFAULT_HINT);
}
Ref<Result> ByQuadrantReader::decode(Ref<BinaryBitmap> image, DecodeHints hints){
int width = image->getWidth();
int height = image->getHeight();
int halfWidth = width / 2;
int halfHeight = height / 2;
Ref<BinaryBitmap> topLeft = image->crop(0, 0, halfWidth, halfHeight);
try {
return delegate_.decode(topLeft, hints);
} catch (ReaderException re) {
// continue
}
Ref<BinaryBitmap> topRight = image->crop(halfWidth, 0, halfWidth, halfHeight);
try {
return delegate_.decode(topRight, hints);
} catch (ReaderException re) {
// continue
}
Ref<BinaryBitmap> bottomLeft = image->crop(0, halfHeight, halfWidth, halfHeight);
try {
return delegate_.decode(bottomLeft, hints);
} catch (ReaderException re) {
// continue
}
Ref<BinaryBitmap> bottomRight = image->crop(halfWidth, halfHeight, halfWidth, halfHeight);
try {
return delegate_.decode(bottomRight, hints);
} catch (ReaderException re) {
// continue
}
int quarterWidth = halfWidth / 2;
int quarterHeight = halfHeight / 2;
Ref<BinaryBitmap> center = image->crop(quarterWidth, quarterHeight, halfWidth, halfHeight);
return delegate_.decode(center, hints);
}
} // End zxing::multi namespace
} // End zxing namespace

View file

@ -0,0 +1,40 @@
#ifndef __BY_QUADRANT_READER_H__
#define __BY_QUADRANT_READER_H__
/*
* Copyright 2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/Reader.h>
#include <zxing/BinaryBitmap.h>
#include <zxing/Result.h>
#include <zxing/DecodeHints.h>
namespace zxing {
namespace multi {
class ByQuadrantReader : public Reader {
private:
Reader& delegate_;
public:
ByQuadrantReader(Reader& delegate);
virtual ~ByQuadrantReader();
virtual Ref<Result> decode(Ref<BinaryBitmap> image);
virtual Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints);
};
} // End zxing::multi namespace
} // End zxing namespace
#endif // __BY_QUADRANT_READER_H__

View file

@ -0,0 +1,128 @@
/*
* Copyright 2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/multi/GenericMultipleBarcodeReader.h>
#include <zxing/ReaderException.h>
#include <zxing/ResultPoint.h>
namespace zxing {
namespace multi {
GenericMultipleBarcodeReader::GenericMultipleBarcodeReader(Reader& delegate) :
delegate_(delegate)
{
}
GenericMultipleBarcodeReader::~GenericMultipleBarcodeReader(){}
std::vector<Ref<Result> > GenericMultipleBarcodeReader::decodeMultiple(
Ref<BinaryBitmap> image, DecodeHints hints)
{
std::vector<Ref<Result> > results;
doDecodeMultiple(image, hints, results, 0, 0);
if (results.empty()){
throw ReaderException("No code detected");
}
return results;
}
void GenericMultipleBarcodeReader::doDecodeMultiple(Ref<BinaryBitmap> image,
DecodeHints hints, std::vector<Ref<Result> >& results, int xOffset, int yOffset)
{
Ref<Result> result;
try {
result = delegate_.decode(image, hints);
} catch (ReaderException re) {
return;
}
bool alreadyFound = false;
for (unsigned int i = 0; i < results.size(); i++) {
Ref<Result> existingResult = results[i];
if (existingResult->getText()->getText() == result->getText()->getText()) {
alreadyFound = true;
break;
}
}
if (alreadyFound) {
return;
}
results.push_back(translateResultPoints(result, xOffset, yOffset));
const std::vector<Ref<ResultPoint> > resultPoints = result->getResultPoints();
if (resultPoints.empty()) {
return;
}
int width = image->getWidth();
int height = image->getHeight();
float minX = width;
float minY = height;
float maxX = 0.0f;
float maxY = 0.0f;
for (unsigned int i = 0; i < resultPoints.size(); i++) {
Ref<ResultPoint> point = resultPoints[i];
float x = point->getX();
float y = point->getY();
if (x < minX) {
minX = x;
}
if (y < minY) {
minY = y;
}
if (x > maxX) {
maxX = x;
}
if (y > maxY) {
maxY = y;
}
}
// Decode left of barcode
if (minX > MIN_DIMENSION_TO_RECUR) {
doDecodeMultiple(image->crop(0, 0, (int) minX, height),
hints, results, xOffset, yOffset);
}
// Decode above barcode
if (minY > MIN_DIMENSION_TO_RECUR) {
doDecodeMultiple(image->crop(0, 0, width, (int) minY),
hints, results, xOffset, yOffset);
}
// Decode right of barcode
if (maxX < width - MIN_DIMENSION_TO_RECUR) {
doDecodeMultiple(image->crop((int) maxX, 0, width - (int) maxX, height),
hints, results, xOffset + (int) maxX, yOffset);
}
// Decode below barcode
if (maxY < height - MIN_DIMENSION_TO_RECUR) {
doDecodeMultiple(image->crop(0, (int) maxY, width, height - (int) maxY),
hints, results, xOffset, yOffset + (int) maxY);
}
}
Ref<Result> GenericMultipleBarcodeReader::translateResultPoints(Ref<Result> result, int xOffset, int yOffset){
const std::vector<Ref<ResultPoint> > oldResultPoints = result->getResultPoints();
if (oldResultPoints.empty()) {
return result;
}
std::vector<Ref<ResultPoint> > newResultPoints;
for (unsigned int i = 0; i < oldResultPoints.size(); i++) {
Ref<ResultPoint> oldPoint = oldResultPoints[i];
newResultPoints.push_back(Ref<ResultPoint>(new ResultPoint(oldPoint->getX() + xOffset, oldPoint->getY() + yOffset)));
}
return Ref<Result>(new Result(result->getText(), result->getRawBytes(), newResultPoints, result->getBarcodeFormat()));
}
} // End zxing::multi namespace
} // End zxing namespace

View file

@ -0,0 +1,47 @@
#ifndef __GENERIC_MULTIPLE_BARCODE_READER_H__
#define __GENERIC_MULTIPLE_BARCODE_READER_H__
/*
* Copyright 2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/multi/MultipleBarcodeReader.h>
#include <zxing/Reader.h>
namespace zxing {
namespace multi {
class GenericMultipleBarcodeReader : public MultipleBarcodeReader {
private:
static Ref<Result> translateResultPoints(Ref<Result> result,
int xOffset,
int yOffset);
void doDecodeMultiple(Ref<BinaryBitmap> image,
DecodeHints hints,
std::vector<Ref<Result> >& results,
int xOffset,
int yOffset);
Reader& delegate_;
static const int MIN_DIMENSION_TO_RECUR = 100;
public:
GenericMultipleBarcodeReader(Reader& delegate);
virtual ~GenericMultipleBarcodeReader();
virtual std::vector<Ref<Result> > decodeMultiple(Ref<BinaryBitmap> image,
DecodeHints hints);
};
} // End zxing::multi namespace
} // End zxing namespace
#endif // __GENERIC_MULTIPLE_BARCODE_READER_H__

View file

@ -0,0 +1,29 @@
/*
* Copyright 2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/multi/MultipleBarcodeReader.h>
namespace zxing {
namespace multi {
MultipleBarcodeReader::~MultipleBarcodeReader() { }
std::vector<Ref<Result> > MultipleBarcodeReader::decodeMultiple(Ref<BinaryBitmap> image) {
return decodeMultiple(image, DecodeHints::DEFAULT_HINT);
}
} // End zxing::multi namespace
} // End zxing namespace

View file

@ -0,0 +1,39 @@
#ifndef __MULTIPLE_BARCODE_READER_H__
#define __MULTIPLE_BARCODE_READER_H__
/*
* Copyright 2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/common/Counted.h>
#include <zxing/Result.h>
#include <zxing/BinaryBitmap.h>
#include <zxing/DecodeHints.h>
#include <vector>
namespace zxing {
namespace multi {
class MultipleBarcodeReader : public Counted {
protected:
MultipleBarcodeReader() {}
public:
virtual std::vector<Ref<Result> > decodeMultiple(Ref<BinaryBitmap> image);
virtual std::vector<Ref<Result> > decodeMultiple(Ref<BinaryBitmap> image, DecodeHints hints) = 0;
virtual ~MultipleBarcodeReader();
};
} // End zxing::multi namespace
} // End zxing namespace
#endif // __MULTIPLE_BARCODE_READER_H__

View file

@ -0,0 +1,57 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* Copyright 2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/multi/qrcode/QRCodeMultiReader.h>
#include <zxing/ReaderException.h>
#include <zxing/multi/qrcode/detector/MultiDetector.h>
#include <zxing/BarcodeFormat.h>
namespace zxing {
namespace multi {
QRCodeMultiReader::QRCodeMultiReader(){}
QRCodeMultiReader::~QRCodeMultiReader(){}
std::vector<Ref<Result> > QRCodeMultiReader::decodeMultiple(Ref<BinaryBitmap> image,
DecodeHints hints)
{
std::vector<Ref<Result> > results;
MultiDetector detector(image->getBlackMatrix());
std::vector<Ref<DetectorResult> > detectorResult = detector.detectMulti(hints);
for (unsigned int i = 0; i < detectorResult.size(); i++) {
try {
Ref<DecoderResult> decoderResult = getDecoder().decode(detectorResult[i]->getBits());
std::vector<Ref<ResultPoint> > points = detectorResult[i]->getPoints();
Ref<Result> result = Ref<Result>(new Result(decoderResult->getText(),
decoderResult->getRawBytes(),
points, BarcodeFormat_QR_CODE));
// result->putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult->getByteSegments());
// result->putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult->getECLevel().toString());
results.push_back(result);
} catch (ReaderException re) {
// ignore and continue
}
}
if (results.empty()){
throw ReaderException("No code detected");
}
return results;
}
} // End zxing::multi namespace
} // End zxing namespace

View file

@ -0,0 +1,34 @@
#ifndef __QRCODE_MULTI_READER_H__
#define __QRCODE_MULTI_READER_H__
/*
* Copyright 2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/multi/MultipleBarcodeReader.h>
#include <zxing/qrcode/QRCodeReader.h>
namespace zxing {
namespace multi {
class QRCodeMultiReader: public zxing::qrcode::QRCodeReader, public MultipleBarcodeReader {
public:
QRCodeMultiReader();
virtual ~QRCodeMultiReader();
virtual std::vector<Ref<Result> > decodeMultiple(Ref<BinaryBitmap> image, DecodeHints hints);
};
} // End zxing::multi namespace
} // End zxing namespace
#endif // __QRCODE_MULTI_READER_H__

View file

@ -0,0 +1,46 @@
/*
* Copyright 2011 ZXing authors
*
* 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/multi/qrcode/detector/MultiDetector.h>
#include <zxing/multi/qrcode/detector/MultiFinderPatternFinder.h>
#include <zxing/ReaderException.h>
namespace zxing {
namespace multi {
using namespace zxing::qrcode;
MultiDetector::MultiDetector(Ref<BitMatrix> image) : Detector(image) {}
MultiDetector::~MultiDetector(){}
std::vector<Ref<DetectorResult> > MultiDetector::detectMulti(DecodeHints hints){
Ref<BitMatrix> image = getImage();
MultiFinderPatternFinder finder = MultiFinderPatternFinder(image, hints.getResultPointCallback());
std::vector<Ref<FinderPatternInfo> > info = finder.findMulti(hints);
std::vector<Ref<DetectorResult> > result;
for(unsigned int i = 0; i < info.size(); i++){
try{
result.push_back(processFinderPatternInfo(info[i]));
} catch (ReaderException e){
// ignore
}
}
return result;
}
} // End zxing::multi namespace
} // End zxing namespace

View file

@ -0,0 +1,35 @@
#ifndef __MULTI_DETECTOR_H__
#define __MULTI_DETECTOR_H__
/*
* Copyright 2011 ZXing authors
*
* 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/qrcode/detector/Detector.h>
#include <zxing/common/DetectorResult.h>
#include <zxing/DecodeHints.h>
namespace zxing {
namespace multi {
class MultiDetector : public zxing::qrcode::Detector {
public:
MultiDetector(Ref<BitMatrix> image);
virtual ~MultiDetector();
virtual std::vector<Ref<DetectorResult> > detectMulti(DecodeHints hints);
};
} // End zxing::multi namespace
} // End zxing namespace
#endif // __MULTI_DETECTOR_H__

View file

@ -0,0 +1,225 @@
/*
* Copyright 2011 ZXing authors
*
* 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 <algorithm>
#include <math.h>
#include <stdlib.h>
#include <zxing/multi/qrcode/detector/MultiFinderPatternFinder.h>
#include <zxing/DecodeHints.h>
#include <zxing/ReaderException.h>
namespace zxing{
namespace multi {
using namespace zxing::qrcode;
bool compareModuleSize(Ref<FinderPattern> a, Ref<FinderPattern> b){
float value = a->getEstimatedModuleSize() - b->getEstimatedModuleSize();
return value < 0.0;
}
MultiFinderPatternFinder::MultiFinderPatternFinder(Ref<BitMatrix> image,
Ref<ResultPointCallback> resultPointCallback) :
FinderPatternFinder(image, resultPointCallback)
{
}
MultiFinderPatternFinder::~MultiFinderPatternFinder(){}
std::vector<Ref<FinderPatternInfo> > MultiFinderPatternFinder::findMulti(DecodeHints const& hints){
bool tryHarder = hints.getTryHarder();
Ref<BitMatrix> image = image_; // Protected member
int maxI = image->getHeight();
int maxJ = image->getWidth();
// We are looking for black/white/black/white/black modules in
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
// image, and then account for the center being 3 modules in size. This gives the smallest
// number of pixels the center could be, so skip this often. When trying harder, look for all
// QR versions regardless of how dense they are.
int iSkip = (int) (maxI / (MAX_MODULES * 4.0f) * 3);
if (iSkip < MIN_SKIP || tryHarder) {
iSkip = MIN_SKIP;
}
int stateCount[5];
for (int i = iSkip - 1; i < maxI; i += iSkip) {
// Get a row of black/white values
stateCount[0] = 0;
stateCount[1] = 0;
stateCount[2] = 0;
stateCount[3] = 0;
stateCount[4] = 0;
int currentState = 0;
for (int j = 0; j < maxJ; j++) {
if (image->get(j, i)) {
// Black pixel
if ((currentState & 1) == 1) { // Counting white pixels
currentState++;
}
stateCount[currentState]++;
} else { // White pixel
if ((currentState & 1) == 0) { // Counting black pixels
if (currentState == 4) { // A winner?
if (foundPatternCross(stateCount)) { // Yes
bool confirmed = handlePossibleCenter(stateCount, i, j);
if (!confirmed) {
do { // Advance to next black pixel
j++;
} while (j < maxJ && !image->get(j, i));
j--; // back up to that last white pixel
}
// Clear state to start looking again
currentState = 0;
stateCount[0] = 0;
stateCount[1] = 0;
stateCount[2] = 0;
stateCount[3] = 0;
stateCount[4] = 0;
} else { // No, shift counts back by two
stateCount[0] = stateCount[2];
stateCount[1] = stateCount[3];
stateCount[2] = stateCount[4];
stateCount[3] = 1;
stateCount[4] = 0;
currentState = 3;
}
} else {
stateCount[++currentState]++;
}
} else { // Counting white pixels
stateCount[currentState]++;
}
}
} // for j=...
if (foundPatternCross(stateCount)) {
handlePossibleCenter(stateCount, i, maxJ);
} // end if foundPatternCross
} // for i=iSkip-1 ...
std::vector<std::vector<Ref<FinderPattern> > > patternInfo = selectBestPatterns();
std::vector<Ref<FinderPatternInfo> > result;
for (unsigned int i = 0; i < patternInfo.size(); i++) {
std::vector<Ref<FinderPattern> > pattern = patternInfo[i];
FinderPatternFinder::orderBestPatterns(pattern);
result.push_back(Ref<FinderPatternInfo>(new FinderPatternInfo(pattern)));
}
return result;
}
std::vector<std::vector<Ref<FinderPattern> > > MultiFinderPatternFinder::selectBestPatterns(){
std::vector<Ref<FinderPattern> > possibleCenters = possibleCenters_;
int size = possibleCenters.size();
if (size < 3) {
// Couldn't find enough finder patterns
throw ReaderException("No code detected");
}
std::vector<std::vector<Ref<FinderPattern> > > results;
/*
* Begin HE modifications to safely detect multiple codes of equal size
*/
if (size == 3) {
results.push_back(possibleCenters_);
return results;
}
// Sort by estimated module size to speed up the upcoming checks
//TODO do a sort based on module size
std::sort(possibleCenters.begin(), possibleCenters.end(), compareModuleSize);
/*
* Now lets start: build a list of tuples of three finder locations that
* - feature similar module sizes
* - are placed in a distance so the estimated module count is within the QR specification
* - have similar distance between upper left/right and left top/bottom finder patterns
* - form a triangle with 90° angle (checked by comparing top right/bottom left distance
* with pythagoras)
*
* Note: we allow each point to be used for more than one code region: this might seem
* counterintuitive at first, but the performance penalty is not that big. At this point,
* we cannot make a good quality decision whether the three finders actually represent
* a QR code, or are just by chance layouted so it looks like there might be a QR code there.
* So, if the layout seems right, lets have the decoder try to decode.
*/
for (int i1 = 0; i1 < (size - 2); i1++) {
Ref<FinderPattern> p1 = possibleCenters[i1];
for (int i2 = i1 + 1; i2 < (size - 1); i2++) {
Ref<FinderPattern> p2 = possibleCenters[i2];
// Compare the expected module sizes; if they are really off, skip
float vModSize12 = (p1->getEstimatedModuleSize() - p2->getEstimatedModuleSize()) / std::min(p1->getEstimatedModuleSize(), p2->getEstimatedModuleSize());
float vModSize12A = abs(p1->getEstimatedModuleSize() - p2->getEstimatedModuleSize());
if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
// break, since elements are ordered by the module size deviation there cannot be
// any more interesting elements for the given p1.
break;
}
for (int i3 = i2 + 1; i3 < size; i3++) {
Ref<FinderPattern> p3 = possibleCenters[i3];
// Compare the expected module sizes; if they are really off, skip
float vModSize23 = (p2->getEstimatedModuleSize() - p3->getEstimatedModuleSize()) / std::min(p2->getEstimatedModuleSize(), p3->getEstimatedModuleSize());
float vModSize23A = abs(p2->getEstimatedModuleSize() - p3->getEstimatedModuleSize());
if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) {
// break, since elements are ordered by the module size deviation there cannot be
// any more interesting elements for the given p1.
break;
}
std::vector<Ref<FinderPattern> > test;
test.push_back(p1);
test.push_back(p2);
test.push_back(p3);
FinderPatternFinder::orderBestPatterns(test);
// Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
Ref<FinderPatternInfo> info = Ref<FinderPatternInfo>(new FinderPatternInfo(test));
float dA = FinderPatternFinder::distance(info->getTopLeft(), info->getBottomLeft());
float dC = FinderPatternFinder::distance(info->getTopRight(), info->getBottomLeft());
float dB = FinderPatternFinder::distance(info->getTopLeft(), info->getTopRight());
// Check the sizes
float estimatedModuleCount = (dA + dB) / (p1->getEstimatedModuleSize() * 2.0f);
if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) {
continue;
}
// Calculate the difference of the edge lengths in percent
float vABBC = abs((dA - dB) / std::min(dA, dB));
if (vABBC >= 0.1f) {
continue;
}
// Calculate the diagonal length by assuming a 90° angle at topleft
float dCpy = (float) sqrt(dA * dA + dB * dB);
// Compare to the real distance in %
float vPyC = abs((dC - dCpy) / std::min(dC, dCpy));
if (vPyC >= 0.1f) {
continue;
}
// All tests passed!
results.push_back(test);
} // end iterate p3
} // end iterate p2
} // end iterate p1
if (results.empty()){
// Nothing found!
throw ReaderException("No code detected");
}
return results;
}
} // End zxing::multi namespace
} // End zxing namespace

View file

@ -0,0 +1,45 @@
#ifndef __MULTI_FINDER_PATTERN_FINDER_H__
#define __MULTI_FINDER_PATTERN_FINDER_H__
/*
* Copyright 2011 ZXing authors
*
* 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/qrcode/detector/FinderPattern.h>
#include <zxing/qrcode/detector/FinderPatternFinder.h>
#include <zxing/qrcode/detector/FinderPatternInfo.h>
namespace zxing {
namespace multi {
class MultiFinderPatternFinder : zxing::qrcode::FinderPatternFinder {
private:
std::vector<std::vector<Ref<zxing::qrcode::FinderPattern> > > selectBestPatterns();
static const float MAX_MODULE_COUNT_PER_EDGE = 180;
static const float MIN_MODULE_COUNT_PER_EDGE = 9;
static const float DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f;
static const float DIFF_MODSIZE_CUTOFF = 0.5f;
public:
MultiFinderPatternFinder(Ref<BitMatrix> image, Ref<ResultPointCallback> resultPointCallback);
virtual ~MultiFinderPatternFinder();
virtual std::vector<Ref<zxing::qrcode::FinderPatternInfo> > findMulti(DecodeHints const& hints);
};
}
}
#endif // __MULTI_FINDER_PATTERN_FINDER_H__

View file

@ -22,15 +22,7 @@
namespace zxing {
namespace oned {
OneDResultPoint::OneDResultPoint(float posX, float posY) : posX_(posX), posY_(posY) {
}
float OneDResultPoint::getX() const {
return posX_;
}
float OneDResultPoint::getY() const {
return posY_;
OneDResultPoint::OneDResultPoint(float posX, float posY) : ResultPoint(posX,posY) {
}
}
}

View file

@ -25,14 +25,9 @@ namespace zxing {
namespace oned {
class OneDResultPoint : public ResultPoint {
private:
float posX_;
float posY_;
public:
OneDResultPoint(float posX, float posY);
float getX() const;
float getY() const;
};
}
}

View file

@ -79,5 +79,8 @@ namespace zxing {
QRCodeReader::~QRCodeReader() {
}
Decoder& QRCodeReader::getDecoder() {
return decoder_;
}
}
}

View file

@ -32,6 +32,9 @@ namespace zxing {
private:
Decoder decoder_;
protected:
Decoder& getDecoder();
public:
QRCodeReader();
virtual Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints);

View file

@ -27,15 +27,7 @@ namespace qrcode {
using namespace std;
AlignmentPattern::AlignmentPattern(float posX, float posY, float estimatedModuleSize) :
posX_(posX), posY_(posY), estimatedModuleSize_(estimatedModuleSize) {
}
float AlignmentPattern::getX() const {
return posX_;
}
float AlignmentPattern::getY() const {
return posY_;
ResultPoint(posX,posY), estimatedModuleSize_(estimatedModuleSize) {
}
bool AlignmentPattern::aboutEquals(float moduleSize, float i, float j) const {

View file

@ -30,14 +30,10 @@ namespace zxing {
class AlignmentPattern : public ResultPoint {
private:
float posX_;
float posY_;
float estimatedModuleSize_;
public:
AlignmentPattern(float posX, float posY, float estimatedModuleSize);
float getX() const;
float getY() const;
bool aboutEquals(float moduleSize, float i, float j) const;
Ref<AlignmentPattern> combineEstimate(float i, float j,
float newModuleSize) const;

View file

@ -1,3 +1,4 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* Detector.cpp
* zxing
@ -47,7 +48,10 @@ Ref<DetectorResult> Detector::detect(DecodeHints const& hints) {
callback_ = hints.getResultPointCallback();
FinderPatternFinder finder(image_, hints.getResultPointCallback());
Ref<FinderPatternInfo> info(finder.find(hints));
return processFinderPatternInfo(info);
}
Ref<DetectorResult> Detector::processFinderPatternInfo(Ref<FinderPatternInfo> info){
Ref<FinderPattern> topLeft(info->getTopLeft());
Ref<FinderPattern> topRight(info->getTopRight());
Ref<FinderPattern> bottomLeft(info->getBottomLeft());

View file

@ -26,6 +26,7 @@
#include <zxing/qrcode/detector/AlignmentPattern.h>
#include <zxing/common/PerspectiveTransform.h>
#include <zxing/ResultPointCallback.h>
#include <zxing/qrcode/detector/FinderPatternInfo.h>
namespace zxing {
@ -50,6 +51,7 @@ protected:
float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY);
Ref<AlignmentPattern> findAlignmentInRegion(float overallEstModuleSize, int estAlignmentX, int estAlignmentY,
float allowanceFactor);
Ref<DetectorResult> processFinderPatternInfo(Ref<FinderPatternInfo> info);
public:
virtual Ref<PerspectiveTransform> createTransform(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight, Ref <

View file

@ -27,19 +27,11 @@ namespace zxing {
using namespace std;
FinderPattern::FinderPattern(float posX, float posY, float estimatedModuleSize) :
posX_(posX), posY_(posY), estimatedModuleSize_(estimatedModuleSize), count_(1) {
ResultPoint(posX,posY), estimatedModuleSize_(estimatedModuleSize), count_(1) {
}
FinderPattern::FinderPattern(float posX, float posY, float estimatedModuleSize, int count) :
posX_(posX), posY_(posY), estimatedModuleSize_(estimatedModuleSize), count_(count) {
}
float FinderPattern::getX() const {
return posX_;
}
float FinderPattern::getY() const {
return posY_;
ResultPoint(posX,posY), estimatedModuleSize_(estimatedModuleSize), count_(count) {
}
int FinderPattern::getCount() const {

View file

@ -29,16 +29,12 @@ namespace zxing {
class FinderPattern : public ResultPoint {
private:
float posX_;
float posY_;
float estimatedModuleSize_;
int count_;
public:
FinderPattern(float posX, float posY, float estimatedModuleSize);
FinderPattern(float posX, float posY, float estimatedModuleSize, int count);
float getX() const;
float getY() const;
int getCount() const;
float getEstimatedModuleSize() const;
void incrementCount();

View file

@ -36,6 +36,8 @@ namespace qrcode {
class FinderPatternFinder {
private:
static int CENTER_QUORUM;
protected:
static int MIN_SKIP;
static int MAX_MODULES;

View file

@ -1,8 +1,5 @@
/*
* MagickBitmapSource.cpp
* zxing
*
* Copyright 2010 ZXing authors All rights reserved.
* Copyright 2010-2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,11 +26,9 @@ MagickBitmapSource::MagickBitmapSource(Image& image) : image_(image) {
width = image.columns();
height = image.rows();
pixel_cache = image.getConstPixels(0, 0, width, height);
}
MagickBitmapSource::~MagickBitmapSource() {
}
int MagickBitmapSource::getWidth() const {
@ -45,12 +40,13 @@ int MagickBitmapSource::getHeight() const {
}
unsigned char* MagickBitmapSource::getRow(int y, unsigned char* row) {
const Magick::PixelPacket* pixel_cache = image_.getConstPixels(0, y, width, 1);
int width = getWidth();
if (row == NULL) {
row = new unsigned char[width];
}
for (int x = 0; x < width; x++) {
const PixelPacket* p = pixel_cache + y * width + x;
const PixelPacket* p = pixel_cache + x;
// We assume 16 bit values here
// 0x200 = 1<<9, half an lsb of the result to force rounding
row[x] = (unsigned char)((306 * ((int)p->red >> 8) + 601 * ((int)p->green >> 8) +
@ -62,6 +58,7 @@ unsigned char* MagickBitmapSource::getRow(int y, unsigned char* row) {
/** This is a more efficient implementation. */
unsigned char* MagickBitmapSource::getMatrix() {
const Magick::PixelPacket* pixel_cache = image_.getConstPixels(0, 0, width, height);
int width = getWidth();
int height = getHeight();
unsigned char* matrix = new unsigned char[width*height];
@ -79,20 +76,29 @@ unsigned char* MagickBitmapSource::getMatrix() {
}
bool MagickBitmapSource::isRotateSupported() const {
return false;
return true;
}
Ref<LuminanceSource> MagickBitmapSource::rotateCounterClockwise() {
//TODO(flyashi): add rotated image support.
/* this segfaults. I tried a few things, none seemed to work. Perhaps the problem is elsewhere? */
/*
Magick::Image rotated(image_);
rotated.modifyImage();
rotated.rotate(90); // Image::rotate takes CCW degrees as an argument
rotated.syncPixels();
return Ref<MagickBitmapSource> (new MagickBitmapSource(rotated));
*/
return Ref<MagickBitmapSource> (NULL);
}
bool MagickBitmapSource::isCropSupported() const{
return true;
}
Ref<LuminanceSource> MagickBitmapSource::crop(int left, int top, int width, int height){
/* TODO Investigate memory leak:
* This method "possibly leaks" 160 bytes in 1 block */
Image copy(image_);
copy.modifyImage();
copy.crop( Geometry(width,height,left,top));
copy.syncPixels();
return Ref<MagickBitmapSource>(new MagickBitmapSource(copy));
}
}

View file

@ -1,10 +1,7 @@
#ifndef __MAGICK_BITMAP_SOURCE_H_
#define __MAGICK_BITMAP_SOURCE_H_
/*
* MagickBitmapSource.h
* zxing
*
* Copyright 2010 ZXing authors All rights reserved.
* Copyright 2010-2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,10 +23,9 @@ namespace zxing {
class MagickBitmapSource : public LuminanceSource {
private:
Magick::Image& image_;
Magick::Image image_;
int width;
int height;
const Magick::PixelPacket* pixel_cache;
public:
MagickBitmapSource(Magick::Image& image);
@ -40,6 +36,8 @@ public:
int getHeight() const;
unsigned char* getRow(int y, unsigned char* row);
unsigned char* getMatrix();
bool isCropSupported() const;
Ref<LuminanceSource> crop(int left, int top, int width, int height);
bool isRotateSupported() const;
Ref<LuminanceSource> rotateCounterClockwise();
};

View file

@ -1,9 +1,6 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* main.cpp
* zxing
*
* Copyright 2010 ZXing authors All rights reserved.
* Copyright 2010-2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,7 +21,6 @@
#include <Magick++.h>
#include "MagickBitmapSource.h"
#include <zxing/common/Counted.h>
//#include <zxing/qrcode/QRCodeReader.h>
#include <zxing/Binarizer.h>
#include <zxing/MultiFormatReader.h>
#include <zxing/Result.h>
@ -37,6 +33,12 @@
#include <zxing/BinaryBitmap.h>
#include <zxing/DecodeHints.h>
#include <zxing/qrcode/QRCodeReader.h>
#include <zxing/multi/qrcode/QRCodeMultiReader.h>
#include <zxing/multi/ByQuadrantReader.h>
#include <zxing/multi/MultipleBarcodeReader.h>
#include <zxing/multi/GenericMultipleBarcodeReader.h>
//#include <zxing/qrcode/detector/Detector.h>
//#include <zxing/qrcode/detector/QREdgeDetector.h>
//#include <zxing/qrcode/decoder/Decoder.h>
@ -44,12 +46,14 @@
using namespace Magick;
using namespace std;
using namespace zxing;
//using namespace zxing::qrcode;
using namespace zxing::multi;
using namespace zxing::qrcode;
static bool raw_dump = false;
static bool show_format = false;
static bool tryHarder = false;
static bool show_filename = false;
static bool search_multi = false;
static const int MAX_EXPECTED = 1024;
@ -58,6 +62,16 @@ Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints) {
return reader->decode(image, hints);
}
vector<Ref<Result> > decodeMultiple(Ref<BinaryBitmap> image, DecodeHints hints){
MultiFormatReader delegate;
// MultiFormatReader mformat;
// ByQuadrantReader delegate(mformat);
GenericMultipleBarcodeReader reader(delegate);
// QRCodeMultiReader reader;
return reader.decodeMultiple(image,hints);
}
int test_image(Image& image, bool hybrid, string expected = "") {
@ -101,22 +115,20 @@ int test_image(Image& image, bool hybrid, string expected = "") {
if (cell_result.compare(expected)) {
res = -6;
if (!raw_dump) {
cout << (hybrid ? "Hybrid" : "Global") << " binarizer failed:\n";
if (expected.length() >= 0) {
cout << " Expected: " << expected << "\n";
}
cout << " Detected: " << cell_result << endl;
cout << (hybrid ? "Hybrid" : "Global") << " binarizer failed:\n";
if (expected.length() >= 0) {
cout << " Expected: " << expected << "\n";
}
cout << " Detected: " << cell_result << endl;
}
}
if (raw_dump && !hybrid) {/* don't print twice, and global is a bit better */
cout << cell_result;
if (show_format) {
cout << " " << result_format;
}
cout << endl;
}
return res;
}
@ -129,6 +141,64 @@ int test_image_global(Image& image, string expected = "") {
return test_image(image, false, expected);
}
int test_image_multi(Image& image, bool hybrid){
vector<Ref<Result> > results;
string cell_result;
int res = -1;
Ref<BitMatrix> matrix(NULL);
Ref<Binarizer> binarizer(NULL);
try {
Ref<MagickBitmapSource> source(new MagickBitmapSource(image));
if (hybrid) {
binarizer = new HybridBinarizer(source);
} else {
binarizer = new GlobalHistogramBinarizer(source);
}
DecodeHints hints(DecodeHints::DEFAULT_HINT);
hints.setTryHarder(tryHarder);
Ref<BinaryBitmap> binary(new BinaryBitmap(binarizer));
results = decodeMultiple(binary, hints);
res = 0;
} catch (ReaderException e) {
cell_result = "zxing::ReaderException: " + string(e.what());
res = -2;
} catch (zxing::IllegalArgumentException& e) {
cell_result = "zxing::IllegalArgumentException: " + string(e.what());
res = -3;
} catch (zxing::Exception& e) {
cell_result = "zxing::Exception: " + string(e.what());
res = -4;
} catch (std::exception& e) {
cell_result = "std::exception: " + string(e.what());
res = -5;
}
cout << (hybrid ? "Hybrid" : "Global");
if (res != 0){
cout<<" binarizer failed: "<<cell_result<<endl;
} else {
cout<<" binarizer succeeded: "<<endl;
for (unsigned int i = 0; i < results.size(); i++){
cout << " "<<results[i]->getText()->getText();
if (show_format) {
cout << " " << barcodeFormatNames[results[i]->getBarcodeFormat()];
}
cout << endl;
}
}
return res;
}
int test_image_multi_hybrid(Image& image){
return test_image_multi(image, true);
}
int test_image_multi_global(Image& image){
return test_image_multi(image, false);
}
string get_expected(string imagefilename) {
string textfilename = imagefilename;
int dotpos = textfilename.rfind(".");
@ -146,8 +216,8 @@ string get_expected(string imagefilename) {
rewind(fp);
if (toread > MAX_EXPECTED) {
cerr << "MAX_EXPECTED = " << MAX_EXPECTED << " but file '" << textfilename << "' has " << toread
<< " bytes! Skipping..." << endl;
cerr << "MAX_EXPECTED = " << MAX_EXPECTED << " but file '" << textfilename << "' has " << toread
<< " bytes! Skipping..." << endl;
fclose(fp);
return "";
}
@ -166,7 +236,7 @@ string get_expected(string imagefilename) {
int main(int argc, char** argv) {
if (argc <= 1) {
cout << "Usage: " << argv[0] << " [--dump-raw] [--show-format] [--try-harder] [--show-filename] <filename1> [<filename2> ...]" << endl;
cout << "Usage: " << argv[0] << " [--dump-raw] [--show-format] [--try-harder] [--search_multi] [--show-filename] <filename1> [<filename2> ...]" << endl;
return 1;
}
@ -199,6 +269,10 @@ int main(int argc, char** argv) {
show_filename = true;
continue;
}
if (infilename.compare("--search_multi") == 0){
search_multi = true;
continue;
}
if (!raw_dump)
cerr << "Processing: " << infilename << endl;
if (show_filename)
@ -214,27 +288,39 @@ int main(int argc, char** argv) {
string expected;
expected = get_expected(infilename);
int gresult = 1;
int hresult = 1;
if (search_multi){
int gresult = 1;
int hresult = 1;
gresult = test_image_multi_global(image);
hresult = test_image_multi_hybrid(image);
gresult = gresult == 0;
hresult = hresult == 0;
gonly += gresult && !hresult;
honly += hresult && !gresult;
both += gresult && hresult;
neither += !gresult && !hresult;
total = total + 1;
} else {
int gresult = 1;
int hresult = 1;
hresult = test_image_hybrid(image, expected);
gresult = test_image_global(image, expected);
gresult = gresult == 0;
hresult = hresult == 0;
hresult = test_image_hybrid(image, expected);
gresult = test_image_global(image, expected);
gresult = gresult == 0;
hresult = hresult == 0;
gonly += gresult && !hresult;
honly += hresult && !gresult;
both += gresult && hresult;
neither += !gresult && !hresult;
total = total + 1;
gonly += gresult && !hresult;
honly += hresult && !gresult;
both += gresult && hresult;
neither += !gresult && !hresult;
total = total + 1;
}
}
if (!raw_dump)
cout << (honly+both) << " passed hybrid, " << (gonly+both) << " passed global, "
<< both << " pass both, " << neither << " pass neither, " << honly
<< " passed only hybrid, " << gonly << " passed only global, of " << total
<< " total." << endl;
<< both << " pass both, " << neither << " pass neither, " << honly
<< " passed only hybrid, " << gonly << " passed only global, of " << total
<< " total." << endl;
return 0;
}