From: Alexander Alekhin Date: Tue, 28 Jan 2020 10:16:03 +0000 (+0300) Subject: Merge remote-tracking branch 'upstream/3.4' into merge-3.4 X-Git-Tag: submit/tizen/20210224.033012~2^2~327 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=560f85f8e51226340e5da4e0fa0047f0920e663e;p=platform%2Fupstream%2Fopencv.git Merge remote-tracking branch 'upstream/3.4' into merge-3.4 --- 560f85f8e51226340e5da4e0fa0047f0920e663e diff --cc modules/dnn/include/opencv2/dnn/version.hpp index 6dd2c39,0000000..152c8b2 mode 100644,000000..100644 --- a/modules/dnn/include/opencv2/dnn/version.hpp +++ b/modules/dnn/include/opencv2/dnn/version.hpp @@@ -1,21 -1,0 +1,21 @@@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_DNN_VERSION_HPP +#define OPENCV_DNN_VERSION_HPP + +/// Use with major OpenCV version only. - #define OPENCV_DNN_API_VERSION 20191202 ++#define OPENCV_DNN_API_VERSION 20200128 + +#if !defined CV_DOXYGEN && !defined CV_STATIC_ANALYSIS && !defined CV_DNN_DONT_ADD_INLINE_NS +#define CV__DNN_INLINE_NS __CV_CAT(dnn4_v, OPENCV_DNN_API_VERSION) +#define CV__DNN_INLINE_NS_BEGIN namespace CV__DNN_INLINE_NS { +#define CV__DNN_INLINE_NS_END } +namespace cv { namespace dnn { namespace CV__DNN_INLINE_NS { } using namespace CV__DNN_INLINE_NS; }} +#else +#define CV__DNN_INLINE_NS_BEGIN +#define CV__DNN_INLINE_NS_END +#endif + +#endif // OPENCV_DNN_VERSION_HPP diff --cc modules/dnn/src/layers/slice_layer.cpp index 8d035fb,662ade8..2d70cc1 --- a/modules/dnn/src/layers/slice_layer.cpp +++ b/modules/dnn/src/layers/slice_layer.cpp @@@ -120,8 -114,8 +120,9 @@@ public virtual bool supportBackend(int backendId) CV_OVERRIDE { return backendId == DNN_BACKEND_OPENCV || + backendId == DNN_BACKEND_CUDA || - ((backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) && + (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && sliceRanges.size() == 1) || + (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && #ifdef HAVE_INF_ENGINE INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R1) && #endif diff --cc modules/dnn/src/layers/softmax_layer.cpp index 500665f,d7ffef0..8e45fee --- a/modules/dnn/src/layers/softmax_layer.cpp +++ b/modules/dnn/src/layers/softmax_layer.cpp @@@ -98,10 -91,9 +98,11 @@@ public virtual bool supportBackend(int backendId) CV_OVERRIDE { return backendId == DNN_BACKEND_OPENCV || + backendId == DNN_BACKEND_CUDA || (backendId == DNN_BACKEND_HALIDE && haveHalide() && axisRaw == 1) || - ((backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) && haveInfEngine() && !logSoftMax) || + backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH || - (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && haveInfEngine() && !logSoftMax); ++ (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && haveInfEngine() && !logSoftMax) || + (backendId == DNN_BACKEND_VKCOM && haveVulkan()); } #ifdef HAVE_OPENCL diff --cc modules/imgproc/CMakeLists.txt index 0c7b326,a74c883..bf66abb --- a/modules/imgproc/CMakeLists.txt +++ b/modules/imgproc/CMakeLists.txt @@@ -9,4 -9,6 +9,5 @@@ ocv_add_dispatched_file(color_yuv SSE2 ocv_add_dispatched_file(median_blur SSE2 SSE4_1 AVX2) ocv_add_dispatched_file(morph SSE2 SSE4_1 AVX2) ocv_add_dispatched_file(smooth SSE2 SSE4_1 AVX2) + ocv_add_dispatched_file(sumpixels SSE2 AVX2 AVX512_SKX) -ocv_add_dispatched_file(undistort SSE2 AVX2) ocv_define_module(imgproc opencv_core WRAP java python js) diff --cc modules/java/generator/gen_java.py index dc47e6f,ea1b89e..543b8b5 --- a/modules/java/generator/gen_java.py +++ b/modules/java/generator/gen_java.py @@@ -70,16 -70,7 +70,25 @@@ type_dict = "size_t" : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" }, "__int64" : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" }, "int64" : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" }, - "double[]": { "j_type" : "double[]", "jn_type" : "double[]", "jni_type" : "jdoubleArray", "suffix" : "_3D" } + "double[]": { "j_type" : "double[]", "jn_type" : "double[]", "jni_type" : "jdoubleArray", "suffix" : "_3D" }, + 'string' : { # std::string, see "String" in modules/core/misc/java/gen_dict.json + 'j_type': 'String', + 'jn_type': 'String', + 'jni_name': 'n_%(n)s', + 'jni_type': 'jstring', + 'jni_var': 'const char* utf_%(n)s = env->GetStringUTFChars(%(n)s, 0); std::string n_%(n)s( utf_%(n)s ? utf_%(n)s : "" ); env->ReleaseStringUTFChars(%(n)s, utf_%(n)s)', + 'suffix': 'Ljava_lang_String_2', + 'j_import': 'java.lang.String' + }, ++ 'vector_string': { # std::vector, see "vector_String" in modules/core/misc/java/gen_dict.json ++ 'j_type': 'List', ++ 'jn_type': 'List', ++ 'jni_type': 'jobject', ++ 'jni_var': 'std::vector< std::string > %(n)s', ++ 'suffix': 'Ljava_util_List', ++ 'v_type': 'string', ++ 'j_import': 'java.lang.String' ++ }, } # Defines a rule to add extra prefixes for names from specific namespaces. diff --cc modules/java/generator/src/cpp/listconverters.cpp index d1f45c6,d1f45c6..94702ac --- a/modules/java/generator/src/cpp/listconverters.cpp +++ b/modules/java/generator/src/cpp/listconverters.cpp @@@ -57,3 -57,3 +57,54 @@@ void Copy_vector_String_to_List(JNIEnv env->DeleteLocalRef(element); } } ++ ++ ++jobject vector_string_to_List(JNIEnv* env, std::vector& vs) { ++ ++ static jclass juArrayList = ARRAYLIST(env); ++ static jmethodID m_create = CONSTRUCTOR(env, juArrayList); ++ jmethodID m_add = LIST_ADD(env, juArrayList); ++ ++ jobject result = env->NewObject(juArrayList, m_create, vs.size()); ++ for (std::vector::iterator it = vs.begin(); it != vs.end(); ++it) { ++ jstring element = env->NewStringUTF((*it).c_str()); ++ env->CallBooleanMethod(result, m_add, element); ++ env->DeleteLocalRef(element); ++ } ++ return result; ++} ++ ++std::vector List_to_vector_string(JNIEnv* env, jobject list) ++{ ++ static jclass juArrayList = ARRAYLIST(env); ++ jmethodID m_size = LIST_SIZE(env,juArrayList); ++ jmethodID m_get = LIST_GET(env, juArrayList); ++ ++ jint len = env->CallIntMethod(list, m_size); ++ std::vector result; ++ result.reserve(len); ++ for (jint i=0; i(env->CallObjectMethod(list, m_get, i)); ++ const char* pchars = env->GetStringUTFChars(element, NULL); ++ result.push_back(pchars); ++ env->ReleaseStringUTFChars(element, pchars); ++ env->DeleteLocalRef(element); ++ } ++ return result; ++} ++ ++void Copy_vector_string_to_List(JNIEnv* env, std::vector& vs, jobject list) ++{ ++ static jclass juArrayList = ARRAYLIST(env); ++ jmethodID m_clear = LIST_CLEAR(env, juArrayList); ++ jmethodID m_add = LIST_ADD(env, juArrayList); ++ ++ env->CallVoidMethod(list, m_clear); ++ for (std::vector::iterator it = vs.begin(); it != vs.end(); ++it) ++ { ++ jstring element = env->NewStringUTF((*it).c_str()); ++ env->CallBooleanMethod(list, m_add, element); ++ env->DeleteLocalRef(element); ++ } ++} diff --cc modules/java/generator/src/cpp/listconverters.hpp index 4d63d9d,4d63d9d..0ffd934 --- a/modules/java/generator/src/cpp/listconverters.hpp +++ b/modules/java/generator/src/cpp/listconverters.hpp @@@ -16,4 -16,4 +16,11 @@@ std::vector List_to_vector_ void Copy_vector_String_to_List(JNIEnv* env, std::vector& vs, jobject list); ++ ++jobject vector_string_to_List(JNIEnv* env, std::vector& vs); ++ ++std::vector List_to_vector_string(JNIEnv* env, jobject list); ++ ++void Copy_vector_string_to_List(JNIEnv* env, std::vector& vs, jobject list); ++ #endif /* LISTCONVERTERS_HPP */ diff --cc modules/objdetect/include/opencv2/objdetect.hpp index 1cbbf84,5bd6a11..097c592 --- a/modules/objdetect/include/opencv2/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect.hpp @@@ -702,11 -705,44 +702,44 @@@ public /** @brief Both detects and decodes QR code @param img grayscale or color (BGR) image containing QR code. - @param points opiotnal output array of vertices of the found QR code quadrangle. Will be empty if not found. + @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 detectAndDecode(InputArray img, OutputArray points=noArray(), + CV_WRAP std::string detectAndDecode(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. + */ + CV_WRAP + bool detectMulti(InputArray img, OutputArray points) const; + + /** @brief Decodes QR codes in image once it's found by the detect() method. + @param img grayscale or color (BGR) image containing QR codes. + @param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded. + @param points vector of Quadrangle vertices found by detect() method (or some other algorithm). + @param straight_qrcode The optional output vector of images containing rectified and binarized QR codes + */ + CV_WRAP + bool decodeMulti( + InputArray img, InputArray points, - CV_OUT std::vector& decoded_info, ++ CV_OUT std::vector& decoded_info, + OutputArrayOfArrays straight_qrcode = noArray() + ) const; + + /** @brief Both detects and decodes QR codes + @param img grayscale or color (BGR) image containing QR codes. + @param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded. + @param points optional output vector of vertices of the found QR code quadrangles. Will be empty if not found. + @param straight_qrcode The optional output vector of images containing rectified and binarized QR codes + */ + CV_WRAP + bool detectAndDecodeMulti( - InputArray img, CV_OUT std::vector& decoded_info, ++ InputArray img, CV_OUT std::vector& decoded_info, + OutputArray points = noArray(), + OutputArrayOfArrays straight_qrcode = noArray() + ) const; + protected: struct Impl; Ptr p; diff --cc modules/objdetect/src/qrcode.cpp index 925f288,3467803..a80fa16 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@@ -1158,22 -1212,19 +1203,12 @@@ bool QRDecode::fullDecodingProcess( #endif } -bool decodeQRCode(InputArray in, InputArray points, std::string &decoded_info, OutputArray straight_qrcode) -{ - QRCodeDetector qrcode; - decoded_info = qrcode.decode(in, points, straight_qrcode); - return !decoded_info.empty(); -} - -cv::String QRCodeDetector::decode(InputArray in, InputArray points, - OutputArray straight_qrcode) +std::string QRCodeDetector::decode(InputArray in, InputArray points, + OutputArray straight_qrcode) { - Mat inarr = in.getMat(); - CV_Assert(!inarr.empty()); - CV_Assert(inarr.depth() == CV_8U); - if (inarr.cols <= 20 || inarr.rows <= 20) - return cv::String(); // image data is not enough for providing reliable results - - int incn = inarr.channels(); - if( incn == 3 || incn == 4 ) - { - Mat gray; - cvtColor(inarr, gray, COLOR_BGR2GRAY); - inarr = gray; - } + Mat inarr; + if (!checkQRInputImage(in, inarr)) + return std::string(); vector src_points; points.copyTo(src_points); @@@ -1196,22 -1247,15 +1231,15 @@@ return ok ? decoded_info : std::string(); } -cv::String QRCodeDetector::detectAndDecode(InputArray in, - OutputArray points_, - OutputArray straight_qrcode) +std::string QRCodeDetector::detectAndDecode(InputArray in, + OutputArray points_, + OutputArray straight_qrcode) { - Mat inarr = in.getMat(); - CV_Assert(!inarr.empty()); - CV_Assert(inarr.depth() == CV_8U); - if (inarr.cols <= 20 || inarr.rows <= 20) - return cv::String(); // image data is not enough for providing reliable results - - int incn = inarr.channels(); - if( incn == 3 || incn == 4 ) + Mat inarr; + if (!checkQRInputImage(in, inarr)) { - Mat gray; - cvtColor(inarr, gray, COLOR_BGR2GRAY); - inarr = gray; + points_.release(); + return std::string(); } vector points; @@@ -1229,4 -1270,1131 +1254,1112 @@@ return decoded_info; } ++ + class QRDetectMulti : public QRDetect + { + public: + void init(const Mat& src, double eps_vertical_ = 0.2, double eps_horizontal_ = 0.1); + bool localization(); + bool computeTransformationPoints(const size_t cur_ind); + vector< vector < Point2f > > getTransformationPoints() { return transformation_points;} + + protected: + int findNumberLocalizationPoints(vector& tmp_localization_points); + void findQRCodeContours(vector& tmp_localization_points, vector< vector< Point2f > >& true_points_group, const int& num_qrcodes); + bool checkSets(vector >& true_points_group, vector >& true_points_group_copy, + vector& tmp_localization_points); + void deleteUsedPoints(vector >& true_points_group, vector >& loc, + vector& tmp_localization_points); + void fixationPoints(vector &local_point); + bool checkPoints(const vector& quadrangle_points); + bool checkPointsInsideQuadrangle(const vector& quadrangle_points); + bool checkPointsInsideTriangle(const vector& triangle_points); + + Mat bin_barcode_fullsize, bin_barcode_temp; + vector not_resized_loc_points; + vector resized_loc_points; + vector< vector< Point2f > > localization_points, transformation_points; + struct compareDistanse_y + { + bool operator()(const Point2f& a, const Point2f& b) const + { + return a.y < b.y; + } + }; + struct compareSquare + { + const vector& points; + compareSquare(const vector& points_) : points(points_) {} + bool operator()(const Vec3i& a, const Vec3i& b) const; + }; + Mat original; + class ParallelSearch : public ParallelLoopBody + { + public: + ParallelSearch(vector< vector< Point2f > >& true_points_group_, + vector< vector< Point2f > >& loc_, int iter_, int* end_, + vector< vector< Vec3i > >& all_points_, + QRDetectMulti& cl_) + : + true_points_group(true_points_group_), + loc(loc_), + iter(iter_), + end(end_), + all_points(all_points_), + cl(cl_) + { + } + void operator()(const Range& range) const CV_OVERRIDE; + vector< vector< Point2f > >& true_points_group; + vector< vector< Point2f > >& loc; + int iter; + int* end; + vector< vector< Vec3i > >& all_points; + QRDetectMulti& cl; + }; + }; + + void QRDetectMulti::ParallelSearch::operator()(const Range& range) const + { + for (int s = range.start; s < range.end; s++) + { + bool flag = false; + for (int r = iter; r < end[s]; r++) + { + if (flag) + break; + + size_t x = iter + s; + size_t k = r - iter; + vector triangle; + + for (int l = 0; l < 3; l++) + { + triangle.push_back(true_points_group[s][all_points[s][k][l]]); + } + + if (cl.checkPointsInsideTriangle(triangle)) + { + bool flag_for_break = false; + cl.fixationPoints(triangle); + if (triangle.size() == 3) + { + cl.localization_points[x] = triangle; + if (cl.purpose == cl.SHRINKING) + { + + for (size_t j = 0; j < 3; j++) + { + cl.localization_points[x][j] *= cl.coeff_expansion; + } + } + else if (cl.purpose == cl.ZOOMING) + { + for (size_t j = 0; j < 3; j++) + { + cl.localization_points[x][j] /= cl.coeff_expansion; + } + } + for (size_t i = 0; i < 3; i++) + { + for (size_t j = i + 1; j < 3; j++) + { + if (norm(cl.localization_points[x][i] - cl.localization_points[x][j]) < 10) + { + cl.localization_points[x].clear(); + flag_for_break = true; + break; + } + } + if (flag_for_break) + break; + } + if ((!flag_for_break) + && (cl.localization_points[x].size() == 3) + && (cl.computeTransformationPoints(x)) + && (cl.checkPointsInsideQuadrangle(cl.transformation_points[x])) + && (cl.checkPoints(cl.transformation_points[x]))) + { + for (int l = 0; l < 3; l++) + { + loc[s][all_points[s][k][l]].x = -1; + } + + flag = true; + break; + } + } + if (flag) + { + break; + } + else + { + cl.transformation_points[x].clear(); + cl.localization_points[x].clear(); + } + } + } + } + } + + void QRDetectMulti::init(const Mat& src, double eps_vertical_, double eps_horizontal_) + { + CV_TRACE_FUNCTION(); + + CV_Assert(!src.empty()); + const double min_side = std::min(src.size().width, src.size().height); + if (min_side < 512.0) + { + purpose = ZOOMING; + coeff_expansion = 512.0 / min_side; + const int width = cvRound(src.size().width * coeff_expansion); + const int height = cvRound(src.size().height * coeff_expansion); + Size new_size(width, height); + resize(src, barcode, new_size, 0, 0, INTER_LINEAR); + } + else if (min_side > 512.0) + { + purpose = SHRINKING; + coeff_expansion = min_side / 512.0; + const int width = cvRound(src.size().width / coeff_expansion); + const int height = cvRound(src.size().height / coeff_expansion); + Size new_size(width, height); + resize(src, barcode, new_size, 0, 0, INTER_AREA); + } + else + { + purpose = UNCHANGED; + coeff_expansion = 1.0; + barcode = src.clone(); + } + + eps_vertical = eps_vertical_; + eps_horizontal = eps_horizontal_; + adaptiveThreshold(barcode, bin_barcode, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 83, 2); + adaptiveThreshold(src, bin_barcode_fullsize, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 83, 2); + } + + void QRDetectMulti::fixationPoints(vector &local_point) + { + CV_TRACE_FUNCTION(); + + Point2f v0(local_point[1] - local_point[2]); + Point2f v1(local_point[0] - local_point[2]); + Point2f v2(local_point[1] - local_point[0]); + + double cos_angles[3], norm_triangl[3]; + norm_triangl[0] = norm(v0); + norm_triangl[1] = norm(v1); + norm_triangl[2] = norm(v2); + + cos_angles[0] = v2.dot(-v1) / (norm_triangl[1] * norm_triangl[2]); + cos_angles[1] = v2.dot(v0) / (norm_triangl[0] * norm_triangl[2]); + cos_angles[2] = v1.dot(v0) / (norm_triangl[0] * norm_triangl[1]); + + const double angle_barrier = 0.85; + if (fabs(cos_angles[0]) > angle_barrier || fabs(cos_angles[1]) > angle_barrier || fabs(cos_angles[2]) > angle_barrier) + { + local_point.clear(); + return; + } + + size_t i_min_cos = + (cos_angles[0] < cos_angles[1] && cos_angles[0] < cos_angles[2]) ? 0 : + (cos_angles[1] < cos_angles[0] && cos_angles[1] < cos_angles[2]) ? 1 : 2; + + size_t index_max = 0; + double max_area = std::numeric_limits::min(); + for (size_t i = 0; i < local_point.size(); i++) + { + const size_t current_index = i % 3; + const size_t left_index = (i + 1) % 3; + const size_t right_index = (i + 2) % 3; + + const Point2f current_point(local_point[current_index]); + const Point2f left_point(local_point[left_index]); + const Point2f right_point(local_point[right_index]); + const Point2f central_point(intersectionLines( + current_point, + Point2f(static_cast((local_point[left_index].x + local_point[right_index].x) * 0.5), + static_cast((local_point[left_index].y + local_point[right_index].y) * 0.5)), + Point2f(0, static_cast(bin_barcode_temp.rows - 1)), + 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); + + vector list_line_iter; + list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, left_point)); + list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, central_point)); + list_line_iter.push_back(LineIterator(bin_barcode_temp, current_point, right_point)); + + for (size_t k = 0; k < list_line_iter.size(); k++) + { + LineIterator& li = list_line_iter[k]; + uint8_t future_pixel = 255, count_index = 0; + for (int j = 0; j < li.count; j++, ++li) + { + Point p = li.pos(); + if (p.x >= bin_barcode_temp.cols || + p.y >= bin_barcode_temp.rows) + { + break; + } + + const uint8_t value = bin_barcode_temp.at(p); + if (value == future_pixel) + { + future_pixel = static_cast(~future_pixel); + count_index++; + if (count_index == 3) + { + list_area_pnt.push_back(p); + break; + } + } + } + } + + const double temp_check_area = contourArea(list_area_pnt); + if (temp_check_area > max_area) + { + index_max = current_index; + max_area = temp_check_area; + } + + } + if (index_max == i_min_cos) + { + std::swap(local_point[0], local_point[index_max]); + } + else + { + local_point.clear(); + return; + } + + const Point2f rpt = local_point[0], bpt = local_point[1], gpt = local_point[2]; + Matx22f m(rpt.x - bpt.x, rpt.y - bpt.y, gpt.x - rpt.x, gpt.y - rpt.y); + if (determinant(m) > 0) + { + std::swap(local_point[1], local_point[2]); + } } + + bool QRDetectMulti::checkPoints(const vector& quadrangle_points) + { + if (quadrangle_points.size() != 4) + return false; + vector quadrangle = quadrangle_points; + std::sort(quadrangle.begin(), quadrangle.end(), compareDistanse_y()); + LineIterator it1(bin_barcode_fullsize, quadrangle[1], quadrangle[0]); + LineIterator it2(bin_barcode_fullsize, quadrangle[2], quadrangle[0]); + LineIterator it3(bin_barcode_fullsize, quadrangle[1], quadrangle[3]); + LineIterator it4(bin_barcode_fullsize, quadrangle[2], quadrangle[3]); + vector list_line_iter; + list_line_iter.push_back(it1); + list_line_iter.push_back(it2); + list_line_iter.push_back(it3); + list_line_iter.push_back(it4); + int count_w = 0; + int count_b = 0; + for (int j = 0; j < 3; j +=2) + { + LineIterator& li = list_line_iter[j]; + LineIterator& li2 = list_line_iter[j + 1]; + for (int i = 0; i < li.count; i++) + { + + Point pt1 = li.pos(); + Point pt2 = li2.pos(); + LineIterator it0(bin_barcode_fullsize, pt1, pt2); + for (int r = 0; r < it0.count; r++) + { + int pixel = bin_barcode.at(it0.pos().y , it0.pos().x); + if (pixel == 255) + { + count_w++; + } + if (pixel == 0) + { + count_b++; + } + it0++; + } + li++; + li2++; + } + } + + double frac = double(count_b) / double(count_w); + double bottom_bound = 0.76; + double upper_bound = 1.24; + if ((frac <= bottom_bound) || (frac >= upper_bound)) + return false; + return true; + } + + bool QRDetectMulti::checkPointsInsideQuadrangle(const vector& quadrangle_points) + { + if (quadrangle_points.size() != 4) + return false; + + int count = 0; + for (size_t i = 0; i < not_resized_loc_points.size(); i++) + { + if (pointPolygonTest(quadrangle_points, not_resized_loc_points[i], true) > 0) + { + count++; + } + } + if (count == 3) + return true; + else + return false; + } + + bool QRDetectMulti::checkPointsInsideTriangle(const vector& triangle_points) + { + if (triangle_points.size() != 3) + return false; + double eps = 3; + for (size_t i = 0; i < resized_loc_points.size(); i++) + { + if (pointPolygonTest( triangle_points, resized_loc_points[i], true ) > 0) + { + if ((abs(resized_loc_points[i].x - triangle_points[0].x) > eps) + && (abs(resized_loc_points[i].x - triangle_points[1].x) > eps) + && (abs(resized_loc_points[i].x - triangle_points[2].x) > eps)) + { + return false; + } + } + } + return true; + } + + bool QRDetectMulti::compareSquare::operator()(const Vec3i& a, const Vec3i& b) const + { + Point2f a0 = points[a[0]]; + Point2f a1 = points[a[1]]; + Point2f a2 = points[a[2]]; + Point2f b0 = points[b[0]]; + Point2f b1 = points[b[1]]; + Point2f b2 = points[b[2]]; + return fabs((a1.x - a0.x) * (a2.y - a0.y) - (a2.x - a0.x) * (a1.y - a0.y)) < + fabs((b1.x - b0.x) * (b2.y - b0.y) - (b2.x - b0.x) * (b1.y - b0.y)); + } + + int QRDetectMulti::findNumberLocalizationPoints(vector& tmp_localization_points) + { + size_t number_possible_purpose = 1; + if (purpose == SHRINKING) + number_possible_purpose = 2; + Mat tmp_shrinking = bin_barcode; + int tmp_num_points = 0; + int num_points = -1; + for (eps_horizontal = 0.1; eps_horizontal < 0.4; eps_horizontal += 0.1) + { + tmp_num_points = 0; + num_points = -1; + if (purpose == SHRINKING) + number_possible_purpose = 2; + else + number_possible_purpose = 1; + for (size_t k = 0; k < number_possible_purpose; k++) + { + if (k == 1) + bin_barcode = bin_barcode_fullsize; + vector list_lines_x = searchHorizontalLines(); + if (list_lines_x.empty()) + { + if (k == 0) + { + k = 1; + bin_barcode = bin_barcode_fullsize; + list_lines_x = searchHorizontalLines(); + if (list_lines_x.empty()) + break; + } + else + break; + } + vector list_lines_y = extractVerticalLines(list_lines_x, eps_horizontal); + if (list_lines_y.size() < 3) + { + if (k == 0) + { + k = 1; + bin_barcode = bin_barcode_fullsize; + list_lines_x = searchHorizontalLines(); + if (list_lines_x.empty()) + break; + list_lines_y = extractVerticalLines(list_lines_x, eps_horizontal); + if (list_lines_y.size() < 3) + break; + } + else + break; + } + vector index_list_lines_y; + for (size_t i = 0; i < list_lines_y.size(); i++) + index_list_lines_y.push_back(-1); + num_points = 0; + for (size_t i = 0; i < list_lines_y.size() - 1; i++) + { + for (size_t j = i; j < list_lines_y.size(); j++ ) + { + + double points_distance = norm(list_lines_y[i] - list_lines_y[j]); + if (points_distance <= 10) + { + if ((index_list_lines_y[i] == -1) && (index_list_lines_y[j] == -1)) + { + index_list_lines_y[i] = num_points; + index_list_lines_y[j] = num_points; + num_points++; + } + else if (index_list_lines_y[i] != -1) + index_list_lines_y[j] = index_list_lines_y[i]; + else if (index_list_lines_y[j] != -1) + index_list_lines_y[i] = index_list_lines_y[j]; + } + } + } + for (size_t i = 0; i < index_list_lines_y.size(); i++) + { + if (index_list_lines_y[i] == -1) + { + index_list_lines_y[i] = num_points; + num_points++; + } + } + if ((tmp_num_points < num_points) && (k == 1)) + { + purpose = UNCHANGED; + tmp_num_points = num_points; + bin_barcode = bin_barcode_fullsize; + coeff_expansion = 1.0; + } + if ((tmp_num_points < num_points) && (k == 0)) + { + tmp_num_points = num_points; + } + } + + if ((tmp_num_points < 3) && (tmp_num_points >= 1)) + { + const double min_side = std::min(bin_barcode_fullsize.size().width, bin_barcode_fullsize.size().height); + if (min_side > 512) + { + bin_barcode = tmp_shrinking; + purpose = SHRINKING; + coeff_expansion = min_side / 512.0; + } + if (min_side < 512) + { + bin_barcode = tmp_shrinking; + purpose = ZOOMING; + coeff_expansion = 512 / min_side; + } + } + else + break; + } + if (purpose == SHRINKING) + bin_barcode = tmp_shrinking; + num_points = tmp_num_points; + vector list_lines_x = searchHorizontalLines(); + if (list_lines_x.empty()) + return num_points; + vector list_lines_y = extractVerticalLines(list_lines_x, eps_horizontal); + if (list_lines_y.size() < 3) + return num_points; + if (num_points < 3) + return num_points; + + Mat labels; + kmeans(list_lines_y, num_points, labels, + TermCriteria( TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1), + num_points, KMEANS_PP_CENTERS, tmp_localization_points); + bin_barcode_temp = bin_barcode.clone(); + if (purpose == SHRINKING) + { + const int width = cvRound(bin_barcode.size().width * coeff_expansion); + const int height = cvRound(bin_barcode.size().height * coeff_expansion); + Size new_size(width, height); + Mat intermediate; + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + bin_barcode = intermediate.clone(); + } + else if (purpose == ZOOMING) + { + const int width = cvRound(bin_barcode.size().width / coeff_expansion); + const int height = cvRound(bin_barcode.size().height / coeff_expansion); + Size new_size(width, height); + Mat intermediate; + resize(bin_barcode, intermediate, new_size, 0, 0, INTER_LINEAR); + bin_barcode = intermediate.clone(); + } + else + { + bin_barcode = bin_barcode_fullsize.clone(); + } + return num_points; + } + + void QRDetectMulti::findQRCodeContours(vector& tmp_localization_points, + vector< vector< Point2f > >& true_points_group, const int& num_qrcodes) + { + Mat gray, blur_image, threshold_output; + Mat bar = barcode; + const int width = cvRound(bin_barcode.size().width); + const int height = cvRound(bin_barcode.size().height); + Size new_size(width, height); + resize(bar, bar, new_size, 0, 0, INTER_LINEAR); + blur(bar, blur_image, Size(3, 3)); + threshold(blur_image, threshold_output, 50, 255, THRESH_BINARY); + + vector< vector< Point > > contours; + vector hierarchy; + findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0)); + vector all_contours_points; + for (size_t i = 0; i < contours.size(); i++) + { + for (size_t j = 0; j < contours[i].size(); j++) + { + all_contours_points.push_back(contours[i][j]); + } + } + Mat qrcode_labels; + vector clustered_localization_points; + int count_contours = num_qrcodes; + if (all_contours_points.size() < size_t(num_qrcodes)) + count_contours = (int)all_contours_points.size(); + kmeans(all_contours_points, count_contours, qrcode_labels, + TermCriteria( TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1), + count_contours, KMEANS_PP_CENTERS, clustered_localization_points); + + vector< vector< Point2f > > qrcode_clusters(count_contours); + for (int i = 0; i < count_contours; i++) + for (int j = 0; j < int(all_contours_points.size()); j++) + { + if (qrcode_labels.at(j, 0) == i) + { + qrcode_clusters[i].push_back(all_contours_points[j]); + } + } + vector< vector< Point2f > > hull(count_contours); + for (size_t i = 0; i < qrcode_clusters.size(); i++) + convexHull(Mat(qrcode_clusters[i]), hull[i]); + not_resized_loc_points = tmp_localization_points; + resized_loc_points = tmp_localization_points; + if (purpose == SHRINKING) + { + for (size_t j = 0; j < not_resized_loc_points.size(); j++) + { + not_resized_loc_points[j] *= coeff_expansion; + } + } + else if (purpose == ZOOMING) + { + for (size_t j = 0; j < not_resized_loc_points.size(); j++) + { + not_resized_loc_points[j] /= coeff_expansion; + } + } + + true_points_group.resize(hull.size()); + + for (size_t j = 0; j < hull.size(); j++) + { + for (size_t i = 0; i < not_resized_loc_points.size(); i++) + { + if (pointPolygonTest(hull[j], not_resized_loc_points[i], true) > 0) + { + true_points_group[j].push_back(tmp_localization_points[i]); + tmp_localization_points[i].x = -1; + } + + } + } + vector copy; + for (size_t j = 0; j < tmp_localization_points.size(); j++) + { + if (tmp_localization_points[j].x != -1) + copy.push_back(tmp_localization_points[j]); + } + tmp_localization_points = copy; + } + + bool QRDetectMulti::checkSets(vector >& true_points_group, vector >& true_points_group_copy, + vector& tmp_localization_points) + { + for (size_t i = 0; i < true_points_group.size(); i++) + { + if (true_points_group[i].size() < 3) + { + for (size_t j = 0; j < true_points_group[i].size(); j++) + tmp_localization_points.push_back(true_points_group[i][j]); + true_points_group[i].clear(); + } + } + vector< vector< Point2f > > temp_for_copy; + for (size_t i = 0; i < true_points_group.size(); i++) + { + if (true_points_group[i].size() != 0) + temp_for_copy.push_back(true_points_group[i]); + } + true_points_group = temp_for_copy; + if (true_points_group.size() == 0) + { + true_points_group.push_back(tmp_localization_points); + tmp_localization_points.clear(); + } + if (true_points_group.size() == 0) + return false; + if (true_points_group[0].size() < 3) + return false; + + + int* set_size = new int[true_points_group.size()]; + for (size_t i = 0; i < true_points_group.size(); i++) + { + set_size[i] = int(0.5 * (true_points_group[i].size() - 2 ) * (true_points_group[i].size() - 1)); + } + vector< vector< Vec3i > > all_points(true_points_group.size()); + for (size_t i = 0; i < true_points_group.size(); i++) + all_points[i].resize(set_size[i]); + int cur_cluster = 0; + for (size_t i = 0; i < true_points_group.size(); i++) + { + cur_cluster = 0; + for (size_t j = 1; j < true_points_group[i].size() - 1; j++) + for (size_t k = j + 1; k < true_points_group[i].size(); k++) + { + all_points[i][cur_cluster][0] = 0; + all_points[i][cur_cluster][1] = int(j); + all_points[i][cur_cluster][2] = int(k); + cur_cluster++; + } + } + + for (size_t i = 0; i < true_points_group.size(); i++) + { + std::sort(all_points[i].begin(), all_points[i].end(), compareSquare(true_points_group[i])); + } + if (true_points_group.size() == 1) + { + int check_number = 35; + if (set_size[0] > check_number) + set_size[0] = check_number; + all_points[0].resize(set_size[0]); + } + int iter = (int)localization_points.size(); + localization_points.resize(iter + true_points_group.size()); + transformation_points.resize(iter + true_points_group.size()); + + true_points_group_copy = true_points_group; + int* end = new int[true_points_group.size()]; + for (size_t i = 0; i < true_points_group.size(); i++) + end[i] = iter + set_size[i]; + ParallelSearch parallelSearch(true_points_group, + true_points_group_copy, iter, end, all_points, *this); + parallel_for_(Range(0, (int)true_points_group.size()), parallelSearch); + + return true; + } + + void QRDetectMulti::deleteUsedPoints(vector >& true_points_group, vector >& loc, + vector& tmp_localization_points) + { + size_t iter = localization_points.size() - true_points_group.size() ; + for (size_t s = 0; s < true_points_group.size(); s++) + { + if (localization_points[iter + s].empty()) + loc[s][0].x = -2; + + if (loc[s].size() == 3) + { + + if ((true_points_group.size() > 1) || ((true_points_group.size() == 1) && (tmp_localization_points.size() != 0)) ) + { + for (size_t j = 0; j < true_points_group[s].size(); j++) + { + if (loc[s][j].x != -1) + { + loc[s][j].x = -1; + tmp_localization_points.push_back(true_points_group[s][j]); + } + } + } + } + vector for_copy; + for (size_t j = 0; j < loc[s].size(); j++) + { + if ((loc[s][j].x != -1) && (loc[s][j].x != -2) ) + { + for_copy.push_back(true_points_group[s][j]); + } + if ((loc[s][j].x == -2) && (true_points_group.size() > 1)) + { + tmp_localization_points.push_back(true_points_group[s][j]); + } + } + true_points_group[s] = for_copy; + } + + vector< vector< Point2f > > for_copy_loc; + vector< vector< Point2f > > for_copy_trans; + + + for (size_t i = 0; i < localization_points.size(); i++) + { + if ((localization_points[i].size() == 3) && (transformation_points[i].size() == 4)) + { + for_copy_loc.push_back(localization_points[i]); + for_copy_trans.push_back(transformation_points[i]); + } + } + localization_points = for_copy_loc; + transformation_points = for_copy_trans; + } + + bool QRDetectMulti::localization() + { + CV_TRACE_FUNCTION(); + vector tmp_localization_points; + int num_points = findNumberLocalizationPoints(tmp_localization_points); + if (num_points < 3) + return false; + int num_qrcodes = divUp(num_points, 3); + vector > true_points_group; + findQRCodeContours(tmp_localization_points, true_points_group, num_qrcodes); + for (int q = 0; q < num_qrcodes; q++) + { + vector > loc; + size_t iter = localization_points.size(); + + if (!checkSets(true_points_group, loc, tmp_localization_points)) + break; + deleteUsedPoints(true_points_group, loc, tmp_localization_points); + if ((localization_points.size() - iter) == 1) + q--; + if (((localization_points.size() - iter) == 0) && (tmp_localization_points.size() == 0) && (true_points_group.size() == 1) ) + break; + } + if ((transformation_points.size() == 0) || (localization_points.size() == 0)) + return false; + return true; + } + + bool QRDetectMulti::computeTransformationPoints(const size_t cur_ind) + { + CV_TRACE_FUNCTION(); + + if (localization_points[cur_ind].size() != 3) + { + return false; + } + + vector locations, non_zero_elem[3], newHull; + vector new_non_zero_elem[3]; + for (size_t i = 0; i < 3 ; i++) + { + Mat mask = Mat::zeros(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1); + uint8_t next_pixel, future_pixel = 255; + int localization_point_x = cvRound(localization_points[cur_ind][i].x); + int localization_point_y = cvRound(localization_points[cur_ind][i].y); + int count_test_lines = 0, index = localization_point_x; + for (; index < bin_barcode.cols - 1; index++) + { + next_pixel = bin_barcode.at(localization_point_y, index + 1); + if (next_pixel == future_pixel) + { + future_pixel = static_cast(~future_pixel); + count_test_lines++; + + if (count_test_lines == 2) + { + // TODO avoid drawing functions + floodFill(bin_barcode, mask, + Point(index + 1, localization_point_y), 255, + 0, Scalar(), Scalar(), FLOODFILL_MASK_ONLY); + break; + } + } + + } + Mat mask_roi = mask(Range(1, bin_barcode.rows - 1), Range(1, bin_barcode.cols - 1)); + findNonZero(mask_roi, non_zero_elem[i]); + newHull.insert(newHull.end(), non_zero_elem[i].begin(), non_zero_elem[i].end()); + } + convexHull(newHull, locations); + for (size_t i = 0; i < locations.size(); i++) + { + for (size_t j = 0; j < 3; j++) + { + for (size_t k = 0; k < non_zero_elem[j].size(); k++) + { + if (locations[i] == non_zero_elem[j][k]) + { + new_non_zero_elem[j].push_back(locations[i]); + } + } + } + } + + if (new_non_zero_elem[0].size() == 0) + return false; + + double pentagon_diag_norm = -1; + Point2f down_left_edge_point, up_right_edge_point, up_left_edge_point; + for (size_t i = 0; i < new_non_zero_elem[1].size(); i++) + { + for (size_t j = 0; j < new_non_zero_elem[2].size(); j++) + { + double temp_norm = norm(new_non_zero_elem[1][i] - new_non_zero_elem[2][j]); + if (temp_norm > pentagon_diag_norm) + { + down_left_edge_point = new_non_zero_elem[1][i]; + up_right_edge_point = new_non_zero_elem[2][j]; + pentagon_diag_norm = temp_norm; + } + } + } + + if (down_left_edge_point == Point2f(0, 0) || + up_right_edge_point == Point2f(0, 0)) + { + return false; + } + + double max_area = -1; + up_left_edge_point = new_non_zero_elem[0][0]; + + for (size_t i = 0; i < new_non_zero_elem[0].size(); i++) + { + vector list_edge_points; + list_edge_points.push_back(new_non_zero_elem[0][i]); + list_edge_points.push_back(down_left_edge_point); + list_edge_points.push_back(up_right_edge_point); + + double temp_area = fabs(contourArea(list_edge_points)); + if (max_area < temp_area) + { + up_left_edge_point = new_non_zero_elem[0][i]; + max_area = temp_area; + } + } + + Point2f down_max_delta_point, up_max_delta_point; + double norm_down_max_delta = -1, norm_up_max_delta = -1; + for (size_t i = 0; i < new_non_zero_elem[1].size(); i++) + { + double temp_norm_delta = norm(up_left_edge_point - new_non_zero_elem[1][i]) + norm(down_left_edge_point - new_non_zero_elem[1][i]); + if (norm_down_max_delta < temp_norm_delta) + { + down_max_delta_point = new_non_zero_elem[1][i]; + norm_down_max_delta = temp_norm_delta; + } + } + + + for (size_t i = 0; i < new_non_zero_elem[2].size(); i++) + { + double temp_norm_delta = norm(up_left_edge_point - new_non_zero_elem[2][i]) + norm(up_right_edge_point - new_non_zero_elem[2][i]); + if (norm_up_max_delta < temp_norm_delta) + { + up_max_delta_point = new_non_zero_elem[2][i]; + norm_up_max_delta = temp_norm_delta; + } + } + vector tmp_transformation_points; + tmp_transformation_points.push_back(down_left_edge_point); + tmp_transformation_points.push_back(up_left_edge_point); + tmp_transformation_points.push_back(up_right_edge_point); + tmp_transformation_points.push_back(intersectionLines( + down_left_edge_point, down_max_delta_point, + up_right_edge_point, up_max_delta_point)); + transformation_points[cur_ind] = tmp_transformation_points; + + vector quadrilateral = getQuadrilateral(transformation_points[cur_ind]); + transformation_points[cur_ind] = quadrilateral; + + return true; + } + + bool QRCodeDetector::detectMulti(InputArray in, OutputArray points) const + { + Mat inarr; + if (!checkQRInputImage(in, inarr)) + { + points.release(); + return false; + } + + QRDetectMulti qrdet; + qrdet.init(inarr, p->epsX, p->epsY); + if (!qrdet.localization()) + { + points.release(); + return false; + } + vector< vector< Point2f > > pnts2f = qrdet.getTransformationPoints(); + vector trans_points; + for(size_t i = 0; i < pnts2f.size(); i++) + for(size_t j = 0; j < pnts2f[i].size(); j++) + trans_points.push_back(pnts2f[i][j]); + + updatePointsResult(points, trans_points); + + return true; + } + -bool detectQRCodeMulti(InputArray in, vector< Point > &points, double eps_x, double eps_y) -{ - QRCodeDetector qrdetector; - qrdetector.setEpsX(eps_x); - qrdetector.setEpsY(eps_y); - return qrdetector.detectMulti(in, points); -} - + class ParallelDecodeProcess : public ParallelLoopBody + { + public: + ParallelDecodeProcess(Mat& inarr_, vector& qrdec_, vector& decoded_info_, + vector& straight_barcode_, vector< vector< Point2f > >& src_points_) + : inarr(inarr_), qrdec(qrdec_), decoded_info(decoded_info_) + , straight_barcode(straight_barcode_), src_points(src_points_) + { + // nothing + } + void operator()(const Range& range) const CV_OVERRIDE + { + for (int i = range.start; i < range.end; i++) + { + qrdec[i].init(inarr, src_points[i]); + bool ok = qrdec[i].fullDecodingProcess(); + if (ok) + { + decoded_info[i] = qrdec[i].getDecodeInformation(); + straight_barcode[i] = qrdec[i].getStraightBarcode(); + } + else if (std::min(inarr.size().width, inarr.size().height) > 512) + { + const int min_side = std::min(inarr.size().width, inarr.size().height); + double coeff_expansion = min_side / 512; + const int width = cvRound(inarr.size().width / coeff_expansion); + const int height = cvRound(inarr.size().height / coeff_expansion); + Size new_size(width, height); + Mat inarr2; + resize(inarr, inarr2, new_size, 0, 0, INTER_AREA); + for (size_t j = 0; j < 4; j++) + { + src_points[i][j] /= static_cast(coeff_expansion); + } + qrdec[i].init(inarr2, src_points[i]); + ok = qrdec[i].fullDecodingProcess(); + if (ok) + { + decoded_info[i] = qrdec[i].getDecodeInformation(); + straight_barcode[i] = qrdec[i].getStraightBarcode(); + } + } + if (decoded_info[i].empty()) + decoded_info[i] = ""; + } + } + + private: + Mat& inarr; + vector& qrdec; + vector& decoded_info; + vector& straight_barcode; + vector< vector< Point2f > >& src_points; + + }; + + bool QRCodeDetector::decodeMulti( + InputArray img, + InputArray points, + CV_OUT std::vector& decoded_info, + OutputArrayOfArrays straight_qrcode + ) const + { + Mat inarr; + if (!checkQRInputImage(img, inarr)) + return false; + CV_Assert(points.size().width > 0); + CV_Assert((points.size().width % 4) == 0); + vector< vector< Point2f > > src_points ; + Mat qr_points = points.getMat(); + for (int i = 0; i < points.size().width ; i += 4) + { + vector tempMat = qr_points.colRange(i, i + 4); + if (contourArea(tempMat) > 0.0) + { + src_points.push_back(tempMat); + } + } + CV_Assert(src_points.size() > 0); + vector qrdec(src_points.size()); + vector straight_barcode(src_points.size()); + vector info(src_points.size()); + ParallelDecodeProcess parallelDecodeProcess(inarr, qrdec, info, straight_barcode, src_points); + parallel_for_(Range(0, int(src_points.size())), parallelDecodeProcess); + vector for_copy; + for (size_t i = 0; i < straight_barcode.size(); i++) + { + if (!(straight_barcode[i].empty())) + for_copy.push_back(straight_barcode[i]); + } + straight_barcode = for_copy; + vector tmp_straight_qrcodes; + if (straight_qrcode.needed()) + { + for (size_t i = 0; i < straight_barcode.size(); i++) + { + Mat tmp_straight_qrcode; + tmp_straight_qrcodes.push_back(tmp_straight_qrcode); + straight_barcode[i].convertTo(((OutputArray)tmp_straight_qrcodes[i]), + ((OutputArray)tmp_straight_qrcodes[i]).fixedType() ? + ((OutputArray)tmp_straight_qrcodes[i]).type() : CV_32FC2); + } + straight_qrcode.createSameSize(tmp_straight_qrcodes, CV_32FC2); + straight_qrcode.assign(tmp_straight_qrcodes); + } + decoded_info.clear(); + for (size_t i = 0; i < info.size(); i++) + { + decoded_info.push_back(info[i]); + } + if (!decoded_info.empty()) + return true; + else + return false; + } + + bool QRCodeDetector::detectAndDecodeMulti( + InputArray img, + CV_OUT std::vector& decoded_info, + OutputArray points_, + OutputArrayOfArrays straight_qrcode + ) const + { + Mat inarr; + if (!checkQRInputImage(img, inarr)) + { + points_.release(); + return false; + } + + vector points; + bool ok = detectMulti(inarr, points); + if (!ok) + { + points_.release(); + return false; + } + updatePointsResult(points_, points); + decoded_info.clear(); + ok = decodeMulti(inarr, points, decoded_info, straight_qrcode); + return ok; + } + -bool decodeQRCodeMulti( - InputArray in, InputArray points, - vector &decoded_info, OutputArrayOfArrays straight_qrcode) -{ - QRCodeDetector qrcode; - vector info; - bool ok = qrcode.decodeMulti(in, points, info, straight_qrcode); - for (size_t i = 0; i < info.size(); i++) - decoded_info.push_back(info[i]); - return ok; -} - + } // namespace diff --cc modules/python/src2/cv2.cpp index 0b8788f,0b516a4..1aa3e14 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@@ -319,6 -316,6 +319,7 @@@ typedef std::vector > typedef std::vector vector_UMat; typedef std::vector vector_DMatch; typedef std::vector vector_String; ++typedef std::vector vector_string; typedef std::vector vector_Scalar; typedef std::vector > vector_vector_char; diff --cc modules/videoio/test/test_ffmpeg.cpp index f769b5c,10f41da..7b1bfe4 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@@ -180,109 -303,275 +180,157 @@@ const videoio_container_params_t videoi INSTANTIATE_TEST_CASE_P(/**/, videoio_container, testing::ValuesIn(videoio_container_params)); + typedef tuple videoio_skip_params_t; + typedef testing::TestWithParam< videoio_skip_params_t > videoio_skip; + + TEST_P(videoio_skip, DISABLED_read) // optional test, may fail in some configurations + { + #if CV_VERSION_MAJOR >= 4 + if (!videoio_registry::hasBackend(CAP_FFMPEG)) + throw SkipTestException("Backend was not found"); + #endif + + const string path = get<0>(GetParam()); + const string env = get<1>(GetParam()); + const int expectedFrameNumber = get<2>(GetParam()); + + #ifdef _WIN32 + _putenv_s("OPENCV_FFMPEG_CAPTURE_OPTIONS", env.c_str()); + #else + setenv("OPENCV_FFMPEG_CAPTURE_OPTIONS", env.c_str(), 1); + #endif + VideoCapture container(findDataFile(path), CAP_FFMPEG); + #ifdef _WIN32 + _putenv_s("OPENCV_FFMPEG_CAPTURE_OPTIONS", ""); + #else + setenv("OPENCV_FFMPEG_CAPTURE_OPTIONS", "", 1); + #endif + + ASSERT_TRUE(container.isOpened()); + + Mat reference; + int nframes = 0, n_err = 0; + while (container.isOpened()) + { + if (container.read(reference)) + nframes++; + else if (++n_err > 3) + break; + } + EXPECT_EQ(expectedFrameNumber, nframes); + } + + const videoio_skip_params_t videoio_skip_params[] = + { + videoio_skip_params_t("video/big_buck_bunny.mp4", "", 125), + videoio_skip_params_t("video/big_buck_bunny.mp4", "avdiscard;nonkey", 11) + }; + + INSTANTIATE_TEST_CASE_P(/**/, videoio_skip, testing::ValuesIn(videoio_skip_params)); + //========================================================================== -//////////////////////////////// Parallel VideoWriters and VideoCaptures //////////////////////////////////// - -class CreateVideoWriterInvoker : - public ParallelLoopBody +static void generateFrame(Mat &frame, unsigned int i, const Point ¢er, const Scalar &color) { -public: - const static Size FrameSize; - static std::string TmpDirectory; + frame = Scalar::all(i % 255); + stringstream buf(ios::out); + buf << "frame #" << i; + putText(frame, buf.str(), Point(50, center.y), FONT_HERSHEY_SIMPLEX, 5.0, color, 5, CV_AA); + circle(frame, center, i + 2, color, 2, CV_AA); +} - CreateVideoWriterInvoker(std::vector< cv::Ptr >& _writers, std::vector& _files) : - writers(_writers), files(_files) +TEST(videoio_ffmpeg, parallel) +{ + if (!videoio_registry::hasBackend(CAP_FFMPEG)) + throw SkipTestException("FFmpeg backend was not found"); + + const int NUM = 4; + const int GRAN = 4; + const Range R(0, NUM); + const Size sz(1020, 900); + const int frameNum = 300; + const Scalar color(Scalar::all(0)); + const Point center(sz.height / 2, sz.width / 2); + + // Generate filenames + vector files; + for (int i = 0; i < NUM; ++i) { + ostringstream stream; + stream << i << ".avi"; + files.push_back(tempfile(stream.str().c_str())); } - - virtual void operator() (const Range& range) const CV_OVERRIDE + // Write videos { - for (int i = range.start; i != range.end; ++i) + vector< Ptr > writers(NUM); + auto makeWriters = [&](const Range &r) { - std::ostringstream stream; - stream << i << ".avi"; - std::string fileName = tempfile(stream.str().c_str()); - - files[i] = fileName; - writers[i] = makePtr(fileName, CAP_FFMPEG, VideoWriter::fourcc('X','V','I','D'), 25.0f, FrameSize); - - CV_Assert(writers[i]->isOpened()); + for (int i = r.start; i != r.end; ++i) + writers[i] = makePtr(files[i], + CAP_FFMPEG, + VideoWriter::fourcc('X','V','I','D'), + 25.0f, + sz); + }; + parallel_for_(R, makeWriters, GRAN); + for(int i = 0; i < NUM; ++i) + { + ASSERT_TRUE(writers[i]); + ASSERT_TRUE(writers[i]->isOpened()); } - } - -private: - std::vector< cv::Ptr >& writers; - std::vector& files; -}; - -std::string CreateVideoWriterInvoker::TmpDirectory; -const Size CreateVideoWriterInvoker::FrameSize(1020, 900); - -class WriteVideo_Invoker : - public ParallelLoopBody -{ -public: - enum { FrameCount = 300 }; - - static const Scalar ObjectColor; - static const Point Center; - - WriteVideo_Invoker(const std::vector< cv::Ptr >& _writers) : - ParallelLoopBody(), writers(&_writers) - { - } - - static void GenerateFrame(Mat& frame, unsigned int i) - { - frame = Scalar::all(i % 255); - - std::string text = to_string(i); - putText(frame, text, Point(50, Center.y), FONT_HERSHEY_SIMPLEX, 5.0, ObjectColor, 5, CV_AA); - circle(frame, Center, i + 2, ObjectColor, 2, CV_AA); - } - - virtual void operator() (const Range& range) const CV_OVERRIDE - { - for (int j = range.start; j < range.end; ++j) + auto writeFrames = [&](const Range &r) { - VideoWriter* writer = writers->operator[](j); - CV_Assert(writer != NULL); - CV_Assert(writer->isOpened()); - - Mat frame(CreateVideoWriterInvoker::FrameSize, CV_8UC3); - for (unsigned int i = 0; i < FrameCount; ++i) + for (int j = r.start; j < r.end; ++j) { - GenerateFrame(frame, i); - writer->operator<< (frame); + Mat frame(sz, CV_8UC3); + for (int i = 0; i < frameNum; ++i) + { + generateFrame(frame, i, center, color); + writers[j]->write(frame); + } } - } - } - -protected: - static std::string to_string(unsigned int i) - { - std::stringstream stream(std::ios::out); - stream << "frame #" << i; - return stream.str(); + }; + parallel_for_(R, writeFrames, GRAN); } - -private: - const std::vector< cv::Ptr >* writers; -}; - -const Scalar WriteVideo_Invoker::ObjectColor(Scalar::all(0)); -const Point WriteVideo_Invoker::Center(CreateVideoWriterInvoker::FrameSize.height / 2, - CreateVideoWriterInvoker::FrameSize.width / 2); - -class CreateVideoCaptureInvoker : - public ParallelLoopBody -{ -public: - CreateVideoCaptureInvoker(std::vector< cv::Ptr >& _readers, const std::vector& _files) : - ParallelLoopBody(), readers(&_readers), files(&_files) + // Read videos { - } - - virtual void operator() (const Range& range) const CV_OVERRIDE - { - for (int i = range.start; i != range.end; ++i) + vector< Ptr > readers(NUM); + auto makeCaptures = [&](const Range &r) + { + for (int i = r.start; i != r.end; ++i) + readers[i] = makePtr(files[i], CAP_FFMPEG); + }; + parallel_for_(R, makeCaptures, GRAN); + for(int i = 0; i < NUM; ++i) { - readers->operator[](i) = makePtr(files->operator[](i), CAP_FFMPEG); - CV_Assert(readers->operator[](i)->isOpened()); + ASSERT_TRUE(readers[i]); + ASSERT_TRUE(readers[i]->isOpened()); } - } -private: - std::vector< cv::Ptr >* readers; - const std::vector* files; -}; - -class ReadImageAndTest : - public ParallelLoopBody -{ -public: - ReadImageAndTest(const std::vector< cv::Ptr >& _readers, cvtest::TS* _ts) : - ParallelLoopBody(), readers(&_readers), ts(_ts) - { - } - - virtual void operator() (const Range& range) const CV_OVERRIDE - { - for (int j = range.start; j < range.end; ++j) + auto readFrames = [&](const Range &r) { - VideoCapture* capture = readers->operator[](j).get(); - CV_Assert(capture != NULL); - CV_Assert(capture->isOpened()); - - const static double eps = 23.0; - unsigned int frameCount = static_cast(capture->get(CAP_PROP_FRAME_COUNT)); - CV_Assert(frameCount == WriteVideo_Invoker::FrameCount); - Mat reference(CreateVideoWriterInvoker::FrameSize, CV_8UC3); - - for (unsigned int i = 0; i < frameCount && next; ++i) + for (int j = r.start; j < r.end; ++j) { - SCOPED_TRACE(cv::format("frame=%d/%d", (int)i, (int)frameCount)); - - Mat actual; - (*capture) >> actual; - - WriteVideo_Invoker::GenerateFrame(reference, i); - - EXPECT_EQ(reference.cols, actual.cols); - EXPECT_EQ(reference.rows, actual.rows); - EXPECT_EQ(reference.depth(), actual.depth()); - EXPECT_EQ(reference.channels(), actual.channels()); - - double psnr = cvtest::PSNR(actual, reference); - if (psnr < eps) + Mat reference(sz, CV_8UC3); + for (int i = 0; i < frameNum; ++i) { - #define SUM cvtest::TS::SUMMARY - ts->printf(SUM, "\nPSNR: %lf\n", psnr); - ts->printf(SUM, "Video #: %d\n", range.start); - ts->printf(SUM, "Frame #: %d\n", i); - #undef SUM - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - ts->set_gtest_status(); - - Mat diff; - absdiff(actual, reference, diff); - - EXPECT_EQ(countNonZero(diff.reshape(1) > 1), 0); - - next = false; + Mat actual; + EXPECT_TRUE(readers[j]->read(actual)); + EXPECT_FALSE(actual.empty()); + generateFrame(reference, i, center, color); + EXPECT_EQ(reference.size(), actual.size()); + EXPECT_EQ(reference.depth(), actual.depth()); + EXPECT_EQ(reference.channels(), actual.channels()); + EXPECT_GE(cvtest::PSNR(actual, reference), 35.0) << "cap" << j << ", frame " << i; } } - } + }; + parallel_for_(R, readFrames, GRAN); } - - static bool next; - -private: - const std::vector< cv::Ptr >* readers; - cvtest::TS* ts; -}; - -bool ReadImageAndTest::next; - -TEST(Videoio_Video_parallel_writers_and_readers, accuracy) -{ - const unsigned int threadsCount = 4; - cvtest::TS* ts = cvtest::TS::ptr(); - - // creating VideoWriters - std::vector< cv::Ptr > writers(threadsCount); - Range range(0, threadsCount); - std::vector files(threadsCount); - CreateVideoWriterInvoker invoker1(writers, files); - parallel_for_(range, invoker1); - - // write a video - parallel_for_(range, WriteVideo_Invoker(writers)); - - // deleting the writers - writers.clear(); - - std::vector > readers(threadsCount); - CreateVideoCaptureInvoker invoker2(readers, files); - parallel_for_(range, invoker2); - - ReadImageAndTest::next = true; - - parallel_for_(range, ReadImageAndTest(readers, ts)); - - // deleting tmp video files - for (std::vector::const_iterator i = files.begin(), end = files.end(); i != end; ++i) + // Remove files + for(int i = 0; i < NUM; ++i) { - int code = remove(i->c_str()); - if (code == 1) - std::cerr << "Couldn't delete " << *i << std::endl; + remove(files[i].c_str()); } - - // delete the readers - readers.clear(); } -#endif }} // namespace diff --cc platforms/android/build_sdk.py index 6a52be7,8c0ef89..9c36243 --- a/platforms/android/build_sdk.py +++ b/platforms/android/build_sdk.py @@@ -224,12 -226,9 +224,15 @@@ class Builder if self.ninja_path != 'ninja': cmake_vars['CMAKE_MAKE_PROGRAM'] = self.ninja_path + if self.debug: + cmake_vars['CMAKE_BUILD_TYPE'] = "Debug" + + if self.debug_info: # Release with debug info + cmake_vars['BUILD_WITH_DEBUG_INFO'] = "ON" + + if self.config.modules_list is not None: + cmd.append("-DBUILD_LIST='%s'" % self.config.modules_list) + if self.config.extra_modules_path is not None: cmd.append("-DOPENCV_EXTRA_MODULES_PATH='%s'" % self.config.extra_modules_path)