vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_ARRAY_HPP__
|
||||
#define __ZXING_COMMON_ARRAY_HPP__
|
||||
|
||||
#include "counted.hpp"
|
||||
|
||||
namespace zxing {
|
||||
|
||||
template <typename T>
|
||||
class Array : public Counted {
|
||||
protected:
|
||||
public:
|
||||
std::vector<T> values_;
|
||||
Array() {}
|
||||
explicit Array(int n) : Counted(), values_(n, T()) {}
|
||||
Array(T const *ts, int n) : Counted(), values_(ts, ts + n) {}
|
||||
Array(T const *ts, T const *te) : Counted(), values_(ts, te) {}
|
||||
Array(T v, int n) : Counted(), values_(n, v) {}
|
||||
explicit Array(std::vector<T> &v) : Counted(), values_(v) {}
|
||||
Array(Array<T> &other) : Counted(), values_(other.values_) {}
|
||||
explicit Array(Array<T> *other) : Counted(), values_(other->values_) {}
|
||||
virtual ~Array() {}
|
||||
Array<T> &operator=(const Array<T> &other) {
|
||||
values_ = other.values_;
|
||||
return *this;
|
||||
}
|
||||
Array<T> &operator=(const std::vector<T> &array) {
|
||||
values_ = array;
|
||||
return *this;
|
||||
}
|
||||
T const &operator[](int i) const { return values_[i]; }
|
||||
T &operator[](int i) { return values_[i]; }
|
||||
int size() const { return values_.size(); }
|
||||
bool empty() const { return values_.size() == 0; }
|
||||
std::vector<T> const &values() const { return values_; }
|
||||
std::vector<T> &values() { return values_; }
|
||||
|
||||
T *data() {
|
||||
// return values_.data();
|
||||
return &values_[0];
|
||||
}
|
||||
void append(T value) { values_.push_back(value); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ArrayRef : public Counted {
|
||||
private:
|
||||
public:
|
||||
Array<T> *array_;
|
||||
ArrayRef() : array_(0) {}
|
||||
explicit ArrayRef(int n) : array_(0) { reset(new Array<T>(n)); }
|
||||
ArrayRef(T *ts, int n) : array_(0) { reset(new Array<T>(ts, n)); }
|
||||
explicit ArrayRef(Array<T> *a) : array_(0) { reset(a); }
|
||||
ArrayRef(const ArrayRef &other) : Counted(), array_(0) { reset(other.array_); }
|
||||
|
||||
~ArrayRef() {
|
||||
if (array_) {
|
||||
array_->release();
|
||||
}
|
||||
array_ = 0;
|
||||
}
|
||||
|
||||
T const &operator[](int i) const { return (*array_)[i]; }
|
||||
|
||||
T &operator[](int i) { return (*array_)[i]; }
|
||||
|
||||
void reset(Array<T> *a) {
|
||||
if (a) {
|
||||
a->retain();
|
||||
}
|
||||
if (array_) {
|
||||
array_->release();
|
||||
}
|
||||
array_ = a;
|
||||
}
|
||||
void reset(const ArrayRef<T> &other) { reset(other.array_); }
|
||||
ArrayRef<T> &operator=(const ArrayRef<T> &other) {
|
||||
reset(other);
|
||||
return *this;
|
||||
}
|
||||
ArrayRef<T> &operator=(Array<T> *a) {
|
||||
reset(a);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Array<T> &operator*() const { return *array_; }
|
||||
|
||||
Array<T> *operator->() const { return array_; }
|
||||
|
||||
operator bool() const { return array_ != 0; }
|
||||
bool operator!() const { return array_ == 0; }
|
||||
|
||||
T *data() { return array_->data(); }
|
||||
|
||||
void clear() {
|
||||
T *ptr = array_->data();
|
||||
memset(ptr, 0, array_->size());
|
||||
}
|
||||
void append(T value) { array_->append(value); }
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_ARRAY_HPP__
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
#include "../../../precomp.hpp"
|
||||
#include "adaptive_threshold_mean_binarizer.hpp"
|
||||
using zxing::AdaptiveThresholdMeanBinarizer;
|
||||
|
||||
namespace {
|
||||
const int BLOCK_SIZE = 25;
|
||||
const int Bias = 10;
|
||||
} // namespace
|
||||
|
||||
AdaptiveThresholdMeanBinarizer::AdaptiveThresholdMeanBinarizer(Ref<LuminanceSource> source)
|
||||
: GlobalHistogramBinarizer(source) {}
|
||||
|
||||
AdaptiveThresholdMeanBinarizer::~AdaptiveThresholdMeanBinarizer() {}
|
||||
|
||||
Ref<Binarizer> AdaptiveThresholdMeanBinarizer::createBinarizer(Ref<LuminanceSource> source) {
|
||||
return Ref<Binarizer>(new AdaptiveThresholdMeanBinarizer(source));
|
||||
}
|
||||
|
||||
Ref<BitArray> AdaptiveThresholdMeanBinarizer::getBlackRow(int y, Ref<BitArray> row,
|
||||
ErrorHandler& err_handler) {
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
if (!matrix0_) {
|
||||
binarizeImage(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitArray>();
|
||||
}
|
||||
|
||||
// Call parent getBlackMatrix to get current matrix
|
||||
return Binarizer::getBlackRow(y, row, err_handler);
|
||||
}
|
||||
|
||||
Ref<BitMatrix> AdaptiveThresholdMeanBinarizer::getBlackMatrix(ErrorHandler& err_handler) {
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
if (!matrix0_) {
|
||||
binarizeImage(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitMatrix>();
|
||||
}
|
||||
return Binarizer::getBlackMatrix(err_handler);
|
||||
}
|
||||
|
||||
int AdaptiveThresholdMeanBinarizer::binarizeImage(ErrorHandler& err_handler) {
|
||||
if (width >= BLOCK_SIZE && height >= BLOCK_SIZE) {
|
||||
LuminanceSource& source = *getLuminanceSource();
|
||||
Ref<BitMatrix> matrix(new BitMatrix(width, height, err_handler));
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
auto src = (unsigned char*)source.getMatrix()->data();
|
||||
auto dst = matrix->getPtr();
|
||||
cv::Mat mDst;
|
||||
mDst = cv::Mat::zeros(cv::Size(width, height), CV_8UC1);
|
||||
TransBufferToMat(src, mDst, width, height);
|
||||
cv::Mat result;
|
||||
int bs = width / 10;
|
||||
bs = bs + bs % 2 - 1;
|
||||
if (!(bs % 2 == 1 && bs > 1)) return -1;
|
||||
cv::adaptiveThreshold(mDst, result, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY,
|
||||
bs, Bias);
|
||||
TransMatToBuffer(result, dst, width, height);
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
matrix0_ = matrix;
|
||||
} else {
|
||||
matrix0_ = GlobalHistogramBinarizer::getBlackMatrix(err_handler);
|
||||
if (err_handler.ErrCode()) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AdaptiveThresholdMeanBinarizer::TransBufferToMat(unsigned char* pBuffer, cv::Mat& mDst,
|
||||
int nWidth, int nHeight) {
|
||||
for (int j = 0; j < nHeight; ++j) {
|
||||
unsigned char* data = mDst.ptr<unsigned char>(j);
|
||||
unsigned char* pSubBuffer = pBuffer + (nHeight - 1 - j) * nWidth;
|
||||
memcpy(data, pSubBuffer, nWidth);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AdaptiveThresholdMeanBinarizer::TransMatToBuffer(cv::Mat mSrc, unsigned char* ppBuffer,
|
||||
int& nWidth, int& nHeight) {
|
||||
nWidth = mSrc.cols;
|
||||
// nWidth = ((nWidth + 3) / 4) * 4;
|
||||
nHeight = mSrc.rows;
|
||||
for (int j = 0; j < nHeight; ++j) {
|
||||
unsigned char* pdi = ppBuffer + j * nWidth;
|
||||
for (int z = 0; z < nWidth; ++z) {
|
||||
int nj = nHeight - j - 1;
|
||||
int value = *(uint8_t*)(mSrc.ptr<uint8_t>(nj) + z);
|
||||
if (value > 120)
|
||||
pdi[z] = 0;
|
||||
else
|
||||
pdi[z] = 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
#ifndef __ZXING_COMMON_ADAPTIVE_THRESHOLD_MEAN_BINARIZER_HPP__
|
||||
#define __ZXING_COMMON_ADAPTIVE_THRESHOLD_MEAN_BINARIZER_HPP__
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include "../../binarizer.hpp"
|
||||
#include "../../errorhandler.hpp"
|
||||
#include "../bitarray.hpp"
|
||||
#include "../bitmatrix.hpp"
|
||||
#include "../bytematrix.hpp"
|
||||
#include "global_histogram_binarizer.hpp"
|
||||
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class AdaptiveThresholdMeanBinarizer : public GlobalHistogramBinarizer {
|
||||
public:
|
||||
explicit AdaptiveThresholdMeanBinarizer(Ref<LuminanceSource> source);
|
||||
virtual ~AdaptiveThresholdMeanBinarizer();
|
||||
|
||||
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler& err_handler) override;
|
||||
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) override;
|
||||
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
|
||||
|
||||
private:
|
||||
int binarizeImage(ErrorHandler& err_handler);
|
||||
int TransBufferToMat(unsigned char* pBuffer, cv::Mat& mDst, int nWidth, int nHeight);
|
||||
int TransMatToBuffer(cv::Mat mSrc, unsigned char* ppBuffer, int& nWidth, int& nHeight);
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
#endif // __ZXING_COMMON_ADAPTIVE_THRESHOLD_MEAN_BINARIZER_HPP__
|
||||
@@ -0,0 +1,285 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../../precomp.hpp"
|
||||
#include "fast_window_binarizer.hpp"
|
||||
using zxing::FastWindowBinarizer;
|
||||
|
||||
|
||||
namespace {
|
||||
const int BLOCK_SIZE = 6;
|
||||
// const int BLOCK_SIZE = 8; // not as good as BLOCK_SIZE = 6
|
||||
const float WINDOW_FRACTION = 0.13f;
|
||||
|
||||
static int min(int a, int b) { return a < b ? a : b; }
|
||||
|
||||
static int max(int a, int b) { return a > b ? a : b; }
|
||||
|
||||
} // namespace
|
||||
|
||||
FastWindowBinarizer::FastWindowBinarizer(Ref<LuminanceSource> source)
|
||||
: GlobalHistogramBinarizer(source), matrix_(NULL), cached_row_(NULL) {
|
||||
width = source->getWidth();
|
||||
height = source->getHeight();
|
||||
int aw = width / BLOCK_SIZE;
|
||||
int ah = height / BLOCK_SIZE;
|
||||
|
||||
int ah2 = ah;
|
||||
int ow2 = aw + 1;
|
||||
|
||||
_luminancesInt = new int[width * height];
|
||||
_blockTotals = new int[ah * aw];
|
||||
_totals = new int[(ah + 1) * (aw + 1)];
|
||||
_rowTotals = new int[ah2 * ow2];
|
||||
|
||||
_internal = new unsigned int[(height + 1) * (width + 1)];
|
||||
}
|
||||
|
||||
FastWindowBinarizer::~FastWindowBinarizer() {
|
||||
delete[] _totals;
|
||||
delete[] _blockTotals;
|
||||
delete[] _luminancesInt;
|
||||
delete[] _rowTotals;
|
||||
|
||||
delete[] _internal;
|
||||
}
|
||||
|
||||
Ref<Binarizer> FastWindowBinarizer::createBinarizer(Ref<LuminanceSource> source) {
|
||||
return Ref<Binarizer>(new FastWindowBinarizer(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the final BitMatrix once for all requests. This could be called
|
||||
* once from the constructor instead, but there are some advantages to doing it
|
||||
* lazily, such as making profiling easier, and not doing heavy lifting when
|
||||
* callers don't expect it.
|
||||
*/
|
||||
Ref<BitMatrix> FastWindowBinarizer::getBlackMatrix(ErrorHandler& err_handler) {
|
||||
if (!matrix0_) {
|
||||
binarizeImage1(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitMatrix>();
|
||||
}
|
||||
|
||||
return Binarizer::getBlackMatrix(err_handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate black row from BitMatrix
|
||||
* If BitMatrix has been calculated then just get the row
|
||||
* If BitMatrix has not been calculated then call getBlackMatrix first
|
||||
*/
|
||||
|
||||
Ref<BitArray> FastWindowBinarizer::getBlackRow(int y, Ref<BitArray> row,
|
||||
ErrorHandler& err_handler) {
|
||||
if (!matrix0_) {
|
||||
binarizeImage1(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitArray>();
|
||||
}
|
||||
// Call parent getBlackMatrix to get current matrix
|
||||
return Binarizer::getBlackRow(y, row, err_handler);
|
||||
}
|
||||
|
||||
void FastWindowBinarizer::calcBlockTotals(int* luminancesInt, int* output, int aw, int ah) {
|
||||
for (int by = 0; by < ah; by++) {
|
||||
int ey = (by + 1) * BLOCK_SIZE;
|
||||
for (int bx = 0; bx < aw; bx++) {
|
||||
int t = 0;
|
||||
for (int y = by * BLOCK_SIZE; y < ey; y++) {
|
||||
int offset = y * width + bx * BLOCK_SIZE;
|
||||
int ex = offset + BLOCK_SIZE;
|
||||
for (; offset < ex; offset++) {
|
||||
// int v = luminancesInt[offset] & 0xff;
|
||||
t += luminancesInt[offset];
|
||||
}
|
||||
}
|
||||
output[by * aw + bx] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FastWindowBinarizer::cumulative(int* data, int* output, int _width, int _height) {
|
||||
int ah = _height;
|
||||
int aw = _width;
|
||||
int ow = _width + 1;
|
||||
// int[][] totals = new int[ah + 1][aw + 1];
|
||||
// int* rowTotals = new int[ah*ow];
|
||||
|
||||
for (int y = 0; y < ah; y++) {
|
||||
int* row = _rowTotals + (y * ow);
|
||||
int* rowdata = data + (y * aw);
|
||||
int t = 0;
|
||||
row[0] = t;
|
||||
|
||||
for (int x = 0; x < aw; x++) {
|
||||
t += rowdata[x];
|
||||
row[x + 1] = t;
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x <= aw; x++) {
|
||||
output[x] = 0; // First row
|
||||
int t = 0;
|
||||
|
||||
for (int y = 0; y < ah; y++) {
|
||||
t += _rowTotals[y * ow + x];
|
||||
output[(y + 1) * ow + x] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FastWindowBinarizer::fastIntegral(const unsigned char* inputMatrix,
|
||||
unsigned int* outputMatrix) {
|
||||
// memset(outputMatrix,0,sizeof(int)*(height+1)*(width+1));
|
||||
// unsigned int *columnSum = new unsigned int[width]; // sum of each column
|
||||
// calculate integral of the first line
|
||||
outputMatrix[0] = outputMatrix[width + 1] = 0;
|
||||
for (int i = 0; i < width; i++) {
|
||||
// columnSum[i]=inputMatrix[i];
|
||||
outputMatrix[i + 1] = 0;
|
||||
outputMatrix[width + 1 + i + 1] = outputMatrix[width + 1 + i] + inputMatrix[i];
|
||||
}
|
||||
for (int i = 1; i < height; i++) {
|
||||
const unsigned char* psi = inputMatrix + i * width;
|
||||
unsigned int* pdi = outputMatrix + (i + 1) * (width + 1);
|
||||
// first column of each line
|
||||
pdi[0] = 0;
|
||||
pdi[1] = psi[0];
|
||||
int row_sum = psi[0];
|
||||
// other columns
|
||||
for (int j = 1; j < width; j++) {
|
||||
row_sum += psi[j];
|
||||
pdi[j + 1] = pdi[j + 1 - width - 1] + row_sum;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int FastWindowBinarizer::binarizeImage1(ErrorHandler& err_handler) {
|
||||
LuminanceSource& source = *getLuminanceSource();
|
||||
Ref<BitMatrix> matrix(new BitMatrix(width, height, err_handler));
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
|
||||
ArrayRef<char> localLuminances = source.getMatrix();
|
||||
|
||||
unsigned char* src = (unsigned char*)localLuminances->data();
|
||||
unsigned char* dst = matrix->getPtr();
|
||||
fastWindow(src, dst, err_handler);
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
|
||||
matrix0_ = matrix;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FastWindowBinarizer::fastWindow(const unsigned char* src, unsigned char* dst,
|
||||
ErrorHandler& err_handler) {
|
||||
int r = (int)(min(width, height) * WINDOW_FRACTION / BLOCK_SIZE / 2 + 1);
|
||||
const int NEWH_BLOCK_SIZE = BLOCK_SIZE * r;
|
||||
if (height < NEWH_BLOCK_SIZE || width < NEWH_BLOCK_SIZE) {
|
||||
matrix_ = GlobalHistogramBinarizer::getBlackMatrix(err_handler);
|
||||
return;
|
||||
}
|
||||
const unsigned char* _img = src;
|
||||
fastIntegral(_img, _internal);
|
||||
int aw = width / BLOCK_SIZE;
|
||||
int ah = height / BLOCK_SIZE;
|
||||
memset(dst, 0, sizeof(char) * height * width);
|
||||
for (int ai = 0; ai < ah; ai++) {
|
||||
int top = max(0, ((ai - r + 1) * BLOCK_SIZE));
|
||||
int bottom = min(height, (ai + r) * BLOCK_SIZE);
|
||||
unsigned int* pt = _internal + top * (width + 1);
|
||||
unsigned int* pb = _internal + bottom * (width + 1);
|
||||
for (int aj = 0; aj < aw; aj++) {
|
||||
int left = max(0, (aj - r + 1) * BLOCK_SIZE);
|
||||
int right = min(width, (aj + r) * BLOCK_SIZE);
|
||||
unsigned int block = pb[right] + pt[left] - pt[right] - pb[left];
|
||||
int pixels = (bottom - top) * (right - left);
|
||||
int avg = (int)block / pixels;
|
||||
for (int bi = ai * BLOCK_SIZE; bi < height && bi < (ai + 1) * BLOCK_SIZE; bi++) {
|
||||
const unsigned char* psi = src + bi * width;
|
||||
unsigned char* pdi = dst + bi * width;
|
||||
for (int bj = aj * BLOCK_SIZE; bj < width && bj < (aj + 1) * BLOCK_SIZE; bj++) {
|
||||
if ((int)psi[bj] < avg)
|
||||
pdi[bj] = 1;
|
||||
else
|
||||
pdi[bj] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// delete [] _internal;
|
||||
return;
|
||||
}
|
||||
|
||||
int FastWindowBinarizer::binarizeImage0(ErrorHandler& err_handler) {
|
||||
// if (matrix_) {
|
||||
// return matrix_;
|
||||
//}
|
||||
|
||||
LuminanceSource& source = *getLuminanceSource();
|
||||
if (width >= BLOCK_SIZE && height >= BLOCK_SIZE) {
|
||||
int r = (int)(min(width, height) * WINDOW_FRACTION / BLOCK_SIZE / 2 + 1);
|
||||
|
||||
int aw = width / BLOCK_SIZE;
|
||||
int ah = height / BLOCK_SIZE;
|
||||
int ow = aw + 1;
|
||||
|
||||
ArrayRef<char> _luminances = source.getMatrix();
|
||||
|
||||
// Get luminances for int value first
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
_luminancesInt[i] = _luminances[i] & 0xff;
|
||||
}
|
||||
|
||||
calcBlockTotals(_luminancesInt, _blockTotals, aw, ah);
|
||||
|
||||
cumulative(_blockTotals, _totals, aw, ah);
|
||||
|
||||
Ref<BitMatrix> newMatrix(new BitMatrix(width, height, err_handler));
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
unsigned char* newimg = newMatrix->getPtr();
|
||||
for (int by = 0; by < ah; by++) {
|
||||
int top = max(0, by - r + 1);
|
||||
int bottom = min(ah, by + r);
|
||||
|
||||
for (int bx = 0; bx < aw; bx++) {
|
||||
int left = max(0, bx - r + 1);
|
||||
int right = min(aw, bx + r);
|
||||
|
||||
int block = _totals[bottom * ow + right] + _totals[top * ow + left] -
|
||||
_totals[top * ow + right] - _totals[bottom * ow + left];
|
||||
|
||||
int pixels = (bottom - top) * (right - left) * BLOCK_SIZE * BLOCK_SIZE;
|
||||
int avg = block / pixels;
|
||||
|
||||
for (int y = by * BLOCK_SIZE; y < (by + 1) * BLOCK_SIZE; y++) {
|
||||
// int offset = y*width;
|
||||
int* plumint = _luminancesInt + y * width;
|
||||
unsigned char* pn = newimg + y * width;
|
||||
for (int x = bx * BLOCK_SIZE; x < (bx + 1) * BLOCK_SIZE; x++) {
|
||||
// int pixel = luminances[y*width + x] & 0xff;
|
||||
// if(plumint[x] < avg)
|
||||
// newMatrix->set(x, y);
|
||||
if (plumint[x] < avg)
|
||||
pn[x] = 1;
|
||||
else
|
||||
pn[x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// delete[] data;
|
||||
matrix_ = newMatrix;
|
||||
} else {
|
||||
// If the image is too small, fall back to the global histogram
|
||||
// approach.
|
||||
matrix_ = GlobalHistogramBinarizer::getBlackMatrix(err_handler);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_DETECTOR_RESULT_HPP__
|
||||
#define __ZXING_COMMON_DETECTOR_RESULT_HPP__
|
||||
|
||||
#include "../../binarizer.hpp"
|
||||
#include "../../errorhandler.hpp"
|
||||
#include "../bitarray.hpp"
|
||||
#include "../bitmatrix.hpp"
|
||||
#include "global_histogram_binarizer.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class FastWindowBinarizer : public GlobalHistogramBinarizer {
|
||||
private:
|
||||
Ref<BitMatrix> matrix_;
|
||||
Ref<BitArray> cached_row_;
|
||||
|
||||
int* _luminancesInt;
|
||||
int* _blockTotals;
|
||||
int* _totals;
|
||||
int* _rowTotals;
|
||||
|
||||
unsigned int* _internal;
|
||||
|
||||
public:
|
||||
explicit FastWindowBinarizer(Ref<LuminanceSource> source);
|
||||
virtual ~FastWindowBinarizer();
|
||||
|
||||
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler& err_handler) override;
|
||||
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) override;
|
||||
|
||||
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
|
||||
|
||||
private:
|
||||
void calcBlockTotals(int* luminancesInt, int* output, int aw, int ah);
|
||||
void cumulative(int* data, int* output, int _width, int _height);
|
||||
int binarizeImage0(ErrorHandler& err_handler);
|
||||
void fastIntegral(const unsigned char* inputMatrix, unsigned int* outputMatrix);
|
||||
int binarizeImage1(ErrorHandler& err_handler);
|
||||
void fastWindow(const unsigned char* src, unsigned char* dst, ErrorHandler& err_handler);
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_DETECTOR_RESULT_HPP__
|
||||
@@ -0,0 +1,308 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../../precomp.hpp"
|
||||
#include "global_histogram_binarizer.hpp"
|
||||
using zxing::GlobalHistogramBinarizer;
|
||||
|
||||
namespace {
|
||||
const int LUMINANCE_BITS = 5;
|
||||
const int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
|
||||
const int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
|
||||
const ArrayRef<char> EMPTY(0);
|
||||
} // namespace
|
||||
|
||||
GlobalHistogramBinarizer::GlobalHistogramBinarizer(Ref<LuminanceSource> source)
|
||||
: Binarizer(source), luminances(EMPTY), buckets(LUMINANCE_BUCKETS) {
|
||||
filtered = false;
|
||||
}
|
||||
|
||||
GlobalHistogramBinarizer::~GlobalHistogramBinarizer() {}
|
||||
|
||||
void GlobalHistogramBinarizer::initArrays(int luminanceSize) {
|
||||
if (luminances->size() < luminanceSize) {
|
||||
luminances = ArrayRef<char>(luminanceSize);
|
||||
}
|
||||
for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
|
||||
buckets[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Applies simple sharpening to the row data to improve performance of the 1D
|
||||
// readers.
|
||||
Ref<BitArray> GlobalHistogramBinarizer::getBlackRow(int y, Ref<BitArray> row,
|
||||
ErrorHandler& err_handler) {
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
if (!matrix0_) {
|
||||
binarizeImage0(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitArray>();
|
||||
}
|
||||
// Call parent getBlackMatrix to get current matrix
|
||||
return Binarizer::getBlackRow(y, row, err_handler);
|
||||
}
|
||||
|
||||
// Does not sharpen the data, as this call is intended to only be used by 2D
|
||||
// readers.
|
||||
Ref<BitMatrix> GlobalHistogramBinarizer::getBlackMatrix(ErrorHandler& err_handler) {
|
||||
binarizeImage0(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitMatrix>();
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
// Call parent getBlackMatrix to get current matrix
|
||||
return Binarizer::getBlackMatrix(err_handler);
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
int GlobalHistogramBinarizer::estimateBlackPoint(ArrayRef<int> const& _buckets,
|
||||
ErrorHandler& err_handler) {
|
||||
// Find tallest peak in histogram
|
||||
int numBuckets = _buckets->size();
|
||||
int maxBucketCount = 0;
|
||||
int firstPeak = 0;
|
||||
int firstPeakSize = 0;
|
||||
for (int x = 0; x < numBuckets; x++) {
|
||||
if (_buckets[x] > firstPeakSize) {
|
||||
firstPeak = x;
|
||||
firstPeakSize = _buckets[x];
|
||||
}
|
||||
if (_buckets[x] > maxBucketCount) {
|
||||
maxBucketCount = _buckets[x];
|
||||
}
|
||||
}
|
||||
|
||||
// Find second-tallest peak -- well, another peak that is tall and not
|
||||
// so close to the first one
|
||||
int secondPeak = 0;
|
||||
int secondPeakScore = 0;
|
||||
for (int x = 0; x < numBuckets; x++) {
|
||||
int distanceToBiggest = x - firstPeak;
|
||||
// Encourage more distant second peaks by multiplying by square of
|
||||
// distance
|
||||
int score = _buckets[x] * distanceToBiggest * distanceToBiggest;
|
||||
if (score > secondPeakScore) {
|
||||
secondPeak = x;
|
||||
secondPeakScore = score;
|
||||
}
|
||||
}
|
||||
// Make sure firstPeak corresponds to the black peak.
|
||||
if (firstPeak > secondPeak) {
|
||||
int temp = firstPeak;
|
||||
firstPeak = secondPeak;
|
||||
secondPeak = temp;
|
||||
}
|
||||
|
||||
// Kind of arbitrary; if the two peaks are very close, then we figure there
|
||||
// is so little dynamic range in the image, that discriminating black and
|
||||
// white is too error-prone. Decoding the image/line is either pointless, or
|
||||
// may in some cases lead to a false positive for 1D formats, which are
|
||||
// relatively lenient. We arbitrarily say "close" is "<= 1/16 of the total
|
||||
// histogram buckets apart" std::cerr << "! " << secondPeak << " " <<
|
||||
// firstPeak << " " << numBuckets << std::endl;
|
||||
if (secondPeak - firstPeak <= numBuckets >> 4) {
|
||||
err_handler = NotFoundErrorHandler("NotFound GlobalHistogramBinarizer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Find a valley between them that is low and closer to the white peak
|
||||
int bestValley = secondPeak - 1;
|
||||
int bestValleyScore = -1;
|
||||
for (int x = secondPeak - 1; x > firstPeak; x--) {
|
||||
int fromFirst = x - firstPeak;
|
||||
// Favor a "valley" that is not too close to either peak -- especially
|
||||
// not the black peak -- and that has a low value of course
|
||||
int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]);
|
||||
if (score > bestValleyScore) {
|
||||
bestValley = x;
|
||||
bestValleyScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
// std::cerr << "bps " << (bestValley << LUMINANCE_SHIFT) << std::endl;
|
||||
return bestValley << LUMINANCE_SHIFT;
|
||||
}
|
||||
|
||||
// codes from sagazhou, only works well on one dataset
|
||||
int GlobalHistogramBinarizer::estimateBlackPoint2(ArrayRef<int> const& _buckets) {
|
||||
int midValue = LUMINANCE_BUCKETS / 2 + 1;
|
||||
// Find tallest and lowest peaks in histogram
|
||||
// const int numBuckets = buckets->size();
|
||||
int maxPointArray[LUMINANCE_BUCKETS] = {0};
|
||||
int maxCrusor = 0;
|
||||
int maxValue = 0, maxIndex = 0;
|
||||
int minPointArray[LUMINANCE_BUCKETS] = {0};
|
||||
int minCrusor = 0;
|
||||
|
||||
for (int i = 2; i < LUMINANCE_BUCKETS - 3; i++) {
|
||||
if (_buckets[i] < _buckets[i + 1] && _buckets[i] < _buckets[i + 2] &&
|
||||
_buckets[i] < _buckets[i - 1] && _buckets[i] < _buckets[i - 2]) {
|
||||
minPointArray[minCrusor++] = i;
|
||||
} else if (_buckets[i] > _buckets[i + 1] && _buckets[i] > _buckets[i + 2] &&
|
||||
_buckets[i] > _buckets[i - 1] && _buckets[i] > _buckets[i - 2]) {
|
||||
maxPointArray[maxCrusor++] = i;
|
||||
if (_buckets[i] > maxValue) {
|
||||
maxValue = _buckets[i];
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool bSlantBlack = true;
|
||||
// most pixels are black
|
||||
for (int i = 0; i < maxCrusor; ++i) {
|
||||
if (maxPointArray[i] > midValue) {
|
||||
bSlantBlack = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool bSlantWhite = true;
|
||||
// most pixels are white
|
||||
for (int i = 0; i < maxCrusor; ++i) {
|
||||
if (maxPointArray[i] < midValue) {
|
||||
bSlantWhite = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bSlantBlack) {
|
||||
int start = maxIndex + 30;
|
||||
int end = midValue;
|
||||
|
||||
if (minCrusor == 0) // unimodal
|
||||
{
|
||||
return 255;
|
||||
} else {
|
||||
int mostLeftIndex = 0;
|
||||
bool bFind = false;
|
||||
|
||||
for (int i = 0; i < minCrusor; ++i) // wave motion
|
||||
{
|
||||
if (minPointArray[i] > start && minPointArray[i] < end) {
|
||||
mostLeftIndex = minPointArray[i];
|
||||
bFind = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bFind) {
|
||||
return mostLeftIndex;
|
||||
} else {
|
||||
return 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bSlantWhite) {
|
||||
int start = midValue;
|
||||
int end = maxIndex - 30;
|
||||
|
||||
if (minCrusor == 0) // unimodal
|
||||
{
|
||||
return 0;
|
||||
} else {
|
||||
int mostRightIndex = 0;
|
||||
bool bFind = false;
|
||||
|
||||
for (int i = 0; i < minCrusor; ++i) // wave motion
|
||||
{
|
||||
if (minPointArray[i] > start && minPointArray[i] < end) {
|
||||
mostRightIndex = i;
|
||||
bFind = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bFind) {
|
||||
return mostRightIndex;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// balanced distribution
|
||||
if (maxIndex < midValue) {
|
||||
// the minest min value
|
||||
if (minCrusor == 0) {
|
||||
return 255; // all black
|
||||
} else {
|
||||
int start = maxIndex + 30;
|
||||
int end = 253;
|
||||
|
||||
for (int i = 0; i < minCrusor; ++i) // wave motion
|
||||
{
|
||||
if (minPointArray[i] > start && minPointArray[i] < end) {
|
||||
return minPointArray[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// maxest min value
|
||||
if (minCrusor == 0) {
|
||||
return 0; // white
|
||||
} else {
|
||||
int start = 0;
|
||||
int end = maxIndex - 30;
|
||||
int mostRightIndex = 0;
|
||||
|
||||
for (int i = 0; i < minCrusor; ++i) // wave motion
|
||||
{
|
||||
if (minPointArray[i] > start && minPointArray[i] < end) {
|
||||
mostRightIndex = minPointArray[i];
|
||||
}
|
||||
}
|
||||
|
||||
return mostRightIndex;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GlobalHistogramBinarizer::binarizeImage0(ErrorHandler& err_handler) {
|
||||
LuminanceSource& source = *getLuminanceSource();
|
||||
Ref<BitMatrix> matrix(new BitMatrix(width, height, err_handler));
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
// Quickly calculates the histogram by sampling four rows from the image.
|
||||
// This proved to be more robust on the blackbox tests than sampling a
|
||||
// diagonal as we used to do.
|
||||
initArrays(width);
|
||||
ArrayRef<int> localBuckets = buckets;
|
||||
|
||||
for (int y = 1; y < 5; y++) {
|
||||
int row = height * y / 5;
|
||||
ArrayRef<char> localLuminances = source.getRow(row, luminances, err_handler);
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
int right = (width << 2) / 5;
|
||||
for (int x = width / 5; x < right; x++) {
|
||||
int pixel = localLuminances[x] & 0xff;
|
||||
localBuckets[pixel >> LUMINANCE_SHIFT]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int blackPoint = estimateBlackPoint(localBuckets, err_handler);
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
|
||||
ArrayRef<char> localLuminances = source.getMatrix();
|
||||
for (int y = 0; y < height; y++) {
|
||||
int offset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pixel = localLuminances[offset + x] & 0xff;
|
||||
if (pixel < blackPoint) {
|
||||
matrix->set(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matrix0_ = matrix;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Ref<Binarizer> GlobalHistogramBinarizer::createBinarizer(Ref<LuminanceSource> source) {
|
||||
return Ref<Binarizer>(new GlobalHistogramBinarizer(source));
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_GLOBAL_HISTOGRAM_BINARIZER_HPP__
|
||||
#define __ZXING_COMMON_GLOBAL_HISTOGRAM_BINARIZER_HPP__
|
||||
|
||||
#include "../../binarizer.hpp"
|
||||
#include "../../errorhandler.hpp"
|
||||
#include "../array.hpp"
|
||||
#include "../bitarray.hpp"
|
||||
#include "../bitmatrix.hpp"
|
||||
#include "../bytematrix.hpp"
|
||||
|
||||
using zxing::ArrayRef;
|
||||
using zxing::Binarizer;
|
||||
using zxing::BitArray;
|
||||
using zxing::BitMatrix;
|
||||
using zxing::ByteMatrix;
|
||||
using zxing::ErrorHandler;
|
||||
using zxing::LuminanceSource;
|
||||
using zxing::Ref;
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class GlobalHistogramBinarizer : public Binarizer {
|
||||
protected:
|
||||
ArrayRef<char> luminances;
|
||||
ArrayRef<int> buckets;
|
||||
|
||||
public:
|
||||
explicit GlobalHistogramBinarizer(Ref<LuminanceSource> source);
|
||||
virtual ~GlobalHistogramBinarizer();
|
||||
|
||||
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler &err_handler) override;
|
||||
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler &err_handler) override;
|
||||
int estimateBlackPoint(ArrayRef<int> const &buckets, ErrorHandler &err_handler);
|
||||
int estimateBlackPoint2(ArrayRef<int> const &buckets);
|
||||
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
|
||||
|
||||
private:
|
||||
int binarizeImage0(ErrorHandler &err_handler);
|
||||
void initArrays(int luminanceSize);
|
||||
bool filtered;
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_GLOBAL_HISTOGRAM_BINARIZER_HPP__
|
||||
@@ -0,0 +1,419 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../../precomp.hpp"
|
||||
#include "hybrid_binarizer.hpp"
|
||||
|
||||
using zxing::HybridBinarizer;
|
||||
using zxing::BINARIZER_BLOCK;
|
||||
|
||||
// This class uses 5*5 blocks to compute local luminance, where each block is
|
||||
// 8*8 pixels So this is the smallest dimension in each axis we can accept.
|
||||
namespace {
|
||||
const int BLOCK_SIZE_POWER = 3;
|
||||
const int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER; // ...0100...00
|
||||
const int BLOCK_SIZE_MASK = BLOCK_SIZE - 1; // ...0011...11
|
||||
const int MINIMUM_DIMENSION = BLOCK_SIZE * 5;
|
||||
#ifdef USE_SET_INT
|
||||
const int BITS_PER_BYTE = 8;
|
||||
const int BITS_PER_WORD = BitMatrix::bitsPerWord;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
HybridBinarizer::HybridBinarizer(Ref<LuminanceSource> source) : GlobalHistogramBinarizer(source) {
|
||||
int subWidth = width >> BLOCK_SIZE_POWER;
|
||||
if ((width & BLOCK_SIZE_MASK) != 0) {
|
||||
subWidth++;
|
||||
}
|
||||
int subHeight = height >> BLOCK_SIZE_POWER;
|
||||
if ((height & BLOCK_SIZE_MASK) != 0) {
|
||||
subHeight++;
|
||||
}
|
||||
|
||||
grayByte_ = source->getByteMatrix();
|
||||
|
||||
blocks_ = getBlockArray(subWidth * subHeight);
|
||||
|
||||
subWidth_ = subWidth;
|
||||
subHeight_ = subHeight;
|
||||
|
||||
initBlocks();
|
||||
initBlockIntegral();
|
||||
}
|
||||
|
||||
HybridBinarizer::~HybridBinarizer() {
|
||||
}
|
||||
|
||||
Ref<Binarizer> HybridBinarizer::createBinarizer(Ref<LuminanceSource> source) {
|
||||
return Ref<Binarizer>(new GlobalHistogramBinarizer(source));
|
||||
}
|
||||
|
||||
int HybridBinarizer::initBlockIntegral() {
|
||||
blockIntegralWidth = subWidth_ + 1;
|
||||
blockIntegralHeight = subHeight_ + 1;
|
||||
blockIntegral_ = new Array<int>(blockIntegralWidth * blockIntegralHeight);
|
||||
|
||||
int* integral = blockIntegral_->data();
|
||||
|
||||
// unsigned char* therow = grayByte_->getByteRow(0);
|
||||
|
||||
// first row only
|
||||
int rs = 0;
|
||||
|
||||
for (int j = 0; j < blockIntegralWidth; j++) {
|
||||
integral[j] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < blockIntegralHeight; i++) {
|
||||
integral[i * blockIntegralWidth] = 0;
|
||||
}
|
||||
|
||||
// remaining cells are sum above and to the left
|
||||
int offsetBlock = 0;
|
||||
int offsetIntegral = 0;
|
||||
|
||||
for (int i = 0; i < subHeight_; ++i) {
|
||||
// therow = grayByte_->getByteRow(i);
|
||||
offsetBlock = i * subWidth_;
|
||||
offsetIntegral = (i + 1) * blockIntegralWidth;
|
||||
rs = 0;
|
||||
|
||||
for (int j = 0; j < subWidth_; ++j) {
|
||||
rs += blocks_[offsetBlock + j].threshold;
|
||||
integral[offsetIntegral + j + 1] = rs + integral[offsetIntegral - blockIntegralWidth + j + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the final BitMatrix once for all requests. This could be called
|
||||
* once from the constructor instead, but there are some advantages to doing it
|
||||
* lazily, such as making profiling easier, and not doing heavy lifting when
|
||||
* callers don't expect it.
|
||||
*/
|
||||
Ref<BitMatrix> HybridBinarizer::getBlackMatrix(ErrorHandler& err_handler) {
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
if (!matrix0_) {
|
||||
binarizeByBlock(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitMatrix>();
|
||||
}
|
||||
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
// Call parent getBlackMatrix to get current matrix
|
||||
return Binarizer::getBlackMatrix(err_handler);
|
||||
}
|
||||
|
||||
#if 1
|
||||
/**
|
||||
* Calculate black row from BitMatrix
|
||||
* If BitMatrix has been calculated then just get the row
|
||||
* If BitMatrix has not been calculated then call getBlackMatrix first
|
||||
*/
|
||||
Ref<BitArray> HybridBinarizer::getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) {
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
if (!matrix0_) {
|
||||
binarizeByBlock(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitArray>();
|
||||
}
|
||||
|
||||
// Call parent getBlackMatrix to get current matrix
|
||||
return Binarizer::getBlackRow(y, row, err_handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
inline int cap(int value, int min, int max) {
|
||||
return value < min ? min : value > max ? max : value;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
// For each block in the image, calculates the average black point using a 5*5
|
||||
// grid of the blocks around it. Also handles the corner cases (fractional
|
||||
// blocks are computed based on the last pixels in the row/column which are also
|
||||
// used in the previous block.)
|
||||
|
||||
#define THRES_BLOCKSIZE 2
|
||||
|
||||
// No use of level now
|
||||
ArrayRef<int> HybridBinarizer::getBlackPoints() {
|
||||
int blackWidth, blackHeight;
|
||||
|
||||
blackWidth = subWidth_;
|
||||
blackHeight = subHeight_;
|
||||
|
||||
ArrayRef<int> blackPoints(blackWidth * blackHeight);
|
||||
|
||||
int* blackArray = blackPoints->data();
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < blackHeight; i++) {
|
||||
offset = i * blackWidth;
|
||||
for (int j = 0; j < blackWidth; j++) {
|
||||
blackArray[offset + j] = blocks_[offset + j].threshold;
|
||||
}
|
||||
}
|
||||
|
||||
return blackPoints;
|
||||
}
|
||||
|
||||
// Original code 20140606
|
||||
void HybridBinarizer::calculateThresholdForBlock(Ref<ByteMatrix>& _luminances, int subWidth,
|
||||
int subHeight, int SIZE_POWER,
|
||||
// ArrayRef<int> &blackPoints,
|
||||
Ref<BitMatrix> const& matrix,
|
||||
ErrorHandler& err_handler) {
|
||||
int block_size = 1 << SIZE_POWER;
|
||||
|
||||
int maxYOffset = height - block_size;
|
||||
int maxXOffset = width - block_size;
|
||||
|
||||
int* blockIntegral = blockIntegral_->data();
|
||||
|
||||
int blockArea = ((2 * THRES_BLOCKSIZE + 1) * (2 * THRES_BLOCKSIZE + 1));
|
||||
|
||||
for (int y = 0; y < subHeight; y++) {
|
||||
int yoffset = y << SIZE_POWER;
|
||||
if (yoffset > maxYOffset) {
|
||||
yoffset = maxYOffset;
|
||||
}
|
||||
for (int x = 0; x < subWidth; x++) {
|
||||
int xoffset = x << SIZE_POWER;
|
||||
if (xoffset > maxXOffset) {
|
||||
xoffset = maxXOffset;
|
||||
}
|
||||
int left = cap(x, THRES_BLOCKSIZE, subWidth - THRES_BLOCKSIZE - 1);
|
||||
int top = cap(y, THRES_BLOCKSIZE, subHeight - THRES_BLOCKSIZE - 1);
|
||||
|
||||
int sum = 0;
|
||||
// int sum2 = 0;
|
||||
|
||||
int offset1 = (top - THRES_BLOCKSIZE) * blockIntegralWidth + left - THRES_BLOCKSIZE;
|
||||
int offset2 = (top + THRES_BLOCKSIZE + 1) * blockIntegralWidth + left - THRES_BLOCKSIZE;
|
||||
|
||||
int blocksize = THRES_BLOCKSIZE * 2 + 1;
|
||||
|
||||
sum = blockIntegral[offset1] - blockIntegral[offset1 + blocksize] -
|
||||
blockIntegral[offset2] + blockIntegral[offset2 + blocksize];
|
||||
|
||||
int average = sum / blockArea;
|
||||
thresholdBlock(_luminances, xoffset, yoffset, average, matrix, err_handler);
|
||||
if (err_handler.ErrCode()) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_SET_INT
|
||||
void HybridBinarizer::thresholdFourBlocks(Ref<ByteMatrix>& luminances, int xoffset, int yoffset,
|
||||
int* thresholds, int stride,
|
||||
Ref<BitMatrix> const& matrix) {
|
||||
int setIntCircle = BITS_PER_WORD / BITS_PER_BYTE;
|
||||
for (int y = 0; y < BLOCK_SIZE; y++) {
|
||||
unsigned char* pTemp = luminances->getByteRow(yoffset + y);
|
||||
pTemp = pTemp + xoffset;
|
||||
unsigned int valueInt = 0;
|
||||
int bitPosition = 0;
|
||||
for (int k = 0; k < setIntCircle; k++) {
|
||||
for (int x = 0; x < BLOCK_SIZE; x++) {
|
||||
int pixel = *pTemp++;
|
||||
if (pixel <= thresholds[k]) {
|
||||
// bitPosition=(3-k)*8+x;
|
||||
valueInt |= (unsigned int)1 << bitPosition;
|
||||
}
|
||||
bitPosition++;
|
||||
}
|
||||
}
|
||||
matrix->setIntOneTime(xoffset, yoffset + y, valueInt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Applies a single threshold to a block of pixels
|
||||
void HybridBinarizer::thresholdBlock(Ref<ByteMatrix>& _luminances, int xoffset, int yoffset,
|
||||
int threshold, Ref<BitMatrix> const& matrix,
|
||||
ErrorHandler& err_handler) {
|
||||
int rowBitsSize = matrix->getRowBitsSize();
|
||||
int rowSize = width;
|
||||
|
||||
int rowBitStep = rowBitsSize - BLOCK_SIZE;
|
||||
int rowStep = rowSize - BLOCK_SIZE;
|
||||
|
||||
unsigned char* pTemp = _luminances->getByteRow(yoffset, err_handler);
|
||||
if (err_handler.ErrCode()) return;
|
||||
bool* bpTemp = matrix->getRowBoolPtr(yoffset);
|
||||
|
||||
pTemp += xoffset;
|
||||
bpTemp += xoffset;
|
||||
|
||||
for (int y = 0; y < BLOCK_SIZE; y++) {
|
||||
for (int x = 0; x < BLOCK_SIZE; x++) {
|
||||
// comparison needs to be <= so that black == 0 pixels are black
|
||||
// even if the threshold is 0.
|
||||
*bpTemp++ = (*pTemp++ <= threshold) ? true : false;
|
||||
}
|
||||
|
||||
pTemp += rowBitStep;
|
||||
bpTemp += rowStep;
|
||||
}
|
||||
}
|
||||
|
||||
void HybridBinarizer::thresholdIrregularBlock(Ref<ByteMatrix>& _luminances, int xoffset,
|
||||
int yoffset, int blockWidth, int blockHeight,
|
||||
int threshold, Ref<BitMatrix> const& matrix,
|
||||
ErrorHandler& err_handler) {
|
||||
for (int y = 0; y < blockHeight; y++) {
|
||||
unsigned char* pTemp = _luminances->getByteRow(yoffset + y, err_handler);
|
||||
if (err_handler.ErrCode()) return;
|
||||
pTemp = pTemp + xoffset;
|
||||
for (int x = 0; x < blockWidth; x++) {
|
||||
// comparison needs to be <= so that black == 0 pixels are black
|
||||
// even if the threshold is 0.
|
||||
int pixel = *pTemp++;
|
||||
if (pixel <= threshold) {
|
||||
matrix->set(xoffset + x, yoffset + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline int getBlackPointFromNeighbors(ArrayRef<BINARIZER_BLOCK> block, int subWidth, int x, int y) {
|
||||
return (block[(y - 1) * subWidth + x].threshold + 2 * block[y * subWidth + x - 1].threshold +
|
||||
block[(y - 1) * subWidth + x - 1].threshold) >>
|
||||
2;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
#define MIN_DYNAMIC_RANGE 24
|
||||
|
||||
// Calculates a single black point for each block of pixels and saves it away.
|
||||
int HybridBinarizer::initBlocks() {
|
||||
Ref<ByteMatrix>& _luminances = grayByte_;
|
||||
int subWidth = subWidth_;
|
||||
int subHeight = subHeight_;
|
||||
|
||||
unsigned char* bytes = _luminances->bytes;
|
||||
|
||||
const int minDynamicRange = 24;
|
||||
|
||||
for (int y = 0; y < subHeight; y++) {
|
||||
int yoffset = y << BLOCK_SIZE_POWER;
|
||||
int maxYOffset = height - BLOCK_SIZE;
|
||||
if (yoffset > maxYOffset) yoffset = maxYOffset;
|
||||
for (int x = 0; x < subWidth; x++) {
|
||||
int xoffset = x << BLOCK_SIZE_POWER;
|
||||
int maxXOffset = width - BLOCK_SIZE;
|
||||
if (xoffset > maxXOffset) xoffset = maxXOffset;
|
||||
int sum = 0;
|
||||
int min = 0xFF;
|
||||
int max = 0;
|
||||
for (int yy = 0, offset = yoffset * width + xoffset; yy < BLOCK_SIZE;
|
||||
yy++, offset += width) {
|
||||
for (int xx = 0; xx < BLOCK_SIZE; xx++) {
|
||||
// int pixel = luminances->bytes[offset + xx] & 0xFF;
|
||||
int pixel = bytes[offset + xx];
|
||||
sum += pixel;
|
||||
|
||||
// still looking for good contrast
|
||||
if (pixel < min) {
|
||||
min = pixel;
|
||||
}
|
||||
if (pixel > max) {
|
||||
max = pixel;
|
||||
}
|
||||
}
|
||||
|
||||
// short-circuit min/max tests once dynamic range is met
|
||||
if (max - min > minDynamicRange) {
|
||||
// finish the rest of the rows quickly
|
||||
for (yy++, offset += width; yy < BLOCK_SIZE; yy++, offset += width) {
|
||||
for (int xx = 0; xx < BLOCK_SIZE; xx += 2) {
|
||||
sum += bytes[offset + xx];
|
||||
sum += bytes[offset + xx + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks_[y * subWidth + x].min = min;
|
||||
blocks_[y * subWidth + x].max = max;
|
||||
blocks_[y * subWidth + x].sum = sum;
|
||||
blocks_[y * subWidth + x].threshold =
|
||||
getBlockThreshold(x, y, subWidth, sum, min, max, minDynamicRange, BLOCK_SIZE_POWER);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int HybridBinarizer::getBlockThreshold(int x, int y, int subWidth, int sum, int min, int max,
|
||||
int minDynamicRange, int SIZE_POWER) {
|
||||
// See
|
||||
// http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
|
||||
|
||||
// The default estimate is the average of the values in the block.
|
||||
int average = sum >> (SIZE_POWER * 2);
|
||||
if (max - min <= minDynamicRange) {
|
||||
// If variation within the block is low, assume this is a block withe
|
||||
// only light or only dark pixels. In that case we do not want to use
|
||||
// the average, as it would divide this low contrast area into black and
|
||||
// white pixels, essentially creating data out of noise. The default
|
||||
// assumption is that the block is light/background. Since no estimate
|
||||
// for the level of dark pixels exists locally, use half the min for the
|
||||
// block.
|
||||
average = min >> 1;
|
||||
if (y > 0 && x > 0) {
|
||||
// Correct the "white background" assumption for blocks that have
|
||||
// neighbors by comparing the pixels in this block to the previously
|
||||
// calculated black points. This is based on the fact that dark
|
||||
// barcode symbology is always surrounded by some amout of light
|
||||
// background for which reasonable black point estimates were made.
|
||||
// The bp estimated at the boundaries is used for the interior.
|
||||
int bp = getBlackPointFromNeighbors(blocks_, subWidth, x, y);
|
||||
// The (min<bp) is arbitrary but works better than other heuristics
|
||||
// that were tried.
|
||||
if (min < bp) {
|
||||
average = bp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// blocks_[y * subWidth + x].average = average;
|
||||
// blocks_[y * subWidth + x].threshold = average;
|
||||
|
||||
return average;
|
||||
}
|
||||
|
||||
|
||||
int HybridBinarizer::binarizeByBlock(ErrorHandler& err_handler) {
|
||||
if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION) {
|
||||
Ref<BitMatrix> newMatrix(new BitMatrix(width, height, err_handler));
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
|
||||
calculateThresholdForBlock(grayByte_, subWidth_, subHeight_, BLOCK_SIZE_POWER, newMatrix,
|
||||
err_handler);
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
|
||||
matrix0_ = newMatrix;
|
||||
|
||||
} else {
|
||||
// If the image is too small, fall back to the global histogram
|
||||
// approach.
|
||||
matrix0_ = GlobalHistogramBinarizer::getBlackMatrix(err_handler);
|
||||
if (err_handler.ErrCode()) return 1;
|
||||
}
|
||||
// return matrix0_;
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_HYBRID_BINARIZER_HPP__
|
||||
#define __ZXING_COMMON_HYBRID_BINARIZER_HPP__
|
||||
|
||||
#include "../../binarizer.hpp"
|
||||
#include "../../errorhandler.hpp"
|
||||
#include "../bitarray.hpp"
|
||||
#include "../bitmatrix.hpp"
|
||||
#include "../bytematrix.hpp"
|
||||
#include "global_histogram_binarizer.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class HybridBinarizer : public GlobalHistogramBinarizer {
|
||||
private:
|
||||
Ref<ByteMatrix> grayByte_;
|
||||
// ArrayRef<int> integral_;
|
||||
ArrayRef<int> blockIntegral_;
|
||||
ArrayRef<BINARIZER_BLOCK> blocks_;
|
||||
|
||||
ArrayRef<int> blackPoints_;
|
||||
int level_;
|
||||
|
||||
int subWidth_;
|
||||
int subHeight_;
|
||||
int blockIntegralWidth;
|
||||
int blockIntegralHeight;
|
||||
|
||||
public:
|
||||
explicit HybridBinarizer(Ref<LuminanceSource> source);
|
||||
virtual ~HybridBinarizer();
|
||||
|
||||
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler& err_handler) override;
|
||||
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler& err_handler) override;
|
||||
|
||||
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
|
||||
|
||||
private:
|
||||
int initIntegral();
|
||||
int initBlockIntegral();
|
||||
int initBlocks();
|
||||
|
||||
// int calculateBlackPoints();
|
||||
ArrayRef<int> getBlackPoints();
|
||||
int getBlockThreshold(int x, int y, int subWidth, int sum, int min, int max,
|
||||
int minDynamicRange, int SIZE_POWER);
|
||||
|
||||
|
||||
void calculateThresholdForBlock(Ref<ByteMatrix>& luminances, int subWidth, int subHeight,
|
||||
int SIZE_POWER, Ref<BitMatrix> const& matrix,
|
||||
ErrorHandler& err_handler);
|
||||
|
||||
|
||||
void thresholdBlock(Ref<ByteMatrix>& luminances, int xoffset, int yoffset, int threshold,
|
||||
Ref<BitMatrix> const& matrix, ErrorHandler& err_handler);
|
||||
|
||||
void thresholdIrregularBlock(Ref<ByteMatrix>& luminances, int xoffset, int yoffset,
|
||||
int blockWidth, int blockHeight, int threshold,
|
||||
Ref<BitMatrix> const& matrix, ErrorHandler& err_handler);
|
||||
|
||||
#ifdef USE_SET_INT
|
||||
void thresholdFourBlocks(Ref<ByteMatrix>& luminances, int xoffset, int yoffset, int* thresholds,
|
||||
int stride, Ref<BitMatrix> const& matrix);
|
||||
#endif
|
||||
|
||||
// Add for binarize image when call getBlackMatrix
|
||||
// By Skylook
|
||||
int binarizeByBlock(ErrorHandler& err_handler);
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_HYBRID_BINARIZER_HPP__
|
||||
@@ -0,0 +1,155 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../../precomp.hpp"
|
||||
#include "simple_adaptive_binarizer.hpp"
|
||||
|
||||
using zxing::SimpleAdaptiveBinarizer;
|
||||
|
||||
|
||||
SimpleAdaptiveBinarizer::SimpleAdaptiveBinarizer(Ref<LuminanceSource> source)
|
||||
: GlobalHistogramBinarizer(source) {
|
||||
filtered = false;
|
||||
}
|
||||
|
||||
SimpleAdaptiveBinarizer::~SimpleAdaptiveBinarizer() {}
|
||||
|
||||
// Applies simple sharpening to the row data to improve performance of the 1D
|
||||
// readers.
|
||||
Ref<BitArray> SimpleAdaptiveBinarizer::getBlackRow(int y, Ref<BitArray> row,
|
||||
ErrorHandler &err_handler) {
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
if (!matrix0_) {
|
||||
binarizeImage0(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitArray>();
|
||||
}
|
||||
// Call parent getBlackMatrix to get current matrix
|
||||
return Binarizer::getBlackRow(y, row, err_handler);
|
||||
}
|
||||
|
||||
// Does not sharpen the data, as this call is intended to only be used by 2D
|
||||
// readers.
|
||||
Ref<BitMatrix> SimpleAdaptiveBinarizer::getBlackMatrix(ErrorHandler &err_handler) {
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
if (!matrix0_) {
|
||||
binarizeImage0(err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitMatrix>();
|
||||
}
|
||||
|
||||
// First call binarize image in child class to get matrix0_ and binCache
|
||||
// Call parent getBlackMatrix to get current matrix
|
||||
return Binarizer::getBlackMatrix(err_handler);
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
int SimpleAdaptiveBinarizer::binarizeImage0(ErrorHandler &err_handler) {
|
||||
LuminanceSource &source = *getLuminanceSource();
|
||||
|
||||
Ref<BitMatrix> matrix(new BitMatrix(width, height, err_handler));
|
||||
if (err_handler.ErrCode()) return -1;
|
||||
|
||||
ArrayRef<char> localLuminances = source.getMatrix();
|
||||
|
||||
unsigned char *src = (unsigned char *)localLuminances->data();
|
||||
unsigned char *dst = matrix->getPtr();
|
||||
|
||||
qrBinarize(src, dst);
|
||||
|
||||
matrix0_ = matrix;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*A simplified adaptive thresholder.
|
||||
This compares the current pixel value to the mean value of a (large) window
|
||||
surrounding it.*/
|
||||
int SimpleAdaptiveBinarizer::qrBinarize(const unsigned char *src, unsigned char *dst) {
|
||||
unsigned char *mask = dst;
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
unsigned *col_sums;
|
||||
int logwindw;
|
||||
int logwindh;
|
||||
int windw;
|
||||
int windh;
|
||||
int y0offs;
|
||||
int y1offs;
|
||||
unsigned g;
|
||||
int x;
|
||||
int y;
|
||||
/*We keep the window size fairly large to ensure it doesn't fit
|
||||
completely inside the center of a finder pattern of a version 1 QR
|
||||
code at full resolution.*/
|
||||
for (logwindw = 4; logwindw < 8 && (1 << logwindw) < ((width + 7) >> 3); logwindw++)
|
||||
;
|
||||
for (logwindh = 4; logwindh < 8 && (1 << logwindh) < ((height + 7) >> 3); logwindh++)
|
||||
;
|
||||
windw = 1 << logwindw;
|
||||
windh = 1 << logwindh;
|
||||
|
||||
int logwinds = (logwindw + logwindh);
|
||||
|
||||
col_sums = (unsigned *)malloc(width * sizeof(*col_sums));
|
||||
/*Initialize sums down each column.*/
|
||||
for (x = 0; x < width; x++) {
|
||||
g = src[x];
|
||||
col_sums[x] = (g << (logwindh - 1)) + g;
|
||||
}
|
||||
for (y = 1; y < (windh >> 1); y++) {
|
||||
y1offs = min(y, height - 1) * width;
|
||||
for (x = 0; x < width; x++) {
|
||||
g = src[y1offs + x];
|
||||
col_sums[x] += g;
|
||||
}
|
||||
}
|
||||
for (y = 0; y < height; y++) {
|
||||
unsigned m;
|
||||
int x0;
|
||||
int x1;
|
||||
/*Initialize the sum over the window.*/
|
||||
m = (col_sums[0] << (logwindw - 1)) + col_sums[0];
|
||||
for (x = 1; x < (windw >> 1); x++) {
|
||||
x1 = min(x, width - 1);
|
||||
m += col_sums[x1];
|
||||
}
|
||||
|
||||
int offset = y * width;
|
||||
|
||||
for (x = 0; x < width; x++) {
|
||||
/*Perform the test against the threshold T = (m/n)-D,
|
||||
where n=windw*windh and D=3.*/
|
||||
g = src[offset + x];
|
||||
mask[offset + x] = ((g + 3) << (logwinds) < m);
|
||||
/*Update the window sum.*/
|
||||
if (x + 1 < width) {
|
||||
x0 = max(0, x - (windw >> 1));
|
||||
x1 = min(x + (windw >> 1), width - 1);
|
||||
m += col_sums[x1] - col_sums[x0];
|
||||
}
|
||||
}
|
||||
/*Update the column sums.*/
|
||||
if (y + 1 < height) {
|
||||
y0offs = max(0, y - (windh >> 1)) * width;
|
||||
y1offs = min(y + (windh >> 1), height - 1) * width;
|
||||
for (x = 0; x < width; x++) {
|
||||
col_sums[x] -= src[y0offs + x];
|
||||
col_sums[x] += src[y1offs + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
free(col_sums);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Ref<Binarizer> SimpleAdaptiveBinarizer::createBinarizer(Ref<LuminanceSource> source) {
|
||||
return Ref<Binarizer>(new SimpleAdaptiveBinarizer(source));
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_SIMPLEADAPTIVEBINARIZER_HPP__
|
||||
#define __ZXING_COMMON_SIMPLEADAPTIVEBINARIZER_HPP__
|
||||
|
||||
#include "../../binarizer.hpp"
|
||||
#include "../array.hpp"
|
||||
#include "../bitarray.hpp"
|
||||
#include "../bitmatrix.hpp"
|
||||
#include "global_histogram_binarizer.hpp"
|
||||
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class SimpleAdaptiveBinarizer : public GlobalHistogramBinarizer {
|
||||
public:
|
||||
explicit SimpleAdaptiveBinarizer(Ref<LuminanceSource> source);
|
||||
virtual ~SimpleAdaptiveBinarizer();
|
||||
|
||||
virtual Ref<BitArray> getBlackRow(int y, Ref<BitArray> row, ErrorHandler &err_handler) override;
|
||||
virtual Ref<BitMatrix> getBlackMatrix(ErrorHandler &err_handler) override;
|
||||
Ref<Binarizer> createBinarizer(Ref<LuminanceSource> source) override;
|
||||
|
||||
private:
|
||||
int binarizeImage0(ErrorHandler &err_handler);
|
||||
int qrBinarize(const unsigned char *src, unsigned char *dst);
|
||||
bool filtered;
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_SIMPLEADAPTIVEBINARIZER_HPP__
|
||||
@@ -0,0 +1,233 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "bitarray.hpp"
|
||||
|
||||
using zxing::ArrayRef;
|
||||
using zxing::BitArray;
|
||||
using zxing::ErrorHandler;
|
||||
using zxing::Ref;
|
||||
|
||||
BitArray::BitArray(int size_) : size(size_), bits(size_), nextSets(size_), nextUnSets(size_) {}
|
||||
|
||||
void BitArray::setUnchar(int i, unsigned char newBits) { bits[i] = newBits; }
|
||||
|
||||
bool BitArray::isRange(int start, int end, bool value, ErrorHandler &err_handler) {
|
||||
if (end < start) {
|
||||
err_handler = IllegalArgumentErrorHandler("isRange");
|
||||
return false;
|
||||
}
|
||||
if (start < 0 || end >= bits->size()) {
|
||||
err_handler = IllegalArgumentErrorHandler("isRange");
|
||||
return false;
|
||||
}
|
||||
if (end == start) {
|
||||
return true; // empty range matches
|
||||
}
|
||||
|
||||
bool startBool = bits[start] != (unsigned char)0;
|
||||
|
||||
int end2 = start;
|
||||
|
||||
if (startBool) {
|
||||
end2 = getNextUnset(start);
|
||||
} else {
|
||||
end2 = getNextSet(start);
|
||||
}
|
||||
|
||||
if (startBool == value) {
|
||||
if (end2 < end) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BitArray::reverse() {
|
||||
bool *rowBits = getRowBoolPtr();
|
||||
bool tempBit;
|
||||
|
||||
for (int i = 0; i < size / 2; i++) {
|
||||
tempBit = rowBits[i];
|
||||
rowBits[i] = rowBits[size - i - 1];
|
||||
rowBits[size - i - 1] = tempBit;
|
||||
}
|
||||
}
|
||||
|
||||
void BitArray::initAllNextSets() {
|
||||
bool *rowBits = getRowBoolPtr();
|
||||
|
||||
int *nextSetArray = nextSets->data();
|
||||
int *nextUnsetArray = nextUnSets->data();
|
||||
|
||||
// Init the last one
|
||||
if (rowBits[size - 1]) {
|
||||
nextSetArray[size - 1] = size - 1;
|
||||
nextUnsetArray[size - 1] = size;
|
||||
} else {
|
||||
nextUnsetArray[size - 1] = size - 1;
|
||||
nextSetArray[size - 1] = size;
|
||||
}
|
||||
|
||||
// do inits
|
||||
for (int i = size - 2; i >= 0; i--) {
|
||||
if (rowBits[i]) {
|
||||
nextSetArray[i] = i;
|
||||
nextUnsetArray[i] = nextUnsetArray[i + 1];
|
||||
} else {
|
||||
nextUnsetArray[i] = i;
|
||||
nextSetArray[i] = nextSetArray[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitArray::initAllNextSetsFromCounters(std::vector<int> counters) {
|
||||
bool *rowBits = getRowBoolPtr();
|
||||
bool isWhite = rowBits[0];
|
||||
int c = 0;
|
||||
int offset = 0;
|
||||
int count = 0;
|
||||
int prevCount = 0;
|
||||
int currCount = 0;
|
||||
int _size = counters.size();
|
||||
|
||||
int *nextSetArray = nextSets->data();
|
||||
int *nextUnsetArray = nextUnSets->data();
|
||||
|
||||
// int* countersArray = counters.data();
|
||||
int *countersArray = &counters[0];
|
||||
|
||||
while (c < _size) {
|
||||
currCount = countersArray[c];
|
||||
|
||||
count += currCount;
|
||||
|
||||
if (isWhite) {
|
||||
for (int i = 0; i < currCount; i++) {
|
||||
offset = prevCount + i;
|
||||
nextSetArray[offset] = prevCount + i;
|
||||
nextUnsetArray[offset] = count;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < currCount; i++) {
|
||||
offset = prevCount + i;
|
||||
nextSetArray[offset] = count;
|
||||
nextUnsetArray[offset] = prevCount + i;
|
||||
}
|
||||
}
|
||||
|
||||
isWhite = !isWhite;
|
||||
|
||||
prevCount += currCount;
|
||||
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
int BitArray::getNextSet(int from) {
|
||||
if (from >= size) {
|
||||
return size;
|
||||
}
|
||||
return nextSets[from];
|
||||
}
|
||||
|
||||
int BitArray::getNextUnset(int from) {
|
||||
if (from >= size) {
|
||||
return size;
|
||||
}
|
||||
return nextUnSets[from];
|
||||
}
|
||||
|
||||
BitArray::~BitArray() {}
|
||||
|
||||
int BitArray::getSize() const { return size; }
|
||||
|
||||
void BitArray::clear() {
|
||||
int max = bits->size();
|
||||
for (int i = 0; i < max; i++) {
|
||||
bits[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
BitArray::Reverse::Reverse(Ref<BitArray> array_) : array(array_) { array->reverse(); }
|
||||
|
||||
BitArray::Reverse::~Reverse() { array->reverse(); }
|
||||
|
||||
void BitArray::appendBit(bool value) {
|
||||
ArrayRef<unsigned char> newBits(size + 1);
|
||||
for (int i = 0; i < size; i++) {
|
||||
newBits[i] = bits[i];
|
||||
}
|
||||
bits = newBits;
|
||||
if (value) {
|
||||
set(size);
|
||||
}
|
||||
++size;
|
||||
}
|
||||
|
||||
int BitArray::getSizeInBytes() const { return size; }
|
||||
|
||||
// Appends the least-significant bits, from value, in order from
|
||||
// most-significant to least-significant. For example, appending 6 bits
|
||||
// from 0x000001E will append the bits 0, 1, 1, 1, 1, 0 in that order.
|
||||
void BitArray::appendBits(int value, int numBits, ErrorHandler &err_handler) {
|
||||
if (numBits < 0 || numBits > 32) {
|
||||
err_handler = IllegalArgumentErrorHandler("Number of bits must be between 0 and 32");
|
||||
return;
|
||||
}
|
||||
ArrayRef<unsigned char> newBits(size + numBits);
|
||||
for (int i = 0; i < size; i++) newBits[i] = bits[i];
|
||||
bits = newBits;
|
||||
for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {
|
||||
if (((value >> (numBitsLeft - 1)) & 0x01) == 1) {
|
||||
set(size);
|
||||
}
|
||||
++size;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void BitArray::appendBitArray(const BitArray &array) {
|
||||
ArrayRef<unsigned char> newBits(size + array.getSize());
|
||||
for (int i = 0; i < size; ++i) {
|
||||
newBits[i] = bits[i];
|
||||
}
|
||||
bits = newBits;
|
||||
for (int i = 0; i < array.getSize(); ++i) {
|
||||
if (array.get(i)) {
|
||||
set(size);
|
||||
}
|
||||
++size;
|
||||
}
|
||||
}
|
||||
|
||||
void BitArray::toBytes(int bitOffset, ArrayRef<int> &array, int offset, int numBytes) {
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
int theByte = 0;
|
||||
if (get(bitOffset)) {
|
||||
theByte = 1;
|
||||
}
|
||||
bitOffset++;
|
||||
array[offset + i] = theByte;
|
||||
}
|
||||
}
|
||||
void BitArray::bitXOR(const BitArray &other, ErrorHandler &err_handler) {
|
||||
if (size != other.size) {
|
||||
err_handler = IllegalArgumentErrorHandler("Sizes don't match");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bits->size(); i++) {
|
||||
bits[i] = bits[i] == other.bits[i] ? 0 : 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_BITARRAY_HPP__
|
||||
#define __ZXING_COMMON_BITARRAY_HPP__
|
||||
|
||||
#include "../errorhandler.hpp"
|
||||
#include "../zxing.hpp"
|
||||
#include "array.hpp"
|
||||
#include "counted.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class BitArray : public Counted {
|
||||
private:
|
||||
int size;
|
||||
ArrayRef<unsigned char> bits;
|
||||
ArrayRef<int> nextSets;
|
||||
ArrayRef<int> nextUnSets;
|
||||
// bool nextSetsInited;
|
||||
|
||||
public:
|
||||
explicit BitArray(int size);
|
||||
~BitArray();
|
||||
int getSize() const;
|
||||
|
||||
bool get(int i) const { return bits[i] != 0; }
|
||||
void set(int i) {
|
||||
// bits[i] |= 0xFF;
|
||||
bits[i] = true;
|
||||
}
|
||||
void setOneRow(unsigned char* rowBits, int length) {
|
||||
unsigned char* dst = bits->data();
|
||||
memcpy(dst, rowBits, length);
|
||||
}
|
||||
|
||||
bool* getRowBoolPtr() {
|
||||
// return (bool*)bits.data();
|
||||
return (bool*)bits->data();
|
||||
}
|
||||
|
||||
// Init for next sets and unsets to speed up
|
||||
void initAllNextSets();
|
||||
void initAllNextSetsFromCounters(std::vector<int> counters);
|
||||
|
||||
int getNextSet(int from);
|
||||
int getNextUnset(int from);
|
||||
|
||||
void setUnchar(int i, unsigned char newBist);
|
||||
|
||||
void clear();
|
||||
bool isRange(int start, int end, bool value, ErrorHandler& err_handler);
|
||||
|
||||
void reverse();
|
||||
|
||||
class Reverse {
|
||||
private:
|
||||
Ref<BitArray> array;
|
||||
|
||||
public:
|
||||
explicit Reverse(Ref<BitArray> array);
|
||||
~Reverse();
|
||||
};
|
||||
|
||||
void appendBit(bool value);
|
||||
int getSizeInBytes() const;
|
||||
void appendBits(int value, int numberOfBits, ErrorHandler& err_handler);
|
||||
void appendBitArray(const BitArray& array);
|
||||
void toBytes(int bitOffset, ArrayRef<int>& array, int offset, int numBytes);
|
||||
void bitXOR(const BitArray& other, ErrorHandler& err_handler);
|
||||
|
||||
#ifndef USE_BYTE_FOR_BIT
|
||||
private:
|
||||
static int makeArraySize(int size);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_BITARRAY_HPP__
|
||||
@@ -0,0 +1,397 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "bitmatrix.hpp"
|
||||
|
||||
using zxing::ArrayRef;
|
||||
using zxing::BitArray;
|
||||
using zxing::BitMatrix;
|
||||
using zxing::ErrorHandler;
|
||||
using zxing::Ref;
|
||||
|
||||
void BitMatrix::init(int _width, int _height, ErrorHandler& err_handler) {
|
||||
if (_width < 1 || _height < 1) {
|
||||
err_handler = IllegalArgumentErrorHandler("Both dimensions must be greater than 0");
|
||||
return;
|
||||
}
|
||||
width = _width;
|
||||
height = _height;
|
||||
this->rowBitsSize = width;
|
||||
bits = ArrayRef<unsigned char>(width * height);
|
||||
rowOffsets = ArrayRef<int>(height);
|
||||
|
||||
// offsetRowSize = new int[height];
|
||||
rowOffsets[0] = 0;
|
||||
for (int i = 1; i < height; i++) {
|
||||
rowOffsets[i] = rowOffsets[i - 1] + width;
|
||||
}
|
||||
|
||||
isInitRowCounters = false;
|
||||
isInitColsCounters = false;
|
||||
}
|
||||
|
||||
void BitMatrix::init(int _width, int _height, unsigned char* bitsPtr, ErrorHandler& err_handler) {
|
||||
init(_width, _height, err_handler);
|
||||
if (err_handler.ErrCode()) return;
|
||||
memcpy(bits->data(), bitsPtr, width * height * sizeof(unsigned char));
|
||||
}
|
||||
|
||||
void BitMatrix::initRowCounters() {
|
||||
if (isInitRowCounters == true) {
|
||||
return;
|
||||
}
|
||||
|
||||
row_counters = vector<COUNTER_TYPE>(width * height, 0);
|
||||
row_counters_offset = vector<COUNTER_TYPE>(width * height, 0);
|
||||
row_point_offset = vector<COUNTER_TYPE>(width * height, 0);
|
||||
row_counter_offset_end = vector<COUNTER_TYPE>(height, 0);
|
||||
|
||||
row_counters_recorded = vector<bool>(height, false);
|
||||
|
||||
isInitRowCounters = true;
|
||||
}
|
||||
void BitMatrix::initColsCounters() {
|
||||
if (isInitColsCounters == true) {
|
||||
return;
|
||||
}
|
||||
|
||||
cols_counters = vector<COUNTER_TYPE>(width * height, 0);
|
||||
cols_counters_offset = vector<COUNTER_TYPE>(width * height, 0);
|
||||
cols_point_offset = vector<COUNTER_TYPE>(width * height, 0);
|
||||
cols_counter_offset_end = vector<COUNTER_TYPE>(width, 0);
|
||||
|
||||
cols_counters_recorded = vector<bool>(width, false);
|
||||
|
||||
isInitColsCounters = true;
|
||||
}
|
||||
|
||||
BitMatrix::BitMatrix(int dimension, ErrorHandler& err_handler) {
|
||||
init(dimension, dimension, err_handler);
|
||||
}
|
||||
|
||||
BitMatrix::BitMatrix(int _width, int _height, ErrorHandler& err_handler) {
|
||||
init(_width, _height, err_handler);
|
||||
}
|
||||
|
||||
BitMatrix::BitMatrix(int _width, int _height, unsigned char* bitsPtr, ErrorHandler& err_handler) {
|
||||
init(_width, _height, bitsPtr, err_handler);
|
||||
}
|
||||
// Copy bitMatrix
|
||||
void BitMatrix::copyOf(Ref<BitMatrix> _bits, ErrorHandler& err_handler) {
|
||||
int _width = _bits->getWidth();
|
||||
int _height = _bits->getHeight();
|
||||
init(_width, _height, err_handler);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
bool* rowPtr = _bits->getRowBoolPtr(y);
|
||||
setRowBool(y, rowPtr);
|
||||
}
|
||||
}
|
||||
|
||||
void BitMatrix::xxor(Ref<BitMatrix> _bits) {
|
||||
if (width != _bits->getWidth() || height != _bits->getHeight()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int y = 0; y < height && y < _bits->getHeight(); ++y) {
|
||||
bool* rowPtrA = _bits->getRowBoolPtr(y);
|
||||
bool* rowPtrB = getRowBoolPtr(y);
|
||||
|
||||
for (int x = 0; x < width && x < _bits->getWidth(); ++x) {
|
||||
rowPtrB[x] = rowPtrB[x] ^ rowPtrA[x];
|
||||
}
|
||||
setRowBool(y, rowPtrB);
|
||||
}
|
||||
}
|
||||
|
||||
BitMatrix::~BitMatrix() {}
|
||||
|
||||
void BitMatrix::flip(int x, int y) {
|
||||
bits[rowOffsets[y] + x] = (bits[rowOffsets[y] + x] == (unsigned char)0);
|
||||
}
|
||||
|
||||
void BitMatrix::flipAll() {
|
||||
bool* matrixBits = (bool*)bits->data();
|
||||
for (int i = 0; i < bits->size(); i++) {
|
||||
matrixBits[i] = !matrixBits[i];
|
||||
}
|
||||
}
|
||||
|
||||
void BitMatrix::flipRegion(int left, int top, int _width, int _height, ErrorHandler& err_handler) {
|
||||
if (top < 0 || left < 0) {
|
||||
err_handler = IllegalArgumentErrorHandler("Left and top must be nonnegative");
|
||||
return;
|
||||
}
|
||||
if (_height < 1 || _width < 1) {
|
||||
err_handler = IllegalArgumentErrorHandler("Height and width must be at least 1");
|
||||
return;
|
||||
}
|
||||
int right = left + _width;
|
||||
int bottom = top + _height;
|
||||
if (bottom > this->height || right > this->width) {
|
||||
err_handler = IllegalArgumentErrorHandler("The region must fit inside the matrix");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int y = top; y < bottom; y++) {
|
||||
for (int x = left; x < right; x++) {
|
||||
bits[rowOffsets[y] + x] ^= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitMatrix::setRegion(int left, int top, int _width, int _height, ErrorHandler& err_handler) {
|
||||
if (top < 0 || left < 0) {
|
||||
err_handler = IllegalArgumentErrorHandler("Left and top must be nonnegative");
|
||||
return;
|
||||
}
|
||||
if (_height < 1 || _width < 1) {
|
||||
err_handler = IllegalArgumentErrorHandler("Height and width must be at least 1");
|
||||
return;
|
||||
}
|
||||
int right = left + _width;
|
||||
int bottom = top + _height;
|
||||
if (bottom > this->height || right > this->width) {
|
||||
err_handler = IllegalArgumentErrorHandler("The region must fit inside the matrix");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int y = top; y < bottom; y++) {
|
||||
for (int x = left; x < right; x++) {
|
||||
bits[rowOffsets[y] + x] = true;
|
||||
// bits[rowOffsets[y]+x] |= 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<BitArray> BitMatrix::getRow(int y, Ref<BitArray> row) {
|
||||
if (row.empty() || row->getSize() < width) {
|
||||
row = new BitArray(width);
|
||||
}
|
||||
|
||||
// row->
|
||||
unsigned char* src = bits.data() + rowOffsets[y];
|
||||
row->setOneRow(src, width);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
ArrayRef<int> BitMatrix::getTopLeftOnBit() const {
|
||||
int bitsOffset = 0;
|
||||
while (bitsOffset < bits->size() && bits[bitsOffset] == 0) {
|
||||
bitsOffset++;
|
||||
}
|
||||
if (bitsOffset == bits->size()) {
|
||||
return ArrayRef<int>();
|
||||
}
|
||||
int y = bitsOffset / width;
|
||||
int x = bitsOffset % width;
|
||||
ArrayRef<int> res(2);
|
||||
res[0] = x;
|
||||
res[1] = y;
|
||||
return res;
|
||||
}
|
||||
|
||||
ArrayRef<int> BitMatrix::getBottomRightOnBit() const {
|
||||
int bitsOffset = bits->size() - 1;
|
||||
while (bitsOffset >= 0 && bits[bitsOffset] == 0) {
|
||||
bitsOffset--;
|
||||
}
|
||||
if (bitsOffset < 0) {
|
||||
return ArrayRef<int>();
|
||||
}
|
||||
|
||||
int y = bitsOffset / width;
|
||||
int x = bitsOffset % width;
|
||||
ArrayRef<int> res(2);
|
||||
res[0] = x;
|
||||
res[1] = y;
|
||||
return res;
|
||||
}
|
||||
|
||||
void BitMatrix::getRowBool(int y, bool* getrow) {
|
||||
int offset = rowOffsets[y];
|
||||
unsigned char* src = bits.data() + offset;
|
||||
memcpy(getrow, src, rowBitsSize * sizeof(bool));
|
||||
}
|
||||
|
||||
void BitMatrix::setRowBool(int y, bool* row) {
|
||||
int offset = rowOffsets[y];
|
||||
unsigned char* dst = bits.data() + offset;
|
||||
memcpy(dst, row, rowBitsSize * sizeof(bool));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool* BitMatrix::getRowBoolPtr(int y) {
|
||||
int offset = y * rowBitsSize;
|
||||
unsigned char* src = bits.data() + offset;
|
||||
return (bool*)src;
|
||||
}
|
||||
|
||||
void BitMatrix::clear() {
|
||||
int size = bits->size();
|
||||
|
||||
unsigned char* dst = bits->data();
|
||||
memset(dst, 0, size * sizeof(unsigned char));
|
||||
}
|
||||
|
||||
int BitMatrix::getWidth() const { return width; }
|
||||
|
||||
int BitMatrix::getHeight() const { return height; }
|
||||
|
||||
COUNTER_TYPE* BitMatrix::getRowPointInRecords(int y) {
|
||||
if (!row_point_offset[y]) {
|
||||
setRowRecords(y);
|
||||
}
|
||||
int offset = y * width;
|
||||
COUNTER_TYPE* counters = &row_point_offset[0] + offset;
|
||||
return (COUNTER_TYPE*)counters;
|
||||
}
|
||||
|
||||
COUNTER_TYPE* BitMatrix::getRowRecords(int y) {
|
||||
if (!row_counters_recorded[y]) {
|
||||
setRowRecords(y);
|
||||
}
|
||||
int offset = y * width;
|
||||
COUNTER_TYPE* counters = &row_counters[0] + offset;
|
||||
return (COUNTER_TYPE*)counters;
|
||||
}
|
||||
|
||||
COUNTER_TYPE* BitMatrix::getRowRecordsOffset(int y) {
|
||||
if (!row_counters_recorded[y]) {
|
||||
setRowRecords(y);
|
||||
}
|
||||
int offset = y * width;
|
||||
COUNTER_TYPE* counters = &row_counters_offset[0] + offset;
|
||||
return (COUNTER_TYPE*)counters;
|
||||
}
|
||||
|
||||
bool BitMatrix::getRowFirstIsWhite(int y) {
|
||||
bool is_white = !get(0, y);
|
||||
return is_white;
|
||||
}
|
||||
|
||||
bool BitMatrix::getRowLastIsWhite(int y) {
|
||||
bool last_is_white = !get(width - 1, y);
|
||||
return last_is_white;
|
||||
}
|
||||
|
||||
COUNTER_TYPE BitMatrix::getRowCounterOffsetEnd(int y) {
|
||||
if (!row_counters_recorded[y]) {
|
||||
setRowRecords(y);
|
||||
}
|
||||
return row_counter_offset_end[y];
|
||||
}
|
||||
|
||||
void BitMatrix::setRowRecords(int y) {
|
||||
COUNTER_TYPE* cur_row_counters = &row_counters[0] + y * width;
|
||||
COUNTER_TYPE* cur_row_counters_offset = &row_counters_offset[0] + y * width;
|
||||
COUNTER_TYPE* cur_row_point_in_counters = &row_point_offset[0] + y * width;
|
||||
int end = width;
|
||||
|
||||
bool* rowBit = getRowBoolPtr(y);
|
||||
bool isWhite = !rowBit[0];
|
||||
int counterPosition = 0;
|
||||
int i = 0;
|
||||
cur_row_counters_offset[0] = 0;
|
||||
while (i < end) {
|
||||
if (rowBit[i] ^ isWhite) { // that is, exactly one is true
|
||||
cur_row_counters[counterPosition]++;
|
||||
} else {
|
||||
counterPosition++;
|
||||
if (counterPosition == end) {
|
||||
break;
|
||||
} else {
|
||||
cur_row_counters[counterPosition] = 1;
|
||||
isWhite = !isWhite;
|
||||
cur_row_counters_offset[counterPosition] = i;
|
||||
}
|
||||
}
|
||||
cur_row_point_in_counters[i] = counterPosition;
|
||||
i++;
|
||||
}
|
||||
|
||||
// use the last row__onedReaderData->counter_size to record
|
||||
// _onedReaderData->counter_size
|
||||
row_counter_offset_end[y] = counterPosition < end ? (counterPosition + 1) : end;
|
||||
|
||||
row_counters_recorded[y] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
COUNTER_TYPE* BitMatrix::getColsPointInRecords(int x) {
|
||||
if (!cols_point_offset[x]) {
|
||||
setColsRecords(x);
|
||||
}
|
||||
int offset = x * height;
|
||||
COUNTER_TYPE* counters = &cols_point_offset[0] + offset;
|
||||
return (COUNTER_TYPE*)counters;
|
||||
}
|
||||
|
||||
COUNTER_TYPE* BitMatrix::getColsRecords(int x) {
|
||||
if (!cols_counters_recorded[x]) {
|
||||
setColsRecords(x);
|
||||
}
|
||||
int offset = x * height;
|
||||
COUNTER_TYPE* counters = &cols_counters[0] + offset;
|
||||
return (COUNTER_TYPE*)counters;
|
||||
}
|
||||
|
||||
COUNTER_TYPE* BitMatrix::getColsRecordsOffset(int x) {
|
||||
if (!cols_counters_recorded[x]) {
|
||||
setColsRecords(x);
|
||||
}
|
||||
int offset = x * height;
|
||||
COUNTER_TYPE* counters = &cols_counters_offset[0] + offset;
|
||||
return (COUNTER_TYPE*)counters;
|
||||
}
|
||||
|
||||
COUNTER_TYPE BitMatrix::getColsCounterOffsetEnd(int x) {
|
||||
if (!cols_counters_recorded[x]) {
|
||||
setColsRecords(x);
|
||||
}
|
||||
return cols_counter_offset_end[x];
|
||||
}
|
||||
|
||||
void BitMatrix::setColsRecords(int x) {
|
||||
COUNTER_TYPE* cur_cols_counters = &cols_counters[0] + x * height;
|
||||
COUNTER_TYPE* cur_cols_counters_offset = &cols_counters_offset[0] + x * height;
|
||||
COUNTER_TYPE* cur_cols_point_in_counters = &cols_point_offset[0] + x * height;
|
||||
int end = height;
|
||||
|
||||
bool* rowBit = getRowBoolPtr(0);
|
||||
bool isWhite = !rowBit[0];
|
||||
int counterPosition = 0;
|
||||
int i = 0;
|
||||
cur_cols_counters_offset[0] = 0;
|
||||
while (i < end) {
|
||||
if (rowBit[i] ^ isWhite) { // that is, exactly one is true
|
||||
cur_cols_counters[counterPosition]++;
|
||||
} else {
|
||||
counterPosition++;
|
||||
if (counterPosition == end) {
|
||||
break;
|
||||
} else {
|
||||
cur_cols_counters[counterPosition] = 1;
|
||||
isWhite = !isWhite;
|
||||
cur_cols_counters_offset[counterPosition] = i;
|
||||
}
|
||||
}
|
||||
cur_cols_point_in_counters[i] = counterPosition;
|
||||
i++;
|
||||
rowBit += width;
|
||||
}
|
||||
|
||||
cols_counter_offset_end[x] = counterPosition < end ? (counterPosition + 1) : end;
|
||||
|
||||
cols_counters_recorded[x] = true;
|
||||
return;
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_BITMATRIX_HPP__
|
||||
#define __ZXING_COMMON_BITMATRIX_HPP__
|
||||
|
||||
#include "../errorhandler.hpp"
|
||||
#include "array.hpp"
|
||||
#include "bitarray.hpp"
|
||||
#include "counted.hpp"
|
||||
using namespace std;
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class BitMatrix : public Counted {
|
||||
public:
|
||||
static const int bitsPerWord = std::numeric_limits<unsigned int>::digits;
|
||||
|
||||
private:
|
||||
int width;
|
||||
int height;
|
||||
int rowBitsSize;
|
||||
|
||||
vector<COUNTER_TYPE> row_counters;
|
||||
vector<COUNTER_TYPE> row_counters_offset;
|
||||
vector<bool> row_counters_recorded;
|
||||
vector<COUNTER_TYPE> row_counter_offset_end;
|
||||
vector<COUNTER_TYPE> row_point_offset;
|
||||
|
||||
vector<COUNTER_TYPE> cols_counters;
|
||||
vector<COUNTER_TYPE> cols_counters_offset;
|
||||
vector<bool> cols_counters_recorded;
|
||||
vector<COUNTER_TYPE> cols_counter_offset_end;
|
||||
vector<COUNTER_TYPE> cols_point_offset;
|
||||
|
||||
ArrayRef<unsigned char> bits;
|
||||
ArrayRef<int> rowOffsets;
|
||||
|
||||
public:
|
||||
BitMatrix(int _width, int _height, unsigned char* bitsPtr, ErrorHandler& err_handler);
|
||||
BitMatrix(int dimension, ErrorHandler& err_handler);
|
||||
BitMatrix(int _width, int _height, ErrorHandler& err_handler);
|
||||
|
||||
void copyOf(Ref<BitMatrix> _bits, ErrorHandler& err_handler);
|
||||
void xxor(Ref<BitMatrix> _bits);
|
||||
|
||||
~BitMatrix();
|
||||
|
||||
unsigned char get(int x, int y) const { return bits[width * y + x]; }
|
||||
|
||||
void set(int x, int y) { bits[rowOffsets[y] + x] = (unsigned char)1; }
|
||||
|
||||
void set(int x, int y, unsigned char value) { bits[rowOffsets[y] + x] = value; }
|
||||
|
||||
void swap(int srcX, int srcY, int dstX, int dstY) {
|
||||
auto temp = bits[width * srcY + srcX];
|
||||
bits[width * srcY + srcX] = bits[width * dstY + dstX];
|
||||
bits[width * dstY + dstX] = temp;
|
||||
}
|
||||
|
||||
void getRowBool(int y, bool* row);
|
||||
bool* getRowBoolPtr(int y);
|
||||
void setRowBool(int y, bool* row);
|
||||
int getRowBitsSize() { return rowBitsSize; }
|
||||
unsigned char* getPtr() { return bits->data(); }
|
||||
|
||||
void flip(int x, int y);
|
||||
void flipAll();
|
||||
void clear();
|
||||
void setRegion(int left, int top, int _width, int _height, ErrorHandler& err_handler);
|
||||
void flipRegion(int left, int top, int _width, int _height, ErrorHandler& err_handler);
|
||||
Ref<BitArray> getRow(int y, Ref<BitArray> row);
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
ArrayRef<int> getTopLeftOnBit() const;
|
||||
ArrayRef<int> getBottomRightOnBit() const;
|
||||
|
||||
bool isInitRowCounters;
|
||||
void initRowCounters();
|
||||
COUNTER_TYPE* getRowRecords(int y);
|
||||
COUNTER_TYPE* getRowRecordsOffset(int y);
|
||||
bool getRowFirstIsWhite(int y);
|
||||
COUNTER_TYPE getRowCounterOffsetEnd(int y);
|
||||
bool getRowLastIsWhite(int y);
|
||||
COUNTER_TYPE* getRowPointInRecords(int y);
|
||||
|
||||
bool isInitColsCounters;
|
||||
void initColsCounters();
|
||||
COUNTER_TYPE* getColsRecords(int x);
|
||||
COUNTER_TYPE* getColsRecordsOffset(int x);
|
||||
COUNTER_TYPE* getColsPointInRecords(int x);
|
||||
COUNTER_TYPE getColsCounterOffsetEnd(int x);
|
||||
|
||||
private:
|
||||
inline void init(int, int, ErrorHandler& err_handler);
|
||||
inline void init(int _width, int _height, unsigned char* bitsPtr, ErrorHandler& err_handler);
|
||||
|
||||
void setRowRecords(int y);
|
||||
void setColsRecords(int x);
|
||||
|
||||
BitMatrix(const BitMatrix&, ErrorHandler& err_handler);
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_BITMATRIX_HPP__
|
||||
@@ -0,0 +1,62 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "bitsource.hpp"
|
||||
#include <sstream>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
int BitSource::readBits(int numBits, ErrorHandler& err_handler) {
|
||||
if (numBits < 0 || numBits > 32 || numBits > available()) {
|
||||
std::ostringstream oss;
|
||||
oss << numBits;
|
||||
err_handler = IllegalArgumentErrorHandler(oss.str().c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
|
||||
// First, read remainder from current byte
|
||||
if (bitOffset_ > 0) {
|
||||
int bitsLeft = 8 - bitOffset_;
|
||||
int toRead = numBits < bitsLeft ? numBits : bitsLeft;
|
||||
int bitsToNotRead = bitsLeft - toRead;
|
||||
int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
|
||||
result = (bytes_[byteOffset_] & mask) >> bitsToNotRead;
|
||||
numBits -= toRead;
|
||||
bitOffset_ += toRead;
|
||||
if (bitOffset_ == 8) {
|
||||
bitOffset_ = 0;
|
||||
byteOffset_++;
|
||||
}
|
||||
}
|
||||
|
||||
// Next read whole bytes
|
||||
if (numBits > 0) {
|
||||
while (numBits >= 8) {
|
||||
result = (result << 8) | (bytes_[byteOffset_] & 0xFF);
|
||||
byteOffset_++;
|
||||
numBits -= 8;
|
||||
}
|
||||
|
||||
// Finally read a partial byte
|
||||
if (numBits > 0) {
|
||||
int bitsToNotRead = 8 - numBits;
|
||||
int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
|
||||
result = (result << numBits) | ((bytes_[byteOffset_] & mask) >> bitsToNotRead);
|
||||
bitOffset_ += numBits;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int BitSource::available() { return 8 * (bytes_->size() - byteOffset_) - bitOffset_; }
|
||||
} // namespace zxing
|
||||
@@ -0,0 +1,57 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_BITSOURCE_HPP__
|
||||
#define __ZXING_COMMON_BITSOURCE_HPP__
|
||||
|
||||
#include "../errorhandler.hpp"
|
||||
#include "array.hpp"
|
||||
|
||||
namespace zxing {
|
||||
/**
|
||||
* <p>This provides an easy abstraction to read bits at a time from a sequence
|
||||
* of bytes, where the number of bits read is not often a multiple of 8.</p>
|
||||
*
|
||||
* <p>This class is not thread-safe.</p>
|
||||
*
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
* @author christian.brunschen@gmail.com (Christian Brunschen)
|
||||
*/
|
||||
class BitSource : public Counted {
|
||||
typedef char byte;
|
||||
|
||||
private:
|
||||
ArrayRef<byte> bytes_;
|
||||
int byteOffset_;
|
||||
int bitOffset_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param bytes bytes from which this will read bits. Bits will be read from
|
||||
* the first byte first. Bits are read within a byte from most-significant
|
||||
* to least-significant bit.
|
||||
*/
|
||||
explicit BitSource(ArrayRef<byte> &bytes) : bytes_(bytes), byteOffset_(0), bitOffset_(0) {}
|
||||
|
||||
int getBitOffset() { return bitOffset_; }
|
||||
|
||||
int getByteOffset() { return byteOffset_; }
|
||||
|
||||
int readBits(int numBits, ErrorHandler &err_handler);
|
||||
|
||||
/**
|
||||
* @return number of bits that can be read successfully
|
||||
*/
|
||||
int available();
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_BITSOURCE_HPP__
|
||||
@@ -0,0 +1,54 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
#include "../../precomp.hpp"
|
||||
#include "bytematrix.hpp"
|
||||
|
||||
using zxing::ArrayRef;
|
||||
using zxing::ByteMatrix;
|
||||
using zxing::ErrorHandler;
|
||||
using zxing::Ref;
|
||||
|
||||
void ByteMatrix::init(int _width, int _height) {
|
||||
if (_width < 1 || _height < 1) {
|
||||
return;
|
||||
}
|
||||
this->width = _width;
|
||||
this->height = _height;
|
||||
bytes = new unsigned char[width * height];
|
||||
row_offsets = new int[height];
|
||||
row_offsets[0] = 0;
|
||||
for (int i = 1; i < height; i++) {
|
||||
row_offsets[i] = row_offsets[i - 1] + width;
|
||||
}
|
||||
}
|
||||
|
||||
ByteMatrix::ByteMatrix(int dimension) : bytes(nullptr) { init(dimension, dimension); }
|
||||
|
||||
ByteMatrix::ByteMatrix(int _width, int _height) : bytes(nullptr) { init(_width, _height); }
|
||||
|
||||
ByteMatrix::ByteMatrix(int _width, int _height, ArrayRef<char> source) : bytes(nullptr) {
|
||||
init(_width, _height);
|
||||
|
||||
if( bytes != nullptr)
|
||||
{
|
||||
const size_t size = _width * _height;
|
||||
memcpy(&bytes[0], &source[0], size);
|
||||
}
|
||||
}
|
||||
|
||||
ByteMatrix::~ByteMatrix() {
|
||||
if (bytes) delete[] bytes;
|
||||
if (row_offsets) delete[] row_offsets;
|
||||
}
|
||||
|
||||
unsigned char* ByteMatrix::getByteRow(int y, ErrorHandler& err_handler) {
|
||||
if (y < 0 || y >= getHeight()) {
|
||||
err_handler = IllegalArgumentErrorHandler("Requested row is outside the image.");
|
||||
return NULL;
|
||||
}
|
||||
return &bytes[row_offsets[y]];
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
#ifndef __ZXING_COMMON_BYTEMATRIX_HPP__
|
||||
#define __ZXING_COMMON_BYTEMATRIX_HPP__
|
||||
|
||||
#include "../errorhandler.hpp"
|
||||
#include "array.hpp"
|
||||
#include "bitarray.hpp"
|
||||
#include "counted.hpp"
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class ByteMatrix : public Counted {
|
||||
public:
|
||||
explicit ByteMatrix(int dimension);
|
||||
ByteMatrix(int _width, int _height);
|
||||
ByteMatrix(int _width, int _height, ArrayRef<char> source);
|
||||
~ByteMatrix();
|
||||
|
||||
char get(int x, int y) const {
|
||||
int offset = row_offsets[y] + x;
|
||||
return bytes[offset];
|
||||
}
|
||||
|
||||
void set(int x, int y, char char_value) {
|
||||
int offset = row_offsets[y] + x;
|
||||
bytes[offset] = char_value & 0XFF;
|
||||
}
|
||||
|
||||
unsigned char* getByteRow(int y, ErrorHandler& err_handler);
|
||||
|
||||
int getWidth() const { return width; }
|
||||
int getHeight() const { return height; }
|
||||
|
||||
unsigned char* bytes;
|
||||
|
||||
private:
|
||||
int width;
|
||||
int height;
|
||||
|
||||
// ArrayRef<char> bytes;
|
||||
// ArrayRef<int> row_offsets;
|
||||
int* row_offsets;
|
||||
|
||||
private:
|
||||
inline void init(int, int);
|
||||
ByteMatrix(const ByteMatrix&);
|
||||
ByteMatrix& operator=(const ByteMatrix&);
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_BYTEMATRIX_HPP__
|
||||
@@ -0,0 +1,111 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "characterseteci.hpp"
|
||||
using zxing::common::CharacterSetECI;
|
||||
|
||||
// Fix memory leak
|
||||
// https://github.com/ukeller/zxing-cpp/commit/c632ffe47ca7342f894ae533263be249cbdfd37e
|
||||
// std::map<int, CharacterSetECI*> CharacterSetECI::VALUE_TO_ECI;
|
||||
// std::map<std::string, CharacterSetECI*> CharacterSetECI::NAME_TO_ECI;
|
||||
std::map<int, zxing::Ref<CharacterSetECI> > CharacterSetECI::VALUE_TO_ECI;
|
||||
std::map<std::string, zxing::Ref<CharacterSetECI> > CharacterSetECI::NAME_TO_ECI;
|
||||
|
||||
const bool CharacterSetECI::inited = CharacterSetECI::init_tables();
|
||||
|
||||
#define ADD_CHARACTER_SET(VALUES, STRINGS) \
|
||||
{ \
|
||||
static int values[] = {VALUES, -1}; \
|
||||
static char const* strings[] = {STRINGS, 0}; \
|
||||
addCharacterSet(values, strings); \
|
||||
}
|
||||
|
||||
#define XC ,
|
||||
|
||||
bool CharacterSetECI::init_tables() {
|
||||
ADD_CHARACTER_SET(0 XC 2, "Cp437");
|
||||
ADD_CHARACTER_SET(1 XC 3, "ISO8859_1" XC "ISO-8859-1");
|
||||
ADD_CHARACTER_SET(4, "ISO8859_2" XC "ISO-8859-2");
|
||||
ADD_CHARACTER_SET(5, "ISO8859_3" XC "ISO-8859-3");
|
||||
ADD_CHARACTER_SET(6, "ISO8859_4" XC "ISO-8859-4");
|
||||
ADD_CHARACTER_SET(7, "ISO8859_5" XC "ISO-8859-5");
|
||||
ADD_CHARACTER_SET(8, "ISO8859_6" XC "ISO-8859-6");
|
||||
ADD_CHARACTER_SET(9, "ISO8859_7" XC "ISO-8859-7");
|
||||
ADD_CHARACTER_SET(10, "ISO8859_8" XC "ISO-8859-8");
|
||||
ADD_CHARACTER_SET(11, "ISO8859_9" XC "ISO-8859-9");
|
||||
ADD_CHARACTER_SET(12, "ISO8859_10" XC "ISO-8859-10");
|
||||
ADD_CHARACTER_SET(13, "ISO8859_11" XC "ISO-8859-11");
|
||||
ADD_CHARACTER_SET(15, "ISO8859_13" XC "ISO-8859-13");
|
||||
ADD_CHARACTER_SET(16, "ISO8859_14" XC "ISO-8859-14");
|
||||
ADD_CHARACTER_SET(17, "ISO8859_15" XC "ISO-8859-15");
|
||||
ADD_CHARACTER_SET(18, "ISO8859_16" XC "ISO-8859-16");
|
||||
ADD_CHARACTER_SET(20, "SJIS" XC "Shift_JIS");
|
||||
ADD_CHARACTER_SET(21, "Cp1250" XC "windows-1250");
|
||||
ADD_CHARACTER_SET(22, "Cp1251" XC "windows-1251");
|
||||
ADD_CHARACTER_SET(23, "Cp1252" XC "windows-1252");
|
||||
ADD_CHARACTER_SET(24, "Cp1256" XC "windows-1256");
|
||||
ADD_CHARACTER_SET(25, "UnicodeBigUnmarked" XC "UTF-16BE" XC "UnicodeBig");
|
||||
ADD_CHARACTER_SET(26, "UTF8" XC "UTF-8");
|
||||
ADD_CHARACTER_SET(27 XC 170, "ASCII" XC "US-ASCII");
|
||||
ADD_CHARACTER_SET(28, "Big5");
|
||||
ADD_CHARACTER_SET(29, "GB18030" XC "GB2312" XC "EUC_CN" XC "GBK");
|
||||
ADD_CHARACTER_SET(30, "EUC_KR" XC "EUC-KR");
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef XC
|
||||
|
||||
CharacterSetECI::CharacterSetECI(int const* values, char const* const* names)
|
||||
: values_(values), names_(names) {
|
||||
zxing::Ref<CharacterSetECI> this_ref(this);
|
||||
|
||||
for (int const* p_values = values_; *p_values != -1; p_values++) {
|
||||
// VALUE_TO_ECI[*values] = this;
|
||||
VALUE_TO_ECI[*p_values] = this_ref;
|
||||
}
|
||||
for (char const* const* p_names = names_; *p_names; p_names++) {
|
||||
// NAME_TO_ECI[string(*names)] = this;
|
||||
NAME_TO_ECI[string(*p_names)] = this_ref;
|
||||
}
|
||||
}
|
||||
|
||||
char const* CharacterSetECI::name() const { return names_[0]; }
|
||||
|
||||
int CharacterSetECI::getValue() const { return values_[0]; }
|
||||
|
||||
void CharacterSetECI::addCharacterSet(int const* values, char const* const* names) {
|
||||
new CharacterSetECI(values, names);
|
||||
}
|
||||
|
||||
CharacterSetECI* CharacterSetECI::getCharacterSetECIByValueFind(int value) {
|
||||
if (value < 0 || value >= 900) {
|
||||
return zxing::Ref<CharacterSetECI>(0);
|
||||
}
|
||||
|
||||
std::map<int, zxing::Ref<CharacterSetECI> >::iterator iter;
|
||||
iter = VALUE_TO_ECI.find(value);
|
||||
|
||||
if (iter != VALUE_TO_ECI.end()) {
|
||||
return iter->second;
|
||||
} else {
|
||||
return zxing::Ref<CharacterSetECI>(0);
|
||||
}
|
||||
}
|
||||
|
||||
CharacterSetECI* CharacterSetECI::getCharacterSetECIByName(string const& name) {
|
||||
std::map<std::string, zxing::Ref<CharacterSetECI> >::iterator iter;
|
||||
iter = NAME_TO_ECI.find(name);
|
||||
|
||||
if (iter != NAME_TO_ECI.end()) {
|
||||
return iter->second;
|
||||
} else {
|
||||
return zxing::Ref<CharacterSetECI>(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_CHARACTERSETECI_HPP__
|
||||
#define __ZXING_COMMON_CHARACTERSETECI_HPP__
|
||||
|
||||
#include <map>
|
||||
#include "../decodehints.hpp"
|
||||
#include "counted.hpp"
|
||||
|
||||
namespace zxing {
|
||||
namespace common {
|
||||
|
||||
class CharacterSetECI : public Counted {
|
||||
private:
|
||||
static std::map<int, zxing::Ref<CharacterSetECI> > VALUE_TO_ECI;
|
||||
static std::map<std::string, zxing::Ref<CharacterSetECI> > NAME_TO_ECI;
|
||||
static const bool inited;
|
||||
static bool init_tables();
|
||||
|
||||
int const* const values_;
|
||||
char const* const* const names_;
|
||||
|
||||
CharacterSetECI(int const* values, char const* const* names);
|
||||
|
||||
static void addCharacterSet(int const* value, char const* const* encodingNames);
|
||||
|
||||
public:
|
||||
char const* name() const;
|
||||
int getValue() const;
|
||||
|
||||
static CharacterSetECI* getCharacterSetECIByValueFind(int value);
|
||||
static CharacterSetECI* getCharacterSetECIByName(std::string const& name);
|
||||
};
|
||||
|
||||
} // namespace common
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_CHARACTERSETECI_HPP__
|
||||
@@ -0,0 +1,110 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_COUNTED_HPP__
|
||||
#define __ZXING_COMMON_COUNTED_HPP__
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
namespace zxing {
|
||||
|
||||
/* base class for reference-counted objects */
|
||||
class Counted {
|
||||
private:
|
||||
unsigned int count_;
|
||||
|
||||
public:
|
||||
Counted() : count_(0) {}
|
||||
virtual ~Counted() {}
|
||||
Counted* retain() {
|
||||
count_++;
|
||||
return this;
|
||||
}
|
||||
void release() {
|
||||
count_--;
|
||||
if (count_ == 0) {
|
||||
count_ = 0xDEADF001;
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
/* return the current count for denugging purposes or similar */
|
||||
int count() const { return count_; }
|
||||
};
|
||||
|
||||
/* counting reference to reference-counted objects */
|
||||
template <typename T>
|
||||
class Ref {
|
||||
private:
|
||||
public:
|
||||
T* object_;
|
||||
explicit Ref(T* o = 0) : object_(0) { reset(o); }
|
||||
Ref(const Ref& other) : object_(0) { reset(other.object_); }
|
||||
|
||||
template <class Y>
|
||||
Ref(const Ref<Y>& other) : object_(0) {
|
||||
reset(other.object_);
|
||||
}
|
||||
|
||||
~Ref() {
|
||||
if (object_) {
|
||||
object_->release();
|
||||
}
|
||||
}
|
||||
|
||||
void reset(T* o) {
|
||||
if (o) {
|
||||
o->retain();
|
||||
}
|
||||
if (object_ != 0) {
|
||||
object_->release();
|
||||
}
|
||||
object_ = o;
|
||||
}
|
||||
Ref& operator=(const Ref& other) {
|
||||
reset(other.object_);
|
||||
return *this;
|
||||
}
|
||||
template <class Y>
|
||||
Ref& operator=(const Ref<Y>& other) {
|
||||
reset(other.object_);
|
||||
return *this;
|
||||
}
|
||||
Ref& operator=(T* o) {
|
||||
reset(o);
|
||||
return *this;
|
||||
}
|
||||
template <class Y>
|
||||
Ref& operator=(Y* o) {
|
||||
reset(o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() { return *object_; }
|
||||
T* operator->() const { return object_; }
|
||||
operator T*() const { return object_; }
|
||||
|
||||
bool operator==(const T* that) { return object_ == that; }
|
||||
bool operator==(const Ref& other) const {
|
||||
return object_ == other.object_ || *object_ == *(other.object_);
|
||||
}
|
||||
template <class Y>
|
||||
bool operator==(const Ref<Y>& other) const {
|
||||
return object_ == other.object_ || *object_ == *(other.object_);
|
||||
}
|
||||
|
||||
bool operator!=(const T* that) { return !(*this == that); }
|
||||
|
||||
bool empty() const { return object_ == 0; }
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_COUNTED_HPP__
|
||||
@@ -0,0 +1,65 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "decoder_result.hpp"
|
||||
|
||||
using zxing::DecoderResult;
|
||||
using zxing::Ref;
|
||||
using zxing::ArrayRef;
|
||||
using zxing::String;
|
||||
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
|
||||
ArrayRef<ArrayRef<char> >& byteSegments, string const& ecLevel)
|
||||
: rawBytes_(rawBytes), text_(text), byteSegments_(byteSegments), ecLevel_(ecLevel) {
|
||||
outputCharset_ = "UTF-8";
|
||||
otherClassName = "";
|
||||
qrcodeVersion_ = -1;
|
||||
}
|
||||
|
||||
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
|
||||
ArrayRef<ArrayRef<char> >& byteSegments, string const& ecLevel,
|
||||
string outputCharset)
|
||||
: rawBytes_(rawBytes),
|
||||
text_(text),
|
||||
byteSegments_(byteSegments),
|
||||
ecLevel_(ecLevel),
|
||||
outputCharset_(outputCharset) {
|
||||
otherClassName = "";
|
||||
qrcodeVersion_ = -1;
|
||||
}
|
||||
|
||||
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
|
||||
ArrayRef<ArrayRef<char> >& byteSegments, string const& ecLevel,
|
||||
string outputCharset, int qrcodeVersion, string& charsetMode)
|
||||
: rawBytes_(rawBytes),
|
||||
text_(text),
|
||||
byteSegments_(byteSegments),
|
||||
ecLevel_(ecLevel),
|
||||
outputCharset_(outputCharset),
|
||||
qrcodeVersion_(qrcodeVersion),
|
||||
charsetMode_(charsetMode) {
|
||||
otherClassName = "";
|
||||
}
|
||||
|
||||
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text)
|
||||
: rawBytes_(rawBytes), text_(text) {
|
||||
outputCharset_ = "UTF-8";
|
||||
otherClassName = "";
|
||||
}
|
||||
|
||||
DecoderResult::DecoderResult(ArrayRef<char> rawBytes, Ref<String> text, std::string outputCharset)
|
||||
: rawBytes_(rawBytes), text_(text), outputCharset_(outputCharset) {
|
||||
otherClassName = "";
|
||||
}
|
||||
|
||||
ArrayRef<char> DecoderResult::getRawBytes() { return rawBytes_; }
|
||||
|
||||
Ref<String> DecoderResult::getText() { return text_; }
|
||||
|
||||
string DecoderResult::getCharset() { return outputCharset_; }
|
||||
@@ -0,0 +1,77 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_DECODER_RESULT_HPP__
|
||||
#define __ZXING_COMMON_DECODER_RESULT_HPP__
|
||||
|
||||
#include "../qrcode/decoder/qrcode_decoder_metadata.hpp"
|
||||
#include "array.hpp"
|
||||
#include "counted.hpp"
|
||||
#include "str.hpp"
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class DecoderResult : public Counted {
|
||||
private:
|
||||
ArrayRef<char> rawBytes_;
|
||||
Ref<String> text_;
|
||||
ArrayRef<ArrayRef<char> > byteSegments_;
|
||||
std::string ecLevel_;
|
||||
std::string outputCharset_;
|
||||
int qrcodeVersion_;
|
||||
std::string charsetMode_;
|
||||
|
||||
Ref<qrcode::QRCodeDecoderMetaData> other_;
|
||||
string otherClassName;
|
||||
|
||||
public:
|
||||
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
|
||||
ArrayRef<ArrayRef<char> >& byteSegments, std::string const& ecLevel);
|
||||
|
||||
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
|
||||
ArrayRef<ArrayRef<char> >& byteSegments, std::string const& ecLevel,
|
||||
std::string outputCharset);
|
||||
|
||||
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text,
|
||||
ArrayRef<ArrayRef<char> >& byteSegments, std::string const& ecLevel,
|
||||
std::string outputCharset, int qrcodeVersion, std::string& charsetMode);
|
||||
|
||||
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text);
|
||||
|
||||
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text, std::string outputCharset);
|
||||
|
||||
ArrayRef<char> getRawBytes();
|
||||
Ref<String> getText();
|
||||
std::string getCharset();
|
||||
|
||||
void setOther(Ref<qrcode::QRCodeDecoderMetaData> other) {
|
||||
other_ = other;
|
||||
otherClassName = "QRCodeDecoderMetaData";
|
||||
};
|
||||
|
||||
Ref<qrcode::QRCodeDecoderMetaData> getOther() {
|
||||
// className = otherClassName;
|
||||
return other_;
|
||||
};
|
||||
|
||||
string getOtherClassName() { return otherClassName; };
|
||||
|
||||
int getQRCodeVersion() const { return qrcodeVersion_; };
|
||||
|
||||
void setText(Ref<String> text) { text_ = text; };
|
||||
|
||||
string getEcLevel() { return ecLevel_; }
|
||||
|
||||
string getCharsetMode() { return charsetMode_; }
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_DECODER_RESULT_HPP__
|
||||
@@ -0,0 +1,27 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "detector_result.hpp"
|
||||
|
||||
namespace zxing {
|
||||
|
||||
DetectorResult::DetectorResult(Ref<BitMatrix> bits, ArrayRef<Ref<ResultPoint> > points,
|
||||
int dimension, float modulesize)
|
||||
: bits_(bits), points_(points), dimension_(dimension), modulesize_(modulesize) {}
|
||||
|
||||
void DetectorResult::SetGray(Ref<ByteMatrix> gray) { gray_ = gray; }
|
||||
|
||||
Ref<BitMatrix> DetectorResult::getBits() { return bits_; }
|
||||
|
||||
Ref<ByteMatrix> DetectorResult::getGray() { return gray_; }
|
||||
|
||||
ArrayRef<Ref<ResultPoint> > DetectorResult::getPoints() { return points_; }
|
||||
|
||||
} // namespace zxing
|
||||
@@ -0,0 +1,42 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_DETECTOR_RESULT_HPP__
|
||||
#define __ZXING_COMMON_DETECTOR_RESULT_HPP__
|
||||
|
||||
#include "../resultpoint.hpp"
|
||||
#include "array.hpp"
|
||||
#include "bitmatrix.hpp"
|
||||
#include "bytematrix.hpp"
|
||||
#include "counted.hpp"
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class DetectorResult : public Counted {
|
||||
private:
|
||||
Ref<BitMatrix> bits_;
|
||||
Ref<ByteMatrix> gray_;
|
||||
ArrayRef<Ref<ResultPoint> > points_;
|
||||
|
||||
public:
|
||||
DetectorResult(Ref<BitMatrix> bits, ArrayRef<Ref<ResultPoint> > points, int dimension = 0,
|
||||
float modulesize = 0);
|
||||
DetectorResult(Ref<ByteMatrix> gray, ArrayRef<Ref<ResultPoint> > points, int dimension = 0,
|
||||
float modulesize = 0);
|
||||
Ref<BitMatrix> getBits();
|
||||
Ref<ByteMatrix> getGray();
|
||||
void SetGray(Ref<ByteMatrix> gray);
|
||||
ArrayRef<Ref<ResultPoint> > getPoints();
|
||||
int dimension_;
|
||||
float modulesize_;
|
||||
};
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_DETECTOR_RESULT_HPP__
|
||||
@@ -0,0 +1,77 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "greyscale_luminance_source.hpp"
|
||||
#include "bytematrix.hpp"
|
||||
#include "greyscale_rotated_luminance_source.hpp"
|
||||
using zxing::ArrayRef;
|
||||
using zxing::ByteMatrix;
|
||||
using zxing::ErrorHandler;
|
||||
using zxing::GreyscaleLuminanceSource;
|
||||
using zxing::LuminanceSource;
|
||||
using zxing::Ref;
|
||||
|
||||
GreyscaleLuminanceSource::GreyscaleLuminanceSource(ArrayRef<char> greyData, int dataWidth,
|
||||
int dataHeight, int left, int top, int width,
|
||||
int height, ErrorHandler& err_handler)
|
||||
: Super(width, height),
|
||||
greyData_(greyData),
|
||||
dataWidth_(dataWidth),
|
||||
dataHeight_(dataHeight),
|
||||
left_(left),
|
||||
top_(top) {
|
||||
if (left + width > dataWidth || top + height > dataHeight || top < 0 || left < 0) {
|
||||
err_handler = IllegalArgumentErrorHandler("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
}
|
||||
|
||||
ArrayRef<char> GreyscaleLuminanceSource::getRow(int y, ArrayRef<char> row,
|
||||
ErrorHandler& err_handler) const {
|
||||
if (y < 0 || y >= this->getHeight()) {
|
||||
err_handler = IllegalArgumentErrorHandler("Requested row is outside the image.");
|
||||
return ArrayRef<char>();
|
||||
}
|
||||
int width = getWidth();
|
||||
if (!row || row->size() < width) {
|
||||
ArrayRef<char> temp(width);
|
||||
row = temp;
|
||||
}
|
||||
int offset = (y + top_) * dataWidth_ + left_;
|
||||
memcpy(&row[0], &greyData_[offset], width);
|
||||
return row;
|
||||
}
|
||||
|
||||
ArrayRef<char> GreyscaleLuminanceSource::getMatrix() const {
|
||||
int size = getWidth() * getHeight();
|
||||
ArrayRef<char> result(size);
|
||||
if (left_ == 0 && top_ == 0 && dataWidth_ == getWidth() && dataHeight_ == getHeight()) {
|
||||
memcpy(&result[0], &greyData_[0], size);
|
||||
} else {
|
||||
for (int row = 0; row < getHeight(); row++) {
|
||||
memcpy(&result[row * getWidth()], &greyData_[(top_ + row) * dataWidth_ + left_],
|
||||
getWidth());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Ref<LuminanceSource> GreyscaleLuminanceSource::rotateCounterClockwise(
|
||||
ErrorHandler& err_handler) const {
|
||||
// Intentionally flip the left, top, width, and height arguments as
|
||||
// needed. dataWidth and dataHeight are always kept unrotated.
|
||||
Ref<LuminanceSource> result(new GreyscaleRotatedLuminanceSource(
|
||||
greyData_, dataWidth_, dataHeight_, top_, left_, getHeight(), getWidth(), err_handler));
|
||||
if (err_handler.ErrCode()) return Ref<LuminanceSource>();
|
||||
return result;
|
||||
}
|
||||
|
||||
Ref<ByteMatrix> GreyscaleLuminanceSource::getByteMatrix() const {
|
||||
return Ref<ByteMatrix>(new ByteMatrix(getWidth(), getHeight(), getMatrix()));
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_GREYSCALE_LUMINANCE_SOURCE_HPP__
|
||||
#define __ZXING_COMMON_GREYSCALE_LUMINANCE_SOURCE_HPP__
|
||||
|
||||
#include "../errorhandler.hpp"
|
||||
#include "../luminance_source.hpp"
|
||||
#include "bytematrix.hpp"
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class GreyscaleLuminanceSource : public LuminanceSource {
|
||||
private:
|
||||
typedef LuminanceSource Super;
|
||||
ArrayRef<char> greyData_;
|
||||
const int dataWidth_;
|
||||
const int dataHeight_;
|
||||
const int left_;
|
||||
const int top_;
|
||||
|
||||
public:
|
||||
GreyscaleLuminanceSource(ArrayRef<char> greyData, int dataWidth, int dataHeight, int left,
|
||||
int top, int width, int height, ErrorHandler& err_handler);
|
||||
|
||||
ArrayRef<char> getRow(int y, ArrayRef<char> row, ErrorHandler& err_handler) const override;
|
||||
ArrayRef<char> getMatrix() const override;
|
||||
Ref<ByteMatrix> getByteMatrix() const override;
|
||||
|
||||
bool isRotateSupported() const override { return true; }
|
||||
|
||||
Ref<LuminanceSource> rotateCounterClockwise(ErrorHandler& err_handler) const override;
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_GREYSCALE_LUMINANCE_SOURCE_HPP__
|
||||
@@ -0,0 +1,67 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "greyscale_rotated_luminance_source.hpp"
|
||||
#include "bytematrix.hpp"
|
||||
using zxing::ArrayRef;
|
||||
using zxing::ByteMatrix;
|
||||
using zxing::ErrorHandler;
|
||||
using zxing::GreyscaleRotatedLuminanceSource;
|
||||
using zxing::Ref;
|
||||
|
||||
// Note that dataWidth and dataHeight are not reversed, as we need to
|
||||
// be able to traverse the greyData correctly, which does not get
|
||||
// rotated.
|
||||
GreyscaleRotatedLuminanceSource::GreyscaleRotatedLuminanceSource(ArrayRef<char> greyData,
|
||||
int dataWidth, int dataHeight,
|
||||
int left, int top, int _width,
|
||||
int _height,
|
||||
ErrorHandler& err_handler)
|
||||
: Super(_width, _height), greyData_(greyData), dataWidth_(dataWidth), left_(left), top_(top) {
|
||||
// Intentionally comparing to the opposite dimension since we're rotated.
|
||||
if (left + _width > dataHeight || top + _height > dataWidth) {
|
||||
err_handler = IllegalArgumentErrorHandler("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
}
|
||||
|
||||
// The API asks for rows, but we're rotated, so we return columns.
|
||||
ArrayRef<char> GreyscaleRotatedLuminanceSource::getRow(int y, ArrayRef<char> row,
|
||||
ErrorHandler& err_handler) const {
|
||||
if (y < 0 || y >= getHeight()) {
|
||||
err_handler = IllegalArgumentErrorHandler("Requested row is outside the image.");
|
||||
return ArrayRef<char>();
|
||||
}
|
||||
if (!row || row->size() < getWidth()) {
|
||||
row = ArrayRef<char>(getWidth());
|
||||
}
|
||||
int offset = (left_ * dataWidth_) + (dataWidth_ - 1 - (y + top_));
|
||||
for (int x = 0; x < getWidth(); x++) {
|
||||
row[x] = greyData_[offset];
|
||||
offset += dataWidth_;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
ArrayRef<char> GreyscaleRotatedLuminanceSource::getMatrix() const {
|
||||
ArrayRef<char> result(getWidth() * getHeight());
|
||||
for (int y = 0; y < getHeight(); y++) {
|
||||
char* row = &result[y * getWidth()];
|
||||
int offset = (left_ * dataWidth_) + (dataWidth_ - 1 - (y + top_));
|
||||
for (int x = 0; x < getWidth(); x++) {
|
||||
row[x] = greyData_[offset];
|
||||
offset += dataWidth_;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Ref<ByteMatrix> GreyscaleRotatedLuminanceSource::getByteMatrix() const {
|
||||
return Ref<ByteMatrix>(new ByteMatrix(getWidth(), getHeight(), getMatrix()));
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_GREYSCALE_ROTATED_LUMINANCE_SOURCE_HPP__
|
||||
#define __ZXING_COMMON_GREYSCALE_ROTATED_LUMINANCE_SOURCE_HPP__
|
||||
|
||||
#include "../errorhandler.hpp"
|
||||
#include "../luminance_source.hpp"
|
||||
#include "bytematrix.hpp"
|
||||
namespace zxing {
|
||||
|
||||
class GreyscaleRotatedLuminanceSource : public LuminanceSource {
|
||||
private:
|
||||
typedef LuminanceSource Super;
|
||||
ArrayRef<char> greyData_;
|
||||
const int dataWidth_;
|
||||
const int left_;
|
||||
const int top_;
|
||||
|
||||
public:
|
||||
GreyscaleRotatedLuminanceSource(ArrayRef<char> greyData, int dataWidth, int dataHeight,
|
||||
int left, int top, int _width, int _height,
|
||||
ErrorHandler& err_handler);
|
||||
|
||||
ArrayRef<char> getRow(int y, ArrayRef<char> row, ErrorHandler& err_handler) const override;
|
||||
ArrayRef<char> getMatrix() const override;
|
||||
Ref<ByteMatrix> getByteMatrix() const override;
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_GREYSCALE_ROTATED_LUMINANCE_SOURCE_HPP__
|
||||
@@ -0,0 +1,119 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "grid_sampler.hpp"
|
||||
#include "perspective_transform.hpp"
|
||||
#include <sstream>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
GridSampler GridSampler::gridSampler;
|
||||
|
||||
GridSampler::GridSampler() {}
|
||||
|
||||
// Samples an image for a rectangular matrix of bits of the given dimension.
|
||||
Ref<BitMatrix> GridSampler::sampleGrid(Ref<BitMatrix> image, int dimension,
|
||||
Ref<PerspectiveTransform> transform,
|
||||
ErrorHandler &err_handler) {
|
||||
Ref<BitMatrix> bits(new BitMatrix(dimension, err_handler));
|
||||
if (err_handler.ErrCode()) return Ref<BitMatrix>();
|
||||
|
||||
vector<float> points(dimension << 1, 0.0f);
|
||||
|
||||
int outlier = 0;
|
||||
int maxOutlier = dimension * dimension * 3 / 10 - 1;
|
||||
|
||||
for (int y = 0; y < dimension; 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);
|
||||
// Quick check to see if points transformed to something inside the
|
||||
// image; sufficient to check the endpoings
|
||||
outlier += checkAndNudgePoints(image->getWidth(), image->getHeight(), points, err_handler);
|
||||
if (err_handler.ErrCode()) return Ref<BitMatrix>();
|
||||
|
||||
if (outlier >= maxOutlier) {
|
||||
ostringstream s;
|
||||
s << "Over 30% points out of bounds.";
|
||||
err_handler = ReaderErrorHandler(s.str().c_str());
|
||||
return Ref<BitMatrix>();
|
||||
}
|
||||
|
||||
for (int x = 0; x < max; x += 2) {
|
||||
if (image->get((int)points[x], (int)points[x + 1])) {
|
||||
// Black (-ish) pixel
|
||||
bits->set(x >> 1, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
int GridSampler::checkAndNudgePoints(int width, int height, vector<float> &points,
|
||||
ErrorHandler &err_handler) {
|
||||
// Modified to support stlport
|
||||
float *pts = NULL;
|
||||
|
||||
if (points.size() > 0) {
|
||||
pts = &points[0];
|
||||
} else {
|
||||
err_handler = ReaderErrorHandler("checkAndNudgePoints:: no points!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int size = (int)points.size() / 2;
|
||||
|
||||
// The Java code assumes that if the start and end points are in bounds, the
|
||||
// rest will also be. However, in some unusual cases points in the middle
|
||||
// may also be out of bounds. Since we can't rely on an
|
||||
// ArrayIndexOutOfBoundsException like Java, we check every point.
|
||||
|
||||
int outCount = 0;
|
||||
// int maxError = (int)(size/2/3 - 1);
|
||||
|
||||
float maxborder = width / size * 3;
|
||||
|
||||
for (size_t offset = 0; offset < points.size(); offset += 2) {
|
||||
int x = (int)pts[offset];
|
||||
int y = (int)pts[offset + 1];
|
||||
// if((int)offset==0)
|
||||
// cout<<"checkAndNudgePoints "<<(int)offset<<": ("<<x<<",
|
||||
//"<<y<<")"<<endl;
|
||||
|
||||
if (x < -1 || x > width || y < -1 || y > height) {
|
||||
outCount++;
|
||||
if (x > width + maxborder || y > height + maxborder || x < -maxborder ||
|
||||
y < -maxborder) {
|
||||
err_handler = ReaderErrorHandler("checkAndNudgePoints::Out of bounds!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (x <= -1) {
|
||||
points[offset] = 0.0f;
|
||||
} else if (x >= width) {
|
||||
points[offset] = float(width - 1);
|
||||
}
|
||||
if (y <= -1) {
|
||||
points[offset + 1] = 0.0f;
|
||||
} else if (y >= height) {
|
||||
points[offset + 1] = float(height - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return outCount;
|
||||
}
|
||||
|
||||
GridSampler &GridSampler::getInstance() { return gridSampler; }
|
||||
} // namespace zxing
|
||||
@@ -0,0 +1,34 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_GRID_SAMPLER_HPP__
|
||||
#define __ZXING_COMMON_GRID_SAMPLER_HPP__
|
||||
|
||||
#include "bitmatrix.hpp"
|
||||
#include "bytematrix.hpp"
|
||||
#include "counted.hpp"
|
||||
#include "perspective_transform.hpp"
|
||||
|
||||
namespace zxing {
|
||||
class GridSampler {
|
||||
private:
|
||||
static GridSampler gridSampler;
|
||||
GridSampler();
|
||||
|
||||
public:
|
||||
Ref<BitMatrix> sampleGrid(Ref<BitMatrix> image, int dimension,
|
||||
Ref<PerspectiveTransform> transform, ErrorHandler &err_handler);
|
||||
static int checkAndNudgePoints(int width, int height, vector<float> &points,
|
||||
ErrorHandler &err_handler);
|
||||
static GridSampler &getInstance();
|
||||
};
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_GRID_SAMPLER_HPP__
|
||||
@@ -0,0 +1,66 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
#include "../../precomp.hpp"
|
||||
#include "imagecut.hpp"
|
||||
|
||||
|
||||
namespace zxing {
|
||||
|
||||
ImageCut::ImageCut() {}
|
||||
|
||||
ImageCut::~ImageCut() {}
|
||||
|
||||
int ImageCut::Cut(uint8_t* poImageData, int iWidth, int iHeight, int iTopLeftX, int iTopLeftY,
|
||||
int iBottomRightX, int iBottomRightY, ImageCutResult& result) {
|
||||
if (iTopLeftX < 0 || iTopLeftX > iBottomRightX || iBottomRightX >= iWidth) return -1;
|
||||
if (iTopLeftY < 0 || iTopLeftY > iBottomRightY || iBottomRightY >= iHeight) return -1;
|
||||
int iNewWidth = iBottomRightX - iTopLeftX + 1;
|
||||
int iNewHeight = iBottomRightY - iTopLeftY + 1;
|
||||
|
||||
result.arrImage = new Array<uint8_t>(iNewWidth * iNewHeight);
|
||||
result.iHeight = iNewHeight;
|
||||
result.iWidth = iNewWidth;
|
||||
|
||||
int idx = 0;
|
||||
for (int y = 0; y < iHeight; ++y) {
|
||||
if (y < iTopLeftY || y > iBottomRightY) continue;
|
||||
for (int x = 0; x < iWidth; ++x) {
|
||||
if (x < iTopLeftX || x > iBottomRightX) continue;
|
||||
result.arrImage[idx++] = poImageData[y * iWidth + x];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ImageCut::Cut(Ref<ByteMatrix> matrix, float fRatio, ImageCutResult& result) {
|
||||
int iWidth = matrix->getWidth();
|
||||
int iHeight = matrix->getHeight();
|
||||
|
||||
int iMinX = iWidth * (1 - fRatio) / 2;
|
||||
int iMinY = iHeight * (1 - fRatio) / 2;
|
||||
int iMaxX = iWidth * (1 + fRatio) / 2 - 1;
|
||||
int iMaxY = iHeight * (1 + fRatio) / 2 - 1;
|
||||
|
||||
if (iMinY < 0 || iMinY > iMaxX || iMaxX >= iWidth) return -1;
|
||||
if (iMinX < 0 || iMinX > iMaxX || iMaxX >= iWidth) return -1;
|
||||
int iNewHeight = iMaxY - iMinY + 1;
|
||||
int iNewWidth = iMaxX - iMinX + 1;
|
||||
|
||||
result.arrImage = new Array<uint8_t>(iNewWidth * iNewHeight);
|
||||
result.iWidth = iNewWidth;
|
||||
result.iHeight = iNewHeight;
|
||||
|
||||
int idx = 0;
|
||||
for (int y = 0; y < iNewHeight; ++y) {
|
||||
for (int x = 0; x < iNewWidth; ++x) {
|
||||
result.arrImage[idx++] = matrix->get(x + iMinX, y + iMinY);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace zxing
|
||||
@@ -0,0 +1,32 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
#ifndef __ZXING_COMMON_IMAGECUT_HPP__
|
||||
#define __ZXING_COMMON_IMAGECUT_HPP__
|
||||
#include "bytematrix.hpp"
|
||||
#include "counted.hpp"
|
||||
|
||||
namespace zxing {
|
||||
|
||||
typedef struct _ImageCutResult {
|
||||
ArrayRef<uint8_t> arrImage;
|
||||
int iWidth;
|
||||
int iHeight;
|
||||
} ImageCutResult;
|
||||
|
||||
class ImageCut {
|
||||
public:
|
||||
ImageCut();
|
||||
~ImageCut();
|
||||
|
||||
static int Cut(uint8_t* poImageData, int iWidth, int iHeight, int iTopLeftX, int iTopLeftY,
|
||||
int iBottomRightX, int iBottomRightY, ImageCutResult& result);
|
||||
static int Cut(Ref<ByteMatrix> matrix, float fRatio, ImageCutResult& result);
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
#endif // __ZXING_COMMON_IMAGECUT_HPP__
|
||||
@@ -0,0 +1,89 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
#include "../../precomp.hpp"
|
||||
#include "kmeans.hpp"
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
namespace zxing {
|
||||
|
||||
double cal_distance(vector<double> a, vector<double> b) {
|
||||
const float KMEANS_COUNT_FACTOR = 0;
|
||||
const float KMEANS_MS_FACTOR = 1;
|
||||
|
||||
uint da = a.size();
|
||||
double val = 0.0;
|
||||
for (uint i = 0; i < da; i++) {
|
||||
if (i == 1)
|
||||
val += KMEANS_MS_FACTOR * pow((a[i] - b[i]), 2);
|
||||
else if (i == 0)
|
||||
val += KMEANS_COUNT_FACTOR * pow((a[i] - b[i]), 2);
|
||||
else
|
||||
val += pow((a[i] - b[i]), 2);
|
||||
}
|
||||
return pow(val, 0.5);
|
||||
}
|
||||
|
||||
/*
|
||||
* maxepoches max iteration epochs
|
||||
* minchanged min central change times
|
||||
*/
|
||||
vector<Cluster> k_means(vector<vector<double> > trainX, uint k, uint maxepoches, uint minchanged) {
|
||||
const uint row_num = trainX.size();
|
||||
const uint col_num = trainX[0].size();
|
||||
|
||||
// initialize the cluster central
|
||||
vector<Cluster> clusters(k);
|
||||
int step = trainX.size() / k;
|
||||
|
||||
for (uint i = 0; i < k; i++) {
|
||||
clusters[i].centroid = trainX[i * step];
|
||||
}
|
||||
|
||||
// try max epochs times iteration untill convergence
|
||||
for (uint it = 0; it < maxepoches; it++) {
|
||||
for (uint i = 0; i < k; i++) {
|
||||
clusters[i].samples.clear();
|
||||
}
|
||||
for (uint j = 0; j < row_num; j++) {
|
||||
uint c = 0;
|
||||
double min_distance = cal_distance(trainX[j], clusters[c].centroid);
|
||||
for (uint i = 1; i < k; i++) {
|
||||
double distance = cal_distance(trainX[j], clusters[i].centroid);
|
||||
if (distance < min_distance) {
|
||||
min_distance = distance;
|
||||
c = i;
|
||||
}
|
||||
}
|
||||
clusters[c].samples.push_back(j);
|
||||
}
|
||||
|
||||
uint changed = 0;
|
||||
// update cluster central
|
||||
for (uint i = 0; i < k; i++) {
|
||||
vector<double> val(col_num, 0.0);
|
||||
for (uint j = 0; j < clusters[i].samples.size(); j++) {
|
||||
uint sample = clusters[i].samples[j];
|
||||
for (uint d = 0; d < col_num; d++) {
|
||||
val[d] += trainX[sample][d];
|
||||
if (j == clusters[i].samples.size() - 1) {
|
||||
double value = val[d] / clusters[i].samples.size();
|
||||
if (clusters[i].centroid[d] != value) {
|
||||
clusters[i].centroid[d] = value;
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed <= minchanged) return clusters;
|
||||
}
|
||||
return clusters;
|
||||
}
|
||||
|
||||
} // namespace zxing
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
#ifndef __ZXING_COMMON_KMEANS_HPP__
|
||||
#define __ZXING_COMMON_KMEANS_HPP__
|
||||
#include <vector>
|
||||
|
||||
namespace zxing {
|
||||
|
||||
using namespace std;
|
||||
typedef unsigned int uint;
|
||||
|
||||
struct Cluster {
|
||||
vector<double> centroid;
|
||||
vector<uint> samples;
|
||||
};
|
||||
|
||||
double cal_distance(vector<double> a, vector<double> b);
|
||||
vector<Cluster> k_means(vector<vector<double> > trainX, uint k, uint maxepoches, uint minchanged);
|
||||
|
||||
} // namespace zxing
|
||||
#endif // __ZXING_COMMON_KMEANS_HPP__
|
||||
@@ -0,0 +1,95 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_MATHUTILS_HPP__
|
||||
#define __ZXING_COMMON_MATHUTILS_HPP__
|
||||
|
||||
#include <cmath>
|
||||
#if (defined __GNUC__ && defined __x86_64__ && defined __SSE2__ && !defined __APPLE__ && \
|
||||
!defined __GXX_WEAK__)
|
||||
#include <ammintrin.h>
|
||||
#elif defined _MSC_VER && (defined _M_X64 || defined _M_IX86)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
namespace zxing {
|
||||
namespace common {
|
||||
|
||||
class MathUtils {
|
||||
private:
|
||||
MathUtils();
|
||||
~MathUtils();
|
||||
|
||||
public:
|
||||
static inline float distance(float aX, float aY, float bX, float bY) {
|
||||
float xDiff = aX - bX;
|
||||
float yDiff = aY - bY;
|
||||
return sqrt(float(xDiff * xDiff + yDiff * yDiff));
|
||||
}
|
||||
|
||||
static inline float distance_4_int(int aX, int aY, int bX, int bY) {
|
||||
return sqrt(float((aX - bX) * (aX - bX) + (aY - bY) * (aY - bY)));
|
||||
}
|
||||
|
||||
static inline void getRangeValues(int& minValue, int& maxValue, int min, int max) {
|
||||
int finalMinValue, finalMaxValue;
|
||||
|
||||
if (minValue < maxValue) {
|
||||
finalMinValue = minValue;
|
||||
finalMaxValue = maxValue;
|
||||
} else {
|
||||
finalMinValue = maxValue;
|
||||
finalMaxValue = minValue;
|
||||
}
|
||||
|
||||
finalMinValue = finalMinValue > min ? finalMinValue : min;
|
||||
finalMaxValue = finalMaxValue < max ? finalMaxValue : max;
|
||||
|
||||
minValue = finalMinValue;
|
||||
maxValue = finalMaxValue;
|
||||
}
|
||||
|
||||
static inline bool isInRange(float x, float y, float width, float height) {
|
||||
if ((x >= 0.0 && x <= (width - 1.0)) && (y >= 0.0 && y <= (height - 1.0))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline float distance(int aX, int aY, int bX, int bY) {
|
||||
int xDiff = aX - bX;
|
||||
int yDiff = aY - bY;
|
||||
return sqrt(float(xDiff * xDiff + yDiff * yDiff));
|
||||
}
|
||||
|
||||
static inline float VecCross(float* v1, float* v2) { return v1[0] * v2[1] - v1[1] * v2[0]; }
|
||||
|
||||
static inline void Stddev(std::vector<float>& resultSet, float& avg, float& stddev) {
|
||||
double sum = std::accumulate(resultSet.begin(), resultSet.end(), 0.0);
|
||||
avg = sum / resultSet.size();
|
||||
|
||||
double accum = 0.0;
|
||||
for (std::size_t i = 0; i < resultSet.size(); i++) {
|
||||
accum += (resultSet[i] - avg) * (resultSet[i] - avg);
|
||||
}
|
||||
|
||||
stddev = sqrt(accum / (resultSet.size()));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace common
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_MATHUTILS_HPP__
|
||||
@@ -0,0 +1,120 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "perspective_transform.hpp"
|
||||
|
||||
|
||||
namespace zxing {
|
||||
|
||||
// This class implements a perspective transform in two dimensions. Given four
|
||||
// source and four destination points, it will compute the transformation
|
||||
// implied between them. The code is based directly upon section 3.4.2 of George
|
||||
// Wolberg's "Digital Image Warping"; see pages 54-56
|
||||
PerspectiveTransform::PerspectiveTransform(float inA11, float inA21, float inA31, float inA12,
|
||||
float inA22, float inA32, float inA13, float inA23,
|
||||
float inA33)
|
||||
: a11(inA11),
|
||||
a12(inA12),
|
||||
a13(inA13),
|
||||
a21(inA21),
|
||||
a22(inA22),
|
||||
a23(inA23),
|
||||
a31(inA31),
|
||||
a32(inA32),
|
||||
a33(inA33) {}
|
||||
|
||||
Ref<PerspectiveTransform> PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||
float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float x0p,
|
||||
float y0p, float x1p, float y1p, float x2p, float y2p, float x3p, float y3p) {
|
||||
Ref<PerspectiveTransform> qToS =
|
||||
PerspectiveTransform::quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
|
||||
Ref<PerspectiveTransform> sToQ =
|
||||
PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
|
||||
return sToQ->times(qToS);
|
||||
}
|
||||
|
||||
Ref<PerspectiveTransform> PerspectiveTransform::squareToQuadrilateral(float x0, float y0, float x1,
|
||||
float y1, float x2, float y2,
|
||||
float x3, float y3) {
|
||||
float dx3 = x0 - x1 + x2 - x3;
|
||||
float dy3 = y0 - y1 + y2 - y3;
|
||||
if (fabs(dx3) <= 1e-6 && fabs(dy3) <= 1e-6) {
|
||||
Ref<PerspectiveTransform> result(
|
||||
new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0f, 0.0f, 1.0f));
|
||||
return result;
|
||||
} else {
|
||||
float dx1 = x1 - x2;
|
||||
float dx2 = x3 - x2;
|
||||
float dy1 = y1 - y2;
|
||||
float dy2 = y3 - y2;
|
||||
float denominator = dx1 * dy2 - dx2 * dy1;
|
||||
float a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
|
||||
float a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
|
||||
Ref<PerspectiveTransform> result(
|
||||
new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1,
|
||||
y3 - y0 + a23 * y3, y0, a13, a23, 1.0f));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<PerspectiveTransform> PerspectiveTransform::quadrilateralToSquare(float x0, float y0, float x1,
|
||||
float y1, float x2, float y2,
|
||||
float x3, float y3) {
|
||||
// Here, the adjoint serves as the inverse:
|
||||
return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3)->buildAdjoint();
|
||||
}
|
||||
|
||||
Ref<PerspectiveTransform> PerspectiveTransform::buildAdjoint() {
|
||||
// Adjoint is the transpose of the cofactor matrix:
|
||||
Ref<PerspectiveTransform> result(new PerspectiveTransform(
|
||||
a22 * a33 - a23 * a32, a23 * a31 - a21 * a33, a21 * a32 - a22 * a31, a13 * a32 - a12 * a33,
|
||||
a11 * a33 - a13 * a31, a12 * a31 - a11 * a32, a12 * a23 - a13 * a22, a13 * a21 - a11 * a23,
|
||||
a11 * a22 - a12 * a21));
|
||||
return result;
|
||||
}
|
||||
|
||||
Ref<PerspectiveTransform> PerspectiveTransform::times(Ref<PerspectiveTransform> other) {
|
||||
Ref<PerspectiveTransform> result(
|
||||
new PerspectiveTransform(a11 * other->a11 + a21 * other->a12 + a31 * other->a13,
|
||||
a11 * other->a21 + a21 * other->a22 + a31 * other->a23,
|
||||
a11 * other->a31 + a21 * other->a32 + a31 * other->a33,
|
||||
a12 * other->a11 + a22 * other->a12 + a32 * other->a13,
|
||||
a12 * other->a21 + a22 * other->a22 + a32 * other->a23,
|
||||
a12 * other->a31 + a22 * other->a32 + a32 * other->a33,
|
||||
a13 * other->a11 + a23 * other->a12 + a33 * other->a13,
|
||||
a13 * other->a21 + a23 * other->a22 + a33 * other->a23,
|
||||
a13 * other->a31 + a23 * other->a32 + a33 * other->a33));
|
||||
return result;
|
||||
}
|
||||
|
||||
void PerspectiveTransform::transformPoints(vector<float>& points) {
|
||||
int max = points.size();
|
||||
|
||||
// Modified to support stlport
|
||||
float* pts = NULL;
|
||||
|
||||
if (points.size() > 0) {
|
||||
pts = &points[0];
|
||||
}
|
||||
|
||||
for (int i = 0; i < max; i += 2) {
|
||||
float x = pts[i];
|
||||
float y = pts[i + 1];
|
||||
|
||||
float denominator = a13 * x + a23 * y + a33;
|
||||
|
||||
float w = 1.0f / denominator;
|
||||
|
||||
pts[i] = (a11 * x + a21 * y + a31) * w;
|
||||
pts[i + 1] = (a12 * x + a22 * y + a32) * w;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zxing
|
||||
@@ -0,0 +1,39 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_PERSPECTIVETRANSFORM_HPP__
|
||||
#define __ZXING_COMMON_PERSPECTIVETRANSFORM_HPP__
|
||||
|
||||
#include "counted.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace zxing {
|
||||
class PerspectiveTransform : public Counted {
|
||||
private:
|
||||
float a11, a12, a13, a21, a22, a23, a31, a32, a33;
|
||||
PerspectiveTransform(float a11, float a21, float a31, float a12, float a22, float a32,
|
||||
float a13, float a23, float a33);
|
||||
|
||||
public:
|
||||
static Ref<PerspectiveTransform> quadrilateralToQuadrilateral(
|
||||
float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float x0p,
|
||||
float y0p, float x1p, float y1p, float x2p, float y2p, float x3p, float y3p);
|
||||
static Ref<PerspectiveTransform> squareToQuadrilateral(float x0, float y0, float x1, float y1,
|
||||
float x2, float y2, float x3, float y3);
|
||||
static Ref<PerspectiveTransform> quadrilateralToSquare(float x0, float y0, float x1, float y1,
|
||||
float x2, float y2, float x3, float y3);
|
||||
Ref<PerspectiveTransform> buildAdjoint();
|
||||
Ref<PerspectiveTransform> times(Ref<PerspectiveTransform> other);
|
||||
void transformPoints(std::vector<float>& points);
|
||||
};
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_PERSPECTIVETRANSFORM_HPP__
|
||||
@@ -0,0 +1,99 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../../precomp.hpp"
|
||||
#include "genericgf.hpp"
|
||||
#include "genericgfpoly.hpp"
|
||||
|
||||
using zxing::ErrorHandler;
|
||||
using zxing::GenericGF;
|
||||
using zxing::GenericGFPoly;
|
||||
using zxing::Ref;
|
||||
|
||||
GenericGF::GenericGF(int primitive_, int size_, int b, ErrorHandler &err_handler)
|
||||
: size(size_), primitive(primitive_), generatorBase(b) {
|
||||
expTable.resize(size);
|
||||
logTable.resize(size);
|
||||
|
||||
int x = 1;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
expTable[i] = x;
|
||||
x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
|
||||
if (x >= size) {
|
||||
x ^= primitive;
|
||||
x &= size - 1;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
logTable[expTable[i]] = i;
|
||||
}
|
||||
// logTable[0] == 0 but this should never be used
|
||||
zero =
|
||||
Ref<GenericGFPoly>(new GenericGFPoly(*this, ArrayRef<int>(new Array<int>(1)), err_handler));
|
||||
zero->getCoefficients()[0] = 0;
|
||||
one =
|
||||
Ref<GenericGFPoly>(new GenericGFPoly(*this, ArrayRef<int>(new Array<int>(1)), err_handler));
|
||||
one->getCoefficients()[0] = 1;
|
||||
if (err_handler.ErrCode()) return;
|
||||
// initialized = true;
|
||||
}
|
||||
|
||||
Ref<GenericGFPoly> GenericGF::getZero() { return zero; }
|
||||
|
||||
Ref<GenericGFPoly> GenericGF::getOne() { return one; }
|
||||
|
||||
Ref<GenericGFPoly> GenericGF::buildMonomial(int degree, int coefficient,
|
||||
ErrorHandler &err_handler) {
|
||||
if (degree < 0) {
|
||||
err_handler = IllegalArgumentErrorHandler("Degree must be non-negative");
|
||||
return Ref<GenericGFPoly>();
|
||||
}
|
||||
if (coefficient == 0) {
|
||||
return zero;
|
||||
}
|
||||
ArrayRef<int> coefficients(new Array<int>(degree + 1));
|
||||
coefficients[0] = coefficient;
|
||||
|
||||
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(*this, coefficients, err_handler));
|
||||
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
|
||||
return gfpoly;
|
||||
}
|
||||
|
||||
int GenericGF::addOrSubtract(int a, int b) { return a ^ b; }
|
||||
|
||||
int GenericGF::exp(int a) { return expTable[a]; }
|
||||
|
||||
int GenericGF::log(int a, ErrorHandler &err_handler) {
|
||||
if (a == 0) {
|
||||
err_handler = IllegalArgumentErrorHandler("cannot give log(0)");
|
||||
return -1;
|
||||
}
|
||||
return logTable[a];
|
||||
}
|
||||
|
||||
int GenericGF::inverse(int a, ErrorHandler &err_handler) {
|
||||
if (a == 0) {
|
||||
err_handler = IllegalArgumentErrorHandler("Cannot calculate the inverse of 0");
|
||||
return -1;
|
||||
}
|
||||
return expTable[size - logTable[a] - 1];
|
||||
}
|
||||
|
||||
int GenericGF::multiply(int a, int b) {
|
||||
if (a == 0 || b == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return expTable[(logTable[a] + logTable[b]) % (size - 1)];
|
||||
}
|
||||
|
||||
int GenericGF::getSize() { return size; }
|
||||
|
||||
int GenericGF::getGeneratorBase() { return generatorBase; }
|
||||
@@ -0,0 +1,58 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_REEDSOLOMON_GENERICGF_HPP__
|
||||
#define __ZXING_COMMON_REEDSOLOMON_GENERICGF_HPP__
|
||||
|
||||
#include "../../errorhandler.hpp"
|
||||
#include "../counted.hpp"
|
||||
|
||||
namespace zxing {
|
||||
class GenericGFPoly;
|
||||
|
||||
static zxing::ErrorHandler gf_err_handler_;
|
||||
#define GF_AZTEC_DATA_12 (new GenericGF(0x1069, 4096, 1, gf_err_handler_))
|
||||
#define GF_AZTEC_DATA_10 (new GenericGF(0x409, 1024, 1, gf_err_handler_))
|
||||
#define GF_AZTEC_DATA_6 (new GenericGF(0x43, 64, 1, gf_err_handler_))
|
||||
#define GF_AZTEC_PARAM (new GenericGF(0x13, 16, 1, gf_err_handler_))
|
||||
#define GF_QR_CODE_FIELD_256 (new GenericGF(0x011D, 256, 0, gf_err_handler_))
|
||||
#define GF_DATA_MATRIX_FIELD_256 (new GenericGF(0x012D, 256, 1, gf_err_handler_))
|
||||
#define GF_AZTEC_DATA_8 (new GenericGF(0x012D, 256, 1, gf_err_handler_))
|
||||
#define GF_MAXICODE_FIELD_64 (new GenericGF(0x43, 64, 1, gf_err_handler_))
|
||||
// #define GF_WXCODE (new GenericGF(0x011D, 256, 0, gf_err_handler_))
|
||||
|
||||
class GenericGF : public Counted {
|
||||
private:
|
||||
std::vector<int> expTable;
|
||||
std::vector<int> logTable;
|
||||
Ref<GenericGFPoly> zero;
|
||||
Ref<GenericGFPoly> one;
|
||||
int size;
|
||||
int primitive;
|
||||
int generatorBase;
|
||||
|
||||
public:
|
||||
GenericGF(int primitive, int size, int b, ErrorHandler &err_handler);
|
||||
|
||||
Ref<GenericGFPoly> getZero();
|
||||
Ref<GenericGFPoly> getOne();
|
||||
int getSize();
|
||||
int getGeneratorBase();
|
||||
Ref<GenericGFPoly> buildMonomial(int degree, int coefficient, ErrorHandler &err_handler);
|
||||
|
||||
static int addOrSubtract(int a, int b);
|
||||
int exp(int a);
|
||||
int log(int a, ErrorHandler &err_handler);
|
||||
int inverse(int a, ErrorHandler &err_handler);
|
||||
int multiply(int a, int b);
|
||||
};
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_REEDSOLOMON_GENERICGF_HPP__
|
||||
@@ -0,0 +1,231 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../../precomp.hpp"
|
||||
#include "genericgfpoly.hpp"
|
||||
#include "genericgf.hpp"
|
||||
|
||||
using zxing::ArrayRef;
|
||||
using zxing::ErrorHandler;
|
||||
using zxing::GenericGFPoly;
|
||||
using zxing::Ref;
|
||||
|
||||
// VC++
|
||||
using zxing::GenericGF;
|
||||
|
||||
GenericGFPoly::GenericGFPoly(GenericGF &field, ArrayRef<int> coefficients,
|
||||
ErrorHandler &err_handler)
|
||||
: field_(field) {
|
||||
if (coefficients->size() == 0) {
|
||||
err_handler = IllegalArgumentErrorHandler("need coefficients");
|
||||
return;
|
||||
}
|
||||
int coefficientsLength = coefficients->size();
|
||||
if (coefficientsLength > 1 && coefficients[0] == 0) {
|
||||
// Leading term must be non-zero for anything except the constant
|
||||
// polynomial "0"
|
||||
int firstNonZero = 1;
|
||||
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
|
||||
firstNonZero++;
|
||||
}
|
||||
if (firstNonZero == coefficientsLength) {
|
||||
coefficients_ = field.getZero()->getCoefficients();
|
||||
} else {
|
||||
coefficients_ = ArrayRef<int>(new Array<int>(coefficientsLength - firstNonZero));
|
||||
for (int i = 0; i < (int)coefficients_->size(); i++) {
|
||||
coefficients_[i] = coefficients[i + firstNonZero];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
coefficients_ = coefficients;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayRef<int> GenericGFPoly::getCoefficients() { return coefficients_; }
|
||||
|
||||
int GenericGFPoly::getDegree() { return coefficients_->size() - 1; }
|
||||
|
||||
bool GenericGFPoly::isZero() { return coefficients_[0] == 0; }
|
||||
|
||||
int GenericGFPoly::getCoefficient(int degree) {
|
||||
return coefficients_[coefficients_->size() - 1 - degree];
|
||||
}
|
||||
|
||||
int GenericGFPoly::evaluateAt(int a) {
|
||||
if (a == 0) {
|
||||
// Just return the x^0 coefficient
|
||||
return getCoefficient(0);
|
||||
}
|
||||
|
||||
int size = coefficients_->size();
|
||||
if (a == 1) {
|
||||
// Just the sum of the coefficients
|
||||
int result = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
result = GenericGF::addOrSubtract(result, coefficients_[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
int result = coefficients_[0];
|
||||
for (int i = 1; i < size; i++) {
|
||||
result = GenericGF::addOrSubtract(field_.multiply(a, result), coefficients_[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Ref<GenericGFPoly> GenericGFPoly::addOrSubtract(Ref<zxing::GenericGFPoly> other,
|
||||
ErrorHandler &err_handler) {
|
||||
if (!(&field_ == &other->field_)) {
|
||||
err_handler =
|
||||
IllegalArgumentErrorHandler("GenericGFPolys do not have same GenericGF field");
|
||||
return Ref<GenericGFPoly>();
|
||||
}
|
||||
if (isZero()) {
|
||||
return other;
|
||||
}
|
||||
if (other->isZero()) {
|
||||
return Ref<GenericGFPoly>(this);
|
||||
}
|
||||
|
||||
ArrayRef<int> smallerCoefficients = coefficients_;
|
||||
ArrayRef<int> largerCoefficients = other->getCoefficients();
|
||||
if (smallerCoefficients->size() > largerCoefficients->size()) {
|
||||
ArrayRef<int> temp = smallerCoefficients;
|
||||
smallerCoefficients = largerCoefficients;
|
||||
largerCoefficients = temp;
|
||||
}
|
||||
|
||||
ArrayRef<int> sumDiff(new Array<int>(largerCoefficients->size()));
|
||||
int lengthDiff = largerCoefficients->size() - smallerCoefficients->size();
|
||||
// Copy high-order terms only found in higher-degree polynomial's
|
||||
// coefficients
|
||||
for (int i = 0; i < lengthDiff; i++) {
|
||||
sumDiff[i] = largerCoefficients[i];
|
||||
}
|
||||
|
||||
for (int i = lengthDiff; i < (int)largerCoefficients->size(); i++) {
|
||||
sumDiff[i] =
|
||||
GenericGF::addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
|
||||
}
|
||||
|
||||
// return Ref<GenericGFPoly>(new GenericGFPoly(field_, sumDiff));
|
||||
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(field_, sumDiff, err_handler));
|
||||
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
|
||||
return gfpoly;
|
||||
}
|
||||
|
||||
Ref<GenericGFPoly> GenericGFPoly::multiply(Ref<zxing::GenericGFPoly> other,
|
||||
ErrorHandler &err_handler) {
|
||||
if (!(&field_ == &other->field_)) {
|
||||
err_handler =
|
||||
IllegalArgumentErrorHandler("GenericGFPolys do not have same GenericGF field");
|
||||
return Ref<GenericGFPoly>();
|
||||
}
|
||||
|
||||
if (isZero() || other->isZero()) {
|
||||
return field_.getZero();
|
||||
}
|
||||
|
||||
ArrayRef<int> aCoefficients = coefficients_;
|
||||
int aLength = aCoefficients->size();
|
||||
|
||||
ArrayRef<int> bCoefficients = other->getCoefficients();
|
||||
int bLength = bCoefficients->size();
|
||||
|
||||
ArrayRef<int> product(new Array<int>(aLength + bLength - 1));
|
||||
for (int i = 0; i < aLength; i++) {
|
||||
int aCoeff = aCoefficients[i];
|
||||
for (int j = 0; j < bLength; j++) {
|
||||
product[i + j] =
|
||||
GenericGF::addOrSubtract(product[i + j], field_.multiply(aCoeff, bCoefficients[j]));
|
||||
}
|
||||
}
|
||||
|
||||
// return Ref<GenericGFPoly>(new GenericGFPoly(field_, product));
|
||||
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(field_, product, err_handler));
|
||||
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
|
||||
return gfpoly;
|
||||
}
|
||||
|
||||
Ref<GenericGFPoly> GenericGFPoly::multiply(int scalar, ErrorHandler &err_handler) {
|
||||
if (scalar == 0) {
|
||||
return field_.getZero();
|
||||
}
|
||||
if (scalar == 1) {
|
||||
return Ref<GenericGFPoly>(this);
|
||||
}
|
||||
int size = coefficients_->size();
|
||||
ArrayRef<int> product(new Array<int>(size));
|
||||
for (int i = 0; i < size; i++) {
|
||||
product[i] = field_.multiply(coefficients_[i], scalar);
|
||||
}
|
||||
// return Ref<GenericGFPoly>(new GenericGFPoly(field_, product));
|
||||
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(field_, product, err_handler));
|
||||
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
|
||||
return gfpoly;
|
||||
}
|
||||
|
||||
Ref<GenericGFPoly> GenericGFPoly::multiplyByMonomial(int degree, int coefficient,
|
||||
ErrorHandler &err_handler) {
|
||||
if (degree < 0) {
|
||||
err_handler = IllegalArgumentErrorHandler("degree must not be less then 0");
|
||||
return Ref<GenericGFPoly>();
|
||||
}
|
||||
if (coefficient == 0) {
|
||||
return field_.getZero();
|
||||
}
|
||||
int size = coefficients_->size();
|
||||
ArrayRef<int> product(new Array<int>(size + degree));
|
||||
for (int i = 0; i < size; i++) {
|
||||
product[i] = field_.multiply(coefficients_[i], coefficient);
|
||||
}
|
||||
// return Ref<GenericGFPoly>(new GenericGFPoly(field_, product));
|
||||
Ref<GenericGFPoly> gfpoly(new GenericGFPoly(field_, product, err_handler));
|
||||
if (err_handler.ErrCode()) return Ref<GenericGFPoly>();
|
||||
return gfpoly;
|
||||
}
|
||||
|
||||
std::vector<Ref<GenericGFPoly>> GenericGFPoly::divide(Ref<GenericGFPoly> other,
|
||||
ErrorHandler &err_handler) {
|
||||
if (!(&field_ == &other->field_)) {
|
||||
err_handler =
|
||||
IllegalArgumentErrorHandler("GenericGFPolys do not have same GenericGF field");
|
||||
return std::vector<Ref<GenericGFPoly>>();
|
||||
}
|
||||
if (other->isZero()) {
|
||||
err_handler = IllegalArgumentErrorHandler("divide by 0");
|
||||
return std::vector<Ref<GenericGFPoly>>();
|
||||
}
|
||||
|
||||
Ref<GenericGFPoly> quotient = field_.getZero();
|
||||
Ref<GenericGFPoly> remainder = Ref<GenericGFPoly>(this);
|
||||
|
||||
int denominatorLeadingTerm = other->getCoefficient(other->getDegree());
|
||||
int inverseDenominatorLeadingTerm = field_.inverse(denominatorLeadingTerm, err_handler);
|
||||
if (err_handler.ErrCode()) return std::vector<Ref<GenericGFPoly>>();
|
||||
|
||||
while (remainder->getDegree() >= other->getDegree() && !remainder->isZero()) {
|
||||
int degreeDifference = remainder->getDegree() - other->getDegree();
|
||||
int scale = field_.multiply(remainder->getCoefficient(remainder->getDegree()),
|
||||
inverseDenominatorLeadingTerm);
|
||||
Ref<GenericGFPoly> term = other->multiplyByMonomial(degreeDifference, scale, err_handler);
|
||||
if (err_handler.ErrCode()) return std::vector<Ref<GenericGFPoly>>();
|
||||
Ref<GenericGFPoly> iterationQuotiont =
|
||||
field_.buildMonomial(degreeDifference, scale, err_handler);
|
||||
if (err_handler.ErrCode()) return std::vector<Ref<GenericGFPoly>>();
|
||||
quotient = quotient->addOrSubtract(iterationQuotiont, err_handler);
|
||||
remainder = remainder->addOrSubtract(term, err_handler);
|
||||
if (err_handler.ErrCode()) return std::vector<Ref<GenericGFPoly>>();
|
||||
}
|
||||
|
||||
std::vector<Ref<GenericGFPoly>> returnValue(2);
|
||||
returnValue[0] = quotient;
|
||||
returnValue[1] = remainder;
|
||||
return returnValue;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_REEDSOLOMON_GENERICGFPOLY_HPP__
|
||||
#define __ZXING_COMMON_REEDSOLOMON_GENERICGFPOLY_HPP__
|
||||
|
||||
#include "../../errorhandler.hpp"
|
||||
#include "../array.hpp"
|
||||
#include "../counted.hpp"
|
||||
|
||||
namespace zxing {
|
||||
|
||||
class GenericGF;
|
||||
|
||||
class GenericGFPoly : public Counted {
|
||||
private:
|
||||
GenericGF &field_;
|
||||
ArrayRef<int> coefficients_;
|
||||
|
||||
public:
|
||||
GenericGFPoly(GenericGF &field, ArrayRef<int> coefficients, ErrorHandler &err_handler);
|
||||
ArrayRef<int> getCoefficients();
|
||||
int getDegree();
|
||||
bool isZero();
|
||||
int getCoefficient(int degree);
|
||||
int evaluateAt(int a);
|
||||
Ref<GenericGFPoly> addOrSubtract(Ref<GenericGFPoly> other, ErrorHandler &err_handler);
|
||||
Ref<GenericGFPoly> multiply(Ref<GenericGFPoly> other, ErrorHandler &err_handler);
|
||||
Ref<GenericGFPoly> multiply(int scalar, ErrorHandler &err_handler);
|
||||
Ref<GenericGFPoly> multiplyByMonomial(int degree, int coefficient, ErrorHandler &err_handler);
|
||||
std::vector<Ref<GenericGFPoly>> divide(Ref<GenericGFPoly> other, ErrorHandler &err_handler);
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_REEDSOLOMON_GENERICGFPOLY_HPP__
|
||||
@@ -0,0 +1,185 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../../precomp.hpp"
|
||||
#include "reed_solomon_decoder.hpp"
|
||||
|
||||
using zxing::ArrayRef;
|
||||
using zxing::ErrorHandler;
|
||||
using zxing::GenericGFPoly;
|
||||
using zxing::ReedSolomonDecoder;
|
||||
using zxing::Ref;
|
||||
using zxing::GenericGF;
|
||||
|
||||
ReedSolomonDecoder::ReedSolomonDecoder(Ref<GenericGF> field_) : field(field_) {}
|
||||
|
||||
ReedSolomonDecoder::~ReedSolomonDecoder() {}
|
||||
|
||||
void ReedSolomonDecoder::decode(ArrayRef<int> received, int twoS, ErrorHandler &err_handler) {
|
||||
Ref<GenericGFPoly> poly(new GenericGFPoly(*field, received, err_handler));
|
||||
if (err_handler.ErrCode()) return;
|
||||
ArrayRef<int> syndromeCoefficients(twoS);
|
||||
bool noError = true;
|
||||
for (int i = 0; i < twoS; i++) {
|
||||
int eval = poly->evaluateAt(field->exp(i + field->getGeneratorBase()));
|
||||
syndromeCoefficients[syndromeCoefficients->size() - 1 - i] = eval;
|
||||
if (eval != 0) {
|
||||
noError = false;
|
||||
}
|
||||
}
|
||||
if (noError) {
|
||||
return;
|
||||
}
|
||||
Ref<GenericGFPoly> syndrome(new GenericGFPoly(*field, syndromeCoefficients, err_handler));
|
||||
Ref<GenericGFPoly> monomial = field->buildMonomial(twoS, 1, err_handler);
|
||||
if (!monomial || err_handler.ErrCode()) {
|
||||
err_handler = ErrorHandler("buildMonomial was zero");
|
||||
return;
|
||||
}
|
||||
vector<Ref<GenericGFPoly>> sigmaOmega =
|
||||
runEuclideanAlgorithm(monomial, syndrome, twoS, err_handler);
|
||||
if (err_handler.ErrCode()) return;
|
||||
|
||||
Ref<GenericGFPoly> sigma = sigmaOmega[0];
|
||||
Ref<GenericGFPoly> omega = sigmaOmega[1];
|
||||
ArrayRef<int> errorLocations = findErrorLocations(sigma, err_handler);
|
||||
if (err_handler.ErrCode()) return;
|
||||
|
||||
ArrayRef<int> errorMagitudes = findErrorMagnitudes(omega, errorLocations, err_handler);
|
||||
if (err_handler.ErrCode()) return;
|
||||
for (int i = 0; i < errorLocations->size(); i++) {
|
||||
int position = received->size() - 1 - field->log(errorLocations[i], err_handler);
|
||||
if (position < 0 || err_handler.ErrCode()) {
|
||||
err_handler = ErrorHandler("Bad error location");
|
||||
return;
|
||||
}
|
||||
received[position] = GenericGF::addOrSubtract(received[position], errorMagitudes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
vector<Ref<GenericGFPoly>> ReedSolomonDecoder::runEuclideanAlgorithm(Ref<GenericGFPoly> a,
|
||||
Ref<GenericGFPoly> b, int R,
|
||||
ErrorHandler &err_handler) {
|
||||
vector<Ref<GenericGFPoly>> result(2);
|
||||
|
||||
// Assume a's degree is >= b's
|
||||
if (a->getDegree() < b->getDegree()) {
|
||||
Ref<GenericGFPoly> tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
Ref<GenericGFPoly> rLast(a);
|
||||
Ref<GenericGFPoly> r(b);
|
||||
Ref<GenericGFPoly> tLast(field->getZero());
|
||||
Ref<GenericGFPoly> t(field->getOne());
|
||||
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while (r->getDegree() >= R / 2) {
|
||||
Ref<GenericGFPoly> rLastLast(rLast);
|
||||
Ref<GenericGFPoly> tLastLast(tLast);
|
||||
rLast = r;
|
||||
tLast = t;
|
||||
|
||||
// Divide rLastLast by rLast, with quotient q and remainder r
|
||||
if (rLast->isZero()) {
|
||||
// Oops, Euclidean algorithm already terminated?
|
||||
err_handler = ErrorHandler("r_{i-1} was zero");
|
||||
return vector<Ref<GenericGFPoly>>();
|
||||
}
|
||||
r = rLastLast;
|
||||
Ref<GenericGFPoly> q = field->getZero();
|
||||
int denominatorLeadingTerm = rLast->getCoefficient(rLast->getDegree());
|
||||
int dltInverse = field->inverse(denominatorLeadingTerm, err_handler);
|
||||
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
|
||||
while (r->getDegree() >= rLast->getDegree() && !r->isZero()) {
|
||||
int degreeDiff = r->getDegree() - rLast->getDegree();
|
||||
int scale = field->multiply(r->getCoefficient(r->getDegree()), dltInverse);
|
||||
q = q->addOrSubtract(field->buildMonomial(degreeDiff, scale, err_handler), err_handler);
|
||||
r = r->addOrSubtract(rLast->multiplyByMonomial(degreeDiff, scale, err_handler),
|
||||
err_handler);
|
||||
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
|
||||
}
|
||||
|
||||
Ref<GenericGFPoly> tmp = q->multiply(tLast, err_handler);
|
||||
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
|
||||
t = tmp->addOrSubtract(tLastLast, err_handler);
|
||||
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
|
||||
|
||||
if (r->getDegree() >= rLast->getDegree()) {
|
||||
err_handler = ErrorHandler("Division algorithm failed to reduce polynomial?");
|
||||
return vector<Ref<GenericGFPoly>>();
|
||||
}
|
||||
}
|
||||
|
||||
int sigmaTildeAtZero = t->getCoefficient(0);
|
||||
if (sigmaTildeAtZero == 0) {
|
||||
err_handler = ErrorHandler("sigmaTilde(0) was zero");
|
||||
return vector<Ref<GenericGFPoly>>();
|
||||
}
|
||||
|
||||
int inverse = field->inverse(sigmaTildeAtZero, err_handler);
|
||||
Ref<GenericGFPoly> sigma(t->multiply(inverse, err_handler));
|
||||
Ref<GenericGFPoly> omega(r->multiply(inverse, err_handler));
|
||||
if (err_handler.ErrCode()) return vector<Ref<GenericGFPoly>>();
|
||||
|
||||
result[0] = sigma;
|
||||
result[1] = omega;
|
||||
return result;
|
||||
}
|
||||
|
||||
ArrayRef<int> ReedSolomonDecoder::findErrorLocations(Ref<GenericGFPoly> errorLocator,
|
||||
ErrorHandler &err_handler) {
|
||||
// This is a direct application of Chien's search
|
||||
int numErrors = errorLocator->getDegree();
|
||||
if (numErrors == 1) { // shortcut
|
||||
ArrayRef<int> result(new Array<int>(1));
|
||||
result[0] = errorLocator->getCoefficient(1);
|
||||
return result;
|
||||
}
|
||||
ArrayRef<int> result(new Array<int>(numErrors));
|
||||
int e = 0;
|
||||
for (int i = 1; i < field->getSize() && e < numErrors; i++) {
|
||||
if (errorLocator->evaluateAt(i) == 0) {
|
||||
result[e] = field->inverse(i, err_handler);
|
||||
e++;
|
||||
}
|
||||
}
|
||||
if (e != numErrors || err_handler.ErrCode()) {
|
||||
err_handler = ErrorHandler("Error locator degree does not match number of root");
|
||||
return ArrayRef<int>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ArrayRef<int> ReedSolomonDecoder::findErrorMagnitudes(Ref<GenericGFPoly> errorEvaluator,
|
||||
ArrayRef<int> errorLocations,
|
||||
ErrorHandler &err_handler) {
|
||||
// This is directly applying Forney's Formula
|
||||
int s = errorLocations->size();
|
||||
ArrayRef<int> result(new Array<int>(s));
|
||||
for (int i = 0; i < s; i++) {
|
||||
int xiInverse = field->inverse(errorLocations[i], err_handler);
|
||||
int denominator = 1;
|
||||
for (int j = 0; j < s; j++) {
|
||||
if (i != j) {
|
||||
int term = field->multiply(errorLocations[j], xiInverse);
|
||||
int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1;
|
||||
denominator = field->multiply(denominator, termPlus1);
|
||||
}
|
||||
}
|
||||
result[i] = field->multiply(errorEvaluator->evaluateAt(xiInverse),
|
||||
field->inverse(denominator, err_handler));
|
||||
if (field->getGeneratorBase() != 0) {
|
||||
result[i] = field->multiply(result[i], xiInverse);
|
||||
}
|
||||
}
|
||||
if (err_handler.ErrCode()) return ArrayRef<int>();
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_REEDSOLOMON_REEDSOLOMONDECODER_HPP__
|
||||
#define __ZXING_COMMON_REEDSOLOMON_REEDSOLOMONDECODER_HPP__
|
||||
|
||||
#include "../../errorhandler.hpp"
|
||||
#include "../array.hpp"
|
||||
#include "../counted.hpp"
|
||||
#include "genericgf.hpp"
|
||||
#include "genericgfpoly.hpp"
|
||||
|
||||
namespace zxing {
|
||||
class GenericGFPoly;
|
||||
class GenericGF;
|
||||
|
||||
class ReedSolomonDecoder {
|
||||
private:
|
||||
Ref<GenericGF> field;
|
||||
|
||||
public:
|
||||
explicit ReedSolomonDecoder(Ref<GenericGF> fld);
|
||||
~ReedSolomonDecoder();
|
||||
void decode(ArrayRef<int> received, int twoS, ErrorHandler &err_handler);
|
||||
std::vector<Ref<GenericGFPoly>> runEuclideanAlgorithm(Ref<GenericGFPoly> a,
|
||||
Ref<GenericGFPoly> b, int R,
|
||||
ErrorHandler &err_handler);
|
||||
|
||||
private:
|
||||
ArrayRef<int> findErrorLocations(Ref<GenericGFPoly> errorLocator, ErrorHandler &err_handler);
|
||||
ArrayRef<int> findErrorMagnitudes(Ref<GenericGFPoly> errorEvaluator,
|
||||
ArrayRef<int> errorLocations, ErrorHandler &err_handler);
|
||||
};
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_REEDSOLOMON_REEDSOLOMONDECODER_HPP__
|
||||
@@ -0,0 +1,96 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "str.hpp"
|
||||
|
||||
using zxing::Ref;
|
||||
using zxing::String;
|
||||
using zxing::StrUtil;
|
||||
String::String(const std::string& text) : text_(text) {}
|
||||
|
||||
String::String(int capacity) { text_.reserve(capacity); }
|
||||
|
||||
const std::string& String::getText() const { return text_; }
|
||||
|
||||
char String::charAt(int i) const { return text_[i]; }
|
||||
|
||||
int String::size() const { return text_.size(); }
|
||||
|
||||
int String::length() const { return text_.size(); }
|
||||
|
||||
Ref<String> String::substring(int i) const { return Ref<String>(new String(text_.substr(i))); }
|
||||
|
||||
Ref<String> String::substring(int start, int end) const {
|
||||
return Ref<String>(new String(text_.substr(start, (end - start))));
|
||||
}
|
||||
|
||||
void String::append(const std::string& tail) { text_.append(tail); }
|
||||
|
||||
void String::append(char c) { text_.append(1, c); }
|
||||
|
||||
void String::append(int d) {
|
||||
string str = StrUtil::numberToString(d);
|
||||
text_.append(str);
|
||||
}
|
||||
|
||||
void String::append(Ref<String> str) { append(str->getText()); }
|
||||
|
||||
string StrUtil::COMBINE_STRING(string str1, string str2) {
|
||||
string str = str1;
|
||||
str += str2;
|
||||
return str;
|
||||
}
|
||||
|
||||
string StrUtil::COMBINE_STRING(string str1, char c) {
|
||||
string str = str1;
|
||||
str += c;
|
||||
return str;
|
||||
}
|
||||
|
||||
string StrUtil::COMBINE_STRING(string str1, int d) {
|
||||
string str = str1;
|
||||
str += numberToString(d);
|
||||
return str;
|
||||
}
|
||||
|
||||
Ref<String> StrUtil::COMBINE_STRING(char c1, Ref<String> content, char c2) {
|
||||
Ref<String> str(new String(0));
|
||||
str->append(c1);
|
||||
str->append(content);
|
||||
str->append(c2);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
string StrUtil::numberToString(T Number) {
|
||||
ostringstream ss;
|
||||
ss << Number;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T StrUtil::stringToNumber(const string& Text) {
|
||||
std::istringstream ss(Text);
|
||||
T result;
|
||||
return ss >> result ? result : 0;
|
||||
}
|
||||
|
||||
int StrUtil::indexOf(const char* str, char c) {
|
||||
int len = strlen(str);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (str[i] == c) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_STR_HPP__
|
||||
#define __ZXING_COMMON_STR_HPP__
|
||||
|
||||
#include "counted.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
using std::string;
|
||||
namespace zxing {
|
||||
|
||||
class String : public Counted {
|
||||
private:
|
||||
std::string text_;
|
||||
|
||||
public:
|
||||
explicit String(const std::string& text);
|
||||
explicit String(int);
|
||||
char charAt(int) const;
|
||||
Ref<String> substring(int) const;
|
||||
Ref<String> substring(int, int) const;
|
||||
const std::string& getText() const;
|
||||
int size() const;
|
||||
void append(std::string const& tail);
|
||||
void append(char c);
|
||||
void append(int d);
|
||||
void append(Ref<String> str);
|
||||
int length() const;
|
||||
};
|
||||
|
||||
class StrUtil {
|
||||
public:
|
||||
static string COMBINE_STRING(string str1, string str2);
|
||||
static string COMBINE_STRING(string str1, char c);
|
||||
static string COMBINE_STRING(string str1, int d);
|
||||
static Ref<String> COMBINE_STRING(char c1, Ref<String> content, char c2);
|
||||
|
||||
template <typename T>
|
||||
static string numberToString(T Number);
|
||||
|
||||
template <typename T>
|
||||
static T stringToNumber(const string& Text);
|
||||
|
||||
static int indexOf(const char* str, char c);
|
||||
};
|
||||
|
||||
} // namespace zxing
|
||||
|
||||
#endif // __ZXING_COMMON_STR_HPP__
|
||||
@@ -0,0 +1,683 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
#include "../../precomp.hpp"
|
||||
#include "../common/stringutils.hpp"
|
||||
#include "../decodehints.hpp"
|
||||
|
||||
using namespace zxing::common;
|
||||
|
||||
// N.B.: these are the iconv strings for at least some versions of iconv
|
||||
|
||||
char const* const StringUtils::PLATFORM_DEFAULT_ENCODING = "ANY";
|
||||
char const* const StringUtils::ASCII = "ASCII";
|
||||
char const* const StringUtils::SHIFT_JIS = "SHIFT-JIS";
|
||||
char const* const StringUtils::GBK = "GBK";
|
||||
char const* const StringUtils::EUC_JP = "EUC-JP";
|
||||
char const* const StringUtils::UTF8 = "UTF-8";
|
||||
char const* const StringUtils::ISO88591 = "ISO8859-1";
|
||||
char const* const StringUtils::GB2312 = "GB2312";
|
||||
char const* const StringUtils::BIG5 = "BIG5";
|
||||
char const* const StringUtils::GB18030 = "GB18030";
|
||||
|
||||
const bool StringUtils::ASSUME_SHIFT_JIS = false;
|
||||
|
||||
#ifdef USE_UCHARDET
|
||||
#include "uchardet/uchardet.h"
|
||||
#endif
|
||||
|
||||
// Added convertString
|
||||
#ifndef NO_ICONV
|
||||
#include <iconv.h>
|
||||
|
||||
// Required for compatibility. TODO: test on Symbian
|
||||
//#ifdef ZXING_ICONV_CONST
|
||||
#undef ICONV_CONST
|
||||
#define ICONV_CONST const
|
||||
//#endif
|
||||
|
||||
#ifndef ICONV_CONST
|
||||
#define ICONV_CONST /**/
|
||||
#endif
|
||||
|
||||
// Add this to fix both Mac and Windows compilers
|
||||
// by Skylook
|
||||
template <class T>
|
||||
class sloppy {};
|
||||
|
||||
// convert between T** and const T**
|
||||
template <class T>
|
||||
class sloppy<T**> {
|
||||
T** t;
|
||||
|
||||
public:
|
||||
sloppy(T** mt) : t(mt) {}
|
||||
sloppy(const T** mt) : t(const_cast<T**>(mt)) {}
|
||||
|
||||
operator T* *() const { return t; }
|
||||
operator const T* *() const { return const_cast<const T**>(t); }
|
||||
};
|
||||
#endif
|
||||
|
||||
string StringUtils::convertString(const char* rawData, int length, const char* fromCharset,
|
||||
const char* toCharset) {
|
||||
string result;
|
||||
const char* bufIn = rawData;
|
||||
int nIn = length;
|
||||
|
||||
// If from and to charset are the same, return
|
||||
int ret = strcmp(fromCharset, toCharset);
|
||||
if (ret == 0) {
|
||||
result.append((const char*)bufIn, nIn);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef NO_ICONV
|
||||
if (nIn == 0) {
|
||||
return "";
|
||||
}
|
||||
iconv_t cd;
|
||||
// cout<<src<<endl;
|
||||
cd = iconv_open(toCharset, fromCharset);
|
||||
|
||||
// iconv_t cd = iconv_open(StringUtils::GBK, src);
|
||||
if (cd == (iconv_t)-1) {
|
||||
// result.append((const char *)bufIn, nIn);
|
||||
result = "";
|
||||
return result;
|
||||
}
|
||||
|
||||
const int maxOut = 4 * nIn + 1;
|
||||
char* bufOut = new char[maxOut];
|
||||
|
||||
ICONV_CONST char* fromPtr = (ICONV_CONST char*)bufIn;
|
||||
size_t nFrom = nIn;
|
||||
char* toPtr = (char*)bufOut;
|
||||
size_t nTo = maxOut;
|
||||
|
||||
size_t oneway = -1;
|
||||
|
||||
if (nFrom > 0) {
|
||||
// size_t oneway = iconv(cd, &fromPtr, &nFrom, &toPtr, &nTo);
|
||||
oneway = iconv(cd, sloppy<char**>(&fromPtr), &nFrom, sloppy<char**>(&toPtr), &nTo);
|
||||
}
|
||||
iconv_close(cd);
|
||||
|
||||
int nResult = maxOut - nTo;
|
||||
bufOut[nResult] = '\0';
|
||||
result.append((const char*)bufOut);
|
||||
delete[] bufOut;
|
||||
|
||||
// Cannot convert string
|
||||
if (oneway == (size_t)(-1)) {
|
||||
// result.append((const char *)bufIn, nIn);
|
||||
result = "";
|
||||
}
|
||||
#else
|
||||
result.append((const char*)bufIn, nIn);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string StringUtils::guessEncoding(char* bytes, int length) {
|
||||
#ifdef USE_UCHARDET
|
||||
if (length < 10) {
|
||||
return guessEncodingZXing(bytes, length);
|
||||
} else {
|
||||
return guessEncodingUCharDet(bytes, length);
|
||||
}
|
||||
#else
|
||||
return guessEncodingZXing(bytes, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_UCHARDET
|
||||
|
||||
string StringUtils::guessEncodingUCharDet(char* bytes, int length) {
|
||||
uchardet_t handle = uchardet_new();
|
||||
|
||||
int retval = uchardet_handle_data(handle, bytes, length);
|
||||
|
||||
if (retval != 0) {
|
||||
fprintf(stderr, "Handle data error.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
uchardet_data_end(handle);
|
||||
|
||||
const char* charset = uchardet_get_charset(handle);
|
||||
|
||||
string charsetStr(charset);
|
||||
|
||||
uchardet_delete(handle);
|
||||
|
||||
if (charsetStr.size() != 0) {
|
||||
return charsetStr;
|
||||
} else {
|
||||
return guessEncodingZXing(bytes, length);
|
||||
}
|
||||
|
||||
// Otherwise, we take a wild guess with platform encoding
|
||||
// return PLATFORM_DEFAULT_ENCODING;
|
||||
}
|
||||
#endif
|
||||
|
||||
string StringUtils::guessEncodingZXing(char* bytes, int length) {
|
||||
//
|
||||
// typedef bool boolean;
|
||||
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
|
||||
// which should be by far the most common encodings.
|
||||
bool canBeISO88591 = true;
|
||||
bool canBeShiftJIS = true;
|
||||
bool canBeUTF8 = true;
|
||||
bool canBeGB2312 = true;
|
||||
bool canBeGBK = true;
|
||||
bool canBeBIG5 = true;
|
||||
bool canBeASCII = true;
|
||||
|
||||
int utf8BytesLeft = 0;
|
||||
int utf2BytesChars = 0;
|
||||
int utf3BytesChars = 0;
|
||||
int utf4BytesChars = 0;
|
||||
int sjisBytesLeft = 0;
|
||||
int sjisKatakanaChars = 0;
|
||||
int sjisCurKatakanaWordLength = 0;
|
||||
int sjisCurDoubleBytesWordLength = 0;
|
||||
int sjisMaxKatakanaWordLength = 0;
|
||||
int sjisMaxDoubleBytesWordLength = 0;
|
||||
int isoHighOther = 0;
|
||||
|
||||
int gb2312SCByteChars = 0;
|
||||
int big5TWBytesChars = 0;
|
||||
|
||||
bool utf8bom = length > 3 && (unsigned char)bytes[0] == 0xEF &&
|
||||
(unsigned char)bytes[1] == 0xBB && (unsigned char)bytes[2] == 0xBF;
|
||||
|
||||
for (int i = 0; i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8 || canBeGBK); i++) {
|
||||
int value = bytes[i] & 0xFF;
|
||||
|
||||
// UTF-8 stuff
|
||||
if (canBeUTF8) {
|
||||
if (utf8BytesLeft > 0) {
|
||||
if ((value & 0x80) == 0) {
|
||||
canBeUTF8 = false;
|
||||
} else {
|
||||
utf8BytesLeft--;
|
||||
}
|
||||
} else if ((value & 0x80) != 0) {
|
||||
if ((value & 0x40) == 0) {
|
||||
canBeUTF8 = false;
|
||||
} else {
|
||||
utf8BytesLeft++;
|
||||
if ((value & 0x20) == 0) {
|
||||
utf2BytesChars++;
|
||||
} else {
|
||||
utf8BytesLeft++;
|
||||
if ((value & 0x10) == 0) {
|
||||
utf3BytesChars++;
|
||||
} else {
|
||||
utf8BytesLeft++;
|
||||
if ((value & 0x08) == 0) {
|
||||
utf4BytesChars++;
|
||||
} else {
|
||||
canBeUTF8 = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Shift_JIS stuff
|
||||
if (canBeShiftJIS) {
|
||||
if (sjisBytesLeft > 0) {
|
||||
if (value < 0x40 || value == 0x7F || value > 0xFC) {
|
||||
canBeShiftJIS = false;
|
||||
} else {
|
||||
sjisBytesLeft--;
|
||||
}
|
||||
} else if (value == 0x80 || value == 0xA0 || value > 0xEF) {
|
||||
canBeShiftJIS = false;
|
||||
} else if (value > 0xA0 && value < 0xE0) {
|
||||
sjisKatakanaChars++;
|
||||
sjisCurDoubleBytesWordLength = 0;
|
||||
sjisCurKatakanaWordLength++;
|
||||
if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) {
|
||||
sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength;
|
||||
}
|
||||
} else if (value > 0x7F) {
|
||||
sjisBytesLeft++;
|
||||
// sjisDoubleBytesChars++;
|
||||
sjisCurKatakanaWordLength = 0;
|
||||
sjisCurDoubleBytesWordLength++;
|
||||
if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) {
|
||||
sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength;
|
||||
}
|
||||
} else {
|
||||
// sjisLowChars++;
|
||||
sjisCurKatakanaWordLength = 0;
|
||||
sjisCurDoubleBytesWordLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ISO-8859-1 stuff
|
||||
if (canBeISO88591) {
|
||||
if (value > 0x7F && value < 0xA0) {
|
||||
canBeISO88591 = false;
|
||||
} else if (value > 0x9F) {
|
||||
if (value < 0xC0 || value == 0xD7 || value == 0xF7) {
|
||||
isoHighOther++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get how many chinese sc & tw words
|
||||
gb2312SCByteChars = is_gb2312_code(bytes, length);
|
||||
big5TWBytesChars = is_big5_code(bytes, length);
|
||||
|
||||
if (gb2312SCByteChars <= 0) {
|
||||
canBeGB2312 = false;
|
||||
}
|
||||
|
||||
if (big5TWBytesChars <= 0) {
|
||||
canBeBIG5 = false;
|
||||
}
|
||||
|
||||
if (!is_gbk_code(bytes, length)) {
|
||||
canBeGBK = false;
|
||||
}
|
||||
|
||||
if (canBeUTF8 && utf8BytesLeft > 0) {
|
||||
canBeUTF8 = false;
|
||||
}
|
||||
if (canBeShiftJIS && sjisBytesLeft > 0) {
|
||||
canBeShiftJIS = false;
|
||||
}
|
||||
|
||||
if (is_ascii_code(bytes, length) <= 0) {
|
||||
canBeASCII = false;
|
||||
}
|
||||
|
||||
// Easy -- if there is BOM or at least 1 valid not-single byte character
|
||||
// (and no evidence it can't be UTF-8), done
|
||||
if (canBeUTF8 && (utf8bom || utf2BytesChars + utf3BytesChars + utf4BytesChars > 0)) {
|
||||
return UTF8;
|
||||
}
|
||||
|
||||
// if ( canBeBIG5 == false && canBeGB2312 == false )
|
||||
int chineseWordLen =
|
||||
gb2312SCByteChars > big5TWBytesChars ? gb2312SCByteChars : big5TWBytesChars;
|
||||
int chineseByteLen = chineseWordLen * 2;
|
||||
int japaneseByteLen = sjisMaxKatakanaWordLength + sjisMaxDoubleBytesWordLength * 2;
|
||||
|
||||
// if ( chineseByteLen < japaneseByteLen || (japaneseByteLen == 0 &&
|
||||
// chineseByteLen == 0) ) if ( (gb2312SCByteChars < sjisKatakanaChars) &&
|
||||
// (big5TWBytesChars < sjisKatakanaChars) )
|
||||
//{
|
||||
// Easy -- if assuming Shift_JIS or at least 3 valid consecutive not-ascii
|
||||
// characters (and no evidence it can't be), done
|
||||
if (canBeShiftJIS &&
|
||||
(ASSUME_SHIFT_JIS || sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3)) {
|
||||
// return SHIFT_JIS;
|
||||
if (chineseByteLen <= japaneseByteLen) {
|
||||
if (chineseByteLen == japaneseByteLen) {
|
||||
if (chineseWordLen < sjisKatakanaChars) {
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
} else {
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short
|
||||
// words. The crude heuristic is:
|
||||
// - If we saw
|
||||
// - only two consecutive katakana chars in the whole text, or
|
||||
// - at least 10% of bytes that could be "upper" not-alphanumeric Latin1,
|
||||
// - then we conclude Shift_JIS, else ISO-8859-1
|
||||
if (canBeISO88591 && canBeShiftJIS) {
|
||||
if ((sjisMaxKatakanaWordLength == 2 && sjisKatakanaChars == 2) ||
|
||||
isoHighOther * 10 >= length) {
|
||||
/*
|
||||
if ( chineseByteLen < japaneseByteLen )
|
||||
{
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
*/
|
||||
if (chineseByteLen <= japaneseByteLen) {
|
||||
if (chineseByteLen == japaneseByteLen) {
|
||||
if (chineseWordLen < sjisKatakanaChars) {
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
} else {
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (chineseByteLen <= 0 && !canBeGB2312 && !canBeBIG5) {
|
||||
return ISO88591;
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
// Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to
|
||||
// default platform encoding
|
||||
if (canBeGB2312) {
|
||||
return GB2312;
|
||||
}
|
||||
|
||||
if (canBeBIG5) {
|
||||
return BIG5;
|
||||
}
|
||||
|
||||
if (canBeShiftJIS) {
|
||||
return SHIFT_JIS;
|
||||
}
|
||||
|
||||
if (canBeGBK) {
|
||||
return GBK;
|
||||
}
|
||||
|
||||
if (canBeISO88591) {
|
||||
return ISO88591;
|
||||
}
|
||||
|
||||
if (canBeUTF8) {
|
||||
return UTF8;
|
||||
}
|
||||
|
||||
if (canBeASCII) {
|
||||
return ASCII;
|
||||
}
|
||||
|
||||
// Otherwise, we take a wild guess with platform encoding
|
||||
return PLATFORM_DEFAULT_ENCODING;
|
||||
}
|
||||
|
||||
// judge the byte whether begin with binary 10
|
||||
int StringUtils::is_utf8_special_byte(unsigned char c) {
|
||||
unsigned char special_byte = 0X02; // binary 00000010
|
||||
if (c >> 6 == special_byte) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int StringUtils::is_utf8_code(char* str, int length) {
|
||||
unsigned char one_byte = 0X00; // binary 00000000
|
||||
unsigned char two_byte = 0X06; // binary 00000110
|
||||
unsigned char three_byte = 0X0E; // binary 00001110
|
||||
unsigned char four_byte = 0X1E; // binary 00011110
|
||||
unsigned char five_byte = 0X3E; // binary 00111110
|
||||
unsigned char six_byte = 0X7E; // binary 01111110
|
||||
|
||||
int utf8_yes = 0;
|
||||
int utf8_no = 0;
|
||||
|
||||
unsigned char k = 0;
|
||||
unsigned char m = 0;
|
||||
unsigned char n = 0;
|
||||
unsigned char p = 0;
|
||||
unsigned char q = 0;
|
||||
|
||||
unsigned char c = 0;
|
||||
for (int i = 0; i < length;) {
|
||||
c = (unsigned char)str[i];
|
||||
if (c >> 7 == one_byte) {
|
||||
i++;
|
||||
continue;
|
||||
} else if (c >> 5 == two_byte) {
|
||||
k = (unsigned char)str[i + 1];
|
||||
if (is_utf8_special_byte(k)) {
|
||||
utf8_yes++;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
} else if (c >> 4 == three_byte) {
|
||||
m = (unsigned char)str[i + 1];
|
||||
n = (unsigned char)str[i + 2];
|
||||
if (is_utf8_special_byte(m) && is_utf8_special_byte(n)) {
|
||||
utf8_yes++;
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
} else if (c >> 3 == four_byte) {
|
||||
k = (unsigned char)str[i + 1];
|
||||
m = (unsigned char)str[i + 2];
|
||||
n = (unsigned char)str[i + 3];
|
||||
if (is_utf8_special_byte(k) && is_utf8_special_byte(m) && is_utf8_special_byte(n)) {
|
||||
utf8_yes++;
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
} else if (c >> 2 == five_byte) {
|
||||
k = (unsigned char)str[i + 1];
|
||||
m = (unsigned char)str[i + 2];
|
||||
n = (unsigned char)str[i + 3];
|
||||
p = (unsigned char)str[i + 4];
|
||||
if (is_utf8_special_byte(k) && is_utf8_special_byte(m) && is_utf8_special_byte(n) &&
|
||||
is_utf8_special_byte(p)) {
|
||||
utf8_yes++;
|
||||
i += 5;
|
||||
continue;
|
||||
}
|
||||
} else if (c >> 1 == six_byte) {
|
||||
k = (unsigned char)str[i + 1];
|
||||
m = (unsigned char)str[i + 2];
|
||||
n = (unsigned char)str[i + 3];
|
||||
p = (unsigned char)str[i + 4];
|
||||
q = (unsigned char)str[i + 5];
|
||||
if (is_utf8_special_byte(k) && is_utf8_special_byte(m) && is_utf8_special_byte(n) &&
|
||||
is_utf8_special_byte(p) && is_utf8_special_byte(q)) {
|
||||
utf8_yes++;
|
||||
i += 6;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
utf8_no++;
|
||||
i++;
|
||||
}
|
||||
|
||||
// printf("uft8_yes: %d utf8_no:%d\n", utf8_yes, utf8_no);
|
||||
if ((utf8_yes + utf8_no) != 0) {
|
||||
int ret = (100 * utf8_yes) / (utf8_yes + utf8_no);
|
||||
if (ret > 90) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int StringUtils::is_gb2312_code(char* str, int length) {
|
||||
unsigned char one_byte = 0X00; // binary 00000000
|
||||
|
||||
int gb2312_yes = 0;
|
||||
int gb2312_no = 0;
|
||||
|
||||
unsigned char k = 0;
|
||||
|
||||
unsigned char c = 0;
|
||||
for (int i = 0; i < length;) {
|
||||
c = (unsigned char)str[i];
|
||||
if (c >> 7 == one_byte) {
|
||||
i++;
|
||||
continue;
|
||||
} else if (c >= 0XA1 && c <= 0XF7) {
|
||||
k = (unsigned char)str[i + 1];
|
||||
if (k >= 0XA1 && k <= 0XFE) {
|
||||
gb2312_yes++;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
gb2312_no++;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// printf("gb2312_yes: %d gb2312_no:%d\n", gb2312_yes, gb2312_no);
|
||||
if ((gb2312_yes + gb2312_no) > 0) {
|
||||
int ret = (100 * gb2312_yes) / (gb2312_yes + gb2312_no);
|
||||
if (ret == 100) {
|
||||
// if (ret > 90) {
|
||||
// gb2312SCByteChars = gb2312_yes;
|
||||
return gb2312_yes;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StringUtils::is_big5_code(char* str, int length) {
|
||||
unsigned char one_byte = 0X00; // binary 00000000
|
||||
|
||||
int big5_yes = 0;
|
||||
int big5_no = 0;
|
||||
|
||||
unsigned char k = 0;
|
||||
|
||||
unsigned char c = 0;
|
||||
for (int i = 0; i < length;) {
|
||||
c = (unsigned char)str[i];
|
||||
if (c >> 7 == one_byte) {
|
||||
i++;
|
||||
continue;
|
||||
} else if (c >= 0XA1 && c <= 0XF9) {
|
||||
k = (unsigned char)str[i + 1];
|
||||
if ((k >= 0X40 && k <= 0X7E) || (k >= 0XA1 && k <= 0XFE)) {
|
||||
big5_yes++;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
big5_no++;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// printf("%d %d\n", big5_yes, big5_no);
|
||||
if ((big5_yes + big5_no) > 0) {
|
||||
int ret = (100 * big5_yes) / (big5_yes + big5_no);
|
||||
if (ret == 100) {
|
||||
// if (ret > 90) {
|
||||
// big5TWBytesChars = big5_yes;
|
||||
return big5_yes;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StringUtils::is_gbk_code(char* str, int length) {
|
||||
unsigned char one_byte = 0X00; // binary 00000000
|
||||
|
||||
int gbk_yes = 0;
|
||||
int gbk_no = 0;
|
||||
|
||||
unsigned char k = 0;
|
||||
|
||||
unsigned char c = 0;
|
||||
for (int i = 0; i < length;) {
|
||||
c = (unsigned char)str[i];
|
||||
if (c >> 7 == one_byte) {
|
||||
i++;
|
||||
continue;
|
||||
} else if (c >= 0X81 && c <= 0XFE) {
|
||||
k = (unsigned char)str[i + 1];
|
||||
if (k >= 0X40 && k <= 0XFE) {
|
||||
gbk_yes++;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
gbk_no++;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// printf("gbk_yes: %d gbk_no:%d\n", gbk_yes, gbk_no);
|
||||
if ((gbk_yes + gbk_no) > 0) {
|
||||
int ret = (100 * gbk_yes) / (gbk_yes + gbk_no);
|
||||
if (ret == 100) {
|
||||
// if (ret > 90) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StringUtils::is_ascii_code(char* str, int length) {
|
||||
unsigned char c = 0;
|
||||
|
||||
bool isASCII = true;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
c = (unsigned char)str[i];
|
||||
|
||||
if ((c > 127)) {
|
||||
isASCII = false;
|
||||
}
|
||||
}
|
||||
return (isASCII ? 1 : -1);
|
||||
}
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
int StringUtils::shift_jis_to_jis(const unsigned char* may_be_shift_jis, int* jis_first_ptr,
|
||||
int* jis_second_ptr) {
|
||||
int status = 0;
|
||||
unsigned char first = may_be_shift_jis[0];
|
||||
unsigned char second = may_be_shift_jis[1];
|
||||
int jis_first = 0;
|
||||
int jis_second = 0;
|
||||
/* Check first byte is valid shift JIS. */
|
||||
if ((first >= 0x81 && first <= 0x84) || (first >= 0x87 && first <= 0x9f)) {
|
||||
jis_first = 2 * (first - 0x70) - 1;
|
||||
if (second >= 0x40 && second <= 0x9e) {
|
||||
jis_second = second - 31;
|
||||
if (jis_second > 95) {
|
||||
jis_second -= 1;
|
||||
}
|
||||
|
||||
status = 1;
|
||||
} else if (second >= 0x9f && second <= 0xfc) {
|
||||
jis_second = second - 126;
|
||||
jis_first += 1;
|
||||
status = 1;
|
||||
} else {
|
||||
}
|
||||
} else if (first >= 0xe0 && first <= 0xef) {
|
||||
jis_first = 2 * (first - 0xb0) - 1;
|
||||
if (second >= 0x40 && second <= 0x9e) {
|
||||
jis_second = second - 31;
|
||||
if (jis_second > 95) {
|
||||
jis_second -= 1;
|
||||
}
|
||||
status = 1;
|
||||
} else if (second >= 0x9f && second <= 0xfc) {
|
||||
jis_second = second - 126;
|
||||
jis_first += 1;
|
||||
status = 1;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
*jis_first_ptr = jis_first;
|
||||
*jis_second_ptr = jis_second;
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
//
|
||||
// Modified from ZXing. Copyright ZXing authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
||||
#ifndef __ZXING_COMMON_STRINGUTILS_HPP__
|
||||
#define __ZXING_COMMON_STRINGUTILS_HPP__
|
||||
|
||||
#include "../decodehints.hpp"
|
||||
#include "../zxing.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace zxing {
|
||||
namespace common {
|
||||
class StringUtils;
|
||||
}
|
||||
} // namespace zxing
|
||||
using namespace std;
|
||||
|
||||
class zxing::common::StringUtils {
|
||||
private:
|
||||
static char const* const PLATFORM_DEFAULT_ENCODING;
|
||||
|
||||
public:
|
||||
static char const* const ASCII;
|
||||
static char const* const SHIFT_JIS;
|
||||
static char const* const GB2312;
|
||||
static char const* const EUC_JP;
|
||||
static char const* const UTF8;
|
||||
static char const* const ISO88591;
|
||||
static char const* const GBK;
|
||||
static char const* const GB18030;
|
||||
static char const* const BIG5;
|
||||
|
||||
static const bool ASSUME_SHIFT_JIS;
|
||||
|
||||
static std::string guessEncoding(char* bytes, int length);
|
||||
static std::string guessEncodingZXing(char* bytes, int length);
|
||||
|
||||
#ifdef USE_UCHARDET
|
||||
static std::string guessEncodingUCharDet(char* bytes, int length);
|
||||
#endif
|
||||
|
||||
static int is_utf8_special_byte(unsigned char c);
|
||||
// static int is_utf8_code(const string& str);
|
||||
static int is_utf8_code(char* str, int length);
|
||||
static int is_gb2312_code(char* str, int length);
|
||||
static int is_big5_code(char* str, int length);
|
||||
static int is_gbk_code(char* str, int length);
|
||||
static int is_ascii_code(char* str, int length);
|
||||
static int shift_jis_to_jis(const unsigned char* may_be_shift_jis, int* jis_first_ptr,
|
||||
int* jis_second_ptr);
|
||||
|
||||
static std::string convertString(const char* rawData, int length, const char* fromCharset,
|
||||
const char* toCharset);
|
||||
};
|
||||
|
||||
#endif // __ZXING_COMMON_STRINGUTILS_HPP__
|
||||
@@ -0,0 +1,127 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
#include "../../precomp.hpp"
|
||||
#include "unicomblock.hpp"
|
||||
|
||||
namespace zxing {
|
||||
short UnicomBlock::SEARCH_POS[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
||||
UnicomBlock::UnicomBlock(int iMaxHeight, int iMaxWidth)
|
||||
: m_iHeight(iMaxHeight), m_iWidth(iMaxWidth), m_bInit(false) {}
|
||||
|
||||
UnicomBlock::~UnicomBlock() {}
|
||||
|
||||
void UnicomBlock::Init() {
|
||||
if (m_bInit) return;
|
||||
m_vcIndex = std::vector<unsigned int>(m_iHeight * m_iWidth, 0);
|
||||
m_vcCount = std::vector<unsigned int>(m_iHeight * m_iWidth, 0);
|
||||
m_vcMinPnt = std::vector<int>(m_iHeight * m_iWidth, 0);
|
||||
m_vcMaxPnt = std::vector<int>(m_iHeight * m_iWidth, 0);
|
||||
m_vcQueue = std::vector<int>(m_iHeight * m_iWidth, 0);
|
||||
m_bInit = true;
|
||||
}
|
||||
|
||||
void UnicomBlock::Reset(Ref<BitMatrix> poImage) {
|
||||
m_poImage = poImage;
|
||||
memset(&m_vcIndex[0], 0, m_vcIndex.size() * sizeof(m_vcIndex[0]));
|
||||
m_iNowIdx = 0;
|
||||
}
|
||||
|
||||
unsigned short UnicomBlock::GetUnicomBlockIndex(int y, int x) {
|
||||
if (y >= m_iHeight || x >= m_iWidth) return 0;
|
||||
if (m_vcIndex[y * m_iWidth + x]) return m_vcIndex[y * m_iWidth + x];
|
||||
Bfs(y, x);
|
||||
return m_vcIndex[y * m_iWidth + x];
|
||||
}
|
||||
|
||||
int UnicomBlock::GetUnicomBlockSize(int y, int x) {
|
||||
if (y >= m_iHeight || x >= m_iWidth) return 0;
|
||||
if (m_vcIndex[y * m_iWidth + x]) return m_vcCount[y * m_iWidth + x];
|
||||
Bfs(y, x);
|
||||
return m_vcCount[y * m_iWidth + x];
|
||||
}
|
||||
|
||||
int UnicomBlock::GetMinPoint(int y, int x, int &iMinY, int &iMinX) {
|
||||
if (y >= m_iHeight || x >= m_iWidth) return -1;
|
||||
if (m_vcIndex[y * m_iWidth + x]) {
|
||||
iMinY = m_vcMinPnt[y * m_iWidth + x] >> 16;
|
||||
iMinX = m_vcMinPnt[y * m_iWidth + x] & (0xFFFF);
|
||||
return 0;
|
||||
}
|
||||
Bfs(y, x);
|
||||
iMinY = m_vcMinPnt[y * m_iWidth + x] >> 16;
|
||||
iMinX = m_vcMinPnt[y * m_iWidth + x] & (0xFFFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UnicomBlock::GetMaxPoint(int y, int x, int &iMaxY, int &iMaxX) {
|
||||
if (y >= m_iHeight || x >= m_iWidth) return -1;
|
||||
if (m_vcIndex[y * m_iWidth + x]) {
|
||||
iMaxY = m_vcMaxPnt[y * m_iWidth + x] >> 16;
|
||||
iMaxX = m_vcMaxPnt[y * m_iWidth + x] & (0xFFFF);
|
||||
return 0;
|
||||
}
|
||||
Bfs(y, x);
|
||||
iMaxY = m_vcMaxPnt[y * m_iWidth + x] >> 16;
|
||||
iMaxX = m_vcMaxPnt[y * m_iWidth + x] & (0xFFFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UnicomBlock::Bfs(int y, int x) {
|
||||
m_iNowIdx++;
|
||||
|
||||
int iFront = 0;
|
||||
int iTail = 0;
|
||||
int iCount = 1;
|
||||
|
||||
int iMaxX = x, iMaxY = y;
|
||||
int iMinX = x, iMinY = y;
|
||||
|
||||
const bool bValue = (m_poImage->get(x, y) != (unsigned char)0);
|
||||
|
||||
m_vcIndex[y * m_iWidth + x] = m_iNowIdx;
|
||||
m_vcQueue[iTail++] = y << 16 | x;
|
||||
|
||||
while (iFront < iTail) {
|
||||
int iNode = m_vcQueue[iFront++];
|
||||
int iX = iNode & (0xFFFF);
|
||||
int iY = iNode >> 16;
|
||||
iMaxX = max(iX, iMaxX);
|
||||
iMaxY = max(iY, iMaxY);
|
||||
iMinX = min(iX, iMinX);
|
||||
iMinY = min(iY, iMinY);
|
||||
|
||||
iCount++;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
const int iNextX = iX + SEARCH_POS[i][0], iNextY = iY + SEARCH_POS[i][1];
|
||||
const int iPosition = iNextY * m_iWidth + iNextX;
|
||||
|
||||
if (iPosition >= 0 && iPosition < int(m_vcIndex.size()) && 0 == m_vcIndex[iPosition]) {
|
||||
if (iNextX < 0 || iNextX >= m_poImage->getWidth() || iNextY < 0 ||
|
||||
iNextY >= m_poImage->getHeight() ||
|
||||
bValue != (m_poImage->get(iNextX, iNextY) != (unsigned char)0))
|
||||
continue;
|
||||
|
||||
m_vcIndex[iPosition] = m_iNowIdx;
|
||||
m_vcQueue[iTail++] = iNextY << 16 | iNextX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iCount >= (1 << 16) - 1) iCount = 0xFFFF;
|
||||
|
||||
const int iMinCombine = iMinY << 16 | iMinX;
|
||||
const int iMaxCombine = iMaxY << 16 | iMaxX;
|
||||
for (int i = 0; i < iTail; ++i) {
|
||||
const int iPosition = (m_vcQueue[i] >> 16) * m_iWidth + (m_vcQueue[i] & (0xFFFF));
|
||||
|
||||
m_vcCount[iPosition] = iCount;
|
||||
m_vcMinPnt[iPosition] = iMinCombine;
|
||||
m_vcMaxPnt[iPosition] = iMaxCombine;
|
||||
}
|
||||
}
|
||||
} // namespace zxing
|
||||
@@ -0,0 +1,47 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
#ifndef __ZXING_COMMON_UNICOMBLOCK_HPP__
|
||||
#define __ZXING_COMMON_UNICOMBLOCK_HPP__
|
||||
#include "bitmatrix.hpp"
|
||||
#include "counted.hpp"
|
||||
|
||||
namespace zxing {
|
||||
class UnicomBlock : public Counted {
|
||||
public:
|
||||
UnicomBlock(int iMaxHeight, int iMaxWidth);
|
||||
~UnicomBlock();
|
||||
|
||||
void Init();
|
||||
void Reset(Ref<BitMatrix> poImage);
|
||||
|
||||
unsigned short GetUnicomBlockIndex(int y, int x);
|
||||
|
||||
int GetUnicomBlockSize(int y, int x);
|
||||
|
||||
int GetMinPoint(int y, int x, int &iMinY, int &iMinX);
|
||||
int GetMaxPoint(int y, int x, int &iMaxY, int &iMaxX);
|
||||
|
||||
private:
|
||||
void Bfs(int y, int x);
|
||||
|
||||
int m_iHeight;
|
||||
int m_iWidth;
|
||||
|
||||
unsigned int m_iNowIdx;
|
||||
bool m_bInit;
|
||||
std::vector<unsigned int> m_vcIndex;
|
||||
std::vector<unsigned int> m_vcCount;
|
||||
std::vector<int> m_vcMinPnt;
|
||||
std::vector<int> m_vcMaxPnt;
|
||||
std::vector<int> m_vcQueue;
|
||||
static short SEARCH_POS[4][2];
|
||||
|
||||
Ref<BitMatrix> m_poImage;
|
||||
};
|
||||
} // namespace zxing
|
||||
#endif // __ZXING_COMMON_UNICOMBLOCK_HPP__
|
||||
Reference in New Issue
Block a user