Merge remote-tracking branch 'upstream/3.4' into merge-3.4

pull/22067/head
Alexander Alekhin 3 years ago
commit 583bd1a6e2
  1. 11
      doc/js_tutorials/js_setup/js_usage/js_usage.markdown
  2. 10
      doc/opencv.bib
  3. 5
      modules/calib3d/include/opencv2/calib3d.hpp
  4. 1
      modules/core/src/precomp.hpp
  5. 2
      modules/dnn/src/darknet/darknet_importer.cpp
  6. 22
      modules/dnn/src/tensorflow/tf_graph_simplifier.cpp
  7. 28
      modules/dnn/test/test_tf_importer.cpp
  8. 2
      modules/flann/include/opencv2/flann.hpp
  9. 2
      modules/flann/include/opencv2/flann/dist.h
  10. 53
      modules/highgui/src/window_QT.cpp
  11. 52
      modules/imgproc/src/color_hsv.simd.hpp
  12. 10
      modules/imgproc/src/precomp.hpp
  13. 3
      modules/objdetect/perf/perf_qrcode_pipeline.cpp
  14. 76
      modules/objdetect/src/qrcode.cpp
  15. 2
      modules/objdetect/src/qrcode_encoder.cpp
  16. 77
      modules/objdetect/test/test_qrcode.cpp
  17. 78
      modules/objdetect/test/test_qrcode_encode.cpp

@ -122,11 +122,14 @@ imgElement.onload = function() {
mat.delete(); mat.delete();
}; };
function onOpenCvReady() { var Module = {
document.getElementById('status').innerHTML = 'OpenCV.js is ready.'; // https://emscripten.org/docs/api_reference/module.html#Module.onRuntimeInitialized
} onRuntimeInitialized() {
document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
}
};
</script> </script>
<script async src="opencv.js" onload="onOpenCvReady();" type="text/javascript"></script> <script async src="opencv.js" type="text/javascript"></script>
</body> </body>
</html> </html>
@endcode @endcode

@ -1348,3 +1348,13 @@
year={1991}, year={1991},
publisher={IEEE Computer Society} publisher={IEEE Computer Society}
} }
@article{Kannala2006,
author = {Kannala, Juho and Brandt, Sami},
year = {2006},
month = {09},
pages = {1335-40},
title = {A Generic Camera Model and Calibration Method for Conventional, Wide-Angle, and Fish-Eye Lenses},
volume = {28},
journal = {IEEE transactions on pattern analysis and machine intelligence},
doi = {10.1109/TPAMI.2006.153}
}

@ -430,6 +430,9 @@ R & t \\
\f[u = f_x (x' + \alpha y') + c_x \\ \f[u = f_x (x' + \alpha y') + c_x \\
v = f_y y' + c_y\f] v = f_y y' + c_y\f]
Summary:
Generic camera model @cite Kannala2006 with perspective projection and without distortion correction
@defgroup calib3d_c C API @defgroup calib3d_c C API
@} @}
@ -3883,7 +3886,7 @@ namespace fisheye
CV_EXPORTS_W void estimateNewCameraMatrixForUndistortRectify(InputArray K, InputArray D, const Size &image_size, InputArray R, CV_EXPORTS_W void estimateNewCameraMatrixForUndistortRectify(InputArray K, InputArray D, const Size &image_size, InputArray R,
OutputArray P, double balance = 0.0, const Size& new_size = Size(), double fov_scale = 1.0); OutputArray P, double balance = 0.0, const Size& new_size = Size(), double fov_scale = 1.0);
/** @brief Performs camera calibaration /** @brief Performs camera calibration
@param objectPoints vector of vectors of calibration pattern points in the calibration pattern @param objectPoints vector of vectors of calibration pattern points in the calibration pattern
coordinate space. coordinate space.

@ -365,6 +365,7 @@ extern CV_EXPORTS
bool __termination; // skip some cleanups, because process is terminating bool __termination; // skip some cleanups, because process is terminating
// (for example, if ExitProcess() was already called) // (for example, if ExitProcess() was already called)
CV_EXPORTS
cv::Mutex& getInitializationMutex(); cv::Mutex& getInitializationMutex();
/// @brief Returns timestamp in nanoseconds since program launch /// @brief Returns timestamp in nanoseconds since program launch

@ -207,7 +207,7 @@ Net readNetFromDarknet(const String &cfgFile, const String &darknetModel /*= Str
std::ifstream cfgStream(cfgFile.c_str()); std::ifstream cfgStream(cfgFile.c_str());
if (!cfgStream.is_open()) if (!cfgStream.is_open())
{ {
CV_Error(cv::Error::StsParseError, "Failed to parse NetParameter file: " + std::string(cfgFile)); CV_Error(cv::Error::StsParseError, "Failed to open NetParameter file: " + std::string(cfgFile));
} }
if (darknetModel != String()) if (darknetModel != String())
{ {

@ -990,6 +990,7 @@ void sortByExecutionOrder(tensorflow::GraphDef& net)
nodesMap.insert(std::make_pair(node.name(), i)); nodesMap.insert(std::make_pair(node.name(), i));
} }
CV_CheckEQ(nodesMap.size(), (size_t)net.node_size(), "Node names must be unique");
// Indices of nodes which use specific node as input. // Indices of nodes which use specific node as input.
std::vector<std::vector<int> > edges(nodesMap.size()); std::vector<std::vector<int> > edges(nodesMap.size());
std::vector<int> numRefsToAdd(nodesMap.size(), 0); std::vector<int> numRefsToAdd(nodesMap.size(), 0);
@ -1007,7 +1008,7 @@ void sortByExecutionOrder(tensorflow::GraphDef& net)
nodesMapIt = nodesMap.find(inpName); nodesMapIt = nodesMap.find(inpName);
if (nodesMapIt != nodesMap.end()) if (nodesMapIt != nodesMap.end())
{ {
edges[nodesMapIt->second].push_back(i); edges.at(nodesMapIt->second).push_back(i);
numInputsInGraph += 1; numInputsInGraph += 1;
} }
} }
@ -1019,11 +1020,11 @@ void sortByExecutionOrder(tensorflow::GraphDef& net)
{ {
int numControlEdges = 0; int numControlEdges = 0;
for (int j = 0; j < numInputsInGraph; ++j) for (int j = 0; j < numInputsInGraph; ++j)
numControlEdges += node.input(j)[0] == '^'; numControlEdges += node.input(j).at(0) == '^';
numRefsToAdd[i] = numControlEdges + 1; numRefsToAdd.at(i) = numControlEdges + 1;
} }
else else
numRefsToAdd[i] = numInputsInGraph; numRefsToAdd.at(i) = numInputsInGraph;
} }
} }
@ -1035,17 +1036,16 @@ void sortByExecutionOrder(tensorflow::GraphDef& net)
nodesToAdd.pop_back(); nodesToAdd.pop_back();
permIds.push_back(nodeToAdd); permIds.push_back(nodeToAdd);
CV_Assert(nodeToAdd < edges.size()); for (int i = 0; i < edges.at(nodeToAdd).size(); ++i)
for (int i = 0; i < edges[nodeToAdd].size(); ++i)
{ {
int consumerId = edges[nodeToAdd][i]; int consumerId = edges.at(nodeToAdd).at(i);
if (numRefsToAdd[consumerId] > 0) if (numRefsToAdd.at(consumerId) > 0)
{ {
if (numRefsToAdd[consumerId] == 1) if (numRefsToAdd.at(consumerId) == 1)
nodesToAdd.push_back(consumerId); nodesToAdd.push_back(consumerId);
else else
CV_Assert(numRefsToAdd[consumerId] >= 0); CV_Assert(numRefsToAdd.at(consumerId) >= 0);
numRefsToAdd[consumerId] -= 1; numRefsToAdd.at(consumerId) -= 1;
} }
} }
} }

@ -1719,13 +1719,6 @@ TEST_P(Test_TensorFlow_layers, tf2_permute_nhwc_ncwh)
runTensorFlowNet("tf2_permute_nhwc_ncwh"); runTensorFlowNet("tf2_permute_nhwc_ncwh");
} }
// issue #21852
TEST_P(Test_TensorFlow_layers, tf_graph_simplifier_buffer_overflow)
{
// This just shouldn't segfault, otherwise it's fine
EXPECT_ANY_THROW(readNetFromTensorflow(path("tf_graph_simplifier_buffer_overflow_net.pb")));
}
TEST_P(Test_TensorFlow_layers, squeeze) TEST_P(Test_TensorFlow_layers, squeeze)
{ {
#if defined(INF_ENGINE_RELEASE) #if defined(INF_ENGINE_RELEASE)
@ -1899,4 +1892,25 @@ TEST_P(Test_TensorFlow_nets, EfficientDet)
expectNoFallbacksFromIE(net); expectNoFallbacksFromIE(net);
} }
TEST(Test_TensorFlow_Importer, tf_graph_simplifier_buffer_overflow_21852)
{
uint8_t payload[] = {0x08, 0x08, 0x0a, 0x00, 0x0a, 0x00};
EXPECT_ANY_THROW(readNetFromTensorflow(reinterpret_cast<const char*>(payload), sizeof(payload) / sizeof(payload[0])));
}
// can be triggered with -fsanitize=address
TEST(Test_TensorFlow_Importer, tf_graph_simplifier_buffer_overflow_21947)
{
uint8_t payload[] = {0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00,
0xba, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00,
0x0a, 0xbd, 0x00, 0x1a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xba,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xba, 0x0a, 0x00,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0xba,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00,
0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x2a, 0x00, 0xba, 0x0a, 0x00,
0x0a, 0x00, 0x5d, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x40};
EXPECT_ANY_THROW(readNetFromTensorflow(reinterpret_cast<const char*>(payload), sizeof(payload) / sizeof(payload[0])));
}
} }

@ -116,7 +116,7 @@ cv::flann::L2 - Squared Euclidean distance functor, optimized version.
cv::flann::L1 - Manhattan distance functor, optimized version. cv::flann::L1 - Manhattan distance functor, optimized version.
cv::flann::MinkowskiDistance - The Minkowsky distance functor. cv::flann::MinkowskiDistance - The Minkowski distance functor.
This is highly optimised with loop unrolling. This is highly optimised with loop unrolling.
The computation of squared root at the end is omitted for efficiency. The computation of squared root at the end is omitted for efficiency.

@ -375,7 +375,7 @@ struct MinkowskiDistance
MinkowskiDistance(int order_) : order(order_) {} MinkowskiDistance(int order_) : order(order_) {}
/** /**
* Compute the Minkowsky (L_p) distance between two vectors. * Compute the Minkowski (L_p) distance between two vectors.
* *
* This is highly optimised, with loop unrolling, as it is one * This is highly optimised, with loop unrolling, as it is one
* of the most expensive inner loops. * of the most expensive inner loops.

@ -2198,23 +2198,58 @@ void CvWindow::displayPropertiesWin()
global_control_panel->hide(); global_control_panel->hide();
} }
static bool isTranslatableKey(Qt::Key key)
{
// https://github.com/opencv/opencv/issues/21899
// https://doc.qt.io/qt-5/qt.html#Key-enum
// https://doc.qt.io/qt-6/qt.html#Key-enum
// https://github.com/qt/qtbase/blob/dev/src/testlib/qasciikey.cpp
bool ret = false;
switch ( key )
{
// Special keys
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
case Qt::Key_Backspace:
case Qt::Key_Enter:
case Qt::Key_Return:
ret = true;
break;
// latin-1 keys.
default:
ret = (
( ( Qt::Key_Space <= key ) && ( key <= Qt::Key_AsciiTilde ) ) // 0x20--0x7e
||
( ( Qt::Key_nobreakspace <= key ) && ( key <= Qt::Key_ssharp ) ) // 0x0a0--0x0de
||
( key == Qt::Key_division ) // 0x0f7
||
( key == Qt::Key_ydiaeresis ) // 0x0ff
);
break;
}
return ret;
}
//Need more test here ! //Need more test here !
void CvWindow::keyPressEvent(QKeyEvent *evnt) void CvWindow::keyPressEvent(QKeyEvent *evnt)
{ {
//see http://doc.trolltech.com/4.6/qt.html#Key-enum
int key = evnt->key(); int key = evnt->key();
const Qt::Key qtkey = static_cast<Qt::Key>(key);
Qt::Key qtkey = static_cast<Qt::Key>(key); if ( isTranslatableKey( qtkey ) )
char asciiCode = QTest::keyToAscii(qtkey); key = static_cast<int>( QTest::keyToAscii( qtkey ) );
if (asciiCode != 0) else
key = static_cast<int>(asciiCode); key = evnt->nativeVirtualKey(); //same codes as returned by GTK-based backend
else
key = evnt->nativeVirtualKey(); //same codes as returned by GTK-based backend
//control plus (Z, +, -, up, down, left, right) are used for zoom/panning functions //control plus (Z, +, -, up, down, left, right) are used for zoom/panning functions
if (evnt->modifiers() != Qt::ControlModifier) if (evnt->modifiers() != Qt::ControlModifier)
{ {
mutexKey.lock(); mutexKey.lock();
last_key = key; last_key = key;
mutexKey.unlock(); mutexKey.unlock();

@ -39,36 +39,51 @@ struct RGB2HSV_b
: srccn(_srccn), blueIdx(_blueIdx), hrange(_hrange) : srccn(_srccn), blueIdx(_blueIdx), hrange(_hrange)
{ {
CV_Assert( hrange == 180 || hrange == 256 ); CV_Assert( hrange == 180 || hrange == 256 );
const TablesSingleton& global_tables = TablesSingleton::getInstance();
hdiv_table_ = hrange == 180 ? global_tables.hdiv_table180 : global_tables.hdiv_table256;
sdiv_table_ = global_tables.sdiv_table;
} }
void operator()(const uchar* src, uchar* dst, int n) const struct TablesSingleton
{ {
CV_INSTRUMENT_REGION(); int sdiv_table[256];
int hdiv_table180[256];
int i, bidx = blueIdx, scn = srccn; int hdiv_table256[256];
const int hsv_shift = 12;
static int sdiv_table[256]; protected:
static int hdiv_table180[256]; TablesSingleton()
static int hdiv_table256[256];
static volatile bool initialized = false;
int hr = hrange;
const int* hdiv_table = hr == 180 ? hdiv_table180 : hdiv_table256;
if( !initialized )
{ {
const int hsv_shift = 12;
sdiv_table[0] = hdiv_table180[0] = hdiv_table256[0] = 0; sdiv_table[0] = hdiv_table180[0] = hdiv_table256[0] = 0;
for( i = 1; i < 256; i++ ) for (int i = 1; i < 256; i++)
{ {
sdiv_table[i] = saturate_cast<int>((255 << hsv_shift)/(1.*i)); sdiv_table[i] = saturate_cast<int>((255 << hsv_shift)/(1.*i));
hdiv_table180[i] = saturate_cast<int>((180 << hsv_shift)/(6.*i)); hdiv_table180[i] = saturate_cast<int>((180 << hsv_shift)/(6.*i));
hdiv_table256[i] = saturate_cast<int>((256 << hsv_shift)/(6.*i)); hdiv_table256[i] = saturate_cast<int>((256 << hsv_shift)/(6.*i));
} }
initialized = true;
} }
public:
static TablesSingleton& getInstance()
{
static TablesSingleton g_tables;
return g_tables;
}
};
void operator()(const uchar* src, uchar* dst, int n) const
{
CV_INSTRUMENT_REGION();
i = 0; int bidx = blueIdx, scn = srccn;
const int hsv_shift = 12;
int hr = hrange;
const int* hdiv_table/*[256]*/ = hdiv_table_;
const int* sdiv_table/*[256]*/ = sdiv_table_;
int i = 0;
#if CV_SIMD #if CV_SIMD
const int vsize = v_uint8::nlanes; const int vsize = v_uint8::nlanes;
@ -231,6 +246,9 @@ struct RGB2HSV_b
} }
int srccn, blueIdx, hrange; int srccn, blueIdx, hrange;
const int* hdiv_table_/*[256]*/;
const int* sdiv_table_/*[256]*/;
}; };

@ -115,4 +115,12 @@ inline bool isStorageOrMat(void * arr)
CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* nor CvMat*" ); CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* nor CvMat*" );
} }
#endif /*__OPENCV_CV_INTERNAL_H_*/
namespace cv {
CV_EXPORTS
cv::Mutex& getInitializationMutex(); // defined in core module
} // namespace cv
#endif /*__OPENCV_PRECOMP_H__*/

@ -106,10 +106,11 @@ PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti)
INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Objdetect_QRCode, INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Objdetect_QRCode,
::testing::Values( ::testing::Values(
"version_1_down.jpg", "version_1_left.jpg", "version_1_right.jpg", "version_1_up.jpg", "version_1_top.jpg", "version_1_down.jpg", "version_1_left.jpg", "version_1_right.jpg", "version_1_up.jpg", "version_1_top.jpg",
"version_5_down.jpg", "version_5_left.jpg", "version_5_right.jpg", "version_5_up.jpg", "version_5_top.jpg", "version_5_down.jpg", "version_5_left.jpg",/*version_5_right.jpg*/ "version_5_up.jpg", "version_5_top.jpg",
"russian.jpg", "kanji.jpg", "link_github_ocv.jpg", "link_ocv.jpg", "link_wiki_cv.jpg" "russian.jpg", "kanji.jpg", "link_github_ocv.jpg", "link_ocv.jpg", "link_wiki_cv.jpg"
) )
); );
// version_5_right.jpg DISABLED after tile fix, PR #22025
INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Objdetect_QRCode_Multi, INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Objdetect_QRCode_Multi,
::testing::Values( ::testing::Values(

@ -1061,6 +1061,15 @@ protected:
}; };
}; };
float static getMinSideLen(const vector<Point2f> &points) {
CV_Assert(points.size() == 4ull);
double res = norm(points[1]-points[0]);
for (size_t i = 1ull; i < points.size(); i++) {
res = min(res, norm(points[i]-points[(i+1ull) % points.size()]));
}
return static_cast<float>(res);
}
void QRDecode::init(const Mat &src, const vector<Point2f> &points) void QRDecode::init(const Mat &src, const vector<Point2f> &points)
{ {
CV_TRACE_FUNCTION(); CV_TRACE_FUNCTION();
@ -1072,7 +1081,7 @@ void QRDecode::init(const Mat &src, const vector<Point2f> &points)
original_points = bbox; original_points = bbox;
version = 0; version = 0;
version_size = 0; version_size = 0;
test_perspective_size = 251; test_perspective_size = max(getMinSideLen(points)+1.f, 251.f);
result_info = ""; result_info = "";
} }
@ -2088,7 +2097,7 @@ bool QRDecode::straightenQRCodeInParts()
{ {
return false; return false;
} }
float perspective_curved_size = 251.0; float perspective_curved_size = max(getMinSideLen(original_points)+1.f, 251.f);;
const Size temporary_size(cvRound(perspective_curved_size), cvRound(perspective_curved_size)); const Size temporary_size(cvRound(perspective_curved_size), cvRound(perspective_curved_size));
float dist = perspective_curved_size / (number_pnts_to_cut - 1); float dist = perspective_curved_size / (number_pnts_to_cut - 1);
@ -2359,9 +2368,9 @@ bool QRDecode::versionDefinition()
bool QRDecode::samplingForVersion() bool QRDecode::samplingForVersion()
{ {
CV_TRACE_FUNCTION(); CV_TRACE_FUNCTION();
const double multiplyingFactor = (version < 3) ? 1 : const double multiplyingFactor = (version < 3) ? 1. :
(version == 3) ? 1.5 : (version == 3) ? 2. :
version * (version + 1); 3.;
const Size newFactorSize( const Size newFactorSize(
cvRound(no_border_intermediate.size().width * multiplyingFactor), cvRound(no_border_intermediate.size().width * multiplyingFactor),
cvRound(no_border_intermediate.size().height * multiplyingFactor)); cvRound(no_border_intermediate.size().height * multiplyingFactor));
@ -2370,45 +2379,38 @@ bool QRDecode::samplingForVersion()
const int delta_rows = cvRound((postIntermediate.rows * 1.0) / version_size); const int delta_rows = cvRound((postIntermediate.rows * 1.0) / version_size);
const int delta_cols = cvRound((postIntermediate.cols * 1.0) / version_size); const int delta_cols = cvRound((postIntermediate.cols * 1.0) / version_size);
// number of elements in the tail
const int skipped_rows = postIntermediate.rows - delta_rows * version_size;
const int skipped_cols = postIntermediate.cols - delta_cols * version_size;
vector<double> listFrequencyElem; vector<int> deltas_rows(version_size, delta_rows);
for (int r = 0; r < postIntermediate.rows; r += delta_rows) vector<int> deltas_cols(version_size, delta_cols);
{
for (int c = 0; c < postIntermediate.cols; c += delta_cols) for (int i = 0; i < abs(skipped_rows); i++) {
{ // fix deltas_rows at each skip_step
const double skip_step = static_cast<double>(version_size)/abs(skipped_rows);
const int corrected_index = static_cast<int>(i*skip_step + skip_step/2);
deltas_rows[corrected_index] += skipped_rows > 0 ? 1 : -1;
}
for (int i = 0; i < abs(skipped_cols); i++) {
// fix deltas_cols at each skip_step
const double skip_step = static_cast<double>(version_size)/abs(skipped_cols);
const int corrected_index = static_cast<int>(i*skip_step + skip_step/2);
deltas_cols[corrected_index] += skipped_cols > 0 ? 1 : -1;
}
const double totalFrequencyElem = countNonZero(postIntermediate) / static_cast<double>(postIntermediate.total());
straight = Mat(Size(version_size, version_size), CV_8UC1, Scalar(0));
for (int r = 0, i = 0; i < version_size; r += deltas_rows[i], i++) {
for (int c = 0, j = 0; j < version_size; c += deltas_cols[j], j++) {
Mat tile = postIntermediate( Mat tile = postIntermediate(
Range(r, min(r + delta_rows, postIntermediate.rows)), Range(r, min(r + delta_rows, postIntermediate.rows)),
Range(c, min(c + delta_cols, postIntermediate.cols))); Range(c, min(c + delta_cols, postIntermediate.cols)));
const double frequencyElem = (countNonZero(tile) * 1.0) / tile.total(); const double frequencyElem = (countNonZero(tile) * 1.0) / tile.total();
listFrequencyElem.push_back(frequencyElem); straight.ptr<uint8_t>(i)[j] = (frequencyElem < totalFrequencyElem) ? 0 : 255;
} }
} }
double dispersionEFE = std::numeric_limits<double>::max();
double experimentalFrequencyElem = 0;
for (double expVal = 0; expVal < 1; expVal+=0.001)
{
double testDispersionEFE = 0.0;
for (size_t i = 0; i < listFrequencyElem.size(); i++)
{
testDispersionEFE += (listFrequencyElem[i] - expVal) *
(listFrequencyElem[i] - expVal);
}
testDispersionEFE /= (listFrequencyElem.size() - 1);
if (dispersionEFE > testDispersionEFE)
{
dispersionEFE = testDispersionEFE;
experimentalFrequencyElem = expVal;
}
}
straight = Mat(Size(version_size, version_size), CV_8UC1, Scalar(0));
for (int r = 0; r < version_size * version_size; r++)
{
int i = r / straight.cols;
int j = r % straight.cols;
straight.ptr<uint8_t>(i)[j] = (listFrequencyElem[r] < experimentalFrequencyElem) ? 0 : 255;
}
return true; return true;
} }

@ -975,7 +975,7 @@ void QRCodeEncoderImpl::writeReservedArea()
original.at<uint8_t>(x, y) = INVALID_REGION_VALUE; original.at<uint8_t>(x, y) = INVALID_REGION_VALUE;
if (version_level >= 7) if (version_level >= 7)
{ {
for (int i = 0; i <= 6; i++) for (int i = 0; i <= 5; i++)
{ {
for (int j = version_size - 11; j <= version_size - 8; j++) for (int j = version_size - 11; j <= version_size - 8; j++)
{ {

@ -11,8 +11,9 @@ std::string qrcode_images_name[] = {
"version_2_down.jpg", "version_2_left.jpg", "version_2_right.jpg", "version_2_up.jpg", "version_2_top.jpg", "version_2_down.jpg", "version_2_left.jpg", "version_2_right.jpg", "version_2_up.jpg", "version_2_top.jpg",
"version_3_down.jpg", "version_3_left.jpg", "version_3_right.jpg", "version_3_up.jpg", "version_3_top.jpg", "version_3_down.jpg", "version_3_left.jpg", "version_3_right.jpg", "version_3_up.jpg", "version_3_top.jpg",
"version_4_down.jpg", "version_4_left.jpg", "version_4_right.jpg", "version_4_up.jpg", "version_4_top.jpg", "version_4_down.jpg", "version_4_left.jpg", "version_4_right.jpg", "version_4_up.jpg", "version_4_top.jpg",
"version_5_down.jpg", "version_5_left.jpg", "version_5_right.jpg", "version_5_up.jpg", "version_5_top.jpg", "version_5_down.jpg", "version_5_left.jpg"/*"version_5_right.jpg"*/,
"russian.jpg", "kanji.jpg", "link_github_ocv.jpg", "link_ocv.jpg", "link_wiki_cv.jpg" "russian.jpg", "kanji.jpg", "link_github_ocv.jpg", "link_ocv.jpg", "link_wiki_cv.jpg"
// version_5_right.jpg DISABLED after tile fix, PR #22025
}; };
std::string qrcode_images_close[] = { std::string qrcode_images_close[] = {
@ -22,8 +23,9 @@ std::string qrcode_images_monitor[] = {
"monitor_1.png", "monitor_2.png", "monitor_3.png", "monitor_4.png", "monitor_5.png" "monitor_1.png", "monitor_2.png", "monitor_3.png", "monitor_4.png", "monitor_5.png"
}; };
std::string qrcode_images_curved[] = { 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" "curved_1.jpg", "curved_2.jpg", "curved_3.jpg", /*"curved_4.jpg",*/ "curved_5.jpg", /*"curved_6.jpg",*/ "curved_7.jpg", "curved_8.jpg"
}; };
// curved_4.jpg, curved_6.jpg DISABLED after tile fix, PR #22025
std::string qrcode_images_multiple[] = { std::string qrcode_images_multiple[] = {
"2_qrcodes.png", "3_close_qrcodes.png", "3_qrcodes.png", "4_qrcodes.png", "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" "5_qrcodes.png", "6_qrcodes.png", "7_qrcodes.png", "8_close_qrcodes.png"
@ -683,7 +685,78 @@ TEST(Objdetect_QRCode_basic, not_found_qrcode)
#endif #endif
} }
TEST(Objdetect_QRCode_detect, detect_regression_21287)
{
const std::string name_current_image = "issue_21287.png";
const std::string root = "qrcode/";
std::string image_path = findDataFile(root + name_current_image);
Mat src = imread(image_path);
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
QRCodeDetector qrcode;
std::vector<Point> corners;
Mat straight_barcode;
cv::String decoded_info;
EXPECT_TRUE(qrcode.detect(src, corners));
EXPECT_TRUE(!corners.empty());
#ifdef HAVE_QUIRC
EXPECT_NO_THROW(qrcode.decode(src, corners, straight_barcode));
#endif
}
// @author Kumataro, https://github.com/Kumataro
TEST(Objdetect_QRCode_decode, decode_regression_21929)
{
const cv::String expect_msg = "OpenCV";
Mat qrImg;
QRCodeEncoder::Params params;
params.version = 8; // 49x49
Ptr<QRCodeEncoder> qrcode_enc = cv::QRCodeEncoder::create(params);;
qrcode_enc->encode(expect_msg, qrImg);
Mat src;
cv::resize(qrImg, src, Size(200,200), 1.0, 1.0, INTER_NEAREST);
QRCodeDetector qrcode;
std::vector<Point> corners;
Mat straight_barcode;
EXPECT_TRUE(qrcode.detect(src, corners));
EXPECT_TRUE(!corners.empty());
#ifdef HAVE_QUIRC
cv::String decoded_msg;
EXPECT_NO_THROW(decoded_msg = qrcode.decode(src, corners, straight_barcode));
ASSERT_FALSE(straight_barcode.empty()) << "Can't decode qrimage.";
EXPECT_EQ(expect_msg, decoded_msg);
#endif
}
TEST(Objdetect_QRCode_decode, decode_regression_version_25)
{
const cv::String expect_msg = "OpenCV";
Mat qrImg;
QRCodeEncoder::Params params;
params.version = 25; // 117x117
Ptr<QRCodeEncoder> qrcode_enc = cv::QRCodeEncoder::create(params);;
qrcode_enc->encode(expect_msg, qrImg);
Mat src;
cv::resize(qrImg, src, qrImg.size()*3, 1.0, 1.0, INTER_NEAREST);
QRCodeDetector qrcode;
std::vector<Point> corners;
Mat straight_barcode;
EXPECT_TRUE(qrcode.detect(src, corners));
EXPECT_TRUE(!corners.empty());
#ifdef HAVE_QUIRC
cv::String decoded_msg;
EXPECT_NO_THROW(decoded_msg = qrcode.decode(src, corners, straight_barcode));
ASSERT_FALSE(straight_barcode.empty()) << "Can't decode qrimage.";
EXPECT_EQ(expect_msg, decoded_msg);
#endif
}
#endif // UPDATE_QRCODE_TEST_DATA #endif // UPDATE_QRCODE_TEST_DATA

@ -433,4 +433,82 @@ TEST(Objdetect_QRCode_Encode_Decode_Structured_Append, DISABLED_regression)
#endif // UPDATE_QRCODE_TEST_DATA #endif // UPDATE_QRCODE_TEST_DATA
TEST(Objdetect_QRCode_Encode_Decode, regression_issue22029)
{
const cv::String msg = "OpenCV";
const int min_version = 1;
const int max_version = 40;
for ( int v = min_version ; v <= max_version ; v++ )
{
SCOPED_TRACE(cv::format("version=%d",v));
Mat qrimg;
QRCodeEncoder::Params params;
params.version = v;
Ptr<QRCodeEncoder> qrcode_enc = cv::QRCodeEncoder::create(params);
qrcode_enc->encode(msg, qrimg);
const int white_margin = 2;
const int finder_width = 7;
const int timing_pos = white_margin + 6;
int i;
// Horizontal Check
// (1) White margin(Left)
for(i = 0; i < white_margin ; i++ )
{
ASSERT_EQ((uint8_t)255, qrimg.at<uint8_t>(i, timing_pos)) << "i=" << i;
}
// (2) Finder pattern(Left)
for( ; i < white_margin + finder_width ; i++ )
{
ASSERT_EQ((uint8_t)0, qrimg.at<uint8_t>(i, timing_pos)) << "i=" << i;
}
// (3) Timing pattern
for( ; i < qrimg.rows - finder_width - white_margin; i++ )
{
ASSERT_EQ((uint8_t)(i % 2 == 0)?0:255, qrimg.at<uint8_t>(i, timing_pos)) << "i=" << i;
}
// (4) Finder pattern(Right)
for( ; i < qrimg.rows - white_margin; i++ )
{
ASSERT_EQ((uint8_t)0, qrimg.at<uint8_t>(i, timing_pos)) << "i=" << i;
}
// (5) White margin(Right)
for( ; i < qrimg.rows ; i++ )
{
ASSERT_EQ((uint8_t)255, qrimg.at<uint8_t>(i, timing_pos)) << "i=" << i;
}
// Vertical Check
// (1) White margin(Top)
for(i = 0; i < white_margin ; i++ )
{
ASSERT_EQ((uint8_t)255, qrimg.at<uint8_t>(timing_pos, i)) << "i=" << i;
}
// (2) Finder pattern(Top)
for( ; i < white_margin + finder_width ; i++ )
{
ASSERT_EQ((uint8_t)0, qrimg.at<uint8_t>(timing_pos, i)) << "i=" << i;
}
// (3) Timing pattern
for( ; i < qrimg.rows - finder_width - white_margin; i++ )
{
ASSERT_EQ((uint8_t)(i % 2 == 0)?0:255, qrimg.at<uint8_t>(timing_pos, i)) << "i=" << i;
}
// (4) Finder pattern(Bottom)
for( ; i < qrimg.rows - white_margin; i++ )
{
ASSERT_EQ((uint8_t)0, qrimg.at<uint8_t>(timing_pos, i)) << "i=" << i;
}
// (5) White margin(Bottom)
for( ; i < qrimg.rows ; i++ )
{
ASSERT_EQ((uint8_t)255, qrimg.at<uint8_t>(timing_pos, i)) << "i=" << i;
}
}
}
}} // namespace }} // namespace

Loading…
Cancel
Save