From 22ee5c0c4db9111780c6d34eb2a3c85e0f4046ff Mon Sep 17 00:00:00 2001 From: Rob Timpe Date: Wed, 21 Oct 2020 15:51:46 -0700 Subject: [PATCH 1/4] Fix errors when building with cuda stubs Fixes two errors when building with the options WITH_CUDA=ON and BUILD_CUDA_STUBS=ON on a machine without CUDA. In the cudaarithm module, make sure cuda_runtime.h only gets included when CUDA is installed. In the stitching module, don't assume that cuda is present just because cudaarithm and cudawarping are present (as is the case when building with the above options). --- modules/cudaarithm/src/lut.cpp | 5 +++-- modules/stitching/src/blenders.cpp | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/modules/cudaarithm/src/lut.cpp b/modules/cudaarithm/src/lut.cpp index a4b4e02650..5ef2836017 100644 --- a/modules/cudaarithm/src/lut.cpp +++ b/modules/cudaarithm/src/lut.cpp @@ -4,8 +4,6 @@ #include "precomp.hpp" -#include "lut.hpp" - using namespace cv; using namespace cv::cuda; @@ -15,6 +13,9 @@ Ptr cv::cuda::createLookUpTable(InputArray) { throw_no_cuda(); retu #else /* !defined (HAVE_CUDA) || defined (CUDA_DISABLER) */ +// lut.hpp includes cuda_runtime.h and can only be included when we have CUDA +#include "lut.hpp" + Ptr cv::cuda::createLookUpTable(InputArray lut) { return makePtr(lut); diff --git a/modules/stitching/src/blenders.cpp b/modules/stitching/src/blenders.cpp index aeddc142dc..05e7ca85e4 100644 --- a/modules/stitching/src/blenders.cpp +++ b/modules/stitching/src/blenders.cpp @@ -219,7 +219,7 @@ MultiBandBlender::MultiBandBlender(int try_gpu, int num_bands, int weight_type) num_bands_ = 0; setNumBands(num_bands); -#if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) +#if defined(HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) can_use_gpu_ = try_gpu && cuda::getCudaEnabledDeviceCount(); gpu_feed_idx_ = 0; #else @@ -246,7 +246,7 @@ void MultiBandBlender::prepare(Rect dst_roi) Blender::prepare(dst_roi); -#if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) +#if defined(HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) if (can_use_gpu_) { gpu_initialized_ = false; @@ -332,7 +332,7 @@ void MultiBandBlender::feed(InputArray _img, InputArray mask, Point tl) UMat img; -#if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) +#if defined(HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) // If using gpu save the top left coordinate when running first time after prepare if (can_use_gpu_) { @@ -353,7 +353,7 @@ void MultiBandBlender::feed(InputArray _img, InputArray mask, Point tl) { img = _img.getUMat(); } -#if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) +#if defined(HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) else { gpu_img_ = _img.getGpuMat(); @@ -394,7 +394,7 @@ void MultiBandBlender::feed(InputArray _img, InputArray mask, Point tl) int bottom = br_new.y - tl.y - img.rows; int right = br_new.x - tl.x - img.cols; -#if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) +#if defined(HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) if (can_use_gpu_) { if (!gpu_initialized_) @@ -603,7 +603,7 @@ void MultiBandBlender::feed(InputArray _img, InputArray mask, Point tl) void MultiBandBlender::blend(InputOutputArray dst, InputOutputArray dst_mask) { Rect dst_rc(0, 0, dst_roi_final_.width, dst_roi_final_.height); -#if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) +#if defined(HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) if (can_use_gpu_) { if (!gpu_initialized_) @@ -850,7 +850,7 @@ void createLaplacePyr(InputArray img, int num_levels, std::vector &pyr) void createLaplacePyrGpu(InputArray img, int num_levels, std::vector &pyr) { -#if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) +#if defined(HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) pyr.resize(num_levels + 1); std::vector gpu_pyr(num_levels + 1); @@ -891,7 +891,7 @@ void restoreImageFromLaplacePyr(std::vector &pyr) void restoreImageFromLaplacePyrGpu(std::vector &pyr) { -#if defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) +#if defined(HAVE_CUDA) && defined(HAVE_OPENCV_CUDAARITHM) && defined(HAVE_OPENCV_CUDAWARPING) if (pyr.empty()) return; From aac7c5465ba6ccfe0dc665ab0bae87f765e616ba Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 21 Oct 2020 22:47:56 +0000 Subject: [PATCH 2/4] core: move inline code from mat.inl.hpp --- modules/core/include/opencv2/core/mat.inl.hpp | 562 ------------------ modules/core/src/matrix.cpp | 280 ++++++++- modules/core/src/matrix_sparse.cpp | 88 +++ modules/core/src/umatrix.cpp | 146 +++++ 4 files changed, 513 insertions(+), 563 deletions(-) diff --git a/modules/core/include/opencv2/core/mat.inl.hpp b/modules/core/include/opencv2/core/mat.inl.hpp index 9b7df87d8b..b6ffd81795 100644 --- a/modules/core/include/opencv2/core/mat.inl.hpp +++ b/modules/core/include/opencv2/core/mat.inl.hpp @@ -489,158 +489,6 @@ CV__DEBUG_NS_END //////////////////////////////////////////// Mat ////////////////////////////////////////// -inline -Mat::Mat() - : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), - datalimit(0), allocator(0), u(0), size(&rows), step(0) -{} - -inline -Mat::Mat(int _rows, int _cols, int _type) - : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), - datalimit(0), allocator(0), u(0), size(&rows), step(0) -{ - create(_rows, _cols, _type); -} - -inline -Mat::Mat(int _rows, int _cols, int _type, const Scalar& _s) - : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), - datalimit(0), allocator(0), u(0), size(&rows), step(0) -{ - create(_rows, _cols, _type); - *this = _s; -} - -inline -Mat::Mat(Size _sz, int _type) - : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), - datalimit(0), allocator(0), u(0), size(&rows), step(0) -{ - create( _sz.height, _sz.width, _type ); -} - -inline -Mat::Mat(Size _sz, int _type, const Scalar& _s) - : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), - datalimit(0), allocator(0), u(0), size(&rows), step(0) -{ - create(_sz.height, _sz.width, _type); - *this = _s; -} - -inline -Mat::Mat(int _dims, const int* _sz, int _type) - : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), - datalimit(0), allocator(0), u(0), size(&rows), step(0) -{ - create(_dims, _sz, _type); -} - -inline -Mat::Mat(int _dims, const int* _sz, int _type, const Scalar& _s) - : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), - datalimit(0), allocator(0), u(0), size(&rows), step(0) -{ - create(_dims, _sz, _type); - *this = _s; -} - -inline -Mat::Mat(const std::vector& _sz, int _type) - : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), - datalimit(0), allocator(0), u(0), size(&rows), step(0) -{ - create(_sz, _type); -} - -inline -Mat::Mat(const std::vector& _sz, int _type, const Scalar& _s) - : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), - datalimit(0), allocator(0), u(0), size(&rows), step(0) -{ - create(_sz, _type); - *this = _s; -} - -inline -Mat::Mat(const Mat& m) - : flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), data(m.data), - datastart(m.datastart), dataend(m.dataend), datalimit(m.datalimit), allocator(m.allocator), - u(m.u), size(&rows), step(0) -{ - if( u ) - CV_XADD(&u->refcount, 1); - if( m.dims <= 2 ) - { - step[0] = m.step[0]; step[1] = m.step[1]; - } - else - { - dims = 0; - copySize(m); - } -} - -inline -Mat::Mat(int _rows, int _cols, int _type, void* _data, size_t _step) - : flags(MAGIC_VAL + (_type & TYPE_MASK)), dims(2), rows(_rows), cols(_cols), - data((uchar*)_data), datastart((uchar*)_data), dataend(0), datalimit(0), - allocator(0), u(0), size(&rows) -{ - CV_Assert(total() == 0 || data != NULL); - - size_t esz = CV_ELEM_SIZE(_type), esz1 = CV_ELEM_SIZE1(_type); - size_t minstep = cols * esz; - if( _step == AUTO_STEP ) - { - _step = minstep; - } - else - { - CV_DbgAssert( _step >= minstep ); - if (_step % esz1 != 0) - { - CV_Error(Error::BadStep, "Step must be a multiple of esz1"); - } - } - step[0] = _step; - step[1] = esz; - datalimit = datastart + _step * rows; - dataend = datalimit - _step + minstep; - updateContinuityFlag(); -} - -inline -Mat::Mat(Size _sz, int _type, void* _data, size_t _step) - : flags(MAGIC_VAL + (_type & TYPE_MASK)), dims(2), rows(_sz.height), cols(_sz.width), - data((uchar*)_data), datastart((uchar*)_data), dataend(0), datalimit(0), - allocator(0), u(0), size(&rows) -{ - CV_Assert(total() == 0 || data != NULL); - - size_t esz = CV_ELEM_SIZE(_type), esz1 = CV_ELEM_SIZE1(_type); - size_t minstep = cols*esz; - if( _step == AUTO_STEP ) - { - _step = minstep; - } - else - { - CV_DbgAssert( _step >= minstep ); - - if (_step % esz1 != 0) - { - CV_Error(Error::BadStep, "Step must be a multiple of esz1"); - } - } - step[0] = _step; - step[1] = esz; - datalimit = datastart + _step*rows; - dataend = datalimit - _step + minstep; - updateContinuityFlag(); -} - template inline Mat::Mat(const std::vector<_Tp>& vec, bool copyData) : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows((int)vec.size()), @@ -778,43 +626,6 @@ Mat::Mat(const MatCommaInitializer_<_Tp>& commaInitializer) *this = commaInitializer.operator Mat_<_Tp>(); } -inline -Mat::~Mat() -{ - release(); - if( step.p != step.buf ) - fastFree(step.p); -} - -inline -Mat& Mat::operator = (const Mat& m) -{ - if( this != &m ) - { - if( m.u ) - CV_XADD(&m.u->refcount, 1); - release(); - flags = m.flags; - if( dims <= 2 && m.dims <= 2 ) - { - dims = m.dims; - rows = m.rows; - cols = m.cols; - step[0] = m.step[0]; - step[1] = m.step[1]; - } - else - copySize(m); - data = m.data; - datastart = m.datastart; - dataend = m.dataend; - datalimit = m.datalimit; - allocator = m.allocator; - u = m.u; - } - return *this; -} - inline Mat Mat::row(int y) const { @@ -851,67 +662,6 @@ Mat Mat::colRange(const Range& r) const return Mat(*this, Range::all(), r); } -inline -Mat Mat::clone() const -{ - Mat m; - copyTo(m); - return m; -} - -inline -void Mat::assignTo( Mat& m, int _type ) const -{ - if( _type < 0 ) - m = *this; - else - convertTo(m, _type); -} - -inline -void Mat::create(int _rows, int _cols, int _type) -{ - _type &= TYPE_MASK; - if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && data ) - return; - int sz[] = {_rows, _cols}; - create(2, sz, _type); -} - -inline -void Mat::create(Size _sz, int _type) -{ - create(_sz.height, _sz.width, _type); -} - -inline -void Mat::addref() -{ - if( u ) - CV_XADD(&u->refcount, 1); -} - -inline -void Mat::release() -{ - if( u && CV_XADD(&u->refcount, -1) == 1 ) - deallocate(); - u = NULL; - datastart = dataend = datalimit = data = 0; - for(int i = 0; i < dims; i++) - size.p[i] = 0; -#ifdef _DEBUG - flags = MAGIC_VAL; - dims = rows = cols = 0; - if(step.p != step.buf) - { - fastFree(step.p); - step.p = step.buf; - size.p = &rows; - } -#endif -} - inline Mat Mat::operator()( Range _rowRange, Range _colRange ) const { @@ -980,40 +730,6 @@ int Mat::channels() const return CV_MAT_CN(flags); } -inline -size_t Mat::step1(int i) const -{ - return step.p[i] / elemSize1(); -} - -inline -bool Mat::empty() const -{ - return data == 0 || total() == 0 || dims == 0; -} - -inline -size_t Mat::total() const -{ - if( dims <= 2 ) - return (size_t)rows * cols; - size_t p = 1; - for( int i = 0; i < dims; i++ ) - p *= size[i]; - return p; -} - -inline -size_t Mat::total(int startDim, int endDim) const -{ - CV_Assert( 0 <= startDim && startDim <= endDim); - size_t p = 1; - int endDim_ = endDim <= dims ? endDim : dims; - for( int i = startDim; i < endDim_; i++ ) - p *= size[i]; - return p; -} - inline uchar* Mat::ptr(int y) { @@ -1544,22 +1260,6 @@ MatSize::operator const int*() const return p; } -inline -bool MatSize::operator == (const MatSize& sz) const -{ - int d = dims(); - int dsz = sz.dims(); - if( d != dsz ) - return false; - if( d == 2 ) - return p[0] == sz.p[0] && p[1] == sz.p[1]; - - for( int i = 0; i < d; i++ ) - if( p[i] != sz.p[i] ) - return false; - return true; -} - inline bool MatSize::operator != (const MatSize& sz) const { @@ -1820,9 +1520,7 @@ template inline void Mat_<_Tp>::release() { Mat::release(); -#ifdef _DEBUG flags = (flags & ~CV_MAT_TYPE_MASK) | traits::Type<_Tp>::value; -#endif } template inline @@ -2182,51 +1880,6 @@ Mat_<_Tp>::Mat_(MatExpr&& e) ///////////////////////////// SparseMat ///////////////////////////// -inline -SparseMat::SparseMat() - : flags(MAGIC_VAL), hdr(0) -{} - -inline -SparseMat::SparseMat(int _dims, const int* _sizes, int _type) - : flags(MAGIC_VAL), hdr(0) -{ - create(_dims, _sizes, _type); -} - -inline -SparseMat::SparseMat(const SparseMat& m) - : flags(m.flags), hdr(m.hdr) -{ - addref(); -} - -inline -SparseMat::~SparseMat() -{ - release(); -} - -inline -SparseMat& SparseMat::operator = (const SparseMat& m) -{ - if( this != &m ) - { - if( m.hdr ) - CV_XADD(&m.hdr->refcount, 1); - release(); - flags = m.flags; - hdr = m.hdr; - } - return *this; -} - -inline -SparseMat& SparseMat::operator = (const Mat& m) -{ - return (*this = SparseMat(m)); -} - inline SparseMat SparseMat::clone() const { @@ -2235,30 +1888,6 @@ SparseMat SparseMat::clone() const return temp; } -inline -void SparseMat::assignTo( SparseMat& m, int _type ) const -{ - if( _type < 0 ) - m = *this; - else - convertTo(m, _type); -} - -inline -void SparseMat::addref() -{ - if( hdr ) - CV_XADD(&hdr->refcount, 1); -} - -inline -void SparseMat::release() -{ - if( hdr && CV_XADD(&hdr->refcount, -1) == 1 ) - delete hdr; - hdr = 0; -} - inline size_t SparseMat::elemSize() const { @@ -2318,36 +1947,6 @@ size_t SparseMat::nzcount() const return hdr ? hdr->nodeCount : 0; } -inline -size_t SparseMat::hash(int i0) const -{ - return (size_t)i0; -} - -inline -size_t SparseMat::hash(int i0, int i1) const -{ - return (size_t)(unsigned)i0 * HASH_SCALE + (unsigned)i1; -} - -inline -size_t SparseMat::hash(int i0, int i1, int i2) const -{ - return ((size_t)(unsigned)i0 * HASH_SCALE + (unsigned)i1) * HASH_SCALE + (unsigned)i2; -} - -inline -size_t SparseMat::hash(const int* idx) const -{ - size_t h = (unsigned)idx[0]; - if( !hdr ) - return 0; - int d = hdr->dims; - for(int i = 1; i < d; i++ ) - h = h * HASH_SCALE + (unsigned)idx[i]; - return h; -} - template inline _Tp& SparseMat::ref(int i0, size_t* hashval) { @@ -3667,74 +3266,6 @@ const Mat_<_Tp>& operator /= (const Mat_<_Tp>& a, const MatExpr& b) //////////////////////////////// UMat //////////////////////////////// -inline -UMat::UMat(UMatUsageFlags _usageFlags) -: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) -{} - -inline -UMat::UMat(int _rows, int _cols, int _type, UMatUsageFlags _usageFlags) -: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) -{ - create(_rows, _cols, _type); -} - -inline -UMat::UMat(int _rows, int _cols, int _type, const Scalar& _s, UMatUsageFlags _usageFlags) -: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) -{ - create(_rows, _cols, _type); - *this = _s; -} - -inline -UMat::UMat(Size _sz, int _type, UMatUsageFlags _usageFlags) -: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) -{ - create( _sz.height, _sz.width, _type ); -} - -inline -UMat::UMat(Size _sz, int _type, const Scalar& _s, UMatUsageFlags _usageFlags) -: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) -{ - create(_sz.height, _sz.width, _type); - *this = _s; -} - -inline -UMat::UMat(int _dims, const int* _sz, int _type, UMatUsageFlags _usageFlags) -: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) -{ - create(_dims, _sz, _type); -} - -inline -UMat::UMat(int _dims, const int* _sz, int _type, const Scalar& _s, UMatUsageFlags _usageFlags) -: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) -{ - create(_dims, _sz, _type); - *this = _s; -} - -inline -UMat::UMat(const UMat& m) -: flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), allocator(m.allocator), - usageFlags(m.usageFlags), u(m.u), offset(m.offset), size(&rows) -{ - addref(); - if( m.dims <= 2 ) - { - step[0] = m.step[0]; step[1] = m.step[1]; - } - else - { - dims = 0; - copySize(m); - } -} - - template inline UMat::UMat(const std::vector<_Tp>& vec, bool copyData) : flags(MAGIC_VAL | traits::Type<_Tp>::value | CV_MAT_CONT_FLAG), dims(2), rows((int)vec.size()), @@ -3751,33 +3282,6 @@ cols(1), allocator(0), usageFlags(USAGE_DEFAULT), u(0), offset(0), size(&rows) Mat((int)vec.size(), 1, traits::Type<_Tp>::value, (uchar*)&vec[0]).copyTo(*this); } -inline -UMat& UMat::operator = (const UMat& m) -{ - if( this != &m ) - { - const_cast(m).addref(); - release(); - flags = m.flags; - if( dims <= 2 && m.dims <= 2 ) - { - dims = m.dims; - rows = m.rows; - cols = m.cols; - step[0] = m.step[0]; - step[1] = m.step[1]; - } - else - copySize(m); - allocator = m.allocator; - if (usageFlags == USAGE_DEFAULT) - usageFlags = m.usageFlags; - u = m.u; - offset = m.offset; - } - return *this; -} - inline UMat UMat::row(int y) const { @@ -3814,55 +3318,6 @@ UMat UMat::colRange(const Range& r) const return UMat(*this, Range::all(), r); } -inline -UMat UMat::clone() const -{ - UMat m; - copyTo(m); - return m; -} - -inline -void UMat::assignTo( UMat& m, int _type ) const -{ - if( _type < 0 ) - m = *this; - else - convertTo(m, _type); -} - -inline -void UMat::create(int _rows, int _cols, int _type, UMatUsageFlags _usageFlags) -{ - _type &= TYPE_MASK; - if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && u ) - return; - int sz[] = {_rows, _cols}; - create(2, sz, _type, _usageFlags); -} - -inline -void UMat::create(Size _sz, int _type, UMatUsageFlags _usageFlags) -{ - create(_sz.height, _sz.width, _type, _usageFlags); -} - -inline -void UMat::addref() -{ - if( u ) - CV_XADD(&(u->urefcount), 1); -} - -inline void UMat::release() -{ - if( u && CV_XADD(&(u->urefcount), -1) == 1 ) - deallocate(); - for(int i = 0; i < dims; i++) - size.p[i] = 0; - u = 0; -} - inline UMat UMat::operator()( Range _rowRange, Range _colRange ) const { @@ -3937,23 +3392,6 @@ size_t UMat::step1(int i) const return step.p[i] / elemSize1(); } -inline -bool UMat::empty() const -{ - return u == 0 || total() == 0 || dims == 0; -} - -inline -size_t UMat::total() const -{ - if( dims <= 2 ) - return (size_t)rows * cols; - size_t p = 1; - for( int i = 0; i < dims; i++ ) - p *= size[i]; - return p; -} - #ifdef CV_CXX_MOVE_SEMANTICS inline diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index fc9e4c69b2..178e291d3f 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -204,6 +204,21 @@ MatAllocator* Mat::getStdAllocator() //================================================================================================== +bool MatSize::operator==(const MatSize& sz) const +{ + int d = dims(); + int dsz = sz.dims(); + if( d != dsz ) + return false; + if( d == 2 ) + return p[0] == sz.p[0] && p[1] == sz.p[1]; + + for( int i = 0; i < d; i++ ) + if( p[i] != sz.p[i] ) + return false; + return true; +} + void setSize( Mat& m, int _dims, const int* _sz, const size_t* _steps, bool autoSteps) { CV_Assert( 0 <= _dims && _dims <= CV_MAX_DIM ); @@ -320,7 +335,270 @@ void finalizeHdr(Mat& m) m.dataend = m.datalimit = 0; } -//================================================================================================== +//======================================= Mat ====================================================== + +Mat::Mat() + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{} + +Mat::Mat(int _rows, int _cols, int _type) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_rows, _cols, _type); +} + +Mat::Mat(int _rows, int _cols, int _type, const Scalar& _s) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_rows, _cols, _type); + *this = _s; +} + +Mat::Mat(Size _sz, int _type) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create( _sz.height, _sz.width, _type ); +} + +Mat::Mat(Size _sz, int _type, const Scalar& _s) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_sz.height, _sz.width, _type); + *this = _s; +} + +Mat::Mat(int _dims, const int* _sz, int _type) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_dims, _sz, _type); +} + +Mat::Mat(int _dims, const int* _sz, int _type, const Scalar& _s) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_dims, _sz, _type); + *this = _s; +} + +Mat::Mat(const std::vector& _sz, int _type) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_sz, _type); +} + +Mat::Mat(const std::vector& _sz, int _type, const Scalar& _s) + : flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0), + datalimit(0), allocator(0), u(0), size(&rows), step(0) +{ + create(_sz, _type); + *this = _s; +} + +Mat::Mat(const Mat& m) + : flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), data(m.data), + datastart(m.datastart), dataend(m.dataend), datalimit(m.datalimit), allocator(m.allocator), + u(m.u), size(&rows), step(0) +{ + if( u ) + CV_XADD(&u->refcount, 1); + if( m.dims <= 2 ) + { + step[0] = m.step[0]; step[1] = m.step[1]; + } + else + { + dims = 0; + copySize(m); + } +} + +Mat::Mat(int _rows, int _cols, int _type, void* _data, size_t _step) + : flags(MAGIC_VAL + (_type & TYPE_MASK)), dims(2), rows(_rows), cols(_cols), + data((uchar*)_data), datastart((uchar*)_data), dataend(0), datalimit(0), + allocator(0), u(0), size(&rows) +{ + CV_Assert(total() == 0 || data != NULL); + + size_t esz = CV_ELEM_SIZE(_type), esz1 = CV_ELEM_SIZE1(_type); + size_t minstep = cols * esz; + if( _step == AUTO_STEP ) + { + _step = minstep; + } + else + { + CV_Assert( _step >= minstep ); + if (_step % esz1 != 0) + { + CV_Error(Error::BadStep, "Step must be a multiple of esz1"); + } + } + step[0] = _step; + step[1] = esz; + datalimit = datastart + _step * rows; + dataend = datalimit - _step + minstep; + updateContinuityFlag(); +} + +Mat::Mat(Size _sz, int _type, void* _data, size_t _step) + : flags(MAGIC_VAL + (_type & TYPE_MASK)), dims(2), rows(_sz.height), cols(_sz.width), + data((uchar*)_data), datastart((uchar*)_data), dataend(0), datalimit(0), + allocator(0), u(0), size(&rows) +{ + CV_Assert(total() == 0 || data != NULL); + + size_t esz = CV_ELEM_SIZE(_type), esz1 = CV_ELEM_SIZE1(_type); + size_t minstep = cols*esz; + if( _step == AUTO_STEP ) + { + _step = minstep; + } + else + { + CV_Assert(_step >= minstep); + + if (_step % esz1 != 0) + { + CV_Error(Error::BadStep, "Step must be a multiple of esz1"); + } + } + step[0] = _step; + step[1] = esz; + datalimit = datastart + _step*rows; + dataend = datalimit - _step + minstep; + updateContinuityFlag(); +} + + +Mat::~Mat() +{ + release(); + if( step.p != step.buf ) + fastFree(step.p); +} + +Mat& Mat::operator=(const Mat& m) +{ + if( this != &m ) + { + if( m.u ) + CV_XADD(&m.u->refcount, 1); + release(); + flags = m.flags; + if( dims <= 2 && m.dims <= 2 ) + { + dims = m.dims; + rows = m.rows; + cols = m.cols; + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + copySize(m); + data = m.data; + datastart = m.datastart; + dataend = m.dataend; + datalimit = m.datalimit; + allocator = m.allocator; + u = m.u; + } + return *this; +} + +Mat Mat::clone() const +{ + Mat m; + copyTo(m); + return m; +} + +void Mat::assignTo( Mat& m, int _type ) const +{ + if( _type < 0 ) + m = *this; + else + convertTo(m, _type); +} + +void Mat::create(int _rows, int _cols, int _type) +{ + _type &= TYPE_MASK; + if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && data ) + return; + int sz[] = {_rows, _cols}; + create(2, sz, _type); +} + +void Mat::create(Size _sz, int _type) +{ + create(_sz.height, _sz.width, _type); +} + +void Mat::addref() +{ + if( u ) + CV_XADD(&u->refcount, 1); +} + +void Mat::release() +{ + if( u && CV_XADD(&u->refcount, -1) == 1 ) + deallocate(); + u = NULL; + datastart = dataend = datalimit = data = 0; + for(int i = 0; i < dims; i++) + size.p[i] = 0; +#ifdef _DEBUG + flags = MAGIC_VAL; + dims = rows = cols = 0; + if(step.p != step.buf) + { + fastFree(step.p); + step.p = step.buf; + size.p = &rows; + } +#endif +} + +size_t Mat::step1(int i) const +{ + return step.p[i] / elemSize1(); +} + +bool Mat::empty() const +{ + return data == 0 || total() == 0 || dims == 0; +} + +size_t Mat::total() const +{ + if( dims <= 2 ) + return (size_t)rows * cols; + size_t p = 1; + for( int i = 0; i < dims; i++ ) + p *= size[i]; + return p; +} + +size_t Mat::total(int startDim, int endDim) const +{ + CV_Assert( 0 <= startDim && startDim <= endDim); + size_t p = 1; + int endDim_ = endDim <= dims ? endDim : dims; + for( int i = startDim; i < endDim_; i++ ) + p *= size[i]; + return p; +} + + void Mat::create(int d, const int* _sizes, int _type) { diff --git a/modules/core/src/matrix_sparse.cpp b/modules/core/src/matrix_sparse.cpp index 61e7e90a56..05d16d706e 100644 --- a/modules/core/src/matrix_sparse.cpp +++ b/modules/core/src/matrix_sparse.cpp @@ -176,6 +176,94 @@ void SparseMat::Hdr::clear() nodeCount = freeList = 0; } +///////////////////////////// SparseMat ///////////////////////////// + +SparseMat::SparseMat() + : flags(MAGIC_VAL), hdr(0) +{} + +SparseMat::SparseMat(int _dims, const int* _sizes, int _type) + : flags(MAGIC_VAL), hdr(0) +{ + create(_dims, _sizes, _type); +} + +SparseMat::SparseMat(const SparseMat& m) + : flags(m.flags), hdr(m.hdr) +{ + addref(); +} + +SparseMat::~SparseMat() +{ + release(); +} + +SparseMat& SparseMat::operator = (const SparseMat& m) +{ + if( this != &m ) + { + if( m.hdr ) + CV_XADD(&m.hdr->refcount, 1); + release(); + flags = m.flags; + hdr = m.hdr; + } + return *this; +} + +SparseMat& SparseMat::operator=(const Mat& m) +{ + return (*this = SparseMat(m)); +} + +void SparseMat::assignTo(SparseMat& m, int _type) const +{ + if( _type < 0 ) + m = *this; + else + convertTo(m, _type); +} + +void SparseMat::addref() +{ + if( hdr ) + CV_XADD(&hdr->refcount, 1); +} + +void SparseMat::release() +{ + if( hdr && CV_XADD(&hdr->refcount, -1) == 1 ) + delete hdr; + hdr = 0; +} + +size_t SparseMat::hash(int i0) const +{ + return (size_t)i0; +} + +size_t SparseMat::hash(int i0, int i1) const +{ + return (size_t)(unsigned)i0 * HASH_SCALE + (unsigned)i1; +} + +size_t SparseMat::hash(int i0, int i1, int i2) const +{ + return ((size_t)(unsigned)i0 * HASH_SCALE + (unsigned)i1) * HASH_SCALE + (unsigned)i2; +} + +size_t SparseMat::hash(const int* idx) const +{ + size_t h = (unsigned)idx[0]; + if( !hdr ) + return 0; + int d = hdr->dims; + for(int i = 1; i < d; i++ ) + h = h * HASH_SCALE + (unsigned)idx[i]; + return h; +} + SparseMat::SparseMat(const Mat& m) : flags(MAGIC_VAL), hdr(0) diff --git a/modules/core/src/umatrix.cpp b/modules/core/src/umatrix.cpp index 9fe8122d22..f21cf7b7e2 100644 --- a/modules/core/src/umatrix.cpp +++ b/modules/core/src/umatrix.cpp @@ -228,6 +228,152 @@ UMatDataAutoLock::~UMatDataAutoLock() getUMatDataAutoLocker().release(u1, u2); } +//////////////////////////////// UMat //////////////////////////////// + +UMat::UMat(UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{} + +UMat::UMat(int _rows, int _cols, int _type, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_rows, _cols, _type); +} + +UMat::UMat(int _rows, int _cols, int _type, const Scalar& _s, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_rows, _cols, _type); + *this = _s; +} + +UMat::UMat(Size _sz, int _type, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create( _sz.height, _sz.width, _type ); +} + +UMat::UMat(Size _sz, int _type, const Scalar& _s, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_sz.height, _sz.width, _type); + *this = _s; +} + +UMat::UMat(int _dims, const int* _sz, int _type, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_dims, _sz, _type); +} + +UMat::UMat(int _dims, const int* _sz, int _type, const Scalar& _s, UMatUsageFlags _usageFlags) +: flags(MAGIC_VAL), dims(0), rows(0), cols(0), allocator(0), usageFlags(_usageFlags), u(0), offset(0), size(&rows) +{ + create(_dims, _sz, _type); + *this = _s; +} + +UMat::UMat(const UMat& m) +: flags(m.flags), dims(m.dims), rows(m.rows), cols(m.cols), allocator(m.allocator), + usageFlags(m.usageFlags), u(m.u), offset(m.offset), size(&rows) +{ + addref(); + if( m.dims <= 2 ) + { + step[0] = m.step[0]; step[1] = m.step[1]; + } + else + { + dims = 0; + copySize(m); + } +} + +UMat& UMat::operator=(const UMat& m) +{ + if( this != &m ) + { + const_cast(m).addref(); + release(); + flags = m.flags; + if( dims <= 2 && m.dims <= 2 ) + { + dims = m.dims; + rows = m.rows; + cols = m.cols; + step[0] = m.step[0]; + step[1] = m.step[1]; + } + else + copySize(m); + allocator = m.allocator; + if (usageFlags == USAGE_DEFAULT) + usageFlags = m.usageFlags; + u = m.u; + offset = m.offset; + } + return *this; +} + +UMat UMat::clone() const +{ + UMat m; + copyTo(m); + return m; +} + +void UMat::assignTo(UMat& m, int _type) const +{ + if( _type < 0 ) + m = *this; + else + convertTo(m, _type); +} + +void UMat::create(int _rows, int _cols, int _type, UMatUsageFlags _usageFlags) +{ + _type &= TYPE_MASK; + if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && u ) + return; + int sz[] = {_rows, _cols}; + create(2, sz, _type, _usageFlags); +} + +void UMat::create(Size _sz, int _type, UMatUsageFlags _usageFlags) +{ + create(_sz.height, _sz.width, _type, _usageFlags); +} + +void UMat::addref() +{ + if( u ) + CV_XADD(&(u->urefcount), 1); +} + +void UMat::release() +{ + if( u && CV_XADD(&(u->urefcount), -1) == 1 ) + deallocate(); + for(int i = 0; i < dims; i++) + size.p[i] = 0; + u = 0; +} + +bool UMat::empty() const +{ + return u == 0 || total() == 0 || dims == 0; +} + +size_t UMat::total() const +{ + if( dims <= 2 ) + return (size_t)rows * cols; + size_t p = 1; + for( int i = 0; i < dims; i++ ) + p *= size[i]; + return p; +} + MatAllocator* UMat::getStdAllocator() { From 61a8cf8ba7ba540904db432e35690fb72cc683b2 Mon Sep 17 00:00:00 2001 From: Justin Frank Date: Tue, 20 Oct 2020 17:31:34 -0700 Subject: [PATCH 3/4] Fix TypeError when building for WebAssembly with Python 3 --- modules/js/src/make_umd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/js/src/make_umd.py b/modules/js/src/make_umd.py index 8e50da585d..08d9e39e13 100644 --- a/modules/js/src/make_umd.py +++ b/modules/js/src/make_umd.py @@ -103,7 +103,7 @@ def make_umd(opencvjs, cvjs): Module = {}; return cv(Module); })); - """ % (content)).lstrip()) + """ % (content)).lstrip().encode()) if __name__ == "__main__": if len(sys.argv) > 2: From c71f2714c6c986fac00a46f25c0a49dc7774c4a6 Mon Sep 17 00:00:00 2001 From: ann <44146733+APrigarina@users.noreply.github.com> Date: Fri, 23 Oct 2020 21:42:45 +0300 Subject: [PATCH 4/4] Merge pull request #18003 from APrigarina:curved_qrcodes_decoding Detection and decoding of curved QR-codes * temp changes for curved qrcodes * added api for curved qr code decoding * fixed prototypes * refactored curved qr code decoding * refactored curved qr code decoding 2nd part * refactored curved qr code decoding 3rd part * refactored curved qr code decoding 4th part * added tests for curved qr code decoding * refactored curved qr code decoding 5th part --- .../objdetect/include/opencv2/objdetect.hpp | 29 +- modules/objdetect/src/qrcode.cpp | 1387 ++++++++++++++++- modules/objdetect/test/test_qrcode.cpp | 95 ++ 3 files changed, 1465 insertions(+), 46 deletions(-) diff --git a/modules/objdetect/include/opencv2/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect.hpp index ea7b1ac801..0387b10239 100644 --- a/modules/objdetect/include/opencv2/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect.hpp @@ -702,6 +702,15 @@ public: */ CV_WRAP cv::String decode(InputArray img, InputArray points, OutputArray straight_qrcode = noArray()); + /** @brief Decodes QR code on a curved surface in image once it's found by the detect() method. + + Returns UTF8-encoded output string or empty string if the code cannot be decoded. + @param img grayscale or color (BGR) image containing QR code. + @param points Quadrangle vertices found by detect() method (or some other algorithm). + @param straight_qrcode The optional output image containing rectified and binarized QR code + */ + CV_WRAP cv::String decodeCurved(InputArray img, InputArray points, OutputArray straight_qrcode = noArray()); + /** @brief Both detects and decodes QR code @param img grayscale or color (BGR) image containing QR code. @@ -709,7 +718,17 @@ public: @param straight_qrcode The optional output image containing rectified and binarized QR code */ CV_WRAP cv::String detectAndDecode(InputArray img, OutputArray points=noArray(), - OutputArray straight_qrcode = noArray()); + OutputArray straight_qrcode = noArray()); + + /** @brief Both detects and decodes QR code on a curved surface + + @param img grayscale or color (BGR) image containing QR code. + @param points optional output array of vertices of the found QR code quadrangle. Will be empty if not found. + @param straight_qrcode The optional output image containing rectified and binarized QR code + */ + CV_WRAP cv::String detectAndDecodeCurved(InputArray img, OutputArray points=noArray(), + OutputArray straight_qrcode = noArray()); + /** @brief Detects QR codes in image and returns the vector of the quadrangles containing the codes. @param img grayscale or color (BGR) image containing (or not) QR codes. @param points Output vector of vector of vertices of the minimum-area quadrangle containing the codes. @@ -801,6 +820,14 @@ CV_EXPORTS bool detectQRCode(InputArray in, std::vector &points, double e */ CV_EXPORTS bool decodeQRCode(InputArray in, InputArray points, std::string &decoded_info, OutputArray straight_qrcode = noArray()); +/** @brief Decode QR code on a curved surface in image and return text that is encrypted in QR code. + @param in Matrix of the type CV_8UC1 containing an image where QR code are detected. + @param points Input vector of vertices of a quadrangle of minimal area that describes QR code. + @param decoded_info String information that is encrypted in QR code. + @param straight_qrcode Matrix of the type CV_8UC1 containing an binary straight QR code. + */ +CV_EXPORTS bool decodeCurvedQRCode(InputArray in, InputArray points, std::string &decoded_info, OutputArray straight_qrcode = noArray()); + //! @} objdetect } diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index 5b4bb61e9e..5b86f74614 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -18,6 +18,7 @@ #include #include #include +#include namespace cv { @@ -63,7 +64,40 @@ static void updatePointsResult(OutputArray points_, const vector& point } } +static Point2f intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2) +{ + const float divisor = (a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x); + const float eps = 0.001f; + if (abs(divisor) < eps) + return a2; + Point2f result_square_angle( + ((a1.x * a2.y - a1.y * a2.x) * (b1.x - b2.x) - + (b1.x * b2.y - b1.y * b2.x) * (a1.x - a2.x)) / + divisor, + ((a1.x * a2.y - a1.y * a2.x) * (b1.y - b2.y) - + (b1.x * b2.y - b1.y * b2.x) * (a1.y - a2.y)) / + divisor + ); + return result_square_angle; +} +// / | b +// / | +// / | +// a/ | c + +static inline double getCosVectors(Point2f a, Point2f b, Point2f c) +{ + return ((a - b).x * (c - b).x + (a - b).y * (c - b).y) / (norm(a - b) * norm(c - b)); +} + +static bool arePointsNearest(Point2f a, Point2f b, float delta = 0.0) +{ + if ((abs(a.x - b.x) < delta) && (abs(a.y - b.y) < delta)) + return true; + else + return false; +} class QRDetect { @@ -74,15 +108,13 @@ public: Mat getBinBarcode() { return bin_barcode; } Mat getStraightBarcode() { return straight_barcode; } vector getTransformationPoints() { return transformation_points; } - static Point2f intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2); protected: vector searchHorizontalLines(); vector separateVerticalLines(const vector &list_lines); vector extractVerticalLines(const vector &list_lines, double eps); void fixationPoints(vector &local_point); vector getQuadrilateral(vector angle_list); - bool testBypassRoute(vector hull, int start, int finish); - inline double getCosVectors(Point2f a, Point2f b, Point2f c); + bool testByPassRoute(vector hull, int start, int finish); Mat barcode, bin_barcode, resized_barcode, resized_bin_barcode, straight_barcode; vector localization_points, transformation_points; @@ -361,7 +393,6 @@ void QRDetect::fixationPoints(vector &local_point) Point2f(static_cast(bin_barcode.cols - 1), static_cast(bin_barcode.rows - 1)))); - vector list_area_pnt; list_area_pnt.push_back(current_point); @@ -629,7 +660,6 @@ bool QRDetect::computeTransformationPoints() transformation_points.push_back( intersectionLines(down_left_edge_point, down_max_delta_point, up_right_edge_point, up_max_delta_point)); - vector quadrilateral = getQuadrilateral(transformation_points); transformation_points = quadrilateral; @@ -643,23 +673,8 @@ bool QRDetect::computeTransformationPoints() return true; } -Point2f QRDetect::intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2) -{ - Point2f result_square_angle( - ((a1.x * a2.y - a1.y * a2.x) * (b1.x - b2.x) - - (b1.x * b2.y - b1.y * b2.x) * (a1.x - a2.x)) / - ((a1.x - a2.x) * (b1.y - b2.y) - - (a1.y - a2.y) * (b1.x - b2.x)), - ((a1.x * a2.y - a1.y * a2.x) * (b1.y - b2.y) - - (b1.x * b2.y - b1.y * b2.x) * (a1.y - a2.y)) / - ((a1.x - a2.x) * (b1.y - b2.y) - - (a1.y - a2.y) * (b1.x - b2.x)) - ); - return result_square_angle; -} - // test function (if true then ------> else <------ ) -bool QRDetect::testBypassRoute(vector hull, int start, int finish) +bool QRDetect::testByPassRoute(vector hull, int start, int finish) { CV_TRACE_FUNCTION(); int index_hull = start, next_index_hull, hull_size = (int)hull.size(); @@ -764,7 +779,7 @@ vector QRDetect::getQuadrilateral(vector angle_list) int index_hull, extra_index_hull, next_index_hull, extra_next_index_hull; Point result_side_begin[4], result_side_end[4]; - bool bypass_orientation = testBypassRoute(hull, start_line[0], finish_line[0]); + bool bypass_orientation = testByPassRoute(hull, start_line[0], finish_line[0]); min_norm = std::numeric_limits::max(); index_hull = start_line[0]; @@ -805,7 +820,7 @@ vector QRDetect::getQuadrilateral(vector angle_list) min_norm = std::numeric_limits::max(); index_hull = start_line[1]; - bypass_orientation = testBypassRoute(hull, start_line[1], finish_line[1]); + bypass_orientation = testByPassRoute(hull, start_line[1], finish_line[1]); do { if (bypass_orientation) { next_index_hull = index_hull + 1; } @@ -840,8 +855,8 @@ vector QRDetect::getQuadrilateral(vector angle_list) result_side_end[1] = angle_list[1]; } - bypass_orientation = testBypassRoute(hull, start_line[0], unstable_pnt); - const bool extra_bypass_orientation = testBypassRoute(hull, finish_line[1], unstable_pnt); + bypass_orientation = testByPassRoute(hull, start_line[0], unstable_pnt); + const bool extra_bypass_orientation = testByPassRoute(hull, finish_line[1], unstable_pnt); vector result_angle_list(4), test_result_angle_list(4); double min_diff_area = std::numeric_limits::max(); @@ -918,16 +933,6 @@ vector QRDetect::getQuadrilateral(vector angle_list) return result_angle_list; } -// / | b -// / | -// / | -// a/ | c - -inline double QRDetect::getCosVectors(Point2f a, Point2f b, Point2f c) -{ - return ((a - b).x * (c - b).x + (a - b).y * (c - b).y) / (norm(a - b) * norm(c - b)); -} - struct QRCodeDetector::Impl { public: @@ -975,17 +980,79 @@ public: Mat getStraightBarcode() { return straight; } size_t getVersion() { return version; } std::string getDecodeInformation() { return result_info; } - bool fullDecodingProcess(); + bool straightDecodingProcess(); + bool curvedDecodingProcess(); protected: bool updatePerspective(); bool versionDefinition(); bool samplingForVersion(); bool decodingProcess(); - Mat original, no_border_intermediate, intermediate, straight; + inline double pointPosition(Point2f a, Point2f b , Point2f c); + float distancePointToLine(Point2f a, Point2f b , Point2f c); + void getPointsInsideQRCode(const vector &angle_list); + bool computeClosestPoints(const vector &result_integer_hull); + bool computeSidesPoints(const vector &result_integer_hull); + vector getPointsNearUnstablePoint(const vector &side, int start, int end, int step); + bool findAndAddStablePoint(const vector &result_integer_hull); + bool findIndexesCurvedSides(); + bool findIncompleteIndexesCurvedSides(); + Mat getPatternsMask(); + Point findClosestZeroPoint(Point2f original_point); + bool findPatternsContours(vector > &patterns_contours); + bool findPatternsVerticesPoints(vector > &patterns_vertices_points); + bool findTempPatternsAddingPoints(vector > > &temp_patterns_add_points); + bool computePatternsAddingPoints(std::map > &patterns_add_points); + bool addPointsToSides(); + void completeAndSortSides(); + vector > computeSpline(const vector &x_arr, const vector &y_arr); + bool createSpline(vector > &spline_lines); + bool divideIntoEvenSegments(vector > &segments_points); + bool straightenQRCodeInParts(); + bool preparingCurvedQRCodes(); + + const static int NUM_SIDES = 2; + Mat original, bin_barcode, no_border_intermediate, intermediate, straight, curved_to_straight, test_image; vector original_points; + vector original_curved_points; + vector qrcode_locations; + vector > closest_points; + vector > sides_points; + std::pair unstable_pair; + vector curved_indexes, curved_incomplete_indexes; + std::map > complete_curved_sides; std::string result_info; uint8_t version, version_size; float test_perspective_size; + struct sortPairAsc + { + bool operator()(const std::pair &a, + const std::pair &b) const + { + return a.second < b.second; + } + }; + struct sortPairDesc + { + bool operator()(const std::pair &a, + const std::pair &b) const + { + return a.second > b.second; + } + }; + struct sortPointsByX + { + bool operator()(const Point &a, const Point &b) const + { + return a.x < b.x; + } + }; + struct sortPointsByY + { + bool operator()(const Point &a, const Point &b) const + { + return a.y < b.y; + } + }; }; void QRDecode::init(const Mat &src, const vector &points) @@ -993,6 +1060,8 @@ void QRDecode::init(const Mat &src, const vector &points) CV_TRACE_FUNCTION(); vector bbox = points; original = src.clone(); + test_image = src.clone(); + adaptiveThreshold(original, bin_barcode, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 83, 2); intermediate = Mat::zeros(original.size(), CV_8UC1); original_points = bbox; version = 0; @@ -1001,11 +1070,1168 @@ void QRDecode::init(const Mat &src, const vector &points) result_info = ""; } +inline double QRDecode::pointPosition(Point2f a, Point2f b , Point2f c) +{ + return (a.x - b.x) * (c.y - b.y) - (c.x - b.x) * (a.y - b.y); +} + +float QRDecode::distancePointToLine(Point2f a, Point2f b , Point2f c) +{ + float A, B, C, result; + A = c.y - b.y; + B = c.x - b.x; + C = c.x * b.y - b.x * c.y; + float dist = sqrt(A*A + B*B); + if (dist == 0) return 0; + result = abs((A * a.x - B * a.y + C)) / dist; + + return result; +} + +void QRDecode::getPointsInsideQRCode(const vector &angle_list) +{ + CV_TRACE_FUNCTION(); + size_t angle_size = angle_list.size(); + Mat contour_mask = Mat::zeros(bin_barcode.size(), CV_8UC1); + for (size_t i = 0; i < angle_size; i++) + { + LineIterator line_iter(bin_barcode, angle_list[ i % angle_size], + angle_list[(i + 1) % angle_size]); + for(int j = 0; j < line_iter.count; j++, ++line_iter) + { + Point p = line_iter.pos(); + contour_mask.at(p + Point(1, 1)) = 255; + } + } + Point2f center_point = intersectionLines(angle_list[0], angle_list[2], + angle_list[1], angle_list[3]); + floodFill(contour_mask, center_point, 255, 0, Scalar(), Scalar(), FLOODFILL_FIXED_RANGE); + + vector locations; + findNonZero(contour_mask, locations); + + Mat fill_bin_barcode = bin_barcode.clone(); + Mat qrcode_mask = Mat::zeros(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1); + uint8_t value, mask_value; + for(size_t i = 0; i < locations.size(); i++) + { + value = bin_barcode.at(locations[i]); + mask_value = qrcode_mask.at(locations[i] + Point(1, 1)); + if (value == 0 && mask_value == 0) + { + floodFill(fill_bin_barcode, qrcode_mask, locations[i], 255, + 0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY); + } + } + Mat qrcode_mask_roi = qrcode_mask(Range(1, qrcode_mask.rows - 1), Range(1, qrcode_mask.cols - 1)); + findNonZero(qrcode_mask_roi, qrcode_locations); +} + +bool QRDecode::computeClosestPoints(const vector &result_integer_hull) +{ + CV_TRACE_FUNCTION(); + double min_norm, max_norm = 0.0; + size_t idx_min; + for (size_t i = 0; i < original_points.size(); i++) + { + min_norm = std::numeric_limits::max(); + + Point closest_pnt; + for (size_t j = 0; j < result_integer_hull.size(); j++) + { + Point integer_original_point = original_points[i]; + double temp_norm = norm(integer_original_point - result_integer_hull[j]); + if (temp_norm < min_norm) + { + min_norm = temp_norm; + closest_pnt = result_integer_hull[j]; + idx_min = j; + } + } + if (min_norm > max_norm) + { + max_norm = min_norm; + unstable_pair = std::pair(i, closest_pnt); + } + closest_points.push_back(std::pair(idx_min, closest_pnt)); + } + + if (closest_points.size() != 4) + { + return false; + } + + return true; +} + +bool QRDecode::computeSidesPoints(const vector &result_integer_hull) +{ + size_t num_closest_points = closest_points.size(); + vector points; + + for(size_t i = 0; i < num_closest_points; i++) + { + points.clear(); + size_t start = closest_points[i].first, + end = closest_points[(i + 1) % num_closest_points].first; + if (start < end) + { + points.insert(points.end(), + result_integer_hull.begin() + start, + result_integer_hull.begin() + end + 1); + } + else + { + points.insert(points.end(), + result_integer_hull.begin() + start, + result_integer_hull.end()); + points.insert(points.end(), + result_integer_hull.begin(), + result_integer_hull.begin() + end + 1); + } + if (abs(result_integer_hull[start].x - result_integer_hull[end].x) > + abs(result_integer_hull[start].y - result_integer_hull[end].y)) + { + if (points.front().x > points.back().x) + { + reverse(points.begin(), points.end()); + } + } + else + { + if (points.front().y > points.back().y) + { + reverse(points.begin(), points.end()); + } + } + if (points.empty()) + { + return false; + } + sides_points.push_back(points); + } + + return true; +} + +vector QRDecode::getPointsNearUnstablePoint(const vector &side, int start, int end, int step) +{ + vector points; + Point p1, p2, p3; + + double max_neighbour_angle = 1.0; + int index_max_angle = start + step; + bool enough_points = true; + + if(side.size() < 3) + { + points.insert(points.end(), side.begin(), side.end()); + return points; + } + const double cos_angle_threshold = -0.97; + for (int i = start + step; i != end; i+= step) + { + p1 = side[i + step]; + if (norm(p1 - side[i]) < 5) { continue; } + p2 = side[i]; + if (norm(p2 - side[i - step]) < 5) { continue; } + p3 = side[i - step]; + + double neighbour_angle = getCosVectors(p1, p2, p3); + neighbour_angle = floor(neighbour_angle*1000)/1000; + + if ((neighbour_angle <= max_neighbour_angle) && (neighbour_angle < cos_angle_threshold)) + { + max_neighbour_angle = neighbour_angle; + index_max_angle = i; + } + else if (i == end - step) + { + enough_points = false; + index_max_angle = i; + } + } + + if (enough_points) + { + p1 = side[index_max_angle + step]; + p2 = side[index_max_angle]; + p3 = side[index_max_angle - step]; + + points.push_back(p1); + points.push_back(p2); + points.push_back(p3); + } + else + { + p1 = side[index_max_angle]; + p2 = side[index_max_angle - step]; + + points.push_back(p1); + points.push_back(p2); + } + + return points; +} + +bool QRDecode::findAndAddStablePoint(const vector &result_integer_hull) +{ + size_t idx_unstable_point = unstable_pair.first; + Point unstable_point = unstable_pair.second; + + vector current_side_points, next_side_points; + Point a1, a2, b1, b2; + int start_current, end_current, step_current, start_next, end_next, step_next; + vector::iterator it_a, it_b; + + vector ¤t_side = sides_points[(idx_unstable_point + 3) % 4]; + vector &next_side = sides_points[idx_unstable_point]; + + if(current_side.size() < 2 || next_side.size() < 2) + { + return false; + } + + if(arePointsNearest(unstable_point, current_side.front(), 3.0)) + { + start_current = (int)current_side.size() - 1; + end_current = 0; + step_current = -1; + it_a = current_side.begin(); + } + else if(arePointsNearest(unstable_point, current_side.back(), 3.0)) + { + start_current = 0; + end_current = (int)current_side.size() - 1; + step_current = 1; + it_a = current_side.end() - 1; + } + else + { + return false; + } + if(arePointsNearest(unstable_point, next_side.front(), 3.0)) + { + start_next = (int)next_side.size() - 1; + end_next = 0; + step_next = -1; + it_b = next_side.begin(); + } + else if(arePointsNearest(unstable_point, next_side.back(), 3.0)) + { + start_next = 0; + end_next = (int)next_side.size() - 1; + step_next = 1; + it_b = next_side.end() - 1; + } + else + { + return false; + } + current_side_points = getPointsNearUnstablePoint(current_side, start_current, end_current, step_current); + next_side_points = getPointsNearUnstablePoint(next_side, start_next, end_next, step_next); + + if (current_side_points.size() < 2 || next_side_points.size() < 2) + { + return false; + } + + a1 = current_side_points[0]; + a2 = current_side_points[1]; + + b1 = next_side_points[0]; + b2 = next_side_points[1]; + + if(norm(a1 - b1) < 10 && next_side_points.size() > 2) + { + b1 = next_side_points[1]; + b2 = next_side_points[2]; + } + + Point stable_point = intersectionLines(a1, a2, b1, b2); + + const double max_side = std::max(bin_barcode.size().width, bin_barcode.size().height); + if ((abs(stable_point.x) > max_side) || (abs(stable_point.y) > max_side)) + { + return false; + } + + while (*it_a != a1) + { + it_a = current_side.erase(it_a); + if (it_a == current_side.end()) + { + it_a -= step_current; + } + Point point_to_remove_from_current = *it_a; + if (point_to_remove_from_current.x > max_side || point_to_remove_from_current.y > max_side) + { + break; + } + } + while (*it_b != b1) + { + it_b = next_side.erase(it_b); + if (it_b == next_side.end()) + { + it_b -= step_next; + } + Point point_to_remove_from_next = *it_b; + if (point_to_remove_from_next.x > max_side || point_to_remove_from_next.y > max_side) + { + break; + } + } + + bool add_stable_point = true; + + for (size_t i = 0; i < result_integer_hull.size(); i++) + { + if(arePointsNearest(stable_point, original_points[i], 3.0)) + { + add_stable_point = false; + break; + } + } + + if(add_stable_point) + { + current_side.insert(it_a, stable_point); + next_side.insert(it_b, stable_point); + closest_points[unstable_pair.first].second = stable_point; + } + else + { + stable_point = original_points[unstable_pair.first]; + closest_points[unstable_pair.first].second = stable_point; + current_side.insert(it_a, stable_point); + next_side.insert(it_b, stable_point); + } + + return true; +} + +bool QRDecode::findIndexesCurvedSides() +{ + double max_dist_to_arc_side = 0.0; + size_t num_closest_points = closest_points.size(); + int idx_curved_current = -1, idx_curved_opposite = -1; + + for (size_t i = 0; i < num_closest_points; i++) + { + double dist_to_arc = 0.0; + + Point arc_start = closest_points[i].second; + Point arc_end = closest_points[(i + 1) % num_closest_points].second; + + for (size_t j = 0; j < sides_points[i].size(); j++) + { + Point arc_point = sides_points[i][j]; + double dist = distancePointToLine(arc_point, arc_start, arc_end); + dist_to_arc += dist; + } + dist_to_arc /= sides_points[i].size(); + + if (dist_to_arc > max_dist_to_arc_side) + { + max_dist_to_arc_side = dist_to_arc; + idx_curved_current = (int)i; + idx_curved_opposite = (int)(i + 2) % num_closest_points; + } + } + if (idx_curved_current == -1 || idx_curved_opposite == -1) + { + return false; + } + + curved_indexes.push_back(idx_curved_current); + curved_indexes.push_back(idx_curved_opposite); + + return true; +} + +bool QRDecode::findIncompleteIndexesCurvedSides() +{ + int num_closest_points = (int)closest_points.size(); + + for (int i = 0; i < NUM_SIDES; i++) + { + int idx_side = curved_indexes[i]; + int side_size = (int)sides_points[idx_side].size(); + + double max_norm = norm(closest_points[idx_side].second - + closest_points[(idx_side + 1) % num_closest_points].second); + double real_max_norm = 0; + + for (int j = 0; j < side_size - 1; j++) + { + double temp_norm = norm(sides_points[idx_side][j] - + sides_points[idx_side][j + 1]); + if (temp_norm > real_max_norm) + { + real_max_norm = temp_norm; + } + } + if (real_max_norm > (0.5 * max_norm)) + { + curved_incomplete_indexes.push_back(curved_indexes[i]); + } + + } + + if (curved_incomplete_indexes.size() == 0) + { + return false; + } + return true; +} + +Point QRDecode::findClosestZeroPoint(Point2f original_point) +{ + int orig_x = static_cast(original_point.x); + int orig_y = static_cast(original_point.y); + uint8_t value; + Point zero_point; + + const int step = 2; + for (int i = orig_x - step; i >= 0 && i <= orig_x + step; i++) + { + for (int j = orig_y - step; j >= 0 && j <= orig_y + step; j++) + { + Point p(i, j); + value = bin_barcode.at(p); + if (value == 0) zero_point = p; + } + } + + return zero_point; +} + +Mat QRDecode::getPatternsMask() +{ + Mat mask(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1, Scalar(0)); + Mat patterns_mask(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1, Scalar(0)); + Mat fill_bin_barcode = bin_barcode.clone(); + for (size_t i = 0; i < original_points.size(); i++) + { + if (i == 2) continue; + Point p = findClosestZeroPoint(original_points[i]); + floodFill(fill_bin_barcode, mask, p, 255, + 0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY); + patterns_mask += mask; + } + Mat mask_roi = patterns_mask(Range(1, bin_barcode.rows - 1), Range(1, bin_barcode.cols - 1)); + + return mask_roi; +} + +bool QRDecode::findPatternsContours(vector > &patterns_contours) +{ + Mat patterns_mask = getPatternsMask(); + findContours(patterns_mask, patterns_contours, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0)); + if (patterns_contours.size() != 3) { return false; } + return true; +} + +bool QRDecode::findPatternsVerticesPoints(vector > &patterns_vertices_points) +{ + vector > patterns_contours; + if(!findPatternsContours(patterns_contours)) + { + return false; + } + const int num_vertices = 4; + for(size_t i = 0; i < patterns_contours.size(); i++) + { + vector convexhull_contours, new_convexhull_contours; + convexHull(patterns_contours[i], convexhull_contours); + + size_t number_pnts_in_hull = convexhull_contours.size(); + vector > cos_angles_in_hull; + vector min_angle_pnts_indexes; + + for(size_t j = 1; j < number_pnts_in_hull + 1; j++) + { + double cos_angle = getCosVectors(convexhull_contours[(j - 1) % number_pnts_in_hull], + convexhull_contours[ j % number_pnts_in_hull], + convexhull_contours[(j + 1) % number_pnts_in_hull]); + cos_angles_in_hull.push_back(std::pair(j, cos_angle)); + } + + sort(cos_angles_in_hull.begin(), cos_angles_in_hull.end(), sortPairDesc()); + + for (size_t j = 0; j < cos_angles_in_hull.size(); j++) + { + bool add_edge = true; + for(size_t k = 0; k < min_angle_pnts_indexes.size(); k++) + { + if(norm(convexhull_contours[cos_angles_in_hull[j].first % number_pnts_in_hull] - + convexhull_contours[min_angle_pnts_indexes[k] % number_pnts_in_hull]) < 3) + { + add_edge = false; + } + } + if (add_edge) + { + min_angle_pnts_indexes.push_back(cos_angles_in_hull[j].first % number_pnts_in_hull); + } + if ((int)min_angle_pnts_indexes.size() == num_vertices) { break; } + } + sort(min_angle_pnts_indexes.begin(), min_angle_pnts_indexes.end()); + + vector contour_vertices_points; + + for (size_t k = 0; k < min_angle_pnts_indexes.size(); k++) + { + contour_vertices_points.push_back(convexhull_contours[min_angle_pnts_indexes[k]]); + } + patterns_vertices_points.push_back(contour_vertices_points); + } + if (patterns_vertices_points.size() != 3) + { + return false; + } + + return true; +} + +bool QRDecode::findTempPatternsAddingPoints(vector > > &temp_patterns_add_points) +{ + vector >patterns_contours, patterns_vertices_points; + if(!findPatternsVerticesPoints(patterns_vertices_points)) + { + return false; + } + if(!findPatternsContours(patterns_contours)) + { + return false; + } + + for (size_t i = 0; i < curved_incomplete_indexes.size(); i++) + { + int idx_curved_side = curved_incomplete_indexes[i]; + Point close_transform_pnt_curr = original_points[idx_curved_side]; + Point close_transform_pnt_next = original_points[(idx_curved_side + 1) % 4]; + + vector patterns_indexes; + + for (size_t j = 0; j < patterns_vertices_points.size(); j++) + { + for (size_t k = 0; k < patterns_vertices_points[j].size(); k++) + { + if (norm(close_transform_pnt_curr - patterns_vertices_points[j][k]) < 5) + { + patterns_indexes.push_back(j); + break; + } + if (norm(close_transform_pnt_next - patterns_vertices_points[j][k]) < 5) + { + patterns_indexes.push_back(j); + break; + } + } + } + for (size_t j = 0; j < patterns_indexes.size(); j++) + { + vector vertices = patterns_vertices_points[patterns_indexes[j]]; + vector > vertices_dist_pair; + vector points; + for (size_t k = 0; k < vertices.size(); k++) + { + double dist_to_side = distancePointToLine(vertices[k], close_transform_pnt_curr, + close_transform_pnt_next); + vertices_dist_pair.push_back(std::pair((int)k, dist_to_side)); + } + if (vertices_dist_pair.size() == 0) + { + return false; + } + sort(vertices_dist_pair.begin(), vertices_dist_pair.end(), sortPairAsc()); + Point p1, p2; + int index_p1_in_vertices = 0, index_p2_in_vertices = 0; + for (int k = 4; k > 0; k--) + { + if((vertices_dist_pair[0].first == k % 4) && (vertices_dist_pair[1].first == (k - 1) % 4)) + { + index_p1_in_vertices = vertices_dist_pair[0].first; + index_p2_in_vertices = vertices_dist_pair[1].first; + } + else if((vertices_dist_pair[1].first == k % 4) && (vertices_dist_pair[0].first == (k - 1) % 4)) + { + index_p1_in_vertices = vertices_dist_pair[1].first; + index_p2_in_vertices = vertices_dist_pair[0].first; + } + } + if (index_p1_in_vertices == index_p2_in_vertices) return false; + + p1 = vertices[index_p1_in_vertices]; + p2 = vertices[index_p2_in_vertices]; + + size_t index_p1_in_contour = 0, index_p2_in_contour = 0; + vector add_points = patterns_contours[patterns_indexes[j]]; + + for(size_t k = 0; k < add_points.size(); k++) + { + if (add_points[k] == p1) + { + index_p1_in_contour = k; + } + if (add_points[k] == p2) + { + index_p2_in_contour = k; + } + } + + if (index_p1_in_contour > index_p2_in_contour) + { + for (size_t k = index_p1_in_contour; k < add_points.size(); k++) + { + points.push_back(add_points[k]); + } + for (size_t k = 0; k <= index_p2_in_contour; k++) + { + points.push_back(add_points[k]); + } + } + else if (index_p1_in_contour < index_p2_in_contour) + { + for (size_t k = index_p1_in_contour; k <= index_p2_in_contour; k++) + { + points.push_back(add_points[k]); + } + } + else + { + return false; + } + if (abs(p1.x - p2.x) > abs(p1.y - p2.y)) + { + sort(points.begin(), points.end(), sortPointsByX()); + } + else + { + sort(points.begin(), points.end(), sortPointsByY()); + } + + temp_patterns_add_points.push_back(std::pair >(idx_curved_side,points)); + } + } + + return true; +} + +bool QRDecode::computePatternsAddingPoints(std::map > &patterns_add_points) +{ + vector > > temp_patterns_add_points; + if(!findTempPatternsAddingPoints(temp_patterns_add_points)) + { + return false; + } + + const int num_points_in_pattern = 3; + for(size_t i = 0; i < temp_patterns_add_points.size(); i++) + { + int idx_side = temp_patterns_add_points[i].first; + int size = (int)temp_patterns_add_points[i].second.size(); + + float step = static_cast(size) / num_points_in_pattern; + vector temp_points; + for (int j = 0; j < num_points_in_pattern; j++) + { + float val = j * step; + int idx = cvRound(val) >= size ? size - 1 : cvRound(val); + temp_points.push_back(temp_patterns_add_points[i].second[idx]); + } + temp_points.push_back(temp_patterns_add_points[i].second.back()); + if(patterns_add_points.count(idx_side) == 1) + { + patterns_add_points[idx_side].insert(patterns_add_points[idx_side].end(), + temp_points.begin(), temp_points.end()); + } + patterns_add_points.insert(std::pair >(idx_side, temp_points)); + + } + if (patterns_add_points.size() == 0) + { + return false; + } + + return true; +} + +bool QRDecode::addPointsToSides() +{ + if(!computePatternsAddingPoints(complete_curved_sides)) + { + return false; + } + std::map >::iterator it; + double mean_step = 0.0; + size_t num_points_at_side = 0; + for (it = complete_curved_sides.begin(); it != complete_curved_sides.end(); ++it) + { + int count = -1; + const size_t num_points_at_pattern = it->second.size(); + for(size_t j = 0; j < num_points_at_pattern - 1; j++, count++) + { + if (count == 3) continue; + double temp_norm = norm(it->second[j] - + it->second[j + 1]); + mean_step += temp_norm; + } + num_points_at_side += num_points_at_pattern; + } + if (num_points_at_side == 0) + { + return false; + } + mean_step /= num_points_at_side; + + const size_t num_incomplete_sides = curved_incomplete_indexes.size(); + for (size_t i = 0; i < num_incomplete_sides; i++) + { + int idx = curved_incomplete_indexes[i]; + vector sides_points_indexes; + + const int num_points_at_side_to_add = (int)sides_points[idx].size(); + for (int j = 0; j < num_points_at_side_to_add; j++) + { + bool not_too_close = true; + const size_t num_points_at_side_exist = complete_curved_sides[idx].size(); + for (size_t k = 0; k < num_points_at_side_exist; k++) + { + double temp_norm = norm(sides_points[idx][j] - complete_curved_sides[idx][k]); + if (temp_norm < mean_step) + { + not_too_close = false; + break; + } + } + if (not_too_close) + { + sides_points_indexes.push_back(j); + } + } + + for (size_t j = 0; j < sides_points_indexes.size(); j++) + { + bool not_equal = true; + for (size_t k = 0; k < complete_curved_sides[idx].size(); k++) + { + if (sides_points[idx][sides_points_indexes[j]] == + complete_curved_sides[idx][k]) + { + not_equal = false; + } + } + if (not_equal) + { + complete_curved_sides[idx].push_back(sides_points[idx][sides_points_indexes[j]]); + } + } + } + + return true; +} + +void QRDecode::completeAndSortSides() +{ + if (complete_curved_sides.size() < 2) + { + for (int i = 0; i < NUM_SIDES; i++) + { + if(complete_curved_sides.count(curved_indexes[i]) == 0) + { + int idx_second_cur_side = curved_indexes[i]; + complete_curved_sides.insert(std::pair >(idx_second_cur_side, sides_points[idx_second_cur_side])); + } + } + } + std::map >::iterator it; + for (it = complete_curved_sides.begin(); it != complete_curved_sides.end(); ++it) + { + Point p1 = it->second.front(); + Point p2 = it->second.back(); + if (abs(p1.x - p2.x) > abs(p1.y - p2.y)) + { + sort(it->second.begin(), it->second.end(), sortPointsByX()); + } + else + { + sort(it->second.begin(), it->second.end(), sortPointsByY()); + } + } +} + +vector > QRDecode::computeSpline(const vector &x_arr, const vector &y_arr) +{ + const int n = (int)x_arr.size(); + vector a, b(n - 1), d(n - 1), h(n - 1), alpha(n - 1), c(n), l(n), mu(n), z(n); + + for (int i = 0; i < (int)y_arr.size(); i++) + { + a.push_back(static_cast(x_arr[i])); + } + for (int i = 0; i < n - 1; i++) + { + h[i] = static_cast(y_arr[i + 1] - y_arr[i]); + } + for (int i = 1; i < n - 1; i++) + { + alpha[i] = 3 / h[i] * (a[i + 1] - a[i]) - 3 / (h[i - 1]) * (a[i] - a[i - 1]); + } + l[0] = 1; + mu[0] = 0; + z[0] = 0; + + for (int i = 1; i < n - 1; i++) + { + l[i] = 2 * (y_arr[i + 1] - y_arr[i - 1]) - h[i - 1] * mu[i - 1]; + mu[i] = h[i] / l[i]; + z[i] = (alpha[i] - h[i - 1] * z[i - 1]) / l[i]; + } + l[n - 1] = 1; + z[n - 1] = 0; + c[n - 1] = 0; + + for(int j = n - 2; j >= 0; j--) + { + c[j] = z[j] - mu[j] * c[j + 1]; + b[j] = (a[j + 1] - a[j]) / h[j] - (h[j] * (c[j + 1] + 2 * c[j])) / 3; + d[j] = (c[j + 1] - c[j]) / (3 * h[j]); + } + + vector > S(n - 1); + for (int i = 0; i < n - 1; i++) + { + S[i].push_back(a[i]); + S[i].push_back(b[i]); + S[i].push_back(c[i]); + S[i].push_back(d[i]); + } + + return S; +} + +bool QRDecode::createSpline(vector > &spline_lines) +{ + int start, end; + vector > S; + + for (int idx = 0; idx < NUM_SIDES; idx++) + { + int idx_curved_side = curved_indexes[idx]; + + vector spline_points = complete_curved_sides.find(idx_curved_side)->second; + vector x_arr, y_arr; + + for (size_t j = 0; j < spline_points.size(); j++) + { + x_arr.push_back(cvRound(spline_points[j].x)); + y_arr.push_back(cvRound(spline_points[j].y)); + } + + bool horizontal_order = abs(x_arr.front() - x_arr.back()) > abs(y_arr.front() - y_arr.back()); + vector& second_arr = horizontal_order ? x_arr : y_arr; + vector& first_arr = horizontal_order ? y_arr : x_arr; + + S = computeSpline(first_arr, second_arr); + + int closest_point_first = horizontal_order ? closest_points[idx_curved_side].second.x + : closest_points[idx_curved_side].second.y; + int closest_point_second = horizontal_order ? closest_points[(idx_curved_side + 1) % 4].second.x + : closest_points[(idx_curved_side + 1) % 4].second.y; + + start = idx_curved_side; + end = (idx_curved_side + 1) % 4; + if(closest_point_first > closest_point_second) + { + start = (idx_curved_side + 1) % 4; + end = idx_curved_side; + } + + int closest_point_start = horizontal_order ? closest_points[start].second.x : closest_points[start].second.y; + int closest_point_end = horizontal_order ? closest_points[end].second.x : closest_points[end].second.y; + + for (int index = closest_point_start; index <= closest_point_end; index++) + { + if (index == second_arr.front()) + { + spline_lines[idx].push_back(closest_points[start].second); + } + for (size_t i = 0; i < second_arr.size() - 1; i++) + { + if ((index > second_arr[i]) && (index <= second_arr[i + 1])) + { + float val = S[i][0] + S[i][1] * (index - second_arr[i]) + S[i][2] * (index - second_arr[i]) * (index - second_arr[i]) + + S[i][3] * (index - second_arr[i]) * (index - second_arr[i]) * (index - second_arr[i]); + spline_lines[idx].push_back(horizontal_order ? Point2f(static_cast(index), val) : Point2f(val, static_cast(index))); + } + } + } + } + return true; +} + +bool QRDecode::divideIntoEvenSegments(vector > &segments_points) +{ + vector > spline_lines(NUM_SIDES); + if (!createSpline(spline_lines)) + { + return false; + } + float mean_num_points_in_line = 0.0; + for (int i = 0; i < NUM_SIDES; i++) + { + mean_num_points_in_line += spline_lines[i].size(); + } + mean_num_points_in_line /= NUM_SIDES; + const int min_num_points = 1, max_num_points = cvRound(mean_num_points_in_line / 2.0); + float linear_threshold = 0.5f; + for (int num = min_num_points; num < max_num_points; num++) + { + for (int i = 0; i < NUM_SIDES; i++) + { + segments_points[i].clear(); + + int size = (int)spline_lines[i].size(); + float step = static_cast(size) / num; + for (int j = 0; j < num; j++) + { + float val = j * step; + int idx = cvRound(val) >= size ? size - 1 : cvRound(val); + segments_points[i].push_back(spline_lines[i][idx]); + } + segments_points[i].push_back(spline_lines[i].back()); + } + float mean_of_two_sides = 0.0; + for (int i = 0; i < NUM_SIDES; i++) + { + float mean_dist_in_segment = 0.0; + for (size_t j = 0; j < segments_points[i].size() - 1; j++) + { + Point2f segment_start = segments_points[i][j]; + Point2f segment_end = segments_points[i][j + 1]; + vector::iterator it_start, it_end, it; + it_start = find(spline_lines[i].begin(), spline_lines[i].end(), segment_start); + it_end = find(spline_lines[i].begin(), spline_lines[i].end(), segment_end); + float max_dist_to_line = 0.0; + for (it = it_start; it != it_end; it++) + { + float temp_dist = distancePointToLine(*it, segment_start, segment_end); + if (temp_dist > max_dist_to_line) + { + max_dist_to_line = temp_dist; + } + } + mean_dist_in_segment += max_dist_to_line; + } + mean_dist_in_segment /= segments_points[i].size(); + mean_of_two_sides += mean_dist_in_segment; + } + mean_of_two_sides /= NUM_SIDES; + if (mean_of_two_sides < linear_threshold) + { + break; + } + } + + return true; +} + +bool QRDecode::straightenQRCodeInParts() +{ + vector > segments_points(NUM_SIDES); + if (!divideIntoEvenSegments(segments_points)) + { + return false; + } + vector current_curved_side, opposite_curved_side; + + for (int i = 0; i < NUM_SIDES; i++) + { + Point2f temp_point_start = segments_points[i].front(); + Point2f temp_point_end = segments_points[i].back(); + bool horizontal_order = (abs(temp_point_start.x - temp_point_end.x) > + abs(temp_point_start.y - temp_point_end.y)); + float compare_point_current = horizontal_order ? segments_points[i].front().y + : segments_points[(i + 1) % 2].front().x; + float compare_point_opposite = horizontal_order ? segments_points[(i + 1) % 2].front().y + : segments_points[i].front().x; + + if (compare_point_current > compare_point_opposite) + { + current_curved_side = segments_points[i]; + opposite_curved_side = segments_points[(i + 1) % 2]; + } + } + if (current_curved_side.size() != opposite_curved_side.size()) + { + return false; + } + size_t number_pnts_to_cut = current_curved_side.size(); + if (number_pnts_to_cut == 0) + { + return false; + } + float perspective_curved_size = 251.0; + const Size temporary_size(cvRound(perspective_curved_size), cvRound(perspective_curved_size)); + + float dist = perspective_curved_size / (number_pnts_to_cut - 1); + Mat perspective_result = Mat::zeros(temporary_size, CV_8UC1); + vector curved_parts_points; + + float start_cut = 0.0; + vector temp_closest_points(4); + + for (size_t i = 1; i < number_pnts_to_cut; i++) + { + curved_parts_points.clear(); + Mat test_mask = Mat::zeros(bin_barcode.size(), CV_8UC1); + + Point2f start_point = current_curved_side[i]; + Point2f prev_start_point = current_curved_side[i - 1]; + Point2f finish_point = opposite_curved_side[i]; + Point2f prev_finish_point = opposite_curved_side[i - 1]; + + for (size_t j = 0; j < qrcode_locations.size(); j++) + { + if ((pointPosition(start_point, finish_point, qrcode_locations[j]) >= 0) && + (pointPosition(prev_start_point, prev_finish_point, qrcode_locations[j]) <= 0)) + { + test_mask.at(qrcode_locations[j]) = 255; + } + } + + vector perspective_points; + + perspective_points.push_back(Point2f(0.0, start_cut)); + perspective_points.push_back(Point2f(perspective_curved_size, start_cut)); + + perspective_points.push_back(Point2f(perspective_curved_size, start_cut + dist)); + perspective_points.push_back(Point2f(0.0, start_cut+dist)); + + perspective_points.push_back(Point2f(perspective_curved_size * 0.5f, start_cut + dist * 0.5f)); + + if (i == 1) + { + for (size_t j = 0; j < closest_points.size(); j++) + { + if (arePointsNearest(closest_points[j].second, prev_start_point, 3.0)) + { + temp_closest_points[j] = perspective_points[0]; + } + else if (arePointsNearest(closest_points[j].second, prev_finish_point, 3.0)) + { + temp_closest_points[j] = perspective_points[1]; + } + } + } + if (i == number_pnts_to_cut - 1) + { + for (size_t j = 0; j < closest_points.size(); j++) + { + if (arePointsNearest(closest_points[j].second, finish_point, 3.0)) + { + temp_closest_points[j] = perspective_points[2]; + } + else if (arePointsNearest(closest_points[j].second, start_point, 3.0)) + { + temp_closest_points[j] = perspective_points[3]; + } + } + } + start_cut += dist; + + curved_parts_points.push_back(prev_start_point); + curved_parts_points.push_back(prev_finish_point); + curved_parts_points.push_back(finish_point); + curved_parts_points.push_back(start_point); + + Point2f center_point = intersectionLines(curved_parts_points[0], curved_parts_points[2], + curved_parts_points[1], curved_parts_points[3]); + if (cvIsNaN(center_point.x) || cvIsNaN(center_point.y)) + return false; + + vector pts = curved_parts_points; + pts.push_back(center_point); + + Mat H = findHomography(pts, perspective_points); + Mat temp_intermediate(temporary_size, CV_8UC1); + warpPerspective(test_mask, temp_intermediate, H, temporary_size, INTER_NEAREST); + perspective_result += temp_intermediate; + + } + Mat white_mask = Mat(temporary_size, CV_8UC1, Scalar(255)); + Mat inversion = white_mask - perspective_result; + Mat temp_result; + + original_curved_points = temp_closest_points; + + Point2f original_center_point = intersectionLines(original_curved_points[0], original_curved_points[2], + original_curved_points[1], original_curved_points[3]); + + original_curved_points.push_back(original_center_point); + + for (size_t i = 0; i < original_curved_points.size(); i++) + { + if (cvIsNaN(original_curved_points[i].x) || cvIsNaN(original_curved_points[i].y)) + return false; + } + + vector perspective_straight_points; + perspective_straight_points.push_back(Point2f(0.f, 0.f)); + perspective_straight_points.push_back(Point2f(perspective_curved_size, 0.f)); + + perspective_straight_points.push_back(Point2f(perspective_curved_size, perspective_curved_size)); + perspective_straight_points.push_back(Point2f(0.f, perspective_curved_size)); + + perspective_straight_points.push_back(Point2f(perspective_curved_size * 0.5f, perspective_curved_size * 0.5f)); + + Mat H = findHomography(original_curved_points, perspective_straight_points); + warpPerspective(inversion, temp_result, H, temporary_size, INTER_NEAREST, BORDER_REPLICATE); + + no_border_intermediate = temp_result(Range(1, temp_result.rows), Range(1, temp_result.cols)); + const int border = cvRound(0.1 * perspective_curved_size); + const int borderType = BORDER_CONSTANT; + copyMakeBorder(no_border_intermediate, curved_to_straight, border, border, border, border, borderType, Scalar(255)); + intermediate = curved_to_straight; + + return true; +} + +bool QRDecode::preparingCurvedQRCodes() +{ + vector result_integer_hull; + getPointsInsideQRCode(original_points); + if (qrcode_locations.size() == 0) + return false; + convexHull(qrcode_locations, result_integer_hull); + if (!computeClosestPoints(result_integer_hull)) + return false; + if (!computeSidesPoints(result_integer_hull)) + return false; + if (!findAndAddStablePoint(result_integer_hull)) + return false; + if (!findIndexesCurvedSides()) + return false; + if (findIncompleteIndexesCurvedSides()) + { + if(!addPointsToSides()) + return false; + } + completeAndSortSides(); + if (!straightenQRCodeInParts()) + return false; + + return true; +} + bool QRDecode::updatePerspective() { CV_TRACE_FUNCTION(); - const Point2f centerPt = QRDetect::intersectionLines(original_points[0], original_points[2], - original_points[1], original_points[3]); + const Point2f centerPt = intersectionLines(original_points[0], original_points[2], + original_points[1], original_points[3]); if (cvIsNaN(centerPt.x) || cvIsNaN(centerPt.y)) return false; @@ -1121,7 +2347,7 @@ bool QRDecode::samplingForVersion() CV_TRACE_FUNCTION(); const double multiplyingFactor = (version < 3) ? 1 : (version == 3) ? 1.5 : - version * (5 + version - 4); + version * (version + 1); const Size newFactorSize( cvRound(no_border_intermediate.size().width * multiplyingFactor), cvRound(no_border_intermediate.size().height * multiplyingFactor)); @@ -1206,7 +2432,7 @@ bool QRDecode::decodingProcess() } -bool QRDecode::fullDecodingProcess() +bool QRDecode::straightDecodingProcess() { #ifdef HAVE_QUIRC if (!updatePerspective()) { return false; } @@ -1220,6 +2446,20 @@ bool QRDecode::fullDecodingProcess() #endif } +bool QRDecode::curvedDecodingProcess() +{ +#ifdef HAVE_QUIRC + if (!preparingCurvedQRCodes()) { return false; } + if (!versionDefinition()) { return false; } + if (!samplingForVersion()) { return false; } + if (!decodingProcess()) { return false; } + return true; +#else + std::cout << "Library QUIRC is not linked. No decoding is performed. Take it to the OpenCV repository." << std::endl; + return false; +#endif +} + bool decodeQRCode(InputArray in, InputArray points, std::string &decoded_info, OutputArray straight_qrcode) { QRCodeDetector qrcode; @@ -1227,6 +2467,13 @@ bool decodeQRCode(InputArray in, InputArray points, std::string &decoded_info, O return !decoded_info.empty(); } +bool decodeCurvedQRCode(InputArray in, InputArray points, std::string &decoded_info, OutputArray straight_qrcode) +{ + QRCodeDetector qrcode; + decoded_info = qrcode.decodeCurved(in, points, straight_qrcode); + return !decoded_info.empty(); +} + cv::String QRCodeDetector::decode(InputArray in, InputArray points, OutputArray straight_qrcode) { @@ -1241,7 +2488,35 @@ cv::String QRCodeDetector::decode(InputArray in, InputArray points, QRDecode qrdec; qrdec.init(inarr, src_points); - bool ok = qrdec.fullDecodingProcess(); + bool ok = qrdec.straightDecodingProcess(); + + std::string decoded_info = qrdec.getDecodeInformation(); + + if (ok && straight_qrcode.needed()) + { + qrdec.getStraightBarcode().convertTo(straight_qrcode, + straight_qrcode.fixedType() ? + straight_qrcode.type() : CV_32FC2); + } + + return ok ? decoded_info : std::string(); +} + +cv::String QRCodeDetector::decodeCurved(InputArray in, InputArray points, + OutputArray straight_qrcode) +{ + Mat inarr; + if (!checkQRInputImage(in, inarr)) + return std::string(); + + vector src_points; + points.copyTo(src_points); + CV_Assert(src_points.size() == 4); + CV_CheckGT(contourArea(src_points), 0.0, "Invalid QR code source points"); + + QRDecode qrdec; + qrdec.init(inarr, src_points); + bool ok = qrdec.curvedDecodingProcess(); std::string decoded_info = qrdec.getDecodeInformation(); @@ -1278,6 +2553,29 @@ cv::String QRCodeDetector::detectAndDecode(InputArray in, return decoded_info; } +cv::String QRCodeDetector::detectAndDecodeCurved(InputArray in, + OutputArray points_, + OutputArray straight_qrcode) +{ + Mat inarr; + if (!checkQRInputImage(in, inarr)) + { + points_.release(); + return std::string(); + } + + vector points; + bool ok = detect(inarr, points); + if (!ok) + { + points_.release(); + return std::string(); + } + updatePointsResult(points_, points); + std::string decoded_info = decodeCurved(inarr, points, straight_qrcode); + return decoded_info; +} + class QRDetectMulti : public QRDetect { public: @@ -1510,7 +2808,6 @@ void QRDetectMulti::fixationPoints(vector &local_point) Point2f(static_cast(bin_barcode_temp.cols - 1), static_cast(bin_barcode_temp.rows - 1)))); - vector list_area_pnt; list_area_pnt.push_back(current_point); @@ -2241,7 +3538,7 @@ public: for (int i = range.start; i < range.end; i++) { qrdec[i].init(inarr, src_points[i]); - bool ok = qrdec[i].fullDecodingProcess(); + bool ok = qrdec[i].straightDecodingProcess(); if (ok) { decoded_info[i] = qrdec[i].getDecodeInformation(); @@ -2261,7 +3558,7 @@ public: src_points[i][j] /= static_cast(coeff_expansion); } qrdec[i].init(inarr2, src_points[i]); - ok = qrdec[i].fullDecodingProcess(); + ok = qrdec[i].straightDecodingProcess(); if (ok) { decoded_info[i] = qrdec[i].getDecodeInformation(); diff --git a/modules/objdetect/test/test_qrcode.cpp b/modules/objdetect/test/test_qrcode.cpp index a716c837ee..c26cd8a4f2 100644 --- a/modules/objdetect/test/test_qrcode.cpp +++ b/modules/objdetect/test/test_qrcode.cpp @@ -21,6 +21,9 @@ std::string qrcode_images_close[] = { std::string qrcode_images_monitor[] = { "monitor_1.png", "monitor_2.png", "monitor_3.png", "monitor_4.png", "monitor_5.png" }; +std::string qrcode_images_curved[] = { + "curved_1.jpg", "curved_2.jpg", "curved_3.jpg", "curved_4.jpg", "curved_5.jpg", "curved_6.jpg", "curved_7.jpg", "curved_8.jpg" +}; std::string qrcode_images_multiple[] = { "2_qrcodes.png", "3_close_qrcodes.png", "3_qrcodes.png", "4_qrcodes.png", "5_qrcodes.png", "6_qrcodes.png", "7_qrcodes.png", "8_close_qrcodes.png" @@ -137,7 +140,38 @@ TEST(Objdetect_QRCode_Monitor, generate_test_data) file_config << "]"; file_config.release(); } +TEST(Objdetect_QRCode_Curved, generate_test_data) +{ + const std::string root = "qrcode/curved/"; + const std::string dataset_config = findDataFile(root + "dataset_config.json"); + FileStorage file_config(dataset_config, FileStorage::WRITE); + file_config << "test_images" << "["; + size_t images_count = sizeof(qrcode_images_curved) / sizeof(qrcode_images_curved[0]); + for (size_t i = 0; i < images_count; i++) + { + file_config << "{:" << "image_name" << qrcode_images_curved[i]; + std::string image_path = findDataFile(root + qrcode_images_curved[i]); + std::vector corners; + Mat src = imread(image_path, IMREAD_GRAYSCALE), straight_barcode; + std::string decoded_info; + ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; + EXPECT_TRUE(detectQRCode(src, corners)); +#ifdef HAVE_QUIRC + EXPECT_TRUE(decodeCurvedQRCode(src, corners, decoded_info, straight_barcode)); +#endif + file_config << "x" << "[:"; + for (size_t j = 0; j < corners.size(); j++) { file_config << corners[j].x; } + file_config << "]"; + file_config << "y" << "[:"; + for (size_t j = 0; j < corners.size(); j++) { file_config << corners[j].y; } + file_config << "]"; + file_config << "info" << decoded_info; + file_config << "}"; + } + file_config << "]"; + file_config.release(); +} TEST(Objdetect_QRCode_Multi, generate_test_data) { const std::string root = "qrcode/multiple/"; @@ -390,6 +424,66 @@ TEST_P(Objdetect_QRCode_Monitor, regression) } } +typedef testing::TestWithParam< std::string > Objdetect_QRCode_Curved; +TEST_P(Objdetect_QRCode_Curved, regression) +{ + const std::string name_current_image = GetParam(); + const std::string root = "qrcode/curved/"; + const int pixels_error = 3; + + std::string image_path = findDataFile(root + name_current_image); + Mat src = imread(image_path, IMREAD_GRAYSCALE), straight_barcode; + ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; + + std::vector corners; + std::string decoded_info; + QRCodeDetector qrcode; +#ifdef HAVE_QUIRC + decoded_info = qrcode.detectAndDecodeCurved(src, corners, straight_barcode); + ASSERT_FALSE(corners.empty()); + ASSERT_FALSE(decoded_info.empty()); +#else + ASSERT_TRUE(qrcode.detect(src, corners)); +#endif + + const std::string dataset_config = findDataFile(root + "dataset_config.json"); + FileStorage file_config(dataset_config, FileStorage::READ); + ASSERT_TRUE(file_config.isOpened()) << "Can't read validation data: " << dataset_config; + { + FileNode images_list = file_config["test_images"]; + size_t images_count = static_cast(images_list.size()); + ASSERT_GT(images_count, 0u) << "Can't find validation data entries in 'test_images': " << dataset_config; + + for (size_t index = 0; index < images_count; index++) + { + FileNode config = images_list[(int)index]; + std::string name_test_image = config["image_name"]; + if (name_test_image == name_current_image) + { + for (int i = 0; i < 4; i++) + { + int x = config["x"][i]; + int y = config["y"][i]; + EXPECT_NEAR(x, corners[i].x, pixels_error); + EXPECT_NEAR(y, corners[i].y, pixels_error); + } + +#ifdef HAVE_QUIRC + std::string original_info = config["info"]; + EXPECT_EQ(decoded_info, original_info); +#endif + + return; // done + } + } + std::cerr + << "Not found results for '" << name_current_image + << "' image in config file:" << dataset_config << std::endl + << "Re-run tests with enabled UPDATE_QRCODE_TEST_DATA macro to update test data." + << std::endl; + } +} + typedef testing::TestWithParam < std::string > Objdetect_QRCode_Multi; TEST_P(Objdetect_QRCode_Multi, regression) { @@ -478,6 +572,7 @@ TEST_P(Objdetect_QRCode_Multi, regression) INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode, testing::ValuesIn(qrcode_images_name)); INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Close, testing::ValuesIn(qrcode_images_close)); INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Monitor, testing::ValuesIn(qrcode_images_monitor)); +INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Curved, testing::ValuesIn(qrcode_images_curved)); INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Multi, testing::ValuesIn(qrcode_images_multiple)); TEST(Objdetect_QRCode_decodeMulti, decode_regression_16491)