#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
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 ¢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<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