Merge remote-tracking branch 'upstream/3.4' into merge-3.4
authorAlexander Alekhin <alexander.alekhin@intel.com>
Tue, 28 Jan 2020 10:16:03 +0000 (13:16 +0300)
committerAlexander Alekhin <alexander.alekhin@intel.com>
Tue, 28 Jan 2020 11:26:57 +0000 (14:26 +0300)
23 files changed:
1  2 
3rdparty/carotene/hal/CMakeLists.txt
CMakeLists.txt
modules/calib3d/src/solvepnp.cpp
modules/core/src/parallel_impl.cpp
modules/dnn/include/opencv2/dnn/all_layers.hpp
modules/dnn/include/opencv2/dnn/version.hpp
modules/dnn/src/layers/convolution_layer.cpp
modules/dnn/src/layers/pooling_layer.cpp
modules/dnn/src/layers/slice_layer.cpp
modules/dnn/src/layers/softmax_layer.cpp
modules/dnn/src/op_inf_engine.cpp
modules/dnn/src/tensorflow/tf_importer.cpp
modules/dnn/test/test_tf_importer.cpp
modules/imgproc/CMakeLists.txt
modules/java/generator/gen_java.py
modules/java/generator/src/cpp/listconverters.cpp
modules/java/generator/src/cpp/listconverters.hpp
modules/objdetect/include/opencv2/objdetect.hpp
modules/objdetect/src/qrcode.cpp
modules/python/src2/cv2.cpp
modules/videoio/src/cap_ffmpeg_impl.hpp
modules/videoio/test/test_ffmpeg.cpp
platforms/android/build_sdk.py

Simple merge
diff --cc CMakeLists.txt
Simple merge
Simple merge
Simple merge
index 6dd2c39,0000000..152c8b2
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,21 @@@
- #define OPENCV_DNN_API_VERSION 20191202
 +// 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 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
@@@ -120,8 -114,8 +120,9 @@@ public
      virtual bool supportBackend(int backendId) CV_OVERRIDE
      {
          return backendId == DNN_BACKEND_OPENCV ||
-                ((backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) &&
 +               backendId == DNN_BACKEND_CUDA ||
+                (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
@@@ -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
Simple merge
Simple merge
@@@ -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(undistort SSE2 AVX2)
+ ocv_add_dispatched_file(sumpixels SSE2 AVX2 AVX512_SKX)
  ocv_define_module(imgproc opencv_core WRAP java python js)
@@@ -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<std::string>, see "vector_String" in modules/core/misc/java/gen_dict.json
++        'j_type': 'List<String>',
++        'jn_type': 'List<String>',
++        '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.
@@@ -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<std::string>& 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<std::string>::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<std::string> 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<std::string> result;
++    result.reserve(len);
++    for (jint i=0; i<len; i++)
++    {
++        jstring element = static_cast<jstring>(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<std::string>& 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<std::string>::iterator it = vs.begin(); it != vs.end(); ++it)
++    {
++        jstring element = env->NewStringUTF((*it).c_str());
++        env->CallBooleanMethod(list, m_add, element);
++        env->DeleteLocalRef(element);
++    }
++}
@@@ -16,4 -16,4 +16,11 @@@ std::vector<cv::String> List_to_vector_
  
  void Copy_vector_String_to_List(JNIEnv* env, std::vector<cv::String>& vs, jobject list);
  
++
++jobject vector_string_to_List(JNIEnv* env, std::vector<std::string>& vs);
++
++std::vector<std::string> List_to_vector_string(JNIEnv* env, jobject list);
++
++void Copy_vector_string_to_List(JNIEnv* env, std::vector<std::string>& vs, jobject list);
++
  #endif        /* LISTCONVERTERS_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());
 -            CV_OUT std::vector<cv::String>& decoded_info,
+     /** @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,
 -            InputArray img, CV_OUT std::vector<cv::String>& decoded_info,
++            CV_OUT std::vector<std::string>& 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<std::string>& decoded_info,
+             OutputArray points = noArray(),
+             OutputArrayOfArrays straight_qrcode = noArray()
+     ) const;
  protected:
      struct Impl;
      Ptr<Impl> p;
@@@ -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<Point2f> src_points;
      points.copyTo(src_points);
      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<Point2f> points;
      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<Point2f>& tmp_localization_points);
+     void findQRCodeContours(vector<Point2f>& tmp_localization_points, vector< vector< Point2f > >& true_points_group, const int& num_qrcodes);
+     bool checkSets(vector<vector<Point2f> >& true_points_group, vector<vector<Point2f> >& true_points_group_copy,
+                    vector<Point2f>& tmp_localization_points);
+     void deleteUsedPoints(vector<vector<Point2f> >& true_points_group, vector<vector<Point2f> >& loc,
+                           vector<Point2f>& tmp_localization_points);
+     void fixationPoints(vector<Point2f> &local_point);
+     bool checkPoints(const vector<Point2f>& quadrangle_points);
+     bool checkPointsInsideQuadrangle(const vector<Point2f>& quadrangle_points);
+     bool checkPointsInsideTriangle(const vector<Point2f>& triangle_points);
+     Mat bin_barcode_fullsize, bin_barcode_temp;
+     vector<Point2f> not_resized_loc_points;
+     vector<Point2f> 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<Point2f>& points;
+         compareSquare(const vector<Point2f>& 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<Point2f> 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<Point2f> &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<double>::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<float>((local_point[left_index].x + local_point[right_index].x) * 0.5),
+                         static_cast<float>((local_point[left_index].y + local_point[right_index].y) * 0.5)),
+                 Point2f(0, static_cast<float>(bin_barcode_temp.rows - 1)),
+                 Point2f(static_cast<float>(bin_barcode_temp.cols - 1),
+                         static_cast<float>(bin_barcode_temp.rows - 1))));
+         vector<Point2f> list_area_pnt;
+         list_area_pnt.push_back(current_point);
+         vector<LineIterator> 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<uint8_t>(p);
+                 if (value == future_pixel)
+                 {
+                     future_pixel = static_cast<uint8_t>(~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 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);
 -}
 -
+ bool QRDetectMulti::checkPoints(const vector<Point2f>& quadrangle_points)
+ {
+     if (quadrangle_points.size() != 4)
+         return false;
+     vector<Point2f> 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<LineIterator> 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<uchar>(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<Point2f>& 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<Point2f>& 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<Point2f>& 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<Vec3d> 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<Point2f> 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<int> 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<Vec3d> list_lines_x = searchHorizontalLines();
+     if (list_lines_x.empty())
+         return num_points;
+     vector<Point2f> 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<Point2f>& 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<Vec4i> hierarchy;
+     findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
+     vector<Point2f> 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<Point2f> 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<int>(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<Point2f> 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<vector<Point2f> >& true_points_group, vector<vector<Point2f> >& true_points_group_copy,
+                               vector<Point2f>& 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<vector<Point2f> >& true_points_group, vector<vector<Point2f> >& loc,
+                                      vector<Point2f>& 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<Point2f> 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<Point2f> tmp_localization_points;
+     int num_points = findNumberLocalizationPoints(tmp_localization_points);
+     if (num_points < 3)
+         return false;
+     int num_qrcodes = divUp(num_points, 3);
+     vector<vector<Point2f> > true_points_group;
+     findQRCodeContours(tmp_localization_points, true_points_group, num_qrcodes);
+     for (int q = 0; q < num_qrcodes; q++)
+     {
+        vector<vector<Point2f> > 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<Point> locations, non_zero_elem[3], newHull;
+     vector<Point2f> 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<uint8_t>(localization_point_y, index + 1);
+             if (next_pixel == future_pixel)
+             {
+                 future_pixel = static_cast<uint8_t>(~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<Point2f> 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<Point2f> 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<Point2f> 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<Point2f> 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 decodeQRCodeMulti(
 -        InputArray in, InputArray points,
 -        vector<std::string> &decoded_info, OutputArrayOfArrays straight_qrcode)
 -{
 -    QRCodeDetector qrcode;
 -    vector<cv::String> 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;
 -}
 -
+ class ParallelDecodeProcess : public ParallelLoopBody
+ {
+ public:
+     ParallelDecodeProcess(Mat& inarr_, vector<QRDecode>& qrdec_, vector<std::string>& decoded_info_,
+             vector<Mat>& 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<float>(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<QRDecode>& qrdec;
+     vector<std::string>& decoded_info;
+     vector<Mat>& straight_barcode;
+     vector< vector< Point2f > >& src_points;
+ };
+ bool QRCodeDetector::decodeMulti(
+         InputArray img,
+         InputArray points,
+         CV_OUT std::vector<cv::String>& 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<Point2f> tempMat = qr_points.colRange(i, i + 4);
+         if (contourArea(tempMat) > 0.0)
+         {
+             src_points.push_back(tempMat);
+         }
+     }
+     CV_Assert(src_points.size() > 0);
+     vector<QRDecode> qrdec(src_points.size());
+     vector<Mat> straight_barcode(src_points.size());
+     vector<std::string> info(src_points.size());
+     ParallelDecodeProcess parallelDecodeProcess(inarr, qrdec, info, straight_barcode, src_points);
+     parallel_for_(Range(0, int(src_points.size())), parallelDecodeProcess);
+     vector<Mat> 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<Mat> 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<cv::String>& decoded_info,
+         OutputArray points_,
+         OutputArrayOfArrays straight_qrcode
+     ) const
+ {
+     Mat inarr;
+     if (!checkQRInputImage(img, inarr))
+     {
+         points_.release();
+         return false;
+     }
+     vector<Point2f> 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;
+ }
+ }  // namespace
@@@ -319,6 -316,6 +319,7 @@@ typedef std::vector<std::vector<Mat> > 
  typedef std::vector<UMat> vector_UMat;
  typedef std::vector<DMatch> vector_DMatch;
  typedef std::vector<String> vector_String;
++typedef std::vector<std::string> vector_string;
  typedef std::vector<Scalar> vector_Scalar;
  
  typedef std::vector<std::vector<char> > vector_vector_char;
@@@ -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<string, string, int> 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 &center, 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<VideoWriter> >& _writers, std::vector<std::string>& _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<string> 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<VideoWriter> > 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<VideoWriter>(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<VideoWriter>(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<VideoWriter> >& writers;
 -    std::vector<std::string>& 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<VideoWriter> >& _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<VideoWriter> >* 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<VideoCapture> >& _readers, const std::vector<std::string>& _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<VideoCapture> > readers(NUM);
 +        auto makeCaptures = [&](const Range &r)
 +        {
 +            for (int i = r.start; i != r.end; ++i)
 +                readers[i] = makePtr<VideoCapture>(files[i], CAP_FFMPEG);
 +        };
 +        parallel_for_(R, makeCaptures, GRAN);
 +        for(int i = 0; i < NUM; ++i)
          {
 -            readers->operator[](i) = makePtr<VideoCapture>(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<VideoCapture> >* readers;
 -    const std::vector<std::string>* files;
 -};
 -
 -class ReadImageAndTest :
 -    public ParallelLoopBody
 -{
 -public:
 -    ReadImageAndTest(const std::vector< cv::Ptr<VideoCapture> >& _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<unsigned int>(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<VideoCapture> >* 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<VideoWriter> > writers(threadsCount);
 -    Range range(0, threadsCount);
 -    std::vector<std::string> 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<cv::Ptr<VideoCapture> > 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<std::string>::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
@@@ -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)