From eb243847eb52669a0de678f66bf3148b819d7847 Mon Sep 17 00:00:00 2001 From: Thang Tran Date: Tue, 21 May 2019 13:45:18 +0200 Subject: [PATCH 01/21] js: added floodFill function to JS binding previously floodFill() is white-list without any implementation. floodFill() is now fully functional in JS. --- modules/js/src/core_bindings.cpp | 44 ++++++++++++++++++++++++++ modules/js/src/embindgen.py | 2 +- modules/js/test/test_imgproc.js | 54 ++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) diff --git a/modules/js/src/core_bindings.cpp b/modules/js/src/core_bindings.cpp index ecb8579504..1e52541dde 100644 --- a/modules/js/src/core_bindings.cpp +++ b/modules/js/src/core_bindings.cpp @@ -285,6 +285,40 @@ namespace binding_utils cv::minEnclosingCircle(points, circle.center, circle.radius); return circle; } + + int floodFill_withRect_helper(cv::Mat& arg1, cv::Mat& arg2, Point arg3, Scalar arg4, emscripten::val arg5, Scalar arg6 = Scalar(), Scalar arg7 = Scalar(), int arg8 = 4) + { + cv::Rect rect; + + int rc = cv::floodFill(arg1, arg2, arg3, arg4, &rect, arg6, arg7, arg8); + + arg5.set("x", emscripten::val(rect.x)); + arg5.set("y", emscripten::val(rect.y)); + arg5.set("width", emscripten::val(rect.width)); + arg5.set("height", emscripten::val(rect.height)); + + return rc; + } + + int floodFill_wrapper(cv::Mat& arg1, cv::Mat& arg2, Point arg3, Scalar arg4, emscripten::val arg5, Scalar arg6, Scalar arg7, int arg8) { + return floodFill_withRect_helper(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + } + + int floodFill_wrapper_1(cv::Mat& arg1, cv::Mat& arg2, Point arg3, Scalar arg4, emscripten::val arg5, Scalar arg6, Scalar arg7) { + return floodFill_withRect_helper(arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + + int floodFill_wrapper_2(cv::Mat& arg1, cv::Mat& arg2, Point arg3, Scalar arg4, emscripten::val arg5, Scalar arg6) { + return floodFill_withRect_helper(arg1, arg2, arg3, arg4, arg5, arg6); + } + + int floodFill_wrapper_3(cv::Mat& arg1, cv::Mat& arg2, Point arg3, Scalar arg4, emscripten::val arg5) { + return floodFill_withRect_helper(arg1, arg2, arg3, arg4, arg5); + } + + int floodFill_wrapper_4(cv::Mat& arg1, cv::Mat& arg2, Point arg3, Scalar arg4) { + return cv::floodFill(arg1, arg2, arg3, arg4); + } #endif #ifdef HAVE_OPENCV_VIDEO @@ -546,6 +580,16 @@ EMSCRIPTEN_BINDINGS(binding_utils) #ifdef HAVE_OPENCV_IMGPROC function("minEnclosingCircle", select_overload(&binding_utils::minEnclosingCircle)); + + function("floodFill", select_overload(&binding_utils::floodFill_wrapper)); + + function("floodFill", select_overload(&binding_utils::floodFill_wrapper_1)); + + function("floodFill", select_overload(&binding_utils::floodFill_wrapper_2)); + + function("floodFill", select_overload(&binding_utils::floodFill_wrapper_3)); + + function("floodFill", select_overload(&binding_utils::floodFill_wrapper_4)); #endif function("minMaxLoc", select_overload(&binding_utils::minMaxLoc)); diff --git a/modules/js/src/embindgen.py b/modules/js/src/embindgen.py index ec62bc86d0..9319e5f2a4 100644 --- a/modules/js/src/embindgen.py +++ b/modules/js/src/embindgen.py @@ -84,7 +84,7 @@ ignore_list = ['locate', #int& 'minEnclosingCircle', #float& 'checkRange', 'minMaxLoc', #double* - 'floodFill', + 'floodFill', # special case, implemented in core_bindings.cpp 'phaseCorrelate', 'randShuffle', 'calibrationMatrixValues', #double& diff --git a/modules/js/test/test_imgproc.js b/modules/js/test/test_imgproc.js index b2180c3b87..15bc56c65c 100644 --- a/modules/js/test/test_imgproc.js +++ b/modules/js/test/test_imgproc.js @@ -147,6 +147,60 @@ QUnit.test('test_imgProc', function(assert) { dest.delete(); source.delete(); } + + // floodFill + { + let center = new cv.Point(5, 5); + let rect = new cv.Rect(0, 0, 0, 0); + let img = new cv.Mat.zeros(10, 10, cv.CV_8UC1); + let color = new cv.Scalar (255); + cv.circle(img, center, 3, color, 1); + + let edge = new cv.Mat(); + cv.Canny(img, edge, 100, 255); + cv.copyMakeBorder(edge, edge, 1, 1, 1, 1, cv.BORDER_REPLICATE); + + let expected_img_data = new Uint8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 255, 0, 255, 0, 255, 0, 0, + 0, 0, 255, 255, 255, 255, 0, 0, 255, 0, + 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, + 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + let img_elem = 10*10*1; + let expected_img_data_ptr = cv._malloc(img_elem); + let expected_img_data_heap = new Uint8Array(cv.HEAPU8.buffer, + expected_img_data_ptr, + img_elem); + expected_img_data_heap.set(new Uint8Array(expected_img_data.buffer)); + + let expected_img = new cv.Mat( 10, 10, cv.CV_8UC1, expected_img_data_ptr, 0); + + let expected_rect = new cv.Rect(3,3,3,3); + + let compare_result = new cv.Mat(10, 10, cv.CV_8UC1); + + cv.floodFill(img, edge, center, color, rect); + + cv.compare (img, expected_img, compare_result, cv.CMP_EQ); + + // expect every pixels are the same. + assert.equal (cv.countNonZero(compare_result), img.total()); + assert.equal (rect.x, expected_rect.x); + assert.equal (rect.y, expected_rect.y); + assert.equal (rect.width, expected_rect.width); + assert.equal (rect.height, expected_rect.height); + + img.delete(); + edge.delete(); + expected_img.delete(); + compare_result.delete(); + } }); QUnit.test('test_segmentation', function(assert) { From 7ed858e38ebc753716abc1ec880f42c5e181f802 Mon Sep 17 00:00:00 2001 From: catree Date: Mon, 29 Apr 2019 17:23:23 +0200 Subject: [PATCH 02/21] Fix issue with solvePnPRansac and Nx3 1-channel input when the number of points is 5. Try to uniform the input shape of projectPoints and undistortPoints. --- modules/calib3d/include/opencv2/calib3d.hpp | 2 +- modules/calib3d/perf/perf_pnp.cpp | 4 +- modules/calib3d/src/calibration.cpp | 7 + modules/calib3d/src/solvepnp.cpp | 13 + .../calib3d/test/test_cameracalibration.cpp | 177 +++++++++ modules/calib3d/test/test_solvepnp_ransac.cpp | 372 ++++++++++++++++++ modules/calib3d/test/test_undistort.cpp | 176 +++++++++ modules/imgproc/include/opencv2/imgproc.hpp | 12 +- modules/imgproc/src/undistort.cpp | 12 +- 9 files changed, 763 insertions(+), 12 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index c3139572b8..ac41213c41 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -516,7 +516,7 @@ vector\ ), where N is the number of points in the view. @param distCoeffs Input vector of distortion coefficients \f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6 [, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ of 4, 5, 8, 12 or 14 elements. If the vector is empty, the zero distortion coefficients are assumed. -@param imagePoints Output array of image points, 2xN/Nx2 1-channel or 1xN/Nx1 2-channel, or +@param imagePoints Output array of image points, 1xN/Nx1 2-channel, or vector\ . @param jacobian Optional output 2Nx(10+\) jacobian matrix of derivatives of image points with respect to components of the rotation vector, translation vector, focal lengths, diff --git a/modules/calib3d/perf/perf_pnp.cpp b/modules/calib3d/perf/perf_pnp.cpp index 7c7254a0df..997fba4e06 100644 --- a/modules/calib3d/perf/perf_pnp.cpp +++ b/modules/calib3d/perf/perf_pnp.cpp @@ -12,8 +12,8 @@ typedef perf::TestBaseWithParam PointsNum_Algo; typedef perf::TestBaseWithParam PointsNum; PERF_TEST_P(PointsNum_Algo, solvePnP, - testing::Combine( - testing::Values(5, 3*9, 7*13), //TODO: find why results on 4 points are too unstable + testing::Combine( //When non planar, DLT needs at least 6 points for SOLVEPNP_ITERATIVE flag + testing::Values(6, 3*9, 7*13), //TODO: find why results on 4 points are too unstable testing::Values((int)SOLVEPNP_ITERATIVE, (int)SOLVEPNP_EPNP, (int)SOLVEPNP_UPNP, (int)SOLVEPNP_DLS) ) ) diff --git a/modules/calib3d/src/calibration.cpp b/modules/calib3d/src/calibration.cpp index 9fa1af88cc..9bc48661e3 100644 --- a/modules/calib3d/src/calibration.cpp +++ b/modules/calib3d/src/calibration.cpp @@ -1094,6 +1094,7 @@ CV_IMPL void cvFindExtrinsicCameraParams2( const CvMat* objectPoints, else { // non-planar structure. Use DLT method + CV_CheckGE(count, 6, "DLT algorithm needs at least 6 points for pose estimation from 3D-2D point correspondences."); double* L; double LL[12*12], LW[12], LV[12*12], sc; CvMat _LL = cvMat( 12, 12, CV_64F, LL ); @@ -3314,8 +3315,14 @@ void cv::projectPoints( InputArray _opoints, { Mat opoints = _opoints.getMat(); int npoints = opoints.checkVector(3), depth = opoints.depth(); + if (npoints < 0) + opoints = opoints.t(); + npoints = opoints.checkVector(3); CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_64F)); + if (opoints.cols == 3) + opoints = opoints.reshape(3); + CvMat dpdrot, dpdt, dpdf, dpdc, dpddist; CvMat *pdpdrot=0, *pdpdt=0, *pdpdf=0, *pdpdc=0, *pdpddist=0; diff --git a/modules/calib3d/src/solvepnp.cpp b/modules/calib3d/src/solvepnp.cpp index 0fdca0d510..c7735a73a8 100644 --- a/modules/calib3d/src/solvepnp.cpp +++ b/modules/calib3d/src/solvepnp.cpp @@ -245,6 +245,9 @@ bool solvePnPRansac(InputArray _opoints, InputArray _ipoints, if( model_points == npoints ) { + opoints = opoints.reshape(3); + ipoints = ipoints.reshape(2); + bool result = solvePnP(opoints, ipoints, cameraMatrix, distCoeffs, _rvec, _tvec, useExtrinsicGuess, ransac_kernel_method); if(!result) @@ -350,6 +353,11 @@ int solveP3P( InputArray _opoints, InputArray _ipoints, CV_Assert( npoints == 3 || npoints == 4 ); CV_Assert( flags == SOLVEPNP_P3P || flags == SOLVEPNP_AP3P ); + if (opoints.cols == 3) + opoints = opoints.reshape(3); + if (ipoints.cols == 2) + ipoints = ipoints.reshape(2); + Mat cameraMatrix0 = _cameraMatrix.getMat(); Mat distCoeffs0 = _distCoeffs.getMat(); Mat cameraMatrix = Mat_(cameraMatrix0); @@ -745,6 +753,11 @@ int solvePnPGeneric( InputArray _opoints, InputArray _ipoints, CV_Assert( ( (npoints >= 4) || (npoints == 3 && flags == SOLVEPNP_ITERATIVE && useExtrinsicGuess) ) && npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) ); + if (opoints.cols == 3) + opoints = opoints.reshape(3); + if (ipoints.cols == 2) + ipoints = ipoints.reshape(2); + if( flags != SOLVEPNP_ITERATIVE ) useExtrinsicGuess = false; diff --git a/modules/calib3d/test/test_cameracalibration.cpp b/modules/calib3d/test/test_cameracalibration.cpp index f35fd30771..adf4e454be 100644 --- a/modules/calib3d/test/test_cameracalibration.cpp +++ b/modules/calib3d/test/test_cameracalibration.cpp @@ -2071,6 +2071,183 @@ TEST(Calib3d_CalibrationMatrixValues_C, accuracy) { CV_CalibrationMatrixValuesTe TEST(Calib3d_CalibrationMatrixValues_CPP, accuracy) { CV_CalibrationMatrixValuesTest_CPP test; test.safe_run(); } TEST(Calib3d_ProjectPoints_C, accuracy) { CV_ProjectPointsTest_C test; test.safe_run(); } TEST(Calib3d_ProjectPoints_CPP, regression) { CV_ProjectPointsTest_CPP test; test.safe_run(); } + +TEST(Calib3d_ProjectPoints_CPP, inputShape) +{ + Matx31d rvec = Matx31d::zeros(); + Matx31d tvec(0, 0, 1); + Matx33d cameraMatrix = Matx33d::eye(); + const float L = 0.1f; + { + //3xN 1-channel + Mat objectPoints = (Mat_(3, 2) << -L, L, + L, L, + 0, 0); + vector imagePoints; + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(objectPoints.cols, static_cast(imagePoints.size())); + EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits::epsilon()); + } + { + //Nx2 1-channel + Mat objectPoints = (Mat_(2, 3) << -L, L, 0, + L, L, 0); + vector imagePoints; + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(objectPoints.rows, static_cast(imagePoints.size())); + EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits::epsilon()); + } + { + //1xN 3-channel + Mat objectPoints(1, 2, CV_32FC3); + objectPoints.at(0,0) = Vec3f(-L, L, 0); + objectPoints.at(0,1) = Vec3f(L, L, 0); + + vector imagePoints; + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(objectPoints.cols, static_cast(imagePoints.size())); + EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits::epsilon()); + } + { + //Nx1 3-channel + Mat objectPoints(2, 1, CV_32FC3); + objectPoints.at(0,0) = Vec3f(-L, L, 0); + objectPoints.at(1,0) = Vec3f(L, L, 0); + + vector imagePoints; + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(objectPoints.rows, static_cast(imagePoints.size())); + EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits::epsilon()); + } + { + //vector + vector objectPoints; + objectPoints.push_back(Point3f(-L, L, 0)); + objectPoints.push_back(Point3f(L, L, 0)); + + vector imagePoints; + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(objectPoints.size(), imagePoints.size()); + EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits::epsilon()); + } + { + //vector + vector objectPoints; + objectPoints.push_back(Point3d(-L, L, 0)); + objectPoints.push_back(Point3d(L, L, 0)); + + vector imagePoints; + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(objectPoints.size(), imagePoints.size()); + EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits::epsilon()); + } +} + +TEST(Calib3d_ProjectPoints_CPP, outputShape) +{ + Matx31d rvec = Matx31d::zeros(); + Matx31d tvec(0, 0, 1); + Matx33d cameraMatrix = Matx33d::eye(); + const float L = 0.1f; + { + vector objectPoints; + objectPoints.push_back(Point3f(-L, L, 0)); + objectPoints.push_back(Point3f( L, L, 0)); + objectPoints.push_back(Point3f( L, -L, 0)); + + //Mat --> will be Nx1 2-channel + Mat imagePoints; + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(static_cast(objectPoints.size()), imagePoints.rows); + EXPECT_NEAR(imagePoints.at(0,0)(0), -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(0,0)(1), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(1,0)(0), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(1,0)(1), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(2,0)(0), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(2,0)(1), -L, std::numeric_limits::epsilon()); + } + { + vector objectPoints; + objectPoints.push_back(Point3f(-L, L, 0)); + objectPoints.push_back(Point3f( L, L, 0)); + objectPoints.push_back(Point3f( L, -L, 0)); + + //Nx1 2-channel + Mat imagePoints(3,1,CV_32FC2); + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(static_cast(objectPoints.size()), imagePoints.rows); + EXPECT_NEAR(imagePoints.at(0,0)(0), -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(0,0)(1), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(1,0)(0), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(1,0)(1), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(2,0)(0), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(2,0)(1), -L, std::numeric_limits::epsilon()); + } + { + vector objectPoints; + objectPoints.push_back(Point3f(-L, L, 0)); + objectPoints.push_back(Point3f( L, L, 0)); + objectPoints.push_back(Point3f( L, -L, 0)); + + //1xN 2-channel + Mat imagePoints(1,3,CV_32FC2); + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(static_cast(objectPoints.size()), imagePoints.cols); + EXPECT_NEAR(imagePoints.at(0,0)(0), -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(0,0)(1), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(0,1)(0), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(0,1)(1), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(0,2)(0), L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints.at(0,2)(1), -L, std::numeric_limits::epsilon()); + } + { + vector objectPoints; + objectPoints.push_back(Point3f(-L, L, 0)); + objectPoints.push_back(Point3f(L, L, 0)); + + //vector + vector imagePoints; + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(objectPoints.size(), imagePoints.size()); + EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits::epsilon()); + } + { + vector objectPoints; + objectPoints.push_back(Point3d(-L, L, 0)); + objectPoints.push_back(Point3d(L, L, 0)); + + //vector + vector imagePoints; + projectPoints(objectPoints, rvec, tvec, cameraMatrix, noArray(), imagePoints); + EXPECT_EQ(objectPoints.size(), imagePoints.size()); + EXPECT_NEAR(imagePoints[0].x, -L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[0].y, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].x, L, std::numeric_limits::epsilon()); + EXPECT_NEAR(imagePoints[1].y, L, std::numeric_limits::epsilon()); + } +} + TEST(Calib3d_StereoCalibrate_C, regression) { CV_StereoCalibrationTest_C test; test.safe_run(); } TEST(Calib3d_StereoCalibrate_CPP, regression) { CV_StereoCalibrationTest_CPP test; test.safe_run(); } TEST(Calib3d_StereoCalibrateCorner, regression) { CV_StereoCalibrationCornerTest test; test.safe_run(); } diff --git a/modules/calib3d/test/test_solvepnp_ransac.cpp b/modules/calib3d/test/test_solvepnp_ransac.cpp index 77a5d5df8d..0d35fa7126 100644 --- a/modules/calib3d/test/test_solvepnp_ransac.cpp +++ b/modules/calib3d/test/test_solvepnp_ransac.cpp @@ -1211,6 +1211,7 @@ TEST(Calib3d_SolvePnP, translation) p3d.push_back(Point3f(0,10,10)); p3d.push_back(Point3f(10,10,10)); p3d.push_back(Point3f(2,5,5)); + p3d.push_back(Point3f(-4,8,6)); vector p2d; projectPoints(p3d, crvec, ctvec, cameraIntrinsic, noArray(), p2d); @@ -1845,4 +1846,375 @@ TEST(Calib3d_SolvePnP, refine) } } +TEST(Calib3d_SolvePnPRansac, minPoints) +{ + //https://github.com/opencv/opencv/issues/14423 + Mat matK = Mat::eye(3,3,CV_64FC1); + Mat distCoeff = Mat::zeros(1,5,CV_64FC1); + Matx31d true_rvec(0.9072420896651262, 0.09226497171882152, 0.8880772883671504); + Matx31d true_tvec(7.376333362427632, 8.434449036856979, 13.79801619778456); + + { + //nb points = 5 --> ransac_kernel_method = SOLVEPNP_EPNP + Mat keypoints13D = (Mat_(5, 3) << 12.00604, -2.8654366, 18.472504, + 7.6863389, 4.9355154, 11.146358, + 14.260933, 2.8320458, 12.582781, + 3.4562225, 8.2668982, 11.300434, + 15.316854, 3.7486348, 12.491116); + vector imagesPoints; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, imagesPoints); + + Mat keypoints22D(keypoints13D.rows, 2, CV_32FC1); + vector objectPoints; + for (int i = 0; i < static_cast(imagesPoints.size()); i++) + { + keypoints22D.at(i,0) = imagesPoints[i].x; + keypoints22D.at(i,1) = imagesPoints[i].y; + objectPoints.push_back(Point3f(keypoints13D.at(i,0), keypoints13D.at(i,1), keypoints13D.at(i,2))); + } + + Mat rvec = Mat::zeros(1,3,CV_64FC1); + Mat Tvec = Mat::zeros(1,3,CV_64FC1); + solvePnPRansac(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec); + + Mat rvec2, Tvec2; + solvePnP(objectPoints, imagesPoints, matK, distCoeff, rvec2, Tvec2, false, SOLVEPNP_EPNP); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-4); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-4); + EXPECT_LE(cvtest::norm(rvec, rvec2, NORM_INF), 1e-6); + EXPECT_LE(cvtest::norm(Tvec, Tvec2, NORM_INF), 1e-6); + } + { + //nb points = 4 --> ransac_kernel_method = SOLVEPNP_P3P + Mat keypoints13D = (Mat_(4, 3) << 12.00604, -2.8654366, 18.472504, + 7.6863389, 4.9355154, 11.146358, + 14.260933, 2.8320458, 12.582781, + 3.4562225, 8.2668982, 11.300434); + vector imagesPoints; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, imagesPoints); + + Mat keypoints22D(keypoints13D.rows, 2, CV_32FC1); + vector objectPoints; + for (int i = 0; i < static_cast(imagesPoints.size()); i++) + { + keypoints22D.at(i,0) = imagesPoints[i].x; + keypoints22D.at(i,1) = imagesPoints[i].y; + objectPoints.push_back(Point3f(keypoints13D.at(i,0), keypoints13D.at(i,1), keypoints13D.at(i,2))); + } + + Mat rvec = Mat::zeros(1,3,CV_64FC1); + Mat Tvec = Mat::zeros(1,3,CV_64FC1); + solvePnPRansac(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec); + + Mat rvec2, Tvec2; + solvePnP(objectPoints, imagesPoints, matK, distCoeff, rvec2, Tvec2, false, SOLVEPNP_P3P); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-4); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-4); + EXPECT_LE(cvtest::norm(rvec, rvec2, NORM_INF), 1e-6); + EXPECT_LE(cvtest::norm(Tvec, Tvec2, NORM_INF), 1e-6); + } +} + +TEST(Calib3d_SolvePnPRansac, inputShape) +{ + //https://github.com/opencv/opencv/issues/14423 + Mat matK = Mat::eye(3,3,CV_64FC1); + Mat distCoeff = Mat::zeros(1,5,CV_64FC1); + Matx31d true_rvec(0.9072420896651262, 0.09226497171882152, 0.8880772883671504); + Matx31d true_tvec(7.376333362427632, 8.434449036856979, 13.79801619778456); + + { + //Nx3 1-channel + Mat keypoints13D = (Mat_(6, 3) << 12.00604, -2.8654366, 18.472504, + 7.6863389, 4.9355154, 11.146358, + 14.260933, 2.8320458, 12.582781, + 3.4562225, 8.2668982, 11.300434, + 10.00604, 2.8654366, 15.472504, + -4.6863389, 5.9355154, 13.146358); + vector imagesPoints; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, imagesPoints); + + Mat keypoints22D(keypoints13D.rows, 2, CV_32FC1); + for (int i = 0; i < static_cast(imagesPoints.size()); i++) + { + keypoints22D.at(i,0) = imagesPoints[i].x; + keypoints22D.at(i,1) = imagesPoints[i].y; + } + + Mat rvec, Tvec; + solvePnPRansac(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-6); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-6); + } + { + //1xN 3-channel + Mat keypoints13D(1, 6, CV_32FC3); + keypoints13D.at(0,0) = Vec3f(12.00604f, -2.8654366f, 18.472504f); + keypoints13D.at(0,1) = Vec3f(7.6863389f, 4.9355154f, 11.146358f); + keypoints13D.at(0,2) = Vec3f(14.260933f, 2.8320458f, 12.582781f); + keypoints13D.at(0,3) = Vec3f(3.4562225f, 8.2668982f, 11.300434f); + keypoints13D.at(0,4) = Vec3f(10.00604f, 2.8654366f, 15.472504f); + keypoints13D.at(0,5) = Vec3f(-4.6863389f, 5.9355154f, 13.146358f); + + vector imagesPoints; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, imagesPoints); + + Mat keypoints22D(keypoints13D.rows, keypoints13D.cols, CV_32FC2); + for (int i = 0; i < static_cast(imagesPoints.size()); i++) + { + keypoints22D.at(0,i) = Vec2f(imagesPoints[i].x, imagesPoints[i].y); + } + + Mat rvec, Tvec; + solvePnPRansac(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-6); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-6); + } + { + //Nx1 3-channel + Mat keypoints13D(6, 1, CV_32FC3); + keypoints13D.at(0,0) = Vec3f(12.00604f, -2.8654366f, 18.472504f); + keypoints13D.at(1,0) = Vec3f(7.6863389f, 4.9355154f, 11.146358f); + keypoints13D.at(2,0) = Vec3f(14.260933f, 2.8320458f, 12.582781f); + keypoints13D.at(3,0) = Vec3f(3.4562225f, 8.2668982f, 11.300434f); + keypoints13D.at(4,0) = Vec3f(10.00604f, 2.8654366f, 15.472504f); + keypoints13D.at(5,0) = Vec3f(-4.6863389f, 5.9355154f, 13.146358f); + + vector imagesPoints; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, imagesPoints); + + Mat keypoints22D(keypoints13D.rows, keypoints13D.cols, CV_32FC2); + for (int i = 0; i < static_cast(imagesPoints.size()); i++) + { + keypoints22D.at(i,0) = Vec2f(imagesPoints[i].x, imagesPoints[i].y); + } + + Mat rvec, Tvec; + solvePnPRansac(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-6); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-6); + } + { + //vector + vector keypoints13D; + keypoints13D.push_back(Point3f(12.00604f, -2.8654366f, 18.472504f)); + keypoints13D.push_back(Point3f(7.6863389f, 4.9355154f, 11.146358f)); + keypoints13D.push_back(Point3f(14.260933f, 2.8320458f, 12.582781f)); + keypoints13D.push_back(Point3f(3.4562225f, 8.2668982f, 11.300434f)); + keypoints13D.push_back(Point3f(10.00604f, 2.8654366f, 15.472504f)); + keypoints13D.push_back(Point3f(-4.6863389f, 5.9355154f, 13.146358f)); + + vector keypoints22D; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, keypoints22D); + + Mat rvec, Tvec; + solvePnPRansac(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-6); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-6); + } + { + //vector + vector keypoints13D; + keypoints13D.push_back(Point3d(12.00604f, -2.8654366f, 18.472504f)); + keypoints13D.push_back(Point3d(7.6863389f, 4.9355154f, 11.146358f)); + keypoints13D.push_back(Point3d(14.260933f, 2.8320458f, 12.582781f)); + keypoints13D.push_back(Point3d(3.4562225f, 8.2668982f, 11.300434f)); + keypoints13D.push_back(Point3d(10.00604f, 2.8654366f, 15.472504f)); + keypoints13D.push_back(Point3d(-4.6863389f, 5.9355154f, 13.146358f)); + + vector keypoints22D; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, keypoints22D); + + Mat rvec, Tvec; + solvePnPRansac(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-6); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-6); + } +} + +TEST(Calib3d_SolvePnP, inputShape) +{ + //https://github.com/opencv/opencv/issues/14423 + Mat matK = Mat::eye(3,3,CV_64FC1); + Mat distCoeff = Mat::zeros(1,5,CV_64FC1); + Matx31d true_rvec(0.407, 0.092, 0.88); + Matx31d true_tvec(0.576, -0.43, 1.3798); + + vector objectPoints; + const double L = 0.5; + objectPoints.push_back(Point3d(-L, -L, L)); + objectPoints.push_back(Point3d( L, -L, L)); + objectPoints.push_back(Point3d( L, L, L)); + objectPoints.push_back(Point3d(-L, L, L)); + objectPoints.push_back(Point3d(-L, -L, -L)); + objectPoints.push_back(Point3d( L, -L, -L)); + + const int methodsCount = 6; + int methods[] = {SOLVEPNP_ITERATIVE, SOLVEPNP_EPNP, SOLVEPNP_P3P, SOLVEPNP_AP3P, SOLVEPNP_IPPE, SOLVEPNP_IPPE_SQUARE}; + for (int method = 0; method < methodsCount; method++) + { + if (methods[method] == SOLVEPNP_IPPE_SQUARE) + { + objectPoints[0] = Point3d(-L, L, 0); + objectPoints[1] = Point3d( L, L, 0); + objectPoints[2] = Point3d( L, -L, 0); + objectPoints[3] = Point3d(-L, -L, 0); + } + + { + //Nx3 1-channel + Mat keypoints13D; + if (methods[method] == SOLVEPNP_P3P || methods[method] == SOLVEPNP_AP3P || + methods[method] == SOLVEPNP_IPPE || methods[method] == SOLVEPNP_IPPE_SQUARE) + { + keypoints13D = Mat(4, 3, CV_32FC1); + } + else + { + keypoints13D = Mat(6, 3, CV_32FC1); + } + + for (int i = 0; i < keypoints13D.rows; i++) + { + keypoints13D.at(i,0) = static_cast(objectPoints[i].x); + keypoints13D.at(i,1) = static_cast(objectPoints[i].y); + keypoints13D.at(i,2) = static_cast(objectPoints[i].z); + } + + vector imagesPoints; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, imagesPoints); + + Mat keypoints22D(keypoints13D.rows, 2, CV_32FC1); + for (int i = 0; i < static_cast(imagesPoints.size()); i++) + { + keypoints22D.at(i,0) = imagesPoints[i].x; + keypoints22D.at(i,1) = imagesPoints[i].y; + } + + Mat rvec, Tvec; + solvePnP(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec, false, methods[method]); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-3); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-3); + } + { + //1xN 3-channel + Mat keypoints13D; + if (methods[method] == SOLVEPNP_P3P || methods[method] == SOLVEPNP_AP3P || + methods[method] == SOLVEPNP_IPPE || methods[method] == SOLVEPNP_IPPE_SQUARE) + { + keypoints13D = Mat(1, 4, CV_32FC3); + } + else + { + keypoints13D = Mat(1, 6, CV_32FC3); + } + + for (int i = 0; i < keypoints13D.cols; i++) + { + keypoints13D.at(0,i) = Vec3f(static_cast(objectPoints[i].x), + static_cast(objectPoints[i].y), + static_cast(objectPoints[i].z)); + } + + vector imagesPoints; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, imagesPoints); + + Mat keypoints22D(keypoints13D.rows, keypoints13D.cols, CV_32FC2); + for (int i = 0; i < static_cast(imagesPoints.size()); i++) + { + keypoints22D.at(0,i) = Vec2f(imagesPoints[i].x, imagesPoints[i].y); + } + + Mat rvec, Tvec; + solvePnP(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec, false, methods[method]); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-3); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-3); + } + { + //Nx1 3-channel + Mat keypoints13D; + if (methods[method] == SOLVEPNP_P3P || methods[method] == SOLVEPNP_AP3P || + methods[method] == SOLVEPNP_IPPE || methods[method] == SOLVEPNP_IPPE_SQUARE) + { + keypoints13D = Mat(4, 1, CV_32FC3); + } + else + { + keypoints13D = Mat(6, 1, CV_32FC3); + } + + for (int i = 0; i < keypoints13D.rows; i++) + { + keypoints13D.at(i,0) = Vec3f(static_cast(objectPoints[i].x), + static_cast(objectPoints[i].y), + static_cast(objectPoints[i].z)); + } + + vector imagesPoints; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, imagesPoints); + + Mat keypoints22D(keypoints13D.rows, keypoints13D.cols, CV_32FC2); + for (int i = 0; i < static_cast(imagesPoints.size()); i++) + { + keypoints22D.at(i,0) = Vec2f(imagesPoints[i].x, imagesPoints[i].y); + } + + Mat rvec, Tvec; + solvePnP(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec, false, methods[method]); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-3); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-3); + } + { + //vector + vector keypoints13D; + const int nbPts = (methods[method] == SOLVEPNP_P3P || methods[method] == SOLVEPNP_AP3P || + methods[method] == SOLVEPNP_IPPE || methods[method] == SOLVEPNP_IPPE_SQUARE) ? 4 : 6; + for (int i = 0; i < nbPts; i++) + { + keypoints13D.push_back(Point3f(static_cast(objectPoints[i].x), + static_cast(objectPoints[i].y), + static_cast(objectPoints[i].z))); + } + + vector keypoints22D; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, keypoints22D); + + Mat rvec, Tvec; + solvePnP(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec, false, methods[method]); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-3); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-3); + } + { + //vector + vector keypoints13D; + const int nbPts = (methods[method] == SOLVEPNP_P3P || methods[method] == SOLVEPNP_AP3P || + methods[method] == SOLVEPNP_IPPE || methods[method] == SOLVEPNP_IPPE_SQUARE) ? 4 : 6; + for (int i = 0; i < nbPts; i++) + { + keypoints13D.push_back(objectPoints[i]); + } + + vector keypoints22D; + projectPoints(keypoints13D, true_rvec, true_tvec, matK, distCoeff, keypoints22D); + + Mat rvec, Tvec; + solvePnP(keypoints13D, keypoints22D, matK, distCoeff, rvec, Tvec, false, methods[method]); + + EXPECT_LE(cvtest::norm(true_rvec, rvec, NORM_INF), 1e-3); + EXPECT_LE(cvtest::norm(true_tvec, Tvec, NORM_INF), 1e-3); + } + } +} + }} // namespace diff --git a/modules/calib3d/test/test_undistort.cpp b/modules/calib3d/test/test_undistort.cpp index e4fe4fe1f3..a4590b45e6 100644 --- a/modules/calib3d/test/test_undistort.cpp +++ b/modules/calib3d/test/test_undistort.cpp @@ -938,4 +938,180 @@ TEST(Calib3d_DefaultNewCameraMatrix, accuracy) { CV_DefaultNewCameraMatrixTest t TEST(Calib3d_UndistortPoints, accuracy) { CV_UndistortPointsTest test; test.safe_run(); } TEST(Calib3d_InitUndistortRectifyMap, accuracy) { CV_InitUndistortRectifyMapTest test; test.safe_run(); } +TEST(Calib3d_UndistortPoints, inputShape) +{ + //https://github.com/opencv/opencv/issues/14423 + Matx33d cameraMatrix = Matx33d::eye(); + { + //2xN 1-channel + Mat imagePoints(2, 3, CV_32FC1); + imagePoints.at(0,0) = 320; imagePoints.at(1,0) = 240; + imagePoints.at(0,1) = 0; imagePoints.at(1,1) = 240; + imagePoints.at(0,2) = 320; imagePoints.at(1,2) = 0; + + vector normalized; + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(static_cast(normalized.size()), imagePoints.cols); + for (int i = 0; i < static_cast(normalized.size()); i++) { + EXPECT_NEAR(normalized[i].x, imagePoints.at(0,i), std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized[i].y, imagePoints.at(1,i), std::numeric_limits::epsilon()); + } + } + { + //Nx2 1-channel + Mat imagePoints(3, 2, CV_32FC1); + imagePoints.at(0,0) = 320; imagePoints.at(0,1) = 240; + imagePoints.at(1,0) = 0; imagePoints.at(1,1) = 240; + imagePoints.at(2,0) = 320; imagePoints.at(2,1) = 0; + + vector normalized; + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(static_cast(normalized.size()), imagePoints.rows); + for (int i = 0; i < static_cast(normalized.size()); i++) { + EXPECT_NEAR(normalized[i].x, imagePoints.at(i,0), std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized[i].y, imagePoints.at(i,1), std::numeric_limits::epsilon()); + } + } + { + //1xN 2-channel + Mat imagePoints(1, 3, CV_32FC2); + imagePoints.at(0,0) = Vec2f(320, 240); + imagePoints.at(0,1) = Vec2f(0, 240); + imagePoints.at(0,2) = Vec2f(320, 0); + + vector normalized; + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(static_cast(normalized.size()), imagePoints.cols); + for (int i = 0; i < static_cast(normalized.size()); i++) { + EXPECT_NEAR(normalized[i].x, imagePoints.at(0,i)(0), std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized[i].y, imagePoints.at(0,i)(1), std::numeric_limits::epsilon()); + } + } + { + //Nx1 2-channel + Mat imagePoints(3, 1, CV_32FC2); + imagePoints.at(0,0) = Vec2f(320, 240); + imagePoints.at(1,0) = Vec2f(0, 240); + imagePoints.at(2,0) = Vec2f(320, 0); + + vector normalized; + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(static_cast(normalized.size()), imagePoints.rows); + for (int i = 0; i < static_cast(normalized.size()); i++) { + EXPECT_NEAR(normalized[i].x, imagePoints.at(i,0)(0), std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized[i].y, imagePoints.at(i,0)(1), std::numeric_limits::epsilon()); + } + } + { + //vector + vector imagePoints; + imagePoints.push_back(Point2f(320, 240)); + imagePoints.push_back(Point2f(0, 240)); + imagePoints.push_back(Point2f(320, 0)); + + vector normalized; + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(normalized.size(), imagePoints.size()); + for (int i = 0; i < static_cast(normalized.size()); i++) { + EXPECT_NEAR(normalized[i].x, imagePoints[i].x, std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized[i].y, imagePoints[i].y, std::numeric_limits::epsilon()); + } + } + { + //vector + vector imagePoints; + imagePoints.push_back(Point2d(320, 240)); + imagePoints.push_back(Point2d(0, 240)); + imagePoints.push_back(Point2d(320, 0)); + + vector normalized; + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(normalized.size(), imagePoints.size()); + for (int i = 0; i < static_cast(normalized.size()); i++) { + EXPECT_NEAR(normalized[i].x, imagePoints[i].x, std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized[i].y, imagePoints[i].y, std::numeric_limits::epsilon()); + } + } +} + +TEST(Calib3d_UndistortPoints, outputShape) +{ + Matx33d cameraMatrix = Matx33d::eye(); + { + vector imagePoints; + imagePoints.push_back(Point2f(320, 240)); + imagePoints.push_back(Point2f(0, 240)); + imagePoints.push_back(Point2f(320, 0)); + + //Mat --> will be Nx1 2-channel + Mat normalized; + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(static_cast(imagePoints.size()), normalized.rows); + for (int i = 0; i < normalized.rows; i++) { + EXPECT_NEAR(normalized.at(i,0)(0), imagePoints[i].x, std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized.at(i,0)(1), imagePoints[i].y, std::numeric_limits::epsilon()); + } + } + { + vector imagePoints; + imagePoints.push_back(Point2f(320, 240)); + imagePoints.push_back(Point2f(0, 240)); + imagePoints.push_back(Point2f(320, 0)); + + //Nx1 2-channel + Mat normalized(static_cast(imagePoints.size()), 1, CV_32FC2); + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(static_cast(imagePoints.size()), normalized.rows); + for (int i = 0; i < normalized.rows; i++) { + EXPECT_NEAR(normalized.at(i,0)(0), imagePoints[i].x, std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized.at(i,0)(1), imagePoints[i].y, std::numeric_limits::epsilon()); + } + } + { + vector imagePoints; + imagePoints.push_back(Point2f(320, 240)); + imagePoints.push_back(Point2f(0, 240)); + imagePoints.push_back(Point2f(320, 0)); + + //1xN 2-channel + Mat normalized(1, static_cast(imagePoints.size()), CV_32FC2); + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(static_cast(imagePoints.size()), normalized.cols); + for (int i = 0; i < normalized.rows; i++) { + EXPECT_NEAR(normalized.at(0,i)(0), imagePoints[i].x, std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized.at(0,i)(1), imagePoints[i].y, std::numeric_limits::epsilon()); + } + } + { + vector imagePoints; + imagePoints.push_back(Point2f(320, 240)); + imagePoints.push_back(Point2f(0, 240)); + imagePoints.push_back(Point2f(320, 0)); + + //vector + vector normalized; + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(imagePoints.size(), normalized.size()); + for (int i = 0; i < static_cast(normalized.size()); i++) { + EXPECT_NEAR(normalized[i].x, imagePoints[i].x, std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized[i].y, imagePoints[i].y, std::numeric_limits::epsilon()); + } + } + { + vector imagePoints; + imagePoints.push_back(Point2d(320, 240)); + imagePoints.push_back(Point2d(0, 240)); + imagePoints.push_back(Point2d(320, 0)); + + //vector + vector normalized; + undistortPoints(imagePoints, normalized, cameraMatrix, noArray()); + EXPECT_EQ(imagePoints.size(), normalized.size()); + for (int i = 0; i < static_cast(normalized.size()); i++) { + EXPECT_NEAR(normalized[i].x, imagePoints[i].x, std::numeric_limits::epsilon()); + EXPECT_NEAR(normalized[i].y, imagePoints[i].y, std::numeric_limits::epsilon()); + } + } +} + }} // namespace diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index ada11ac073..5d2b5f8ff7 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3116,9 +3116,9 @@ point coordinates out of the normalized distorted point coordinates ("normalized coordinates do not depend on the camera matrix). The function can be used for both a stereo camera head or a monocular camera (when R is empty). - -@param src Observed point coordinates, 1xN or Nx1 2-channel (CV_32FC2 or CV_64FC2). -@param dst Output ideal point coordinates after undistortion and reverse perspective +@param src Observed point coordinates, 2xN/Nx2 1-channel or 1xN/Nx1 2-channel (CV_32FC2 or CV_64FC2) (or +vector\ ). +@param dst Output ideal point coordinates (1xN/Nx1 2-channel or vector\ ) after undistortion and reverse perspective transformation. If matrix P is identity or omitted, dst will contain normalized point coordinates. @param cameraMatrix Camera matrix \f$\vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$ . @param distCoeffs Input vector of distortion coefficients @@ -3131,14 +3131,14 @@ of 4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion */ CV_EXPORTS_W void undistortPoints( InputArray src, OutputArray dst, InputArray cameraMatrix, InputArray distCoeffs, - InputArray R = noArray(), InputArray P = noArray()); + InputArray R = noArray(), InputArray P = noArray() ); /** @overload @note Default version of #undistortPoints does 5 iterations to compute undistorted points. */ CV_EXPORTS_AS(undistortPointsIter) void undistortPoints( InputArray src, OutputArray dst, - InputArray cameraMatrix, InputArray distCoeffs, - InputArray R, InputArray P, TermCriteria criteria); + InputArray cameraMatrix, InputArray distCoeffs, + InputArray R, InputArray P, TermCriteria criteria ); //! @} imgproc_transform diff --git a/modules/imgproc/src/undistort.cpp b/modules/imgproc/src/undistort.cpp index 14e5d37d13..418313df8e 100644 --- a/modules/imgproc/src/undistort.cpp +++ b/modules/imgproc/src/undistort.cpp @@ -561,10 +561,16 @@ void cv::undistortPoints( InputArray _src, OutputArray _dst, Mat src = _src.getMat(), cameraMatrix = _cameraMatrix.getMat(); Mat distCoeffs = _distCoeffs.getMat(), R = _Rmat.getMat(), P = _Pmat.getMat(); - CV_Assert( src.isContinuous() && (src.depth() == CV_32F || src.depth() == CV_64F) && - ((src.rows == 1 && src.channels() == 2) || src.cols*src.channels() == 2)); + int npoints = src.checkVector(2), depth = src.depth(); + if (npoints < 0) + src = src.t(); + npoints = src.checkVector(2); + CV_Assert(npoints >= 0 && src.isContinuous() && (depth == CV_32F || depth == CV_64F)); - _dst.create(src.size(), src.type(), -1, true); + if (src.cols == 2) + src = src.reshape(2); + + _dst.create(npoints, 1, CV_MAKETYPE(depth, 2), -1, true); Mat dst = _dst.getMat(); CvMat _csrc = cvMat(src), _cdst = cvMat(dst), _ccameraMatrix = cvMat(cameraMatrix); From fb9c083a511ed61020461350230cc543f6c39202 Mon Sep 17 00:00:00 2001 From: Jan Starzynski Date: Thu, 23 May 2019 14:40:33 +0200 Subject: [PATCH 03/21] handle all orientations properly --- modules/imgcodecs/src/grfmt_tiff.cpp | 77 +++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index a604423c62..1ab0b1d242 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -334,6 +334,77 @@ bool TiffDecoder::nextPage() readHeader(); } +static void fixOrientationPartial(Mat &img, uint16 orientation) +{ + switch(orientation) { + case ORIENTATION_RIGHTTOP: + case ORIENTATION_LEFTBOT: + flip(img, img, -1); + /* fall through */ + + case ORIENTATION_LEFTTOP: + case ORIENTATION_RIGHTBOT: + transpose(img, img); + break; + } +} + +static void fixOrientationFull(Mat &img, int orientation) +{ + switch(orientation) { + case ORIENTATION_TOPRIGHT: + flip(img, img, 1); + break; + + case ORIENTATION_BOTRIGHT: + flip(img, img, -1); + break; + + case ORIENTATION_BOTLEFT: + flip(img, img, 0); + break; + + case ORIENTATION_LEFTTOP: + transpose(img, img); + break; + + case ORIENTATION_RIGHTTOP: + transpose(img, img); + flip(img, img, 1); + break; + + case ORIENTATION_RIGHTBOT: + transpose(img, img); + flip(img, img, -1); + break; + + case ORIENTATION_LEFTBOT: + transpose(img, img); + flip(img, img, 0); + break; + } +} + +/** + * Fix orientation defined in tag 274. + * For 8 bit some corrections are done by TIFFReadRGBAStrip/Tile already. + * Not so for 16/32/64 bit. + */ +static void fixOrientation(Mat &img, uint16 orientation, int dst_bpp) +{ + switch(dst_bpp) { + case 8: + fixOrientationPartial(img, orientation); + break; + + case 16: + case 32: + case 64: + fixOrientationFull(img, orientation); + break; + } +} + bool TiffDecoder::readData( Mat& img ) { int type_ = img.type(); @@ -363,10 +434,11 @@ bool TiffDecoder::readData( Mat& img ) CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &ncn)); uint16 img_orientation = ORIENTATION_TOPLEFT; CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_ORIENTATION, &img_orientation)); - bool vert_flip = (img_orientation == ORIENTATION_BOTRIGHT) || (img_orientation == ORIENTATION_RIGHTBOT) || - (img_orientation == ORIENTATION_BOTLEFT) || (img_orientation == ORIENTATION_LEFTBOT); const int bitsPerByte = 8; int dst_bpp = (int)(img.elemSize1() * bitsPerByte); + bool vert_flip = dst_bpp == 8 && + (img_orientation == ORIENTATION_BOTRIGHT || img_orientation == ORIENTATION_RIGHTBOT || + img_orientation == ORIENTATION_BOTLEFT || img_orientation == ORIENTATION_LEFTBOT); int wanted_channels = normalizeChannelsNumber(img.channels()); if (dst_bpp == 8) @@ -579,6 +651,7 @@ bool TiffDecoder::readData( Mat& img ) } // for x } // for y } + fixOrientation(img, img_orientation, dst_bpp); } if (m_hdr && depth >= CV_32F) From 2acc7c96e750342230d134d249a3f0afc9d8e758 Mon Sep 17 00:00:00 2001 From: Lukas Mehl Date: Thu, 23 May 2019 17:11:44 +0200 Subject: [PATCH 04/21] remove bgsgm tutorial, set references to OpenCV-Tutorials --- doc/py_tutorials/py_tutorials.markdown | 2 +- .../py_video/images/background.jpg | Bin 4343 -> 0 bytes doc/py_tutorials/py_video/images/camshift.jpg | Bin 5566 -> 0 bytes doc/py_tutorials/py_video/images/lucas.jpg | Bin 5761 -> 0 bytes .../py_video/images/opticalflow.jpeg | Bin 6488 -> 0 bytes .../py_bg_subtraction/images/resframe.jpg | Bin 20013 -> 0 bytes .../py_bg_subtraction/images/resgmg.jpg | Bin 11631 -> 0 bytes .../py_bg_subtraction/images/resmog.jpg | Bin 6749 -> 0 bytes .../py_bg_subtraction/images/resmog2.jpg | Bin 10941 -> 0 bytes .../py_bg_subtraction.markdown | 171 +----------------- .../py_table_of_contents_video.markdown | 14 +- 11 files changed, 3 insertions(+), 184 deletions(-) delete mode 100644 doc/py_tutorials/py_video/images/background.jpg delete mode 100644 doc/py_tutorials/py_video/images/camshift.jpg delete mode 100644 doc/py_tutorials/py_video/images/lucas.jpg delete mode 100644 doc/py_tutorials/py_video/images/opticalflow.jpeg delete mode 100644 doc/py_tutorials/py_video/py_bg_subtraction/images/resframe.jpg delete mode 100644 doc/py_tutorials/py_video/py_bg_subtraction/images/resgmg.jpg delete mode 100644 doc/py_tutorials/py_video/py_bg_subtraction/images/resmog.jpg delete mode 100644 doc/py_tutorials/py_video/py_bg_subtraction/images/resmog2.jpg diff --git a/doc/py_tutorials/py_tutorials.markdown b/doc/py_tutorials/py_tutorials.markdown index 532da4e7ec..7d9298d68e 100644 --- a/doc/py_tutorials/py_tutorials.markdown +++ b/doc/py_tutorials/py_tutorials.markdown @@ -25,7 +25,7 @@ OpenCV-Python Tutorials {#tutorial_py_root} In this section you will learn about feature detectors and descriptors -- @subpage tutorial_py_table_of_contents_video +- @ref tutorial_table_of_content_video In this section you will learn different techniques to work with videos like object tracking etc. diff --git a/doc/py_tutorials/py_video/images/background.jpg b/doc/py_tutorials/py_video/images/background.jpg deleted file mode 100644 index b45de1e2f0c37f6a670fe6fc47b13f9977db36ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4343 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 diff --git a/doc/py_tutorials/py_video/images/camshift.jpg b/doc/py_tutorials/py_video/images/camshift.jpg deleted file mode 100644 index 72a2eff5ae6f7e89a6530db9c7aaec4a5e4c6872..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5566 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 YCAIG{Ay1~M7K5me`fAU^f^L5Q2iY#OH~;_u diff --git a/doc/py_tutorials/py_video/images/lucas.jpg b/doc/py_tutorials/py_video/images/lucas.jpg deleted file mode 100644 index 19d839b63ba0075934e39f9b5445aca45a382eca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5761 zcmbW4XH*kRm&ZesmInyZ4ImJTgd!llDL!;1QUjqk6+#PDKt%*mBZ7pYD808(L+_nX zB|xMjgd!llH(8$d-Lrene%jstoVj1-&Yb%@ch1~9m*bZ|0Ze*Yx>^7-G5~<=Y5*>$ z0QUh@6qJ;d6jWD+ii+wQHIRn-N{saMv_KFeGc$-01iH=!X1~tD#R>v(@NsZ)bMx}@ zUT5cr@bf^xJiI*r971-rmHHYr0}TxW4-1He=l`tB766C_@C@*bf{Y(P4kDufkzIBG zc(3B5BKwyB|0yzZ3d*Y}X=v%_uN*3w0OVv86y#U&UjbZszqnclP=ctK`K0b$yKZPt z&5vS{4vk5t5zr`WWi=Y!hR8U0zow;QW9Q)H5)={^xg~l>R!&|)QR)8Qnp)aAx_ZVY zre<(+3xuPS^W!H^U0i*9{rm%-1qOwMzlnJJ?mZe48yBDO`Ag#0jLfX;oZP(pg7OMn zWmR=eZCzV?M`u@e55AW$GCD^5F+MRlzd%}CT3-3Jy0){sw|{VWbbNC94;LAL;@?1IWOBKJDtq3SSyf*cTC^SEU#gsb?|IG)SO zrVL}|CLNpPk6nK%zN}qYofzp2Olu*P{xQY6PM6t6g({Ut29gTLJ3kxLmcAOglj)eW z^lZz-4(m2dfW}w4 zd~wer6v)Uiv$bI=T##IZ{pIbTSbD%Jx<-Jx7ayLX{o#a8-wmEoBekc|jp8-9ugi-{ z4X^U;qwaPqT!@Ypr{-F^GFuHnKhqD|HmlDYK4oVriYn9K=15*~fj9+?#AS@cU7uj( zk`V7aE3>Y9-esw0#4j$`;h|SUc}hARHYxFUQHq$j-dB}e&ao|{X_a5SrD-h#b`nrZ z8u>U=ZT-T16#Zw!u&zOKvdl7CBXFJgQ45c)q<@-Vn8@It-=$cH41tL7)p*XOC^<%w z(3uNTl+5k?pD~UR3V2d8Z?fmZ0lel${zsuTJ#Zc3LZu$FuH-8>Iqefz$anJ^Jx0Af zTQ{q-$)Xe2$Ho6z%?&hk2`DaHEnnU1iA;HXIRC+y5*Tt1E{2=6B zJZa(VzAZ3K&B4h_NSwzSDT^yIL#7QovNghzLJrm=qPViWuv-Ji6#WAFpbg%~O@~G2 zQEMT<)z7zC-XLb!#Z)R%g1tiMmf1R7T?@HazPyt*)jc>Y&eh-Xf19^T5G>c_CP<33 zzj94@pS@a!9j!uXBKc0f!oE>cI`O<%AIdhXWR*nv5`U;#v8h!{s_@ocxCOqf6whw_ zz3y60|Efc2H*gh^!Z})E3=97DCG!pY(v4S;XvABqd%Wmb?Om45zP+OwZ1(mn7TeX; z)$4*KE%_IZ2}enLL4OsRX61F73@f`1#SUe2)#Q&Pxz*OjuANqJZaOOESs3ue3R>=5 z0_bkO@l97Lz5PjQafO3h`NCCS^@b%n_?>Jar(25P46}|LCe88YQ30C~&DKECF57F{ zru!bXuD9?-5y=* z7H?|ReBYx4+UamA>l129zvZ;p2Pw)Wa@zbHEf4_pZgJrZZYGK=UXT+~sb4kaD~t0h zZ;panqPQ58y@HPNH&QldU2f9X-IPnce|2AN3a9X#RdEQt;XaYJVueM=MpHu8SjKB> z>0euQVir!5GDo57$8fQG@z~D{QH7OH8d~aQ9 z9>KIU(aL3VB=v=9oqAmxEFd7@Iq`$bvWRj3|6)`V&ZfbP$GR@<%o6DC9mHz~4blFp zlhjWf4#lm^7FglkQF)tRxrc=BM{aCb^_DdD^<}6i{LHl2Yeh^Q@@+fEL5n5ix>dft zdw_}^2%@|3ff*aZ;xMJI3|!#^!-eM?!58&sckSNc1)xbQ)r-hHqL3w)F4)J1KYp@R zI}NW+XeesSD73Jcd72vI>Yb^9fC?S1{_>XhH^VL*dlYXtB@6o9;!5{8N7=My&uUcY zs@6WPFcl+D2LymYHqJjMBn)z8&zHVVWZcfj5B`pNb%x$|XX`o*Ry3t+XGwF9jrgU_ zQKGEivfJf3Z#H*2#-sdplRId#`oeF``MKqHs?B(zTKYi`_jyo}PJP%dk7wYc5+I>O z443?1%GGSCnpt1ki$TWLto6D!10LdH@N$nV)`o0Hzb$n_X<5ewyBXH+B=7|WqTsmO{1U}zdO{#~-c{&#Ju`B%v= z9~XfsIQVF%zIh|_cvoSvZj;A1mlwcyR5Km7kd@VZ;H0Yxe^4+bzO+=X)gl@b;Xr+| zasR`(do2AV_ro{3Ug32I-F}*5C?lT(K_+o zm`Kuci$LTBvYIhGe6345U^D2%<1d3oRyE7Ut%(2|q=t^?%Ba|;q;vlf)2AT;U`)c# zu^ILb_D@zFu|0b&=Jq}#+eINjn>MjxjCX)OPye-6g`3OR36S8Z!qcv9;8gS@#csVV ztQOC{AUEjrkU56|_e7)N4nk7Uxopb(I6bQ=W@3+ngHGubZmwaQUHeM(PNMsCB;9UK z*^|NFVKwJPio73@SCy*F(GN_E;`=};;*$jQS4FmWI&Wi^vwja7&rdRC{^?d_o;axX z5Be?7JCOiv(+mK!Qpg&$SvETC5(7*lZhGC1;!PP_N#L5!SL-WZmbL_0VPJmSiw*Ve99LJ+1-;O0h&Bp$Hlz@uS|nG zCDj`A8~4q^Iufc}?4on~X@g(?bZgn#dUU+5{nl#xpfhoj$F86xGA80$W$|GJc_2mh z6AM?SX~BLe1^-HaIADLdVa+Jc%GKBmRlQQL1_fDHi#^H*&=Hn%L+nj@ zLoJ*fXgOcH3VOBcTxN=8u z`YYOC2r>u{RJc_`@^o|LhW;R~Jg4RIFfzEY)P1s0dkJ_Kq*`>!Wci-HKm3a`r}9f9 zFP|y&fpmjL`g@OzW1M^|$%Y{-S2#qCcs|J0wX9hmVcymtQA?jX^mUq0Nt2zTgXW0( z6TXPDDo#EuywOn|;p>HAn&8$7Y~4VZyKFsq2Fm2T1SEX?Ay7u%c&^ETKd)B*yY;d8 z11Do6SNMez_n%bpf$91lt!Zyv``Id-ezs-SH-~1iYd>kaXjGBCxU+~Ttzboct1cZr zU4`zeB2f1bYSRSQbZbEC+LuabUjhmm9@yAIl|y1S43kE=!s>RPcoC)aMfS)uFy8P` z^h|j`c^u4B&e5-(Vh`x8mQvX>zAhInAa+C(rS<*eIPo{!sM{N)-$&N^5%q~|wey+6 zSD9m{0E6o4w_+(%xf49;+nWom%T84S8{++8ifyFmb(>JC{@g+9`9r2gZEDVLUc|0C zS$m*RB}#BJDIsA8z{jx^%_|REvccwj87K_fr5Gf|#;)5^BoQK!@=dc(7SCZe-|`3a z;6|30fL6Y%cG!}YFOpfwiIkj6s2$*H^?m4Yx@Iw}D@lqh6%e>tcHv_g9r6wZdD;Rd z5lj7$wK;z&QSO0mXI8T&aLH8UmItR|mMxkq;MuCSFo|sScN!DE?c`61_;N=5|b#_9+GeJ0jS^UHWKcJwqU_Ajs?yF#Jj+2GOG zl;5H^d?ZaW7h;Kd5U;W``x%Bqud3u{@y)M>Mvso+fA{}zv@X2YquD^wy>HS$@xXx_rHBQWO|#`}L_9JL4=*A3;p^#trWTU0qxI*d1$YQl`r`ugVQ*eUQ$K$U?ETx@fH zePHXkP|UpbFb55xi&6nRp&1DCvhgH_sBSo+n-OQICUTbr#TV0fz)yH#FMsw}L zD}8N1!x*HZB+r7m!}~9Z3e?dQNP5EEh1@GL z;88h3fB*Ax;lUt-k4bX;Y%*It#G; z%Bz^MJDw2jDc%(fHna_(Cs^gbq~3b28r4x$hlwN%dwk5UvkYN0AP6UlHMazX4MLUg z1q$In18i&#s8?#XyU1acdp`>yHROM3J*)xzSp@U;n|{2}Vu&!m!+$9JX_q^FGH|8r zsD%9tPS0fkA8VHRxFbYfEc7URY!g>eK0+8;L?MzR#dG_sc06t`%4zv*Srs?3sBfe3 zveR}Nx4Buo1NYa7je2|Y%Vn(R5828*-}0{g@ETf#CLTv`_jNRn{{SWOf$*;jDi*i3 z^LPjAN)@BnCp9Dr#&Wz#QY>tAg?{XC;V~ZSOl#Lk`-EUxe_hPmTVwb3)%7qsn_J)Y zl@ciJs-0^?4nV{o;HLVTFe_ z<@S$u4#;}SuU-NeoF}^jC__noqLEc|ux$6qwO~7GF}vGGEBtAp#P4rv%+!!>wsG6A z{leRVnXqN6>d#zqn_6V&$gjA#Kac?Xy%dv7g}Lg)#!MSdapG!xj8o)oPdA@|priI& zLZv!jh8`aed2JRo6K$lo!jn^@&&c!(Hh+Tk6Vf%$8E1+z)#>ghm+40-I19! zQT_FJ89;p)gW~41l8*xp>&7m*^tu-Nf9iN`Rodc;@|5M-ju20`O6JlhSLKIKbh+%S ziUDA{k*nXoH%)J~&0juBn$4d=vp*kn+NOj^0kj40buZbXWB)V4!No-v; zqZi68oSoBfP;UKu^-VXJN2Cz(ymWSrD^@ee+B}aWoGt)1v^hXTf17o8KM^?X4lCmb z`R&SU9_BHZKery^`lzMMeC;F-7&l(Tk#zO~8oDF-{`m{Wj}Dat-?vH*b+i%>cEas6 z7D;)4MaL4bp=O|>+t1WyDsi6m=IrQxo@;Nc^xx>rXvtg4`fnhoatoE-?sQ=fr*s@G zeI%|eJGuF;P|H@AAkX&aaIOr640DO2GQdT$<7KpHq1{ta+^8cm5yirC`%@-2OReY3 zoYu3Dx6h&J&tf~hak2W8#xRx4UaGr`F|CVclJD-^Qc+(}{-!7tWuBt(U8%||k)*b7 zXlt#1-NJE(8ruE&$)Y_BbZ3UW;0L_vL(Lcx`Ms)I`AgS*$%MNeWMF0`Z_$m-SfpPr zj5Ur>RGwLom4({f4)D!DuNN=r=CW7i$_W`|jD$@+iq_Y`C|l%cr_i+6<<F?QuL88J~OJt_`pD7sQx#KuJx7t9@KBavCS<(gy@?fy+wny9epuHU<^ z>_{1w1orPlj4bTB6Im(w&+{6#sW~#xCv@1H;giQg-toFZr?H>z`{r2dym#B;FYfuC zKvJYg?LtFE3i064KeD_ssUznSWm1V-#sl88_Ot@v)UDuQO;+5FtTVLsrS=cI<8_5r QLz7zr3f;&~nahd)04aL}kpKVy diff --git a/doc/py_tutorials/py_video/images/opticalflow.jpeg b/doc/py_tutorials/py_video/images/opticalflow.jpeg deleted file mode 100644 index bd2a88232fcec222d87992d129f6803a2b022a36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6488 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 LkrdULbvOMVhY`nv diff --git a/doc/py_tutorials/py_video/py_bg_subtraction/images/resframe.jpg b/doc/py_tutorials/py_video/py_bg_subtraction/images/resframe.jpg deleted file mode 100644 index 70e34dbd85c13b2540d430d7c174cafa30579789..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20013 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 aQ~ng23l==MeH;GzG*dkE_xz0%fdARo^vhZR diff --git a/doc/py_tutorials/py_video/py_bg_subtraction/images/resgmg.jpg b/doc/py_tutorials/py_video/py_bg_subtraction/images/resgmg.jpg deleted file mode 100644 index e7178fc818327d1495dac65bc22b4ac4745a7cc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11631 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 SrxSqz&U1#Wf7sdYng0W$nXsDx diff --git a/doc/py_tutorials/py_video/py_bg_subtraction/py_bg_subtraction.markdown b/doc/py_tutorials/py_video/py_bg_subtraction/py_bg_subtraction.markdown index 4235e91639..78762f2a62 100644 --- a/doc/py_tutorials/py_video/py_bg_subtraction/py_bg_subtraction.markdown +++ b/doc/py_tutorials/py_video/py_bg_subtraction/py_bg_subtraction.markdown @@ -1,173 +1,4 @@ Background Subtraction {#tutorial_py_bg_subtraction} ====================== -Goal ----- - -In this chapter, - -- We will familiarize with the background subtraction methods available in OpenCV. - -Basics ------- - -Background subtraction is a major preprocessing step in many vision-based applications. For -example, consider the case of a visitor counter where a static camera takes the number of visitors -entering or leaving the room, or a traffic camera extracting information about the vehicles etc. In -all these cases, first you need to extract the person or vehicles alone. Technically, you need to -extract the moving foreground from static background. - -If you have an image of background alone, like an image of the room without visitors, image of the road -without vehicles etc, it is an easy job. Just subtract the new image from the background. You get -the foreground objects alone. But in most of the cases, you may not have such an image, so we need -to extract the background from whatever images we have. It becomes more complicated when there are -shadows of the vehicles. Since shadows also move, simple subtraction will mark that also as -foreground. It complicates things. - -Several algorithms were introduced for this purpose. OpenCV has implemented three such algorithms -which are very easy to use. We will see them one-by-one. - -### BackgroundSubtractorMOG - -It is a Gaussian Mixture-based Background/Foreground Segmentation Algorithm. It was introduced in -the paper "An improved adaptive background mixture model for real-time tracking with shadow -detection" by P. KadewTraKuPong and R. Bowden in 2001. It uses a method to model each background -pixel by a mixture of K Gaussian distributions (K = 3 to 5). The weights of the mixture represent -the time proportions that those colours stay in the scene. The probable background colours are the -ones which stay longer and more static. - -While coding, we need to create a background object using the function, -**cv.createBackgroundSubtractorMOG()**. It has some optional parameters like length of history, -number of gaussian mixtures, threshold etc. It is all set to some default values. Then inside the -video loop, use backgroundsubtractor.apply() method to get the foreground mask. - -See a simple example below: -@code{.py} -import numpy as np -import cv2 as cv - -cap = cv.VideoCapture('vtest.avi') - -fgbg = cv.bgsegm.createBackgroundSubtractorMOG() - -while(1): - ret, frame = cap.read() - - fgmask = fgbg.apply(frame) - - cv.imshow('frame',fgmask) - k = cv.waitKey(30) & 0xff - if k == 27: - break - -cap.release() -cv.destroyAllWindows() -@endcode -( All the results are shown at the end for comparison). - -### BackgroundSubtractorMOG2 - -It is also a Gaussian Mixture-based Background/Foreground Segmentation Algorithm. It is based on two -papers by Z.Zivkovic, "Improved adaptive Gaussian mixture model for background subtraction" in 2004 -and "Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction" -in 2006. One important feature of this algorithm is that it selects the appropriate number of -gaussian distribution for each pixel. (Remember, in last case, we took a K gaussian distributions -throughout the algorithm). It provides better adaptability to varying scenes due illumination -changes etc. - -As in previous case, we have to create a background subtractor object. Here, you have an option of -detecting shadows or not. If detectShadows = True (which is so by default), it -detects and marks shadows, but decreases the speed. Shadows will be marked in gray color. -@code{.py} -import numpy as np -import cv2 as cv - -cap = cv.VideoCapture('vtest.avi') - -fgbg = cv.createBackgroundSubtractorMOG2() - -while(1): - ret, frame = cap.read() - - fgmask = fgbg.apply(frame) - - cv.imshow('frame',fgmask) - k = cv.waitKey(30) & 0xff - if k == 27: - break - -cap.release() -cv.destroyAllWindows() -@endcode -(Results given at the end) - -### BackgroundSubtractorGMG - -This algorithm combines statistical background image estimation and per-pixel Bayesian segmentation. -It was introduced by Andrew B. Godbehere, Akihiro Matsukawa, and Ken Goldberg in their paper "Visual -Tracking of Human Visitors under Variable-Lighting Conditions for a Responsive Audio Art -Installation" in 2012. As per the paper, the system ran a successful interactive audio art -installation called “Are We There Yet?” from March 31 - July 31 2011 at the Contemporary Jewish -Museum in San Francisco, California. - -It uses first few (120 by default) frames for background modelling. It employs probabilistic -foreground segmentation algorithm that identifies possible foreground objects using Bayesian -inference. The estimates are adaptive; newer observations are more heavily weighted than old -observations to accommodate variable illumination. Several morphological filtering operations like -closing and opening are done to remove unwanted noise. You will get a black window during first few -frames. - -It would be better to apply morphological opening to the result to remove the noises. -@code{.py} -import numpy as np -import cv2 as cv - -cap = cv.VideoCapture('vtest.avi') - -kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE,(3,3)) -fgbg = cv.bgsegm.createBackgroundSubtractorGMG() - -while(1): - ret, frame = cap.read() - - fgmask = fgbg.apply(frame) - fgmask = cv.morphologyEx(fgmask, cv.MORPH_OPEN, kernel) - - cv.imshow('frame',fgmask) - k = cv.waitKey(30) & 0xff - if k == 27: - break - -cap.release() -cv.destroyAllWindows() -@endcode -Results -------- - -**Original Frame** - -Below image shows the 200th frame of a video - -![image](images/resframe.jpg) - -**Result of BackgroundSubtractorMOG** - -![image](images/resmog.jpg) - -**Result of BackgroundSubtractorMOG2** - -Gray color region shows shadow region. - -![image](images/resmog2.jpg) - -**Result of BackgroundSubtractorGMG** - -Noise is removed with morphological opening. - -![image](images/resgmg.jpg) - -Additional Resources --------------------- - -Exercises ---------- +Tutorial content has been moved: @ref tutorial_background_subtraction diff --git a/doc/py_tutorials/py_video/py_table_of_contents_video.markdown b/doc/py_tutorials/py_video/py_table_of_contents_video.markdown index badb14e200..c1204e231c 100644 --- a/doc/py_tutorials/py_video/py_table_of_contents_video.markdown +++ b/doc/py_tutorials/py_video/py_table_of_contents_video.markdown @@ -1,16 +1,4 @@ Video Analysis {#tutorial_py_table_of_contents_video} ============== -- @ref tutorial_meanshift - - We have already seen - an example of color-based tracking. It is simpler. This time, we see significantly better - algorithms like "Meanshift", and its upgraded version, "Camshift" to find and track them. - -- @ref tutorial_optical_flow - - Now let's discuss an important concept, "Optical Flow", which is related to videos and has many applications. - -- @subpage tutorial_py_bg_subtraction - - In several applications, we need to extract foreground for further operations like object tracking. Background Subtraction is a well-known method in those cases. +Content has been moved: @ref tutorial_table_of_content_video From f9564e053da8e524693c8b4ae9fb8020a3668e65 Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Fri, 24 May 2019 10:45:09 +0200 Subject: [PATCH 05/21] java: test: use assertNotNull and assertFalse --- .../calib3d/misc/java/test/Calib3dTest.java | 6 ++-- modules/core/misc/java/test/MatTest.java | 4 +-- .../core/misc/java/test/RotatedRectTest.java | 28 +++++++++---------- .../misc/java/test/ImgcodecsTest.java | 4 +-- .../src/org/opencv/test/OpenCVTestCase.java | 2 +- .../src/org/opencv/test/OpenCVTestRunner.java | 2 +- .../org/opencv/test/android/UtilsTest.java | 6 ++-- .../src/org/opencv/test/OpenCVTestCase.java | 2 +- .../misc/java/test/CascadeClassifierTest.java | 6 ++-- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/modules/calib3d/misc/java/test/Calib3dTest.java b/modules/calib3d/misc/java/test/Calib3dTest.java index 99153dfb21..3929e29f01 100644 --- a/modules/calib3d/misc/java/test/Calib3dTest.java +++ b/modules/calib3d/misc/java/test/Calib3dTest.java @@ -177,7 +177,7 @@ public class Calib3dTest extends OpenCVTestCase { Size patternSize = new Size(9, 6); MatOfPoint2f corners = new MatOfPoint2f(); Calib3d.findChessboardCorners(grayChess, patternSize, corners); - assertTrue(!corners.empty()); + assertFalse(corners.empty()); } public void testFindChessboardCornersMatSizeMatInt() { @@ -185,7 +185,7 @@ public class Calib3dTest extends OpenCVTestCase { MatOfPoint2f corners = new MatOfPoint2f(); Calib3d.findChessboardCorners(grayChess, patternSize, corners, Calib3d.CALIB_CB_ADAPTIVE_THRESH + Calib3d.CALIB_CB_NORMALIZE_IMAGE + Calib3d.CALIB_CB_FAST_CHECK); - assertTrue(!corners.empty()); + assertFalse(corners.empty()); } public void testFind4QuadCornerSubpix() { @@ -194,7 +194,7 @@ public class Calib3dTest extends OpenCVTestCase { Size region_size = new Size(5, 5); Calib3d.findChessboardCorners(grayChess, patternSize, corners); Calib3d.find4QuadCornerSubpix(grayChess, corners, region_size); - assertTrue(!corners.empty()); + assertFalse(corners.empty()); } public void testFindCirclesGridMatSizeMat() { diff --git a/modules/core/misc/java/test/MatTest.java b/modules/core/misc/java/test/MatTest.java index 4c8c52b09c..cdd7950843 100644 --- a/modules/core/misc/java/test/MatTest.java +++ b/modules/core/misc/java/test/MatTest.java @@ -266,7 +266,7 @@ public class MatTest extends OpenCVTestCase { public void testEmpty() { assertTrue(dst.empty()); - assertTrue(!gray0.empty()); + assertFalse(gray0.empty()); } public void testEyeIntIntInt() { @@ -1194,7 +1194,7 @@ public class MatTest extends OpenCVTestCase { } public void testToString() { - assertTrue(null != gray0.toString()); + assertNotNull(gray0.toString()); } public void testTotal() { diff --git a/modules/core/misc/java/test/RotatedRectTest.java b/modules/core/misc/java/test/RotatedRectTest.java index b1b4a677d3..76966ff4ae 100644 --- a/modules/core/misc/java/test/RotatedRectTest.java +++ b/modules/core/misc/java/test/RotatedRectTest.java @@ -48,7 +48,7 @@ public class RotatedRectTest extends OpenCVTestCase { RotatedRect rrect = new RotatedRect(center, size, angle); RotatedRect clone = rrect.clone(); - assertTrue(clone != null); + assertNotNull(clone); assertTrue(rrect.center.equals(clone.center)); assertTrue(rrect.size.equals(clone.size)); assertTrue(rrect.angle == clone.angle); @@ -66,24 +66,24 @@ public class RotatedRectTest extends OpenCVTestCase { RotatedRect clone2 = rrect2.clone(); assertTrue(rrect1.equals(rrect3)); - assertTrue(!rrect1.equals(rrect2)); + assertFalse(rrect1.equals(rrect2)); assertTrue(rrect2.equals(clone2)); clone2.angle = 10; - assertTrue(!rrect2.equals(clone2)); + assertFalse(rrect2.equals(clone2)); assertTrue(rrect1.equals(clone1)); clone1.center.x += 1; - assertTrue(!rrect1.equals(clone1)); + assertFalse(rrect1.equals(clone1)); clone1.center.x -= 1; assertTrue(rrect1.equals(clone1)); clone1.size.width += 1; - assertTrue(!rrect1.equals(clone1)); + assertFalse(rrect1.equals(clone1)); - assertTrue(!rrect1.equals(size)); + assertFalse(rrect1.equals(size)); } public void testHashCode() { @@ -140,10 +140,10 @@ public class RotatedRectTest extends OpenCVTestCase { public void testRotatedRect() { RotatedRect rr = new RotatedRect(); - assertTrue(rr != null); - assertTrue(rr.center != null); - assertTrue(rr.size != null); - assertTrue(rr.angle == 0.0); + assertNotNull(rr); + assertNotNull(rr.center); + assertNotNull(rr.size); + assertEquals(0.0, rr.angle); } public void testRotatedRectDoubleArray() { @@ -161,10 +161,10 @@ public class RotatedRectTest extends OpenCVTestCase { public void testRotatedRectPointSizeDouble() { RotatedRect rr = new RotatedRect(center, size, 40); - assertTrue(rr != null); - assertTrue(rr.center != null); - assertTrue(rr.size != null); - assertTrue(rr.angle == 40.0); + assertNotNull(rr); + assertNotNull(rr.center); + assertNotNull(rr.size); + assertEquals(40.0, rr.angle); } public void testSet() { diff --git a/modules/imgcodecs/misc/java/test/ImgcodecsTest.java b/modules/imgcodecs/misc/java/test/ImgcodecsTest.java index 50ef147b22..8fe15d2536 100644 --- a/modules/imgcodecs/misc/java/test/ImgcodecsTest.java +++ b/modules/imgcodecs/misc/java/test/ImgcodecsTest.java @@ -38,7 +38,7 @@ public class ImgcodecsTest extends OpenCVTestCase { public void testImreadString() { dst = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH); - assertTrue(!dst.empty()); + assertFalse(dst.empty()); assertEquals(3, dst.channels()); assertTrue(512 == dst.cols()); assertTrue(512 == dst.rows()); @@ -46,7 +46,7 @@ public class ImgcodecsTest extends OpenCVTestCase { public void testImreadStringInt() { dst = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH, 0); - assertTrue(!dst.empty()); + assertFalse(dst.empty()); assertEquals(1, dst.channels()); assertTrue(512 == dst.cols()); assertTrue(512 == dst.rows()); diff --git a/modules/java/test/android_test/src/org/opencv/test/OpenCVTestCase.java b/modules/java/test/android_test/src/org/opencv/test/OpenCVTestCase.java index cc7eb9dca7..e9f6d43f31 100644 --- a/modules/java/test/android_test/src/org/opencv/test/OpenCVTestCase.java +++ b/modules/java/test/android_test/src/org/opencv/test/OpenCVTestCase.java @@ -581,7 +581,7 @@ public class OpenCVTestCase extends TestCase { message = TAG + " :: " + "could not instantiate " + cname + "! Exception: " + ex.getMessage(); } - assertTrue(message, instance!=null); + assertNotNull(message, instance); return instance; } diff --git a/modules/java/test/android_test/src/org/opencv/test/OpenCVTestRunner.java b/modules/java/test/android_test/src/org/opencv/test/OpenCVTestRunner.java index d5c1ecd206..c924cabe3f 100644 --- a/modules/java/test/android_test/src/org/opencv/test/OpenCVTestRunner.java +++ b/modules/java/test/android_test/src/org/opencv/test/OpenCVTestRunner.java @@ -96,7 +96,7 @@ public class OpenCVTestRunner extends InstrumentationTestRunner { } context = getContext(); - Assert.assertTrue("Context can't be 'null'", context != null); + Assert.assertNotNull("Context can't be 'null'", context); LENA_PATH = Utils.exportResource(context, R.drawable.lena); CHESS_PATH = Utils.exportResource(context, R.drawable.chessboard); LBPCASCADE_FRONTALFACE_PATH = Utils.exportResource(context, R.raw.lbpcascade_frontalface); diff --git a/modules/java/test/android_test/src/org/opencv/test/android/UtilsTest.java b/modules/java/test/android_test/src/org/opencv/test/android/UtilsTest.java index 5494513158..6e6acf8193 100644 --- a/modules/java/test/android_test/src/org/opencv/test/android/UtilsTest.java +++ b/modules/java/test/android_test/src/org/opencv/test/android/UtilsTest.java @@ -74,7 +74,7 @@ public class UtilsTest extends OpenCVTestCase { // RGBA Mat imgRGBA = new Mat(); Imgproc.cvtColor(imgBGR, imgRGBA, Imgproc.COLOR_BGR2RGBA); - assertTrue(!imgRGBA.empty() && imgRGBA.channels() == 4); + assertFalse(imgRGBA.empty() && imgRGBA.channels() == 4); bmp16.eraseColor(Color.BLACK); m16.setTo(s0); Utils.matToBitmap(imgRGBA, bmp16); Utils.bitmapToMat(bmp16, m16); @@ -92,7 +92,7 @@ public class UtilsTest extends OpenCVTestCase { // RGB Mat imgRGB = new Mat(); Imgproc.cvtColor(imgBGR, imgRGB, Imgproc.COLOR_BGR2RGB); - assertTrue(!imgRGB.empty() && imgRGB.channels() == 3); + assertFalse(imgRGB.empty() && imgRGB.channels() == 3); bmp16.eraseColor(Color.BLACK); m16.setTo(s0); Utils.matToBitmap(imgRGB, bmp16); Utils.bitmapToMat(bmp16, m16); @@ -110,7 +110,7 @@ public class UtilsTest extends OpenCVTestCase { // Gray Mat imgGray = new Mat(); Imgproc.cvtColor(imgBGR, imgGray, Imgproc.COLOR_BGR2GRAY); - assertTrue(!imgGray.empty() && imgGray.channels() == 1); + assertFalse(imgGray.empty() && imgGray.channels() == 1); Mat tmp = new Mat(); bmp16.eraseColor(Color.BLACK); m16.setTo(s0); diff --git a/modules/java/test/pure_test/src/org/opencv/test/OpenCVTestCase.java b/modules/java/test/pure_test/src/org/opencv/test/OpenCVTestCase.java index 7dc3432add..ccc96ffd2b 100644 --- a/modules/java/test/pure_test/src/org/opencv/test/OpenCVTestCase.java +++ b/modules/java/test/pure_test/src/org/opencv/test/OpenCVTestCase.java @@ -607,7 +607,7 @@ public class OpenCVTestCase extends TestCase { message = TAG + " :: " + "could not instantiate " + cname + "! Exception: " + ex.getMessage(); } - assertTrue(message, instance!=null); + assertNotNull(message, instance); return instance; } diff --git a/modules/objdetect/misc/java/test/CascadeClassifierTest.java b/modules/objdetect/misc/java/test/CascadeClassifierTest.java index 5304fac21e..de7b095414 100644 --- a/modules/objdetect/misc/java/test/CascadeClassifierTest.java +++ b/modules/objdetect/misc/java/test/CascadeClassifierTest.java @@ -22,12 +22,12 @@ public class CascadeClassifierTest extends OpenCVTestCase { public void testCascadeClassifier() { cc = new CascadeClassifier(); - assertTrue(null != cc); + assertNotNull(cc); } public void testCascadeClassifierString() { cc = new CascadeClassifier(OpenCVTestRunner.LBPCASCADE_FRONTALFACE_PATH); - assertTrue(null != cc); + assertNotNull(cc); } public void testDetectMultiScaleMatListOfRect() { @@ -98,7 +98,7 @@ public class CascadeClassifierTest extends OpenCVTestCase { public void testLoad() { cc = new CascadeClassifier(); cc.load(OpenCVTestRunner.LBPCASCADE_FRONTALFACE_PATH); - assertTrue(!cc.empty()); + assertFalse(cc.empty()); } } From f0fb91f2d42dba32fde8dc19c94bf4decddc4b39 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Thu, 23 May 2019 13:45:14 +0300 Subject: [PATCH 06/21] Fixed v_signmask implementation for AVX2, updated universal intrinsics tests. --- modules/core/include/opencv2/core/hal/intrin_avx.hpp | 10 +++------- modules/core/test/test_intrin_utils.hpp | 9 ++++----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_avx.hpp b/modules/core/include/opencv2/core/hal/intrin_avx.hpp index 91e4483444..cd7490bb0d 100644 --- a/modules/core/include/opencv2/core/hal/intrin_avx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_avx.hpp @@ -1227,18 +1227,14 @@ inline int v_signmask(const v_uint8x32& a) { return v_signmask(v_reinterpret_as_s8(a)); } inline int v_signmask(const v_int16x16& a) -{ - v_int8x32 v = v_int8x32(_mm256_packs_epi16(a.val, a.val)); - return v_signmask(v) & 255; -} +{ return v_signmask(v_pack(a, a)) & 0xFFFF; } inline int v_signmask(const v_uint16x16& a) { return v_signmask(v_reinterpret_as_s16(a)); } inline int v_signmask(const v_int32x8& a) { - __m256i a16 = _mm256_packs_epi32(a.val, a.val); - v_int8x32 v = v_int8x32(_mm256_packs_epi16(a16, a16)); - return v_signmask(v) & 15; + v_int16x16 a16 = v_pack(a, a); + return v_signmask(v_pack(a16, a16)) & 0xFF; } inline int v_signmask(const v_uint32x8& a) { return v_signmask(v_reinterpret_as_s32(a)); } diff --git a/modules/core/test/test_intrin_utils.hpp b/modules/core/test/test_intrin_utils.hpp index 6ead0ecc60..3cd1145985 100644 --- a/modules/core/test/test_intrin_utils.hpp +++ b/modules/core/test/test_intrin_utils.hpp @@ -32,8 +32,7 @@ template <> struct initializer<64> return R(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15], d[16], d[17], d[18], d[19], d[20], d[21], d[22], d[23], d[24], d[25], d[26], d[27], d[28], d[29], d[30], d[31], d[32], d[33], d[34], d[35], d[36], d[37], d[38], d[39], d[40], d[41], d[42], d[43], d[44], d[45], d[46], d[47], - d[48], d[49], d[50], d[51], d[52], d[53], d[54], d[55], d[56], d[57], d[58], d[59], d[50], d[51], d[52], d[53], - d[54], d[55], d[56], d[57], d[58], d[59], d[60], d[61], d[62], d[63]); + d[48], d[49], d[50], d[51], d[52], d[53], d[54], d[55], d[56], d[57], d[58], d[59], d[60], d[61], d[62], d[63]); } }; @@ -660,7 +659,7 @@ template struct TheTest { SCOPED_TRACE(cv::format("i=%d", i)); EXPECT_COMPARE_EQ((float)std::sqrt(dataA[i]), (float)resB[i]); - EXPECT_COMPARE_EQ(1/(float)std::sqrt(dataA[i]), (float)resC[i]); + EXPECT_COMPARE_EQ((float)(1/std::sqrt(dataA[i])), (float)resC[i]); EXPECT_COMPARE_EQ((float)abs(dataA[i]), (float)resE[i]); } @@ -808,8 +807,8 @@ template struct TheTest dataC *= (LaneType)-1; R a = dataA, b = dataB, c = dataC, d = dataD, e = dataE; - int m = v_signmask(a); - EXPECT_EQ(2, m); + EXPECT_EQ(2, v_signmask(a)); + EXPECT_EQ(2 | (1 << (R::nlanes / 2)) | (1 << (R::nlanes - 1)), v_signmask(b)); EXPECT_EQ(false, v_check_all(a)); EXPECT_EQ(false, v_check_all(b)); From 22701f0c27052eafc1790f9cdda17beec04a0ba3 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 24 May 2019 14:23:32 +0300 Subject: [PATCH 07/21] imgcodecs(CAP_IMAGES): fix handling of input pattern --- modules/videoio/src/cap_images.cpp | 232 +++++++++++++++++------------ modules/videoio/src/precomp.hpp | 2 + 2 files changed, 135 insertions(+), 99 deletions(-) diff --git a/modules/videoio/src/cap_images.cpp b/modules/videoio/src/cap_images.cpp index 0e3c8f2ac2..76d2574ab8 100644 --- a/modules/videoio/src/cap_images.cpp +++ b/modules/videoio/src/cap_images.cpp @@ -50,24 +50,23 @@ // #include "precomp.hpp" -#include -#ifdef NDEBUG +#include "opencv2/core/utils/filesystem.hpp" + +#if 0 #define CV_WARN(message) #else -#define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__) +#define CV_WARN(message) CV_LOG_INFO(NULL, "CAP_IMAGES warning: %s (%s:%d)" << message) #endif -#ifndef _MAX_PATH -#define _MAX_PATH 1024 -#endif +using namespace cv; +namespace cv { class CvCapture_Images : public CvCapture { public: CvCapture_Images() { - filename = NULL; currentframe = firstframe = 0; length = 0; frame = NULL; @@ -88,7 +87,7 @@ public: int getCaptureDomain() /*const*/ CV_OVERRIDE { return cv::CAP_IMAGES; } protected: - char* filename; // actually a printf-pattern + std::string filename_pattern; // actually a printf-pattern unsigned currentframe; unsigned firstframe; // number of first frame unsigned length; // length of sequence @@ -100,21 +99,16 @@ protected: void CvCapture_Images::close() { - if( filename ) - { - free(filename); - filename = NULL; - } currentframe = firstframe = 0; length = 0; - cvReleaseImage( &frame ); + cvReleaseImage(&frame); } bool CvCapture_Images::grabFrame() { - char str[_MAX_PATH]; - sprintf(str, filename, firstframe + currentframe); + cv::String filename = cv::format(filename_pattern.c_str(), (int)(firstframe + currentframe)); + CV_Assert(!filename.empty()); if (grabbedInOpen) { @@ -125,8 +119,8 @@ bool CvCapture_Images::grabFrame() } cvReleaseImage(&frame); - frame = cvLoadImage(str, CV_LOAD_IMAGE_UNCHANGED); - if( frame ) + frame = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_UNCHANGED); + if (frame) currentframe++; return frame != NULL; @@ -142,7 +136,7 @@ double CvCapture_Images::getProperty(int id) const switch(id) { case CV_CAP_PROP_POS_MSEC: - CV_WARN("collections of images don't have framerates\n"); + CV_WARN("collections of images don't have framerates"); return 0; case CV_CAP_PROP_POS_FRAMES: return currentframe; @@ -155,10 +149,10 @@ double CvCapture_Images::getProperty(int id) const case CV_CAP_PROP_FRAME_HEIGHT: return frame ? frame->height : 0; case CV_CAP_PROP_FPS: - CV_WARN("collections of images don't have framerates\n"); + CV_WARN("collections of images don't have framerates"); return 1; case CV_CAP_PROP_FOURCC: - CV_WARN("collections of images don't have 4-character codes\n"); + CV_WARN("collections of images don't have 4-character codes"); return 0; } return 0; @@ -171,11 +165,11 @@ bool CvCapture_Images::setProperty(int id, double value) case CV_CAP_PROP_POS_MSEC: case CV_CAP_PROP_POS_FRAMES: if(value < 0) { - CV_WARN("seeking to negative positions does not work - clamping\n"); + CV_WARN("seeking to negative positions does not work - clamping"); value = 0; } if(value >= length) { - CV_WARN("seeking beyond end of sequence - clamping\n"); + CV_WARN("seeking beyond end of sequence - clamping"); value = length - 1; } currentframe = cvRound(value); @@ -184,10 +178,10 @@ bool CvCapture_Images::setProperty(int id, double value) return true; case CV_CAP_PROP_POS_AVI_RATIO: if(value > 1) { - CV_WARN("seeking beyond end of sequence - clamping\n"); + CV_WARN("seeking beyond end of sequence - clamping"); value = 1; } else if(value < 0) { - CV_WARN("seeking to negative positions does not work - clamping\n"); + CV_WARN("seeking to negative positions does not work - clamping"); value = 0; } currentframe = cvRound((length - 1) * value); @@ -195,66 +189,92 @@ bool CvCapture_Images::setProperty(int id, double value) grabbedInOpen = false; // grabbed frame is not valid anymore return true; } - CV_WARN("unknown/unhandled property\n"); + CV_WARN("unknown/unhandled property"); return false; } -static char* icvExtractPattern(const char *filename, unsigned *offset) +static +std::string icvExtractPattern(const std::string& filename, unsigned *offset) { - char *name = (char *)filename; + size_t len = filename.size(); + CV_Assert(!filename.empty()); + CV_Assert(offset); - if( !filename ) - return 0; + *offset = 0; // check whether this is a valid image sequence filename - char *at = strchr(name, '%'); - if(at) + std::string::size_type pos = filename.find('%'); + if (pos != std::string::npos) { - unsigned int dummy; - if(sscanf(at + 1, "%ud", &dummy) != 1) - return 0; - name = strdup(filename); + pos++; CV_Assert(pos < len); + if (filename[pos] == '0') // optional zero prefix + { + pos++; CV_Assert(pos < len); + } + if (filename[pos] >= '1' && filename[pos] <= '9') // optional numeric size (1..9) (one symbol only) + { + pos++; CV_Assert(pos < len); + } + if (filename[pos] == 'd' || filename[pos] == 'u') + { + pos++; + if (pos == len) + return filename; // end of string '...%5d' + CV_Assert(pos < len); + if (filename.find('%', pos) == std::string::npos) + return filename; // no more patterns + CV_Error_(Error::StsBadArg, ("CAP_IMAGES: invalid multiple patterns: %s", filename.c_str())); + } + CV_Error_(Error::StsBadArg, ("CAP_IMAGES: error, expected '0?[1-9][du]' pattern, got: %s", filename.c_str())); } else // no pattern filename was given - extract the pattern { - at = name; - - // ignore directory names - char *slash = strrchr(at, '/'); - if (slash) at = slash + 1; - + pos = filename.rfind('/'); #ifdef _WIN32 - slash = strrchr(at, '\\'); - if (slash) at = slash + 1; + if (pos == std::string::npos) + pos = filename.rfind('\\'); #endif + if (pos != std::string::npos) + pos++; + else + pos = 0; - while (*at && !isdigit(*at)) at++; + while (pos < len && !isdigit(filename[pos])) pos++; - if(!*at) - return 0; + if (pos == len) + { + CV_Error_(Error::StsBadArg, ("CAP_IMAGES: can't find starting number (in the name of file): %s", filename.c_str())); + } - sscanf(at, "%u", offset); + std::string::size_type pos0 = pos; - int size = (int)strlen(filename) + 20; - name = (char *)malloc(size); - CV_Assert(name != NULL); - strncpy(name, filename, at - filename); - name[at - filename] = 0; + const int64_t max_number = 1000000000; + CV_Assert(max_number < INT_MAX); // offset is 'int' - strcat(name, "%0"); + int number_str_size = 0; + uint64_t number = 0; + while (pos < len && isdigit(filename[pos])) + { + char ch = filename[pos]; + number = (number * 10) + (uint64_t)((int)ch - (int)'0'); + CV_Assert(number < max_number); + number_str_size++; + CV_Assert(number_str_size <= 64); // don't allow huge zero prefixes + pos++; + } + CV_Assert(number_str_size > 0); - int i; - char *extension; - for(i = 0, extension = at; isdigit(at[i]); i++, extension++) - ; - char places[13] = {0}; - sprintf(places, "%dd", i); + *offset = (int)number; - strcat(name, places); - strcat(name, extension); + std::string result; + if (pos0 > 0) + result += filename.substr(0, pos0); + result += cv::format("%%0%dd", number_str_size); + if (pos < len) + result += filename.substr(pos); + CV_LOG_INFO(NULL, "Pattern: " << result << " @ " << number); + return result; } - - return name; } @@ -263,33 +283,34 @@ bool CvCapture_Images::open(const char * _filename) unsigned offset = 0; close(); - filename = icvExtractPattern(_filename, &offset); - if(!filename) - return false; + CV_Assert(_filename); + filename_pattern = icvExtractPattern(_filename, &offset); + CV_Assert(!filename_pattern.empty()); // determine the length of the sequence - length = 0; - char str[_MAX_PATH]; - for(;;) + for (length = 0; ;) { - sprintf(str, filename, offset + length); - struct stat s; - if(stat(str, &s)) + cv::String filename = cv::format(filename_pattern.c_str(), (int)(offset + length)); + if (!utils::fs::exists(filename)) { - if(length == 0 && offset == 0) // allow starting with 0 or 1 + if (length == 0 && offset == 0) // allow starting with 0 or 1 { offset++; continue; } + break; } - if(!cvHaveImageReader(str)) + if (!cvHaveImageReader(filename.c_str())) + { + CV_LOG_INFO(NULL, "CAP_IMAGES: Stop scanning. Can't read image file: " << filename); break; + } length++; } - if(length == 0) + if (length == 0) { close(); return false; @@ -310,10 +331,18 @@ CvCapture* cvCreateFileCapture_Images(const char * filename) { CvCapture_Images* capture = new CvCapture_Images; - if( capture->open(filename) ) - return capture; + try + { + if (capture->open(filename)) + return capture; + delete capture; + } + catch (...) + { + delete capture; + throw; + } - delete capture; return NULL; } @@ -327,7 +356,6 @@ class CvVideoWriter_Images CV_FINAL : public CvVideoWriter public: CvVideoWriter_Images() { - filename = 0; currentframe = 0; } virtual ~CvVideoWriter_Images() { close(); } @@ -339,19 +367,21 @@ public: int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_IMAGES; } protected: - char* filename; + std::string filename_pattern; unsigned currentframe; std::vector params; }; bool CvVideoWriter_Images::writeFrame( const IplImage* image ) { - char str[_MAX_PATH]; - sprintf(str, filename, currentframe); + CV_Assert(!filename_pattern.empty()); + cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe); + CV_Assert(!filename.empty()); + std::vector image_params = params; image_params.push_back(0); // append parameters 'stop' mark image_params.push_back(0); - int ret = cvSaveImage(str, image, &image_params[0]); + int ret = cvSaveImage(filename.c_str(), image, &image_params[0]); currentframe++; @@ -360,11 +390,6 @@ bool CvVideoWriter_Images::writeFrame( const IplImage* image ) void CvVideoWriter_Images::close() { - if( filename ) - { - free( filename ); - filename = 0; - } currentframe = 0; params.clear(); } @@ -373,16 +398,14 @@ void CvVideoWriter_Images::close() bool CvVideoWriter_Images::open( const char* _filename ) { unsigned offset = 0; - close(); - filename = icvExtractPattern(_filename, &offset); - if(!filename) - return false; + CV_Assert(_filename); + filename_pattern = icvExtractPattern(_filename, &offset); + CV_Assert(!filename_pattern.empty()); - char str[_MAX_PATH]; - sprintf(str, filename, 0); - if(!cvHaveImageWriter(str)) + cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe); + if (!cvHaveImageWriter(filename.c_str())) { close(); return false; @@ -410,9 +433,20 @@ CvVideoWriter* cvCreateVideoWriter_Images( const char* filename ) { CvVideoWriter_Images *writer = new CvVideoWriter_Images; - if( writer->open( filename )) - return writer; + try + { + if (writer->open(filename)) + return writer; + delete writer; + } + catch (...) + { + delete writer; + throw; + } - delete writer; return 0; } + + +} // namespace diff --git a/modules/videoio/src/precomp.hpp b/modules/videoio/src/precomp.hpp index 06f7e4a6f7..ed5fcd61a9 100644 --- a/modules/videoio/src/precomp.hpp +++ b/modules/videoio/src/precomp.hpp @@ -130,8 +130,10 @@ CvCapture* cvCreateCameraCapture_XIMEA( const char* serialNumber ); CvCapture* cvCreateCameraCapture_AVFoundation(int index); CvCapture* cvCreateCameraCapture_Aravis( int index ); +namespace cv { CvCapture* cvCreateFileCapture_Images(const char* filename); CvVideoWriter* cvCreateVideoWriter_Images(const char* filename); +} #define CV_CAP_GSTREAMER_1394 0 From e07ffe902e7919174a657e4d89e4ca04641e47df Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Fri, 24 May 2019 23:01:08 +0300 Subject: [PATCH 08/21] Merge pull request #14616 from savuor:hsv_wide HSV and HLS color conversions rewritten to wide intrinsics (#14616) * RGB2HSV_b vectorized * RGB2HSV_f: widen * RGB2HSV_f: shorten, more intuitive * HSV2RGB_f and HSV2RGB_b widen * hls2rgb_f widen * instrumentation instead vx_cleanup * RGB2HLS_f widen * RGB2HLS_b rewritten to wide universal intrinsics * define guard against no SIMD code * hls2rgb_b rewritten * extra define removed * warning fixed * hls2rgb_b: performance fixed --- modules/imgproc/src/color_hsv.simd.hpp | 1339 +++++++++++------------- 1 file changed, 630 insertions(+), 709 deletions(-) diff --git a/modules/imgproc/src/color_hsv.simd.hpp b/modules/imgproc/src/color_hsv.simd.hpp index 30ae7064bc..d0a93c9aa8 100644 --- a/modules/imgproc/src/color_hsv.simd.hpp +++ b/modules/imgproc/src/color_hsv.simd.hpp @@ -43,6 +43,8 @@ struct RGB2HSV_b void operator()(const uchar* src, uchar* dst, int n) const { + CV_INSTRUMENT_REGION(); + int i, bidx = blueIdx, scn = srccn; const int hsv_shift = 12; @@ -53,7 +55,6 @@ struct RGB2HSV_b int hr = hrange; const int* hdiv_table = hr == 180 ? hdiv_table180 : hdiv_table256; - n *= 3; if( !initialized ) { @@ -67,7 +68,141 @@ struct RGB2HSV_b initialized = true; } - for( i = 0; i < n; i += 3, src += scn ) + i = 0; + +#if CV_SIMD + const int vsize = v_uint8::nlanes; + for ( ; i <= n - vsize; + i += vsize, src += scn*vsize, dst += 3*vsize) + { + v_uint8 b, g, r; + if(scn == 4) + { + v_uint8 a; + v_load_deinterleave(src, b, g, r, a); + } + else + { + v_load_deinterleave(src, b, g, r); + } + + if(bidx) + swap(b, r); + + v_uint8 h, s, v; + v_uint8 vmin; + v = v_max(b, v_max(g, r)); + vmin = v_min(b, v_min(g, r)); + + v_uint8 diff, vr, vg; + diff = v - vmin; + v_uint8 v255 = vx_setall_u8(0xff), vz = vx_setzero_u8(); + vr = v_select(v == r, v255, vz); + vg = v_select(v == g, v255, vz); + + // sdiv = sdiv_table[v] + v_int32 sdiv[4]; + v_uint16 vd[2]; + v_expand(v, vd[0], vd[1]); + v_int32 vq[4]; + v_expand(v_reinterpret_as_s16(vd[0]), vq[0], vq[1]); + v_expand(v_reinterpret_as_s16(vd[1]), vq[2], vq[3]); + { + int32_t CV_DECL_ALIGNED(CV_SIMD_WIDTH) storevq[vsize]; + for (int k = 0; k < 4; k++) + { + v_store_aligned(storevq + k*vsize/4, vq[k]); + } + + for(int k = 0; k < 4; k++) + { + sdiv[k] = vx_lut(sdiv_table, storevq + k*vsize/4); + } + } + + // hdiv = hdiv_table[diff] + v_int32 hdiv[4]; + v_uint16 diffd[2]; + v_expand(diff, diffd[0], diffd[1]); + v_int32 diffq[4]; + v_expand(v_reinterpret_as_s16(diffd[0]), diffq[0], diffq[1]); + v_expand(v_reinterpret_as_s16(diffd[1]), diffq[2], diffq[3]); + { + int32_t CV_DECL_ALIGNED(CV_SIMD_WIDTH) storediffq[vsize]; + for (int k = 0; k < 4; k++) + { + v_store_aligned(storediffq + k*vsize/4, diffq[k]); + } + + for (int k = 0; k < 4; k++) + { + hdiv[k] = vx_lut((int32_t*)hdiv_table, storediffq + k*vsize/4); + } + } + + // s = (diff * sdiv + (1 << (hsv_shift-1))) >> hsv_shift; + v_int32 sq[4]; + v_int32 vdescale = vx_setall_s32(1 << (hsv_shift-1)); + for (int k = 0; k < 4; k++) + { + sq[k] = (diffq[k]*sdiv[k] + vdescale) >> hsv_shift; + } + v_int16 sd[2]; + sd[0] = v_pack(sq[0], sq[1]); + sd[1] = v_pack(sq[2], sq[3]); + s = v_pack_u(sd[0], sd[1]); + + // expand all to 16 bits + v_uint16 bdu[2], gdu[2], rdu[2]; + v_expand(b, bdu[0], bdu[1]); + v_expand(g, gdu[0], gdu[1]); + v_expand(r, rdu[0], rdu[1]); + v_int16 bd[2], gd[2], rd[2]; + bd[0] = v_reinterpret_as_s16(bdu[0]); + bd[1] = v_reinterpret_as_s16(bdu[1]); + gd[0] = v_reinterpret_as_s16(gdu[0]); + gd[1] = v_reinterpret_as_s16(gdu[1]); + rd[0] = v_reinterpret_as_s16(rdu[0]); + rd[1] = v_reinterpret_as_s16(rdu[1]); + + v_int16 vrd[2], vgd[2]; + v_expand(v_reinterpret_as_s8(vr), vrd[0], vrd[1]); + v_expand(v_reinterpret_as_s8(vg), vgd[0], vgd[1]); + v_int16 diffsd[2]; + diffsd[0] = v_reinterpret_as_s16(diffd[0]); + diffsd[1] = v_reinterpret_as_s16(diffd[1]); + + v_int16 hd[2]; + // h before division + for (int k = 0; k < 2; k++) + { + v_int16 gb = gd[k] - bd[k]; + v_int16 br = bd[k] - rd[k] + (diffsd[k] << 1); + v_int16 rg = rd[k] - gd[k] + (diffsd[k] << 2); + hd[k] = (vrd[k] & gb) + ((~vrd[k]) & ((vgd[k] & br) + ((~vgd[k]) & rg))); + } + + // h div and fix + v_int32 hq[4]; + v_expand(hd[0], hq[0], hq[1]); + v_expand(hd[1], hq[2], hq[3]); + for(int k = 0; k < 4; k++) + { + hq[k] = (hq[k]*hdiv[k] + vdescale) >> hsv_shift; + } + hd[0] = v_pack(hq[0], hq[1]); + hd[1] = v_pack(hq[2], hq[3]); + v_int16 vhr = vx_setall_s16((short)hr); + v_int16 vzd = vx_setzero_s16(); + hd[0] += v_select(hd[0] < vzd, vhr, vzd); + hd[1] += v_select(hd[1] < vzd, vhr, vzd); + h = v_pack_u(hd[0], hd[1]); + + v_store_interleave(dst, h, s, v); + } +#endif + + for( ; i < n; i++, src += scn, dst += 3 ) { int b = src[bidx], g = src[1], r = src[bidx^2]; int h, s, v = b; @@ -89,9 +224,9 @@ struct RGB2HSV_b h = (h * hdiv_table[diff] + (1 << (hsv_shift-1))) >> hsv_shift; h += h < 0 ? hr : 0; - dst[i] = saturate_cast(h); - dst[i+1] = (uchar)s; - dst[i+2] = (uchar)v; + dst[0] = saturate_cast(h); + dst[1] = (uchar)s; + dst[2] = (uchar)v; } } @@ -104,95 +239,65 @@ struct RGB2HSV_f typedef float channel_type; RGB2HSV_f(int _srccn, int _blueIdx, float _hrange) - : srccn(_srccn), blueIdx(_blueIdx), hrange(_hrange) { - #if CV_SIMD128 - hasSIMD = hasSIMD128(); - #endif - } + : srccn(_srccn), blueIdx(_blueIdx), hrange(_hrange) + { } - #if CV_SIMD128 - inline void process(v_float32x4& v_r, v_float32x4& v_g, - v_float32x4& v_b, float hscale) const + #if CV_SIMD + inline void process(const v_float32& v_r, const v_float32& v_g, const v_float32& v_b, + v_float32& v_h, v_float32& v_s, v_float32& v_v, + float hscale) const { - v_float32x4 v_min_rgb = v_min(v_min(v_r, v_g), v_b); - v_float32x4 v_max_rgb = v_max(v_max(v_r, v_g), v_b); + v_float32 v_min_rgb = v_min(v_min(v_r, v_g), v_b); + v_float32 v_max_rgb = v_max(v_max(v_r, v_g), v_b); - v_float32x4 v_eps = v_setall_f32(FLT_EPSILON); - v_float32x4 v_diff = v_max_rgb - v_min_rgb; - v_float32x4 v_s = v_diff / (v_abs(v_max_rgb) + v_eps); + v_float32 v_eps = vx_setall_f32(FLT_EPSILON); + v_float32 v_diff = v_max_rgb - v_min_rgb; + v_s = v_diff / (v_abs(v_max_rgb) + v_eps); - v_float32x4 v_r_eq_max = v_r == v_max_rgb; - v_float32x4 v_g_eq_max = v_g == v_max_rgb; - v_float32x4 v_h = v_select(v_r_eq_max, v_g - v_b, - v_select(v_g_eq_max, v_b - v_r, v_r - v_g)); - v_float32x4 v_res = v_select(v_r_eq_max, (v_g < v_b) & v_setall_f32(360.0f), - v_select(v_g_eq_max, v_setall_f32(120.0f), v_setall_f32(240.0f))); - v_float32x4 v_rev_diff = v_setall_f32(60.0f) / (v_diff + v_eps); - v_r = v_muladd(v_h, v_rev_diff, v_res) * v_setall_f32(hscale); + v_float32 v_r_eq_max = v_r == v_max_rgb; + v_float32 v_g_eq_max = v_g == v_max_rgb; + v_h = v_select(v_r_eq_max, v_g - v_b, + v_select(v_g_eq_max, v_b - v_r, v_r - v_g)); + v_float32 v_res = v_select(v_r_eq_max, (v_g < v_b) & vx_setall_f32(360.0f), + v_select(v_g_eq_max, vx_setall_f32(120.0f), vx_setall_f32(240.0f))); + v_float32 v_rev_diff = vx_setall_f32(60.0f) / (v_diff + v_eps); + v_h = v_muladd(v_h, v_rev_diff, v_res) * vx_setall_f32(hscale); - v_g = v_s; - v_b = v_max_rgb; + v_v = v_max_rgb; } #endif void operator()(const float* src, float* dst, int n) const { + CV_INSTRUMENT_REGION(); + int i = 0, bidx = blueIdx, scn = srccn; float hscale = hrange*(1.f/360.f); n *= 3; - #if CV_SIMD128 - if (hasSIMD) +#if CV_SIMD + const int vsize = v_float32::nlanes; + for ( ; i <= n - 3*vsize; i += 3*vsize, src += scn * vsize) { - if (scn == 3) { - if (bidx) { - for ( ; i <= n - 12; i += 12, src += scn * 4) - { - v_float32x4 v_r; - v_float32x4 v_g; - v_float32x4 v_b; - v_load_deinterleave(src, v_r, v_g, v_b); - process(v_r, v_g, v_b, hscale); - v_store_interleave(dst + i, v_r, v_g, v_b); - } - } else { - for ( ; i <= n - 12; i += 12, src += scn * 4) - { - v_float32x4 v_r; - v_float32x4 v_g; - v_float32x4 v_b; - v_load_deinterleave(src, v_r, v_g, v_b); - process(v_b, v_g, v_r, hscale); - v_store_interleave(dst + i, v_b, v_g, v_r); - } - } - } else { // scn == 4 - if (bidx) { - for ( ; i <= n - 12; i += 12, src += scn * 4) - { - v_float32x4 v_r; - v_float32x4 v_g; - v_float32x4 v_b; - v_float32x4 v_a; - v_load_deinterleave(src, v_r, v_g, v_b, v_a); - process(v_r, v_g, v_b, hscale); - v_store_interleave(dst + i, v_r, v_g, v_b); - } - } else { - for ( ; i <= n - 12; i += 12, src += scn * 4) - { - v_float32x4 v_r; - v_float32x4 v_g; - v_float32x4 v_b; - v_float32x4 v_a; - v_load_deinterleave(src, v_r, v_g, v_b, v_a); - process(v_b, v_g, v_r, hscale); - v_store_interleave(dst + i, v_b, v_g, v_r); - } - } + v_float32 r, g, b, a; + if(scn == 4) + { + v_load_deinterleave(src, r, g, b, a); } + else // scn == 3 + { + v_load_deinterleave(src, r, g, b); + } + + if(bidx) + swap(b, r); + + v_float32 h, s, v; + process(b, g, r, h, s, v, hscale); + + v_store_interleave(dst + i, h, s, v); } - #endif +#endif for( ; i < n; i += 3, src += scn ) { @@ -227,36 +332,39 @@ struct RGB2HSV_f int srccn, blueIdx; float hrange; - #if CV_SIMD128 - bool hasSIMD; - #endif }; -#if CV_SIMD128 -inline void HSV2RGB_simd(v_float32x4& v_h, v_float32x4& v_s, v_float32x4& v_v, float hscale) +#if CV_SIMD +inline void HSV2RGB_simd(const v_float32& h, const v_float32& s, const v_float32& v, + v_float32& b, v_float32& g, v_float32& r, float hscale) { - v_h = v_h * v_setall_f32(hscale); - v_float32x4 v_pre_sector = v_cvt_f32(v_trunc(v_h)); - v_h = v_h - v_pre_sector; - v_float32x4 v_tab0 = v_v; - v_float32x4 v_one = v_setall_f32(1.0f); - v_float32x4 v_tab1 = v_v * (v_one - v_s); - v_float32x4 v_tab2 = v_v * (v_one - (v_s * v_h)); - v_float32x4 v_tab3 = v_v * (v_one - (v_s * (v_one - v_h))); + v_float32 v_h = h; + v_float32 v_s = s; + v_float32 v_v = v; - v_float32x4 v_one_sixth = v_setall_f32(1.0f / 6.0f); - v_float32x4 v_sector = v_pre_sector * v_one_sixth; + v_h = v_h * vx_setall_f32(hscale); + + v_float32 v_pre_sector = v_cvt_f32(v_trunc(v_h)); + v_h = v_h - v_pre_sector; + v_float32 v_tab0 = v_v; + v_float32 v_one = vx_setall_f32(1.0f); + v_float32 v_tab1 = v_v * (v_one - v_s); + v_float32 v_tab2 = v_v * (v_one - (v_s * v_h)); + v_float32 v_tab3 = v_v * (v_one - (v_s * (v_one - v_h))); + + v_float32 v_one_sixth = vx_setall_f32(1.0f / 6.0f); + v_float32 v_sector = v_pre_sector * v_one_sixth; v_sector = v_cvt_f32(v_trunc(v_sector)); - v_float32x4 v_six = v_setall_f32(6.0f); + v_float32 v_six = vx_setall_f32(6.0f); v_sector = v_pre_sector - (v_sector * v_six); - v_float32x4 v_two = v_setall_f32(2.0f); + v_float32 v_two = vx_setall_f32(2.0f); v_h = v_tab1 & (v_sector < v_two); v_h = v_h | (v_tab3 & (v_sector == v_two)); - v_float32x4 v_three = v_setall_f32(3.0f); + v_float32 v_three = vx_setall_f32(3.0f); v_h = v_h | (v_tab0 & (v_sector == v_three)); - v_float32x4 v_four = v_setall_f32(4.0f); + v_float32 v_four = vx_setall_f32(4.0f); v_h = v_h | (v_tab0 & (v_sector == v_four)); v_h = v_h | (v_tab2 & (v_sector > v_four)); @@ -272,15 +380,18 @@ inline void HSV2RGB_simd(v_float32x4& v_h, v_float32x4& v_s, v_float32x4& v_v, f v_v = v_v | (v_tab1 & (v_sector == v_three)); v_v = v_v | (v_tab3 & (v_sector == v_four)); v_v = v_v | (v_tab0 & (v_sector > v_four)); + + b = v_h; + g = v_s; + r = v_v; } #endif -inline void HSV2RGB_native(const float* src, float* dst, const float hscale, const int bidx) +inline void HSV2RGB_native(float h, float s, float v, + float& b, float& g, float& r, + const float hscale) { - float h = src[0], s = src[1], v = src[2]; - float b, g, r; - if( s == 0 ) b = g = r = v; else @@ -290,10 +401,7 @@ inline void HSV2RGB_native(const float* src, float* dst, const float hscale, con float tab[4]; int sector; h *= hscale; - if( h < 0 ) - do h += 6; while( h < 0 ); - else if( h >= 6 ) - do h -= 6; while( h >= 6 ); + h = fmod(h, 6.f); sector = cvFloor(h); h -= sector; if( (unsigned)sector >= 6u ) @@ -311,10 +419,6 @@ inline void HSV2RGB_native(const float* src, float* dst, const float hscale, con g = tab[sector_data[sector][1]]; r = tab[sector_data[sector][2]]; } - - dst[bidx] = b; - dst[1] = g; - dst[bidx^2] = r; } @@ -323,63 +427,57 @@ struct HSV2RGB_f typedef float channel_type; HSV2RGB_f(int _dstcn, int _blueIdx, float _hrange) - : dstcn(_dstcn), blueIdx(_blueIdx), hscale(6.f/_hrange) { - #if CV_SIMD128 - hasSIMD = hasSIMD128(); - #endif - } + : dstcn(_dstcn), blueIdx(_blueIdx), hscale(6.f/_hrange) + { } void operator()(const float* src, float* dst, int n) const { + CV_INSTRUMENT_REGION(); + int i = 0, bidx = blueIdx, dcn = dstcn; + float alpha = ColorChannel::max(); + float hs = hscale; n *= 3; - if (dcn == 3) +#if CV_SIMD + const int vsize = v_float32::nlanes; + v_float32 valpha = vx_setall_f32(alpha); + for (; i <= n - vsize*3; i += vsize*3, dst += dcn * vsize) { - #if CV_SIMD128 - if (hasSIMD) + v_float32 h, s, v, b, g, r; + v_load_deinterleave(src + i, h, s, v); + + HSV2RGB_simd(h, s, v, b, g, r, hs); + + if(bidx) + swap(b, r); + + if(dcn == 4) { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_src[3]; - v_load_deinterleave(src + i, v_src[0], v_src[1], v_src[2]); - HSV2RGB_simd(v_src[0], v_src[1], v_src[2], hscale); - v_store_interleave(dst, v_src[bidx], v_src[1], v_src[bidx^2]); - } + v_store_interleave(dst, b, g, r, valpha); } - #endif - for( ; i < n; i += 3, dst += dcn ) + else // dcn == 3 { - HSV2RGB_native(src + i, dst, hscale, bidx); + v_store_interleave(dst, b, g, r); } - } else { // dcn == 4 - float alpha = ColorChannel::max(); - #if CV_SIMD128 - if (hasSIMD) - { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_src[3]; - v_load_deinterleave(src + i, v_src[0], v_src[1], v_src[2]); - HSV2RGB_simd(v_src[0], v_src[1], v_src[2], hscale); - v_float32x4 v_a = v_setall_f32(alpha); - v_store_interleave(dst, v_src[bidx], v_src[1], v_src[bidx^2], v_a); - } - } - #endif - for( ; i < n; i += 3, dst += dcn ) - { - HSV2RGB_native(src + i, dst, hscale, bidx); + } +#endif + for( ; i < n; i += 3, dst += dcn ) + { + float h = src[i + 0], s = src[i + 1], v = src[i + 2]; + float b, g, r; + HSV2RGB_native(h, s, v, b, g, r, hs); + + dst[bidx] = b; + dst[1] = g; + dst[bidx^2] = r; + if(dcn == 4) dst[3] = alpha; - } } } int dstcn, blueIdx; float hscale; - #if CV_SIMD128 - bool hasSIMD; - #endif }; @@ -389,100 +487,100 @@ struct HSV2RGB_b HSV2RGB_b(int _dstcn, int _blueIdx, int _hrange) : dstcn(_dstcn), blueIdx(_blueIdx), hscale(6.0f / _hrange) - { - #if CV_SIMD128 - hasSIMD = hasSIMD128(); - #endif - } + { } void operator()(const uchar* src, uchar* dst, int n) const { + CV_INSTRUMENT_REGION(); + int j = 0, dcn = dstcn; uchar alpha = ColorChannel::max(); - #if CV_SIMD128 - if (hasSIMD) +#if CV_SIMD + const int vsize = v_float32::nlanes; + + for (j = 0; j <= (n - vsize*4) * 3; j += 3 * 4 * vsize, dst += dcn * 4 * vsize) { - for (j = 0; j <= (n - 16) * 3; j += 48, dst += dcn * 16) + v_uint8 h_b, s_b, v_b; + v_uint16 h_w[2], s_w[2], v_w[2]; + v_uint32 h_u[4], s_u[4], v_u[4]; + v_load_deinterleave(src + j, h_b, s_b, v_b); + v_expand(h_b, h_w[0], h_w[1]); + v_expand(s_b, s_w[0], s_w[1]); + v_expand(v_b, v_w[0], v_w[1]); + v_expand(h_w[0], h_u[0], h_u[1]); + v_expand(h_w[1], h_u[2], h_u[3]); + v_expand(s_w[0], s_u[0], s_u[1]); + v_expand(s_w[1], s_u[2], s_u[3]); + v_expand(v_w[0], v_u[0], v_u[1]); + v_expand(v_w[1], v_u[2], v_u[3]); + + v_int32 b_i[4], g_i[4], r_i[4]; + v_float32 v_coeff0 = vx_setall_f32(1.0f / 255.0f); + v_float32 v_coeff1 = vx_setall_f32(255.0f); + + for( int k = 0; k < 4; k++ ) { - v_uint8x16 h_b, s_b, v_b; - v_uint16x8 h_w[2], s_w[2], v_w[2]; - v_uint32x4 h_u[4], s_u[4], v_u[4]; - v_load_deinterleave(src + j, h_b, s_b, v_b); - v_expand(h_b, h_w[0], h_w[1]); - v_expand(s_b, s_w[0], s_w[1]); - v_expand(v_b, v_w[0], v_w[1]); - v_expand(h_w[0], h_u[0], h_u[1]); - v_expand(h_w[1], h_u[2], h_u[3]); - v_expand(s_w[0], s_u[0], s_u[1]); - v_expand(s_w[1], s_u[2], s_u[3]); - v_expand(v_w[0], v_u[0], v_u[1]); - v_expand(v_w[1], v_u[2], v_u[3]); + v_float32 h = v_cvt_f32(v_reinterpret_as_s32(h_u[k])); + v_float32 s = v_cvt_f32(v_reinterpret_as_s32(s_u[k])); + v_float32 v = v_cvt_f32(v_reinterpret_as_s32(v_u[k])); - v_int32x4 b_i[4], g_i[4], r_i[4]; - v_float32x4 v_coeff0 = v_setall_f32(1.0f / 255.0f); - v_float32x4 v_coeff1 = v_setall_f32(255.0f); + s *= v_coeff0; + v *= v_coeff0; + v_float32 b, g, r; + HSV2RGB_simd(h, s, v, b, g, r, hscale); - for( int k = 0; k < 4; k++ ) - { - v_float32x4 v_src[3]; - v_src[0] = v_cvt_f32(v_reinterpret_as_s32(h_u[k])); - v_src[1] = v_cvt_f32(v_reinterpret_as_s32(s_u[k])); - v_src[2] = v_cvt_f32(v_reinterpret_as_s32(v_u[k])); + b *= v_coeff1; + g *= v_coeff1; + r *= v_coeff1; + b_i[k] = v_trunc(b); + g_i[k] = v_trunc(g); + r_i[k] = v_trunc(r); + } - v_src[1] *= v_coeff0; - v_src[2] *= v_coeff0; - HSV2RGB_simd(v_src[0], v_src[1], v_src[2], hscale); + v_uint16 r_w[2], g_w[2], b_w[2]; + v_uint8 r_b, g_b, b_b; - v_src[0] *= v_coeff1; - v_src[1] *= v_coeff1; - v_src[2] *= v_coeff1; - b_i[k] = v_trunc(v_src[0]); - g_i[k] = v_trunc(v_src[1]); - r_i[k] = v_trunc(v_src[2]); - } + r_w[0] = v_pack_u(r_i[0], r_i[1]); + r_w[1] = v_pack_u(r_i[2], r_i[3]); + r_b = v_pack(r_w[0], r_w[1]); + g_w[0] = v_pack_u(g_i[0], g_i[1]); + g_w[1] = v_pack_u(g_i[2], g_i[3]); + g_b = v_pack(g_w[0], g_w[1]); + b_w[0] = v_pack_u(b_i[0], b_i[1]); + b_w[1] = v_pack_u(b_i[2], b_i[3]); + b_b = v_pack(b_w[0], b_w[1]); - v_uint16x8 r_w[2], g_w[2], b_w[2]; - v_uint8x16 r_b, g_b, b_b; - - r_w[0] = v_pack_u(r_i[0], r_i[1]); - r_w[1] = v_pack_u(r_i[2], r_i[3]); - r_b = v_pack(r_w[0], r_w[1]); - g_w[0] = v_pack_u(g_i[0], g_i[1]); - g_w[1] = v_pack_u(g_i[2], g_i[3]); - g_b = v_pack(g_w[0], g_w[1]); - b_w[0] = v_pack_u(b_i[0], b_i[1]); - b_w[1] = v_pack_u(b_i[2], b_i[3]); - b_b = v_pack(b_w[0], b_w[1]); - - if( dcn == 3 ) - { - if( blueIdx == 0 ) - v_store_interleave(dst, b_b, g_b, r_b); - else - v_store_interleave(dst, r_b, g_b, b_b); - } + if( dcn == 3 ) + { + if( blueIdx == 0 ) + v_store_interleave(dst, b_b, g_b, r_b); else - { - v_uint8x16 alpha_b = v_setall_u8(alpha); - if( blueIdx == 0 ) - v_store_interleave(dst, b_b, g_b, r_b, alpha_b); - else - v_store_interleave(dst, r_b, g_b, b_b, alpha_b); - } + v_store_interleave(dst, r_b, g_b, b_b); + } + else + { + v_uint8 alpha_b = vx_setall_u8(alpha); + if( blueIdx == 0 ) + v_store_interleave(dst, b_b, g_b, r_b, alpha_b); + else + v_store_interleave(dst, r_b, g_b, b_b, alpha_b); } } - #endif +#endif + for( ; j < n * 3; j += 3, dst += dcn ) { - float buf[6]; - buf[0] = src[j]; - buf[1] = src[j+1] * (1.0f / 255.0f); - buf[2] = src[j+2] * (1.0f / 255.0f); - HSV2RGB_native(buf, buf + 3, hscale, blueIdx); - dst[0] = saturate_cast(buf[3] * 255.0f); - dst[1] = saturate_cast(buf[4] * 255.0f); - dst[2] = saturate_cast(buf[5] * 255.0f); + float h, s, v, b, g, r; + h = src[j]; + s = src[j+1] * (1.0f / 255.0f); + v = src[j+2] * (1.0f / 255.0f); + HSV2RGB_native(h, s, v, b, g, r, hscale); + + dst[blueIdx] = saturate_cast(b * 255.0f); + dst[1] = saturate_cast(g * 255.0f); + dst[blueIdx^2] = saturate_cast(r * 255.0f); + if( dcn == 4 ) dst[3] = alpha; } @@ -491,9 +589,6 @@ struct HSV2RGB_b int dstcn; int blueIdx; float hscale; - #if CV_SIMD128 - bool hasSIMD; - #endif }; @@ -504,102 +599,78 @@ struct RGB2HLS_f typedef float channel_type; RGB2HLS_f(int _srccn, int _blueIdx, float _hrange) - : srccn(_srccn), blueIdx(_blueIdx), hscale(_hrange/360.f) { - #if CV_SIMD128 - hasSIMD = hasSIMD128(); - #endif - } - - #if CV_SIMD128 - inline void process(v_float32x4& v_r, v_float32x4& v_g, - v_float32x4& v_b, v_float32x4& v_hscale) const + : srccn(_srccn), blueIdx(_blueIdx), hscale(_hrange/360.f) { - v_float32x4 v_max_rgb = v_max(v_max(v_r, v_g), v_b); - v_float32x4 v_min_rgb = v_min(v_min(v_r, v_g), v_b); - - v_float32x4 v_diff = v_max_rgb - v_min_rgb; - v_float32x4 v_sum = v_max_rgb + v_min_rgb; - v_float32x4 v_half = v_setall_f32(0.5f); - v_float32x4 v_l = v_sum * v_half; - - v_float32x4 v_s = v_diff / v_select(v_l < v_half, v_sum, v_setall_f32(2.0f) - v_sum); - - v_float32x4 v_r_eq_max = v_max_rgb == v_r; - v_float32x4 v_g_eq_max = v_max_rgb == v_g; - v_float32x4 v_h = v_select(v_r_eq_max, v_g - v_b, - v_select(v_g_eq_max, v_b - v_r, v_r - v_g)); - v_float32x4 v_res = v_select(v_r_eq_max, (v_g < v_b) & v_setall_f32(360.0f), - v_select(v_g_eq_max, v_setall_f32(120.0f), v_setall_f32(240.0f))); - v_float32x4 v_rev_diff = v_setall_f32(60.0f) / v_diff; - v_h = v_muladd(v_h, v_rev_diff, v_res) * v_hscale; - - v_float32x4 v_diff_gt_eps = v_diff > v_setall_f32(FLT_EPSILON); - v_r = v_diff_gt_eps & v_h; - v_g = v_l; - v_b = v_diff_gt_eps & v_s; } - #endif + +#if CV_SIMD + inline void process(const v_float32& r, const v_float32& g, const v_float32& b, + const v_float32& vhscale, + v_float32& h, v_float32& l, v_float32& s) const + { + v_float32 maxRgb = v_max(v_max(r, g), b); + v_float32 minRgb = v_min(v_min(r, g), b); + + v_float32 diff = maxRgb - minRgb; + v_float32 msum = maxRgb + minRgb; + v_float32 vhalf = vx_setall_f32(0.5f); + l = msum * vhalf; + + s = diff / v_select(l < vhalf, msum, vx_setall_f32(2.0f) - msum); + + v_float32 rMaxMask = maxRgb == r; + v_float32 gMaxMask = maxRgb == g; + + h = v_select(rMaxMask, g - b, v_select(gMaxMask, b - r, r - g)); + v_float32 hpart = v_select(rMaxMask, (g < b) & vx_setall_f32(360.0f), + v_select(gMaxMask, vx_setall_f32(120.0f), vx_setall_f32(240.0f))); + + v_float32 invDiff = vx_setall_f32(60.0f) / diff; + h = v_muladd(h, invDiff, hpart) * vhscale; + + v_float32 diffEpsMask = diff > vx_setall_f32(FLT_EPSILON); + + h = diffEpsMask & h; + // l = l; + s = diffEpsMask & s; + } +#endif void operator()(const float* src, float* dst, int n) const { + CV_INSTRUMENT_REGION(); + int i = 0, bidx = blueIdx, scn = srccn; - n *= 3; - #if CV_SIMD128 - if (hasSIMD) +#if CV_SIMD + const int vsize = v_float32::nlanes; + v_float32 vhscale = vx_setall_f32(hscale); + + for ( ; i <= n - vsize; + i += vsize, src += scn * vsize, dst += 3 * vsize) { - v_float32x4 v_hscale = v_setall_f32(hscale); - if (scn == 3) { - if (bidx) { - for ( ; i <= n - 12; i += 12, src += scn * 4) - { - v_float32x4 v_r; - v_float32x4 v_g; - v_float32x4 v_b; - v_load_deinterleave(src, v_r, v_g, v_b); - process(v_r, v_g, v_b, v_hscale); - v_store_interleave(dst + i, v_r, v_g, v_b); - } - } else { - for ( ; i <= n - 12; i += 12, src += scn * 4) - { - v_float32x4 v_r; - v_float32x4 v_g; - v_float32x4 v_b; - v_load_deinterleave(src, v_r, v_g, v_b); - process(v_b, v_g, v_r, v_hscale); - v_store_interleave(dst + i, v_b, v_g, v_r); - } - } - } else { // scn == 4 - if (bidx) { - for ( ; i <= n - 12; i += 12, src += scn * 4) - { - v_float32x4 v_r; - v_float32x4 v_g; - v_float32x4 v_b; - v_float32x4 v_a; - v_load_deinterleave(src, v_r, v_g, v_b, v_a); - process(v_r, v_g, v_b, v_hscale); - v_store_interleave(dst + i, v_r, v_g, v_b); - } - } else { - for ( ; i <= n - 12; i += 12, src += scn * 4) - { - v_float32x4 v_r; - v_float32x4 v_g; - v_float32x4 v_b; - v_float32x4 v_a; - v_load_deinterleave(src, v_r, v_g, v_b, v_a); - process(v_b, v_g, v_r, v_hscale); - v_store_interleave(dst + i, v_b, v_g, v_r); - } - } - } - } - #endif + v_float32 r, g, b, h, l, s; - for( ; i < n; i += 3, src += scn ) + if(scn == 4) + { + v_float32 a; + v_load_deinterleave(src, b, g, r, a); + } + else // scn == 3 + { + v_load_deinterleave(src, b, g, r); + } + + if(bidx) + swap(r, b); + + process(r, g, b, vhscale, h, l, s); + + v_store_interleave(dst, h, l, s); + } +#endif + + for( ; i < n; i++, src += scn, dst += 3 ) { float b = src[bidx], g = src[1], r = src[bidx^2]; float h = 0.f, s = 0.f, l; @@ -629,186 +700,142 @@ struct RGB2HLS_f if( h < 0.f ) h += 360.f; } - dst[i] = h*hscale; - dst[i+1] = l; - dst[i+2] = s; + dst[0] = h*hscale; + dst[1] = l; + dst[2] = s; } } int srccn, blueIdx; float hscale; - #if CV_SIMD128 - bool hasSIMD; - #endif }; struct RGB2HLS_b { typedef uchar channel_type; + static const int bufChannels = 3; RGB2HLS_b(int _srccn, int _blueIdx, int _hrange) - : srccn(_srccn), cvt(3, _blueIdx, (float)_hrange) - { - #if CV_NEON - v_scale_inv = vdupq_n_f32(1.f/255.f); - v_scale = vdupq_n_f32(255.f); - v_alpha = vdup_n_u8(ColorChannel::max()); - #elif CV_SSE2 - v_scale_inv = _mm_set1_ps(1.f/255.f); - v_zero = _mm_setzero_si128(); - haveSIMD = checkHardwareSupport(CV_CPU_SSE2); - #endif - } - - #if CV_SSE2 - void process(const float * buf, - __m128 & v_coeffs, uchar * dst) const - { - __m128 v_l0f = _mm_load_ps(buf); - __m128 v_l1f = _mm_load_ps(buf + 4); - __m128 v_u0f = _mm_load_ps(buf + 8); - __m128 v_u1f = _mm_load_ps(buf + 12); - - v_l0f = _mm_mul_ps(v_l0f, v_coeffs); - v_u1f = _mm_mul_ps(v_u1f, v_coeffs); - v_coeffs = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(v_coeffs), 0x92)); - v_u0f = _mm_mul_ps(v_u0f, v_coeffs); - v_coeffs = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(v_coeffs), 0x92)); - v_l1f = _mm_mul_ps(v_l1f, v_coeffs); - - __m128i v_l = _mm_packs_epi32(_mm_cvtps_epi32(v_l0f), _mm_cvtps_epi32(v_l1f)); - __m128i v_u = _mm_packs_epi32(_mm_cvtps_epi32(v_u0f), _mm_cvtps_epi32(v_u1f)); - __m128i v_l0 = _mm_packus_epi16(v_l, v_u); - - _mm_storeu_si128((__m128i *)(dst), v_l0); - } - #endif + : srccn(_srccn), cvt(bufChannels, _blueIdx, (float)_hrange) + { } void operator()(const uchar* src, uchar* dst, int n) const { + CV_INSTRUMENT_REGION(); + int i, j, scn = srccn; - float CV_DECL_ALIGNED(16) buf[3*BLOCK_SIZE]; - #if CV_SSE2 - __m128 v_coeffs = _mm_set_ps(1.f, 255.f, 255.f, 1.f); - #endif + +#if CV_SIMD + float CV_DECL_ALIGNED(CV_SIMD_WIDTH) buf[bufChannels*BLOCK_SIZE]; +#else + float CV_DECL_ALIGNED(16) buf[bufChannels*BLOCK_SIZE]; +#endif + +#if CV_SIMD + static const int fsize = v_float32::nlanes; + //TODO: fix that when v_interleave is available + float CV_DECL_ALIGNED(CV_SIMD_WIDTH) interTmpM[fsize*3]; + v_store_interleave(interTmpM, vx_setall_f32(1.f), vx_setall_f32(255.f), vx_setall_f32(255.f)); + v_float32 mhls[3]; + for(int k = 0; k < 3; k++) + { + mhls[k] = vx_load_aligned(interTmpM + k*fsize); + } +#endif for( i = 0; i < n; i += BLOCK_SIZE, dst += BLOCK_SIZE*3 ) { int dn = std::min(n - i, (int)BLOCK_SIZE); j = 0; - #if CV_NEON - for ( ; j <= (dn - 8) * 3; j += 24, src += 8 * scn) +#if CV_SIMD + v_float32 v255inv = vx_setall_f32(1.f/255.f); + if (scn == 3) { - uint16x8_t v_t0, v_t1, v_t2; - - if (scn == 3) + static const int nBlock = fsize*2; + for ( ; j <= (dn * bufChannels - nBlock); + j += nBlock, src += nBlock) { - uint8x8x3_t v_src = vld3_u8(src); - v_t0 = vmovl_u8(v_src.val[0]); - v_t1 = vmovl_u8(v_src.val[1]); - v_t2 = vmovl_u8(v_src.val[2]); + v_uint16 drgb = vx_load_expand(src); + v_int32 qrgb0, qrgb1; + v_expand(v_reinterpret_as_s16(drgb), qrgb0, qrgb1); + v_store_aligned(buf + j + 0*fsize, v_cvt_f32(qrgb0)*v255inv); + v_store_aligned(buf + j + 1*fsize, v_cvt_f32(qrgb1)*v255inv); } - else - { - uint8x8x4_t v_src = vld4_u8(src); - v_t0 = vmovl_u8(v_src.val[0]); - v_t1 = vmovl_u8(v_src.val[1]); - v_t2 = vmovl_u8(v_src.val[2]); - } - - float32x4x3_t v_dst; - v_dst.val[0] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_t0))), v_scale_inv); - v_dst.val[1] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_t1))), v_scale_inv); - v_dst.val[2] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_t2))), v_scale_inv); - vst3q_f32(buf + j, v_dst); - - v_dst.val[0] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_t0))), v_scale_inv); - v_dst.val[1] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_t1))), v_scale_inv); - v_dst.val[2] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_t2))), v_scale_inv); - vst3q_f32(buf + j + 12, v_dst); } - #elif CV_SSE2 - if (scn == 3 && haveSIMD) + else // if (scn == 4) { - for ( ; j <= (dn * 3 - 16); j += 16, src += 16) + static const int nBlock = fsize*4; + for ( ; j <= dn*bufChannels - nBlock*bufChannels; + j += nBlock*bufChannels, src += nBlock*4) { - __m128i v_src = _mm_loadu_si128((__m128i const *)src); + v_uint8 rgb[3], dummy; + v_load_deinterleave(src, rgb[0], rgb[1], rgb[2], dummy); - __m128i v_src_p = _mm_unpacklo_epi8(v_src, v_zero); - _mm_store_ps(buf + j, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(v_src_p, v_zero)), v_scale_inv)); - _mm_store_ps(buf + j + 4, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(v_src_p, v_zero)), v_scale_inv)); + v_uint16 d[3*2]; + for(int k = 0; k < 3; k++) + { + v_expand(rgb[k], d[k*2+0], d[k*2+1]); + } + v_int32 q[3*4]; + for(int k = 0; k < 3*2; k++) + { + v_expand(v_reinterpret_as_s16(d[k]), q[k*2+0], q[k*2+1]); + } - v_src_p = _mm_unpackhi_epi8(v_src, v_zero); - _mm_store_ps(buf + j + 8, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(v_src_p, v_zero)), v_scale_inv)); - _mm_store_ps(buf + j + 12, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(v_src_p, v_zero)), v_scale_inv)); + v_float32 f[3*4]; + for(int k = 0; k < 3*4; k++) + { + f[k] = v_cvt_f32(q[k])*v255inv; + } + + for(int k = 0; k < 4; k++) + { + v_store_interleave(buf + j + k*bufChannels*fsize, f[0*4+k], f[1*4+k], f[2*4+k]); + } } - - int jr = j % 3; - if (jr) - src -= jr, j -= jr; } - else if (scn == 4 && haveSIMD) - { - for ( ; j <= (dn * 3 - 12); j += 12, src += 16) - { - __m128i v_src = _mm_loadu_si128((__m128i const *)src); - - __m128i v_src_lo = _mm_unpacklo_epi8(v_src, v_zero); - __m128i v_src_hi = _mm_unpackhi_epi8(v_src, v_zero); - _mm_storeu_ps(buf + j, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(v_src_lo, v_zero)), v_scale_inv)); - _mm_storeu_ps(buf + j + 3, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(v_src_lo, v_zero)), v_scale_inv)); - _mm_storeu_ps(buf + j + 6, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(v_src_hi, v_zero)), v_scale_inv)); - float tmp = buf[j + 8]; - _mm_storeu_ps(buf + j + 8, _mm_mul_ps(_mm_cvtepi32_ps(_mm_shuffle_epi32(_mm_unpackhi_epi16(v_src_hi, v_zero), 0x90)), v_scale_inv)); - buf[j + 8] = tmp; - } - - int jr = j % 3; - if (jr) - src -= jr, j -= jr; - } - #endif +#endif for( ; j < dn*3; j += 3, src += scn ) { - buf[j] = src[0]*(1.f/255.f); + buf[j+0] = src[0]*(1.f/255.f); buf[j+1] = src[1]*(1.f/255.f); buf[j+2] = src[2]*(1.f/255.f); } cvt(buf, buf, dn); j = 0; - #if CV_NEON - for ( ; j <= (dn - 8) * 3; j += 24) +#if CV_SIMD + for( ; j <= dn*3 - fsize*3*4; j += fsize*3*4) { - float32x4x3_t v_src0 = vld3q_f32(buf + j), v_src1 = vld3q_f32(buf + j + 12); - - uint8x8x3_t v_dst; - v_dst.val[0] = vqmovn_u16(vcombine_u16(vqmovn_u32(cv_vrndq_u32_f32(v_src0.val[0])), - vqmovn_u32(cv_vrndq_u32_f32(v_src1.val[0])))); - v_dst.val[1] = vqmovn_u16(vcombine_u16(vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src0.val[1], v_scale))), - vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src1.val[1], v_scale))))); - v_dst.val[2] = vqmovn_u16(vcombine_u16(vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src0.val[2], v_scale))), - vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src1.val[2], v_scale))))); - vst3_u8(dst + j, v_dst); - } - #elif CV_SSE2 - if (haveSIMD) - { - for ( ; j <= (dn - 16) * 3; j += 48) + v_float32 f[3*4]; + for(int k = 0; k < 3*4; k++) { - process(buf + j, - v_coeffs, dst + j); + f[k] = vx_load_aligned(buf + j + k*fsize); + } - process(buf + j + 16, - v_coeffs, dst + j + 16); + for(int k = 0; k < 4; k++) + { + for(int l = 0; l < 3; l++) + { + f[k*3+l] = f[k*3+l] * mhls[l]; + } + } - process(buf + j + 32, - v_coeffs, dst + j + 32); + v_int32 q[3*4]; + for(int k = 0; k < 3*4; k++) + { + q[k] = v_round(f[k]); + } + + for(int k = 0; k < 3; k++) + { + v_store(dst + j + k*fsize*4, v_pack_u(v_pack(q[k*4+0], q[k*4+1]), + v_pack(q[k*4+2], q[k*4+3]))); } } - #endif +#endif for( ; j < dn*3; j += 3 ) { dst[j] = saturate_cast(buf[j]); @@ -820,14 +847,6 @@ struct RGB2HLS_b int srccn; RGB2HLS_f cvt; - #if CV_NEON - float32x4_t v_scale, v_scale_inv; - uint8x8_t v_alpha; - #elif CV_SSE2 - __m128 v_scale_inv; - __m128i v_zero; - bool haveSIMD; - #endif }; @@ -836,115 +855,79 @@ struct HLS2RGB_f typedef float channel_type; HLS2RGB_f(int _dstcn, int _blueIdx, float _hrange) - : dstcn(_dstcn), blueIdx(_blueIdx), hscale(6.f/_hrange) { - #if CV_SIMD128 - hasSIMD = hasSIMD128(); - #endif - } + : dstcn(_dstcn), blueIdx(_blueIdx), hscale(6.f/_hrange) + { } - #if CV_SIMD128 - inline void process(v_float32x4& v_h, v_float32x4& v_l, v_float32x4& v_s) const +#if CV_SIMD + inline void process(const v_float32& h, const v_float32& l, const v_float32& s, + v_float32& b, v_float32& g, v_float32& r) const { - v_float32x4 v_one = v_setall_f32(1.0f); + v_float32 v1 = vx_setall_f32(1.0f), v2 = vx_setall_f32(2.0f), v4 = vx_setall_f32(4.0f); - v_float32x4 v_l_le_half = v_l <= v_setall_f32(0.5f); - v_float32x4 v_ls = v_l * v_s; - v_float32x4 v_elem0 = v_select(v_l_le_half, v_ls, v_s - v_ls); + v_float32 lBelowHalfMask = l <= vx_setall_f32(0.5f); + v_float32 ls = l * s; + v_float32 elem0 = v_select(lBelowHalfMask, ls, s - ls); - v_float32x4 v_hs_raw = v_h * v_setall_f32(hscale); - v_float32x4 v_pre_hs = v_cvt_f32(v_trunc(v_hs_raw)); - v_float32x4 v_hs = v_hs_raw - v_pre_hs; - v_float32x4 v_sector = v_pre_hs - v_setall_f32(6.0f) * v_cvt_f32(v_trunc(v_hs_raw * v_setall_f32(1.0f / 6.0f))); - v_float32x4 v_elem1 = v_hs + v_hs; + v_float32 hsRaw = h * vx_setall_f32(hscale); + v_float32 preHs = v_cvt_f32(v_trunc(hsRaw)); + v_float32 hs = hsRaw - preHs; + v_float32 sector = preHs - vx_setall_f32(6.0f) * v_cvt_f32(v_trunc(hsRaw * vx_setall_f32(1.0f / 6.0f))); + v_float32 elem1 = hs + hs; - v_float32x4 v_tab0 = v_l + v_elem0; - v_float32x4 v_tab1 = v_l - v_elem0; - v_float32x4 v_tab2 = v_l + v_elem0 - v_elem0 * v_elem1; - v_float32x4 v_tab3 = v_l - v_elem0 + v_elem0 * v_elem1; + v_float32 tab0 = l + elem0; + v_float32 tab1 = l - elem0; + v_float32 tab2 = l + elem0 - elem0 * elem1; + v_float32 tab3 = l - elem0 + elem0 * elem1; - v_float32x4 v_two = v_setall_f32(2.0f); - v_float32x4 v_four = v_setall_f32(4.0f); + b = v_select(sector < v2, tab1, + v_select(sector <= v2, tab3, + v_select(sector <= v4, tab0, tab2))); - v_h = v_select(v_sector < v_two , v_tab1, - v_select(v_sector <= v_two , v_tab3, - v_select(v_sector <= v_four, v_tab0, v_tab2))); + g = v_select(sector < v1, tab3, + v_select(sector <= v2, tab0, + v_select(sector < v4, tab2, tab1))); - v_l = v_select(v_sector < v_one , v_tab3, - v_select(v_sector <= v_two , v_tab0, - v_select(v_sector < v_four, v_tab2, v_tab1))); - - v_s = v_select(v_sector < v_one , v_tab0, - v_select(v_sector < v_two , v_tab2, - v_select(v_sector < v_four, v_tab1, - v_select(v_sector <= v_four, v_tab3, v_tab0)))); + r = v_select(sector < v1, tab0, + v_select(sector < v2, tab2, + v_select(sector < v4, tab1, + v_select(sector <= v4, tab3, tab0)))); } - #endif +#endif void operator()(const float* src, float* dst, int n) const { + CV_INSTRUMENT_REGION(); + int i = 0, bidx = blueIdx, dcn = dstcn; float alpha = ColorChannel::max(); - n *= 3; - #if CV_SIMD128 - if (hasSIMD) +#if CV_SIMD + static const int vsize = v_float32::nlanes; + for (; i <= n - vsize; i += vsize, src += 3*vsize, dst += dcn*vsize) { - if (dcn == 3) + v_float32 h, l, s, r, g, b; + v_load_deinterleave(src, h, l, s); + + process(h, l, s, b, g, r); + + if(bidx) + swap(b, r); + + if(dcn == 3) { - if (bidx) - { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_h; - v_float32x4 v_l; - v_float32x4 v_s; - v_load_deinterleave(src + i, v_h, v_l, v_s); - process(v_h, v_l, v_s); - v_store_interleave(dst, v_s, v_l, v_h); - } - } else { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_h; - v_float32x4 v_l; - v_float32x4 v_s; - v_load_deinterleave(src + i, v_h, v_l, v_s); - process(v_h, v_l, v_s); - v_store_interleave(dst, v_h, v_l, v_s); - } - } - } else { // dcn == 4 - if (bidx) - { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_h; - v_float32x4 v_l; - v_float32x4 v_s; - v_load_deinterleave(src + i, v_h, v_l, v_s); - process(v_h, v_l, v_s); - v_float32x4 v_a = v_setall_f32(alpha); - v_store_interleave(dst, v_s, v_l, v_h, v_a); - } - } else { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_h; - v_float32x4 v_l; - v_float32x4 v_s; - v_load_deinterleave(src + i, v_h, v_l, v_s); - process(v_h, v_l, v_s); - v_float32x4 v_a = v_setall_f32(alpha); - v_store_interleave(dst, v_h, v_l, v_s, v_a); - } - } + v_store_interleave(dst, b, g, r); + } + else + { + v_float32 a = vx_setall_f32(alpha); + v_store_interleave(dst, b, g, r, a); } } - #endif +#endif - for( ; i < n; i += 3, dst += dcn ) + for( ; i < n; i++, src += 3, dst += dcn ) { - float h = src[i], l = src[i+1], s = src[i+2]; + float h = src[0], l = src[1], s = src[2]; float b, g, r; if( s == 0 ) @@ -989,117 +972,84 @@ struct HLS2RGB_f int dstcn, blueIdx; float hscale; - #if CV_SIMD128 - bool hasSIMD; - #endif }; struct HLS2RGB_b { typedef uchar channel_type; + static const int bufChannels = 3; HLS2RGB_b(int _dstcn, int _blueIdx, int _hrange) - : dstcn(_dstcn), cvt(3, _blueIdx, (float)_hrange) - { - #if CV_NEON - v_scale_inv = vdupq_n_f32(1.f/255.f); - v_scale = vdupq_n_f32(255.f); - v_alpha = vdup_n_u8(ColorChannel::max()); - #elif CV_SSE2 - v_scale = _mm_set1_ps(255.f); - v_alpha = _mm_set1_ps(ColorChannel::max()); - v_zero = _mm_setzero_si128(); - haveSIMD = checkHardwareSupport(CV_CPU_SSE2); - #endif - } - - #if CV_SSE2 - void process(__m128i v_r, __m128i v_g, __m128i v_b, - const __m128& v_coeffs_, - float * buf) const - { - __m128 v_r0 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v_r, v_zero)); - __m128 v_g0 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v_g, v_zero)); - __m128 v_b0 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v_b, v_zero)); - - __m128 v_r1 = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v_r, v_zero)); - __m128 v_g1 = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v_g, v_zero)); - __m128 v_b1 = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v_b, v_zero)); - - __m128 v_coeffs = v_coeffs_; - - v_r0 = _mm_mul_ps(v_r0, v_coeffs); - v_g1 = _mm_mul_ps(v_g1, v_coeffs); - - v_coeffs = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(v_coeffs), 0x49)); - - v_r1 = _mm_mul_ps(v_r1, v_coeffs); - v_b0 = _mm_mul_ps(v_b0, v_coeffs); - - v_coeffs = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(v_coeffs), 0x49)); - - v_g0 = _mm_mul_ps(v_g0, v_coeffs); - v_b1 = _mm_mul_ps(v_b1, v_coeffs); - - _mm_store_ps(buf, v_r0); - _mm_store_ps(buf + 4, v_r1); - _mm_store_ps(buf + 8, v_g0); - _mm_store_ps(buf + 12, v_g1); - _mm_store_ps(buf + 16, v_b0); - _mm_store_ps(buf + 20, v_b1); - } - #endif + : dstcn(_dstcn), cvt(bufChannels, _blueIdx, (float)_hrange) + { } void operator()(const uchar* src, uchar* dst, int n) const { + CV_INSTRUMENT_REGION(); + int i, j, dcn = dstcn; uchar alpha = ColorChannel::max(); - float CV_DECL_ALIGNED(16) buf[3*BLOCK_SIZE]; - #if CV_SSE2 - __m128 v_coeffs = _mm_set_ps(1.f, 1.f/255.f, 1.f/255.f, 1.f); - #endif + +#if CV_SIMD + float CV_DECL_ALIGNED(CV_SIMD_WIDTH) buf[bufChannels*BLOCK_SIZE]; +#else + float CV_DECL_ALIGNED(16) buf[bufChannels*BLOCK_SIZE]; +#endif + +#if CV_SIMD + static const int fsize = v_float32::nlanes; + //TODO: fix that when v_interleave is available + float CV_DECL_ALIGNED(CV_SIMD_WIDTH) interTmpM[fsize*3]; + v_float32 v255inv = vx_setall_f32(1.f/255.f); + v_store_interleave(interTmpM, vx_setall_f32(1.f), v255inv, v255inv); + v_float32 mhls[3]; + for(int k = 0; k < 3; k++) + { + mhls[k] = vx_load_aligned(interTmpM + k*fsize); + } +#endif for( i = 0; i < n; i += BLOCK_SIZE, src += BLOCK_SIZE*3 ) { int dn = std::min(n - i, (int)BLOCK_SIZE); j = 0; - #if CV_NEON - for ( ; j <= (dn - 8) * 3; j += 24) +#if CV_SIMD + for( ; j <= dn*3 - 3*4*fsize; j += 3*4*fsize) { - uint8x8x3_t v_src = vld3_u8(src + j); - uint16x8_t v_t0 = vmovl_u8(v_src.val[0]), - v_t1 = vmovl_u8(v_src.val[1]), - v_t2 = vmovl_u8(v_src.val[2]); - - float32x4x3_t v_dst; - v_dst.val[0] = vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_t0))); - v_dst.val[1] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_t1))), v_scale_inv); - v_dst.val[2] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_t2))), v_scale_inv); - vst3q_f32(buf + j, v_dst); - - v_dst.val[0] = vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_t0))); - v_dst.val[1] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_t1))), v_scale_inv); - v_dst.val[2] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_t2))), v_scale_inv); - vst3q_f32(buf + j + 12, v_dst); - } - #elif CV_SSE2 - if (haveSIMD) - { - for ( ; j <= (dn - 8) * 3; j += 24) + // 3x uchar -> 3*4 float + v_uint8 u[3]; + for(int k = 0; k < 3; k++) { - __m128i v_src0 = _mm_loadu_si128((__m128i const *)(src + j)); - __m128i v_src1 = _mm_loadl_epi64((__m128i const *)(src + j + 16)); + u[k] = vx_load(src + j + k*4*fsize); + } + v_uint16 d[3*2]; + for(int k = 0; k < 3; k++) + { + v_expand(u[k], d[k*2+0], d[k*2+1]); + } + v_int32 q[3*4]; + for(int k = 0; k < 3*2; k++) + { + v_expand(v_reinterpret_as_s16(d[k]), q[k*2+0], q[k*2+1]); + } - process(_mm_unpacklo_epi8(v_src0, v_zero), - _mm_unpackhi_epi8(v_src0, v_zero), - _mm_unpacklo_epi8(v_src1, v_zero), - v_coeffs, - buf + j); + v_float32 f[3*4]; + for(int k = 0; k < 4; k++) + { + for(int l = 0; l < 3; l++) + { + f[k*3+l] = v_cvt_f32(q[k*3+l])*mhls[l]; + } + } + + for (int k = 0; k < 4*3; k++) + { + v_store_aligned(buf + j + k*fsize, f[k]); } } - #endif +#endif for( ; j < dn*3; j += 3 ) { buf[j] = src[j]; @@ -1108,108 +1058,79 @@ struct HLS2RGB_b } cvt(buf, buf, dn); - j = 0; - #if CV_NEON - for ( ; j <= (dn - 8) * 3; j += 24, dst += dcn * 8) +#if CV_SIMD + v_float32 v255 = vx_setall_f32(255.f); + if(dcn == 3) { - float32x4x3_t v_src0 = vld3q_f32(buf + j), v_src1 = vld3q_f32(buf + j + 12); - uint8x8_t v_dst0 = vqmovn_u16(vcombine_u16(vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src0.val[0], v_scale))), - vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src1.val[0], v_scale))))); - uint8x8_t v_dst1 = vqmovn_u16(vcombine_u16(vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src0.val[1], v_scale))), - vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src1.val[1], v_scale))))); - uint8x8_t v_dst2 = vqmovn_u16(vcombine_u16(vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src0.val[2], v_scale))), - vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src1.val[2], v_scale))))); - - if (dcn == 4) + int x = 0; + float* pbuf = buf; + for( ; x <= dn - 4*fsize; x += 4*fsize, dst += 4*fsize, pbuf += 4*fsize) { - uint8x8x4_t v_dst; - v_dst.val[0] = v_dst0; - v_dst.val[1] = v_dst1; - v_dst.val[2] = v_dst2; - v_dst.val[3] = v_alpha; - vst4_u8(dst, v_dst); + v_float32 vf[4]; + vf[0] = vx_load_aligned(pbuf + 0*fsize); + vf[1] = vx_load_aligned(pbuf + 1*fsize); + vf[2] = vx_load_aligned(pbuf + 2*fsize); + vf[3] = vx_load_aligned(pbuf + 3*fsize); + v_int32 vi[4]; + vi[0] = v_round(vf[0]*v255); + vi[1] = v_round(vf[1]*v255); + vi[2] = v_round(vf[2]*v255); + vi[3] = v_round(vf[3]*v255); + v_store(dst, v_pack_u(v_pack(vi[0], vi[1]), + v_pack(vi[2], vi[3]))); } - else + for( ; x < dn*3; x++, dst++, pbuf++) { - uint8x8x3_t v_dst; - v_dst.val[0] = v_dst0; - v_dst.val[1] = v_dst1; - v_dst.val[2] = v_dst2; - vst3_u8(dst, v_dst); + dst[0] = saturate_cast(pbuf[0]*255.f); } } - #elif CV_SSE2 - if (dcn == 3 && haveSIMD) + else // dcn == 4 { - for ( ; j <= (dn * 3 - 16); j += 16, dst += 16) + int x = 0; + float* pbuf = buf; + for ( ; x <= dn - 4*fsize; x += fsize, dst += 4*fsize, pbuf += bufChannels*fsize) { - __m128 v_src0 = _mm_mul_ps(_mm_load_ps(buf + j), v_scale); - __m128 v_src1 = _mm_mul_ps(_mm_load_ps(buf + j + 4), v_scale); - __m128 v_src2 = _mm_mul_ps(_mm_load_ps(buf + j + 8), v_scale); - __m128 v_src3 = _mm_mul_ps(_mm_load_ps(buf + j + 12), v_scale); + v_float32 r[4], g[4], b[4]; + v_int32 ir[4], ig[4], ib[4]; + for(int k = 0; k < 4; k++) + { + v_load_deinterleave(pbuf, r[k], g[k], b[k]); + ir[k] = v_round(r[k]*v255); + ig[k] = v_round(g[k]*v255); + ib[k] = v_round(b[k]*v255); + } + v_uint8 ur, ug, ub; + ur = v_pack_u(v_pack(ir[0], ir[1]), v_pack(ir[2], ir[3])); + ug = v_pack_u(v_pack(ig[0], ig[1]), v_pack(ig[2], ig[3])); + ub = v_pack_u(v_pack(ib[0], ib[1]), v_pack(ib[2], ib[3])); - __m128i v_dst0 = _mm_packs_epi32(_mm_cvtps_epi32(v_src0), - _mm_cvtps_epi32(v_src1)); - __m128i v_dst1 = _mm_packs_epi32(_mm_cvtps_epi32(v_src2), - _mm_cvtps_epi32(v_src3)); - - _mm_storeu_si128((__m128i *)dst, _mm_packus_epi16(v_dst0, v_dst1)); + v_uint8 valpha = vx_setall_u8(alpha); + v_store_interleave(dst, ur, ug, ub, valpha); } - int jr = j % 3; - if (jr) - dst -= jr, j -= jr; - } - else if (dcn == 4 && haveSIMD) - { - for ( ; j <= (dn * 3 - 12); j += 12, dst += 16) + for( ; x < dn; x++, dst += dcn, pbuf += bufChannels) { - __m128 v_buf0 = _mm_mul_ps(_mm_load_ps(buf + j), v_scale); - __m128 v_buf1 = _mm_mul_ps(_mm_load_ps(buf + j + 4), v_scale); - __m128 v_buf2 = _mm_mul_ps(_mm_load_ps(buf + j + 8), v_scale); - - __m128 v_ba0 = _mm_unpackhi_ps(v_buf0, v_alpha); - __m128 v_ba1 = _mm_unpacklo_ps(v_buf2, v_alpha); - - __m128i v_src0 = _mm_cvtps_epi32(_mm_shuffle_ps(v_buf0, v_ba0, 0x44)); - __m128i v_src1 = _mm_shuffle_epi32(_mm_cvtps_epi32(_mm_shuffle_ps(v_ba0, v_buf1, 0x4e)), 0x78); - __m128i v_src2 = _mm_cvtps_epi32(_mm_shuffle_ps(v_buf1, v_ba1, 0x4e)); - __m128i v_src3 = _mm_shuffle_epi32(_mm_cvtps_epi32(_mm_shuffle_ps(v_ba1, v_buf2, 0xee)), 0x78); - - __m128i v_dst0 = _mm_packs_epi32(v_src0, v_src1); - __m128i v_dst1 = _mm_packs_epi32(v_src2, v_src3); - - _mm_storeu_si128((__m128i *)dst, _mm_packus_epi16(v_dst0, v_dst1)); + dst[0] = saturate_cast(pbuf[0]*255.f); + dst[1] = saturate_cast(pbuf[1]*255.f); + dst[2] = saturate_cast(pbuf[2]*255.f); + dst[3] = alpha; } - - int jr = j % 3; - if (jr) - dst -= jr, j -= jr; } - #endif - - for( ; j < dn*3; j += 3, dst += dcn ) +#else + for(int x = 0; x < dn*3; x += 3, dst += dcn ) { - dst[0] = saturate_cast(buf[j]*255.f); - dst[1] = saturate_cast(buf[j+1]*255.f); - dst[2] = saturate_cast(buf[j+2]*255.f); + dst[0] = saturate_cast(buf[x+0]*255.f); + dst[1] = saturate_cast(buf[x+1]*255.f); + dst[2] = saturate_cast(buf[x+2]*255.f); if( dcn == 4 ) dst[3] = alpha; } +#endif } } int dstcn; HLS2RGB_f cvt; - #if CV_NEON - float32x4_t v_scale, v_scale_inv; - uint8x8_t v_alpha; - #elif CV_SSE2 - __m128 v_scale; - __m128 v_alpha; - __m128i v_zero; - bool haveSIMD; - #endif }; } // namespace anon From 171dd9eff54f19f44e75e7659fb52d2b2efdbe91 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 26 May 2019 14:37:37 +0000 Subject: [PATCH 09/21] java(test): enable debug mode to show useful stacktraces --- modules/java/test/pure_test/build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/java/test/pure_test/build.xml b/modules/java/test/pure_test/build.xml index 870dd44d74..e9b57fe34d 100644 --- a/modules/java/test/pure_test/build.xml +++ b/modules/java/test/pure_test/build.xml @@ -21,7 +21,7 @@ - + From b95849807498f556749bc6a0d9614b426263bb13 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 26 May 2019 15:09:35 +0000 Subject: [PATCH 10/21] java(test): test package filtering Usage example: - run.py -a -t java --package=calib3d --- modules/java/test/pure_test/build.xml | 7 +++++-- modules/ts/misc/run.py | 2 +- modules/ts/misc/run_suite.py | 5 ++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/java/test/pure_test/build.xml b/modules/java/test/pure_test/build.xml index 870dd44d74..e15d0d67db 100644 --- a/modules/java/test/pure_test/build.xml +++ b/modules/java/test/pure_test/build.xml @@ -4,6 +4,9 @@ + + + @@ -38,7 +41,7 @@ - + @@ -50,7 +53,7 @@ - + diff --git a/modules/ts/misc/run.py b/modules/ts/misc/run.py index 29c0aa6b90..2d5de0708c 100755 --- a/modules/ts/misc/run.py +++ b/modules/ts/misc/run.py @@ -50,7 +50,7 @@ if __name__ == "__main__": parser.add_argument("--android_env", action='append', help="Android: add environment variable (NAME=VALUE)") parser.add_argument("--android_propagate_opencv_env", action="store_true", default=False, help="Android: propagate OPENCV* environment variables") parser.add_argument("--serial", metavar="serial number", default="", help="Android: directs command to the USB device or emulator with the given serial number") - parser.add_argument("--package", metavar="package", default="", help="Android: run jUnit tests for specified package") + parser.add_argument("--package", metavar="package", default="", help="Java: run JUnit tests for specified module or Android package") parser.add_argument("--trace", action="store_true", default=False, help="Trace: enable OpenCV tracing") parser.add_argument("--trace_dump", metavar="trace_dump", default=-1, help="Trace: dump highlight calls (specify max entries count, 0 - dump all)") diff --git a/modules/ts/misc/run_suite.py b/modules/ts/misc/run_suite.py index a3c4be7546..0420d9a968 100644 --- a/modules/ts/misc/run_suite.py +++ b/modules/ts/misc/run_suite.py @@ -112,7 +112,10 @@ class TestSuite(object): args = args[:] exe = os.path.abspath(path) if module == "java": - cmd = [self.cache.ant_executable, "-Dopencv.build.type=%s" % self.cache.build_type, "buildAndTest"] + cmd = [self.cache.ant_executable, "-Dopencv.build.type=%s" % self.cache.build_type] + if self.options.package: + cmd += ["-Dopencv.test.package=%s" % self.options.package] + cmd += ["buildAndTest"] ret = execute(cmd, cwd=self.cache.java_test_dir) return None, ret elif module in ['python2', 'python3']: From 898cf70210de08e765852c496b5c8cb748de3b15 Mon Sep 17 00:00:00 2001 From: utibenkei Date: Mon, 27 May 2019 01:43:34 +0900 Subject: [PATCH 11/21] Merge pull request #14597 from utibenkei:fix_java_missing_consts * java: fix java missing_consts * java: test Calib3d constants --- modules/calib3d/misc/java/gen_dict.json | 15 ---------- .../calib3d/misc/java/test/Calib3dTest.java | 28 +++++++++++++++++++ 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/modules/calib3d/misc/java/gen_dict.json b/modules/calib3d/misc/java/gen_dict.json index 4658096730..caef0d22e8 100644 --- a/modules/calib3d/misc/java/gen_dict.json +++ b/modules/calib3d/misc/java/gen_dict.json @@ -3,21 +3,6 @@ "CirclesGridFinderParameters", "CirclesGridFinderParameters2" ], - "missing_consts" : { - "Calib3d": { - "public" : [ - ["CALIB_USE_INTRINSIC_GUESS", "1"], - ["CALIB_RECOMPUTE_EXTRINSIC", "2"], - ["CALIB_CHECK_COND", "4"], - ["CALIB_FIX_SKEW", "8"], - ["CALIB_FIX_K1", "16"], - ["CALIB_FIX_K2", "32"], - ["CALIB_FIX_K3", "64"], - ["CALIB_FIX_K4", "128"], - ["CALIB_FIX_INTRINSIC", "256"] - ] - } - }, "namespaces_dict": { "cv.fisheye": "fisheye" }, diff --git a/modules/calib3d/misc/java/test/Calib3dTest.java b/modules/calib3d/misc/java/test/Calib3dTest.java index 99153dfb21..ba0199be87 100644 --- a/modules/calib3d/misc/java/test/Calib3dTest.java +++ b/modules/calib3d/misc/java/test/Calib3dTest.java @@ -611,4 +611,32 @@ public class Calib3dTest extends OpenCVTestCase { Calib3d.computeCorrespondEpilines(left, 1, fundamental, lines); assertMatEqual(truth, lines, EPS); } + + public void testConstants() + { + // calib3d.hpp: some constants have conflict with constants from 'fisheye' namespace + assertEquals(1, Calib3d.CALIB_USE_INTRINSIC_GUESS); + assertEquals(2, Calib3d.CALIB_FIX_ASPECT_RATIO); + assertEquals(4, Calib3d.CALIB_FIX_PRINCIPAL_POINT); + assertEquals(8, Calib3d.CALIB_ZERO_TANGENT_DIST); + assertEquals(16, Calib3d.CALIB_FIX_FOCAL_LENGTH); + assertEquals(32, Calib3d.CALIB_FIX_K1); + assertEquals(64, Calib3d.CALIB_FIX_K2); + assertEquals(128, Calib3d.CALIB_FIX_K3); + assertEquals(0x0800, Calib3d.CALIB_FIX_K4); + assertEquals(0x1000, Calib3d.CALIB_FIX_K5); + assertEquals(0x2000, Calib3d.CALIB_FIX_K6); + assertEquals(0x4000, Calib3d.CALIB_RATIONAL_MODEL); + assertEquals(0x8000, Calib3d.CALIB_THIN_PRISM_MODEL); + assertEquals(0x10000, Calib3d.CALIB_FIX_S1_S2_S3_S4); + assertEquals(0x40000, Calib3d.CALIB_TILTED_MODEL); + assertEquals(0x80000, Calib3d.CALIB_FIX_TAUX_TAUY); + assertEquals(0x100000, Calib3d.CALIB_USE_QR); + assertEquals(0x200000, Calib3d.CALIB_FIX_TANGENT_DIST); + assertEquals(0x100, Calib3d.CALIB_FIX_INTRINSIC); + assertEquals(0x200, Calib3d.CALIB_SAME_FOCAL_LENGTH); + assertEquals(0x400, Calib3d.CALIB_ZERO_DISPARITY); + assertEquals((1 << 17), Calib3d.CALIB_USE_LU); + assertEquals((1 << 22), Calib3d.CALIB_USE_EXTRINSIC_GUESS); + } } From 472f3b734736347a6824780aa25a8119a843e05b Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Fri, 24 May 2019 18:29:29 +0300 Subject: [PATCH 12/21] Fix TBB debug --- cmake/OpenCVDetectTBB.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVDetectTBB.cmake b/cmake/OpenCVDetectTBB.cmake index 403b446116..38137f44f0 100644 --- a/cmake/OpenCVDetectTBB.cmake +++ b/cmake/OpenCVDetectTBB.cmake @@ -70,9 +70,13 @@ function(ocv_tbb_env_guess _found) add_library(tbb UNKNOWN IMPORTED) set_target_properties(tbb PROPERTIES IMPORTED_LOCATION "${TBB_ENV_LIB}" - IMPORTED_LOCATION_DEBUG "${TBB_ENV_LIB_DEBUG}" INTERFACE_INCLUDE_DIRECTORIES "${TBB_ENV_INCLUDE}" ) + if (TBB_ENV_LIB_DEBUG) + set_target_properties(tbb PROPERTIES + IMPORTED_LOCATION_DEBUG "${TBB_ENV_LIB_DEBUG}" + ) + endif() # workaround: system TBB library is used for linking instead of provided if(CV_GCC) get_filename_component(_dir "${TBB_ENV_LIB}" DIRECTORY) From a550e506835f0dac4e573a23d47a90c27ddbbe62 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Fri, 24 May 2019 14:37:56 +0300 Subject: [PATCH 13/21] Fix IE status message on MacOS --- CMakeLists.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c52c1cfaf..0ed7e41be3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1583,11 +1583,16 @@ if(WITH_INF_ENGINE OR INF_ENGINE_TARGET) if(INF_ENGINE_TARGET) set(__msg "YES (${INF_ENGINE_RELEASE} / ${INF_ENGINE_VERSION})") get_target_property(_lib ${INF_ENGINE_TARGET} IMPORTED_LOCATION) - if(NOT _lib) - get_target_property(_lib_rel ${INF_ENGINE_TARGET} IMPORTED_IMPLIB_RELEASE) - get_target_property(_lib_dbg ${INF_ENGINE_TARGET} IMPORTED_IMPLIB_DEBUG) - set(_lib "${_lib_rel} / ${_lib_dbg}") - endif() + get_target_property(_lib_imp_rel ${INF_ENGINE_TARGET} IMPORTED_IMPLIB_RELEASE) + get_target_property(_lib_imp_dbg ${INF_ENGINE_TARGET} IMPORTED_IMPLIB_DEBUG) + get_target_property(_lib_rel ${INF_ENGINE_TARGET} IMPORTED_LOCATION_RELEASE) + get_target_property(_lib_dbg ${INF_ENGINE_TARGET} IMPORTED_LOCATION_DEBUG) + ocv_build_features_string(_lib + IF _lib THEN "${_lib}" + IF _lib_imp_rel AND _lib_imp_dbg THEN "${_lib_imp_rel} / ${_lib_imp_dbg}" + IF _lib_rel AND _lib_dbg THEN "${_lib_rel} / ${_lib_dbg}" + ELSE "unknown" + ) get_target_property(_inc ${INF_ENGINE_TARGET} INTERFACE_INCLUDE_DIRECTORIES) status(" Inference Engine:" "${__msg}") status(" libs:" "${_lib}") From 483f28723cff51eae65e86c946f10fb61ec9227d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 27 May 2019 14:39:47 +0300 Subject: [PATCH 14/21] calib3d: initialize local vars, fix indentation in for loops --- modules/calib3d/src/ap3p.cpp | 6 +++--- modules/calib3d/src/epnp.cpp | 42 +++++++++++++++++++----------------- modules/calib3d/src/p3p.cpp | 17 +++++++-------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/modules/calib3d/src/ap3p.cpp b/modules/calib3d/src/ap3p.cpp index 11171f81a6..cc48b6dac3 100644 --- a/modules/calib3d/src/ap3p.cpp +++ b/modules/calib3d/src/ap3p.cpp @@ -328,7 +328,7 @@ int ap3p::computePoses(const double featureVectors[3][4], bool ap3p::solve(cv::Mat &R, cv::Mat &tvec, const cv::Mat &opoints, const cv::Mat &ipoints) { CV_INSTRUMENT_REGION(); - double rotation_matrix[3][3], translation[3]; + double rotation_matrix[3][3] = {}, translation[3] = {}; std::vector points; if (opoints.depth() == ipoints.depth()) { if (opoints.depth() == CV_32F) @@ -353,7 +353,7 @@ bool ap3p::solve(cv::Mat &R, cv::Mat &tvec, const cv::Mat &opoints, const cv::Ma int ap3p::solve(std::vector &Rs, std::vector &tvecs, const cv::Mat &opoints, const cv::Mat &ipoints) { CV_INSTRUMENT_REGION(); - double rotation_matrix[4][3][3], translation[4][3]; + double rotation_matrix[4][3][3] = {}, translation[4][3] = {}; std::vector points; if (opoints.depth() == ipoints.depth()) { if (opoints.depth() == CV_32F) @@ -391,7 +391,7 @@ ap3p::solve(double R[3][3], double t[3], double mu1, double mv1, double X1, double Y1, double Z1, double mu2, double mv2, double X2, double Y2, double Z2, double mu3, double mv3, double X3, double Y3, double Z3) { - double Rs[4][3][3], ts[4][3]; + double Rs[4][3][3] = {}, ts[4][3] = {}; const bool p4p = true; int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2, mu3, mv3, X3, Y3, Z3, p4p); diff --git a/modules/calib3d/src/epnp.cpp b/modules/calib3d/src/epnp.cpp index a173b88087..9a887c8067 100644 --- a/modules/calib3d/src/epnp.cpp +++ b/modules/calib3d/src/epnp.cpp @@ -60,7 +60,7 @@ void epnp::choose_control_points(void) // Take C1, C2, and C3 from PCA on the reference points: CvMat * PW0 = cvCreateMat(number_of_correspondences, 3, CV_64F); - double pw0tpw0[3 * 3], dc[3] = {0}, uct[3 * 3] = {0}; + double pw0tpw0[3 * 3] = {}, dc[3] = {}, uct[3 * 3] = {}; CvMat PW0tPW0 = cvMat(3, 3, CV_64F, pw0tpw0); CvMat DC = cvMat(3, 1, CV_64F, dc); CvMat UCt = cvMat(3, 3, CV_64F, uct); @@ -83,7 +83,7 @@ void epnp::choose_control_points(void) void epnp::compute_barycentric_coordinates(void) { - double cc[3 * 3], cc_inv[3 * 3]; + double cc[3 * 3] = {}, cc_inv[3 * 3] = {}; CvMat CC = cvMat(3, 3, CV_64F, cc); CvMat CC_inv = cvMat(3, 3, CV_64F, cc_inv); @@ -98,10 +98,12 @@ void epnp::compute_barycentric_coordinates(void) double * a = &alphas[0] + 4 * i; for(int j = 0; j < 3; j++) + { a[1 + j] = - ci[3 * j ] * (pi[0] - cws[0][0]) + - ci[3 * j + 1] * (pi[1] - cws[0][1]) + - ci[3 * j + 2] * (pi[2] - cws[0][2]); + ci[3 * j ] * (pi[0] - cws[0][0]) + + ci[3 * j + 1] * (pi[1] - cws[0][1]) + + ci[3 * j + 2] * (pi[2] - cws[0][2]); + } a[0] = 1.0f - a[1] - a[2] - a[3]; } } @@ -132,7 +134,7 @@ void epnp::compute_ccs(const double * betas, const double * ut) const double * v = ut + 12 * (11 - i); for(int j = 0; j < 4; j++) for(int k = 0; k < 3; k++) - ccs[j][k] += betas[i] * v[3 * j + k]; + ccs[j][k] += betas[i] * v[3 * j + k]; } } @@ -157,7 +159,7 @@ void epnp::compute_pose(Mat& R, Mat& t) for(int i = 0; i < number_of_correspondences; i++) fill_M(M, 2 * i, &alphas[0] + 4 * i, us[2 * i], us[2 * i + 1]); - double mtm[12 * 12], d[12], ut[12 * 12]; + double mtm[12 * 12] = {}, d[12] = {}, ut[12 * 12] = {}; CvMat MtM = cvMat(12, 12, CV_64F, mtm); CvMat D = cvMat(12, 1, CV_64F, d); CvMat Ut = cvMat(12, 12, CV_64F, ut); @@ -166,15 +168,15 @@ void epnp::compute_pose(Mat& R, Mat& t) cvSVD(&MtM, &D, &Ut, 0, CV_SVD_MODIFY_A | CV_SVD_U_T); cvReleaseMat(&M); - double l_6x10[6 * 10], rho[6]; + double l_6x10[6 * 10] = {}, rho[6] = {}; CvMat L_6x10 = cvMat(6, 10, CV_64F, l_6x10); CvMat Rho = cvMat(6, 1, CV_64F, rho); compute_L_6x10(ut, l_6x10); compute_rho(rho); - double Betas[4][4], rep_errors[4]; - double Rs[4][3][3], ts[4][3]; + double Betas[4][4] = {}, rep_errors[4] = {}; + double Rs[4][3][3] = {}, ts[4][3] = {}; find_betas_approx_1(&L_6x10, &Rho, Betas[1]); gauss_newton(&L_6x10, &Rho, Betas[1]); @@ -221,7 +223,7 @@ double epnp::dot(const double * v1, const double * v2) void epnp::estimate_R_and_t(double R[3][3], double t[3]) { - double pc0[3], pw0[3]; + double pc0[3] = {}, pw0[3] = {}; pc0[0] = pc0[1] = pc0[2] = 0.0; pw0[0] = pw0[1] = pw0[2] = 0.0; @@ -240,7 +242,7 @@ void epnp::estimate_R_and_t(double R[3][3], double t[3]) pw0[j] /= number_of_correspondences; } - double abt[3 * 3] = {0}, abt_d[3], abt_u[3 * 3], abt_v[3 * 3]; + double abt[3 * 3] = {}, abt_d[3] = {}, abt_u[3 * 3] = {}, abt_v[3 * 3] = {}; CvMat ABt = cvMat(3, 3, CV_64F, abt); CvMat ABt_D = cvMat(3, 1, CV_64F, abt_d); CvMat ABt_U = cvMat(3, 3, CV_64F, abt_u); @@ -284,7 +286,7 @@ void epnp::solve_for_sign(void) if (pcs[2] < 0.0) { for(int i = 0; i < 4; i++) for(int j = 0; j < 3; j++) - ccs[i][j] = -ccs[i][j]; + ccs[i][j] = -ccs[i][j]; for(int i = 0; i < number_of_correspondences; i++) { pcs[3 * i ] = -pcs[3 * i]; @@ -332,7 +334,7 @@ double epnp::reprojection_error(const double R[3][3], const double t[3]) void epnp::find_betas_approx_1(const CvMat * L_6x10, const CvMat * Rho, double * betas) { - double l_6x4[6 * 4], b4[4] = {0}; + double l_6x4[6 * 4] = {}, b4[4] = {}; CvMat L_6x4 = cvMat(6, 4, CV_64F, l_6x4); CvMat B4 = cvMat(4, 1, CV_64F, b4); @@ -364,7 +366,7 @@ void epnp::find_betas_approx_1(const CvMat * L_6x10, const CvMat * Rho, void epnp::find_betas_approx_2(const CvMat * L_6x10, const CvMat * Rho, double * betas) { - double l_6x3[6 * 3], b3[3] = {0}; + double l_6x3[6 * 3] = {}, b3[3] = {}; CvMat L_6x3 = cvMat(6, 3, CV_64F, l_6x3); CvMat B3 = cvMat(3, 1, CV_64F, b3); @@ -396,7 +398,7 @@ void epnp::find_betas_approx_2(const CvMat * L_6x10, const CvMat * Rho, void epnp::find_betas_approx_3(const CvMat * L_6x10, const CvMat * Rho, double * betas) { - double l_6x5[6 * 5], b5[5] = {0}; + double l_6x5[6 * 5] = {}, b5[5] = {}; CvMat L_6x5 = cvMat(6, 5, CV_64F, l_6x5); CvMat B5 = cvMat(5, 1, CV_64F, b5); @@ -431,7 +433,7 @@ void epnp::compute_L_6x10(const double * ut, double * l_6x10) v[2] = ut + 12 * 9; v[3] = ut + 12 * 8; - double dv[4][6][3]; + double dv[4][6][3] = {}; for(int i = 0; i < 4; i++) { int a = 0, b = 1; @@ -442,8 +444,8 @@ void epnp::compute_L_6x10(const double * ut, double * l_6x10) b++; if (b > 3) { - a++; - b = a + 1; + a++; + b = a + 1; } } } @@ -506,7 +508,7 @@ void epnp::gauss_newton(const CvMat * L_6x10, const CvMat * Rho, double betas[4] { const int iterations_number = 5; - double a[6*4], b[6], x[4] = {0}; + double a[6*4] = {}, b[6] = {}, x[4] = {}; CvMat A = cvMat(6, 4, CV_64F, a); CvMat B = cvMat(6, 1, CV_64F, b); CvMat X = cvMat(4, 1, CV_64F, x); diff --git a/modules/calib3d/src/p3p.cpp b/modules/calib3d/src/p3p.cpp index 8ee0f490c7..01b1734db8 100644 --- a/modules/calib3d/src/p3p.cpp +++ b/modules/calib3d/src/p3p.cpp @@ -35,7 +35,7 @@ bool p3p::solve(cv::Mat& R, cv::Mat& tvec, const cv::Mat& opoints, const cv::Mat { CV_INSTRUMENT_REGION(); - double rotation_matrix[3][3], translation[3]; + double rotation_matrix[3][3] = {}, translation[3] = {}; std::vector points; if (opoints.depth() == ipoints.depth()) { @@ -63,7 +63,7 @@ int p3p::solve(std::vector& Rs, std::vector& tvecs, const cv:: { CV_INSTRUMENT_REGION(); - double rotation_matrix[4][3][3], translation[4][3]; + double rotation_matrix[4][3][3] = {}, translation[4][3] = {}; std::vector points; if (opoints.depth() == ipoints.depth()) { @@ -103,7 +103,7 @@ bool p3p::solve(double R[3][3], double t[3], double mu2, double mv2, double X2, double Y2, double Z2, double mu3, double mv3, double X3, double Y3, double Z3) { - double Rs[4][3][3], ts[4][3]; + double Rs[4][3][3] = {}, ts[4][3] = {}; const bool p4p = true; int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2, mu3, mv3, X3, Y3, Z3, p4p); @@ -159,7 +159,7 @@ int p3p::solve(double R[4][3][3], double t[4][3], cosines[1] = mu0 * mu2 + mv0 * mv2 + mk0 * mk2; cosines[2] = mu0 * mu1 + mv0 * mv1 + mk0 * mk1; - double lengths[4][3]; + double lengths[4][3] = {}; int n = solve_for_lengths(lengths, distances, cosines); int nb_solutions = 0; @@ -319,21 +319,21 @@ bool p3p::align(double M_end[3][3], double R[3][3], double T[3]) { // Centroids: - double C_start[3], C_end[3]; + double C_start[3] = {}, C_end[3] = {}; for(int i = 0; i < 3; i++) C_end[i] = (M_end[0][i] + M_end[1][i] + M_end[2][i]) / 3; C_start[0] = (X0 + X1 + X2) / 3; C_start[1] = (Y0 + Y1 + Y2) / 3; C_start[2] = (Z0 + Z1 + Z2) / 3; // Covariance matrix s: - double s[3 * 3]; + double s[3 * 3] = {}; for(int j = 0; j < 3; j++) { s[0 * 3 + j] = (X0 * M_end[0][j] + X1 * M_end[1][j] + X2 * M_end[2][j]) / 3 - C_end[j] * C_start[0]; s[1 * 3 + j] = (Y0 * M_end[0][j] + Y1 * M_end[1][j] + Y2 * M_end[2][j]) / 3 - C_end[j] * C_start[1]; s[2 * 3 + j] = (Z0 * M_end[0][j] + Z1 * M_end[1][j] + Z2 * M_end[2][j]) / 3 - C_end[j] * C_start[2]; } - double Qs[16], evs[4], U[16]; + double Qs[16] = {}, evs[4] = {}, U[16] = {}; Qs[0 * 4 + 0] = s[0 * 3 + 0] + s[1 * 3 + 1] + s[2 * 3 + 2]; Qs[1 * 4 + 1] = s[0 * 3 + 0] - s[1 * 3 + 1] - s[2 * 3 + 2]; @@ -386,7 +386,7 @@ bool p3p::align(double M_end[3][3], bool p3p::jacobi_4x4(double * A, double * D, double * U) { - double B[4], Z[4]; + double B[4] = {}, Z[4] = {}; double Id[16] = {1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., @@ -396,7 +396,6 @@ bool p3p::jacobi_4x4(double * A, double * D, double * U) B[0] = A[0]; B[1] = A[5]; B[2] = A[10]; B[3] = A[15]; memcpy(D, B, 4 * sizeof(double)); - memset(Z, 0, 4 * sizeof(double)); for(int iter = 0; iter < 50; iter++) { double sum = fabs(A[1]) + fabs(A[2]) + fabs(A[3]) + fabs(A[6]) + fabs(A[7]) + fabs(A[11]); From 791ebd05fce702ef7377251d035dcc231657db83 Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Mon, 27 May 2019 16:19:01 +0300 Subject: [PATCH 15/21] out of bounds read fixed in rgb2luv_b --- modules/imgproc/src/color_lab.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/modules/imgproc/src/color_lab.cpp b/modules/imgproc/src/color_lab.cpp index e488d26a8e..337d601f69 100644 --- a/modules/imgproc/src/color_lab.cpp +++ b/modules/imgproc/src/color_lab.cpp @@ -3266,7 +3266,7 @@ struct RGB2Luv_b return; } - int i, j, scn = srccn; + int scn = srccn; #if CV_SIMD float CV_DECL_ALIGNED(CV_SIMD_WIDTH) buf[bufChannels*BLOCK_SIZE]; #else @@ -3295,16 +3295,16 @@ struct RGB2Luv_b } #endif - for( i = 0; i < n; i += BLOCK_SIZE, dst += BLOCK_SIZE*bufChannels ) + for(int i = 0; i < n; i += BLOCK_SIZE, dst += BLOCK_SIZE*bufChannels ) { int dn = std::min(n - i, (int)BLOCK_SIZE); - j = 0; static const softfloat f255inv = softfloat::one()/f255; #if CV_SIMD v_float32 v255inv = vx_setall_f32((float)f255inv); if(scn == 4) { + int j = 0; static const int nBlock = fsize*4; for( ; j <= dn*bufChannels - nBlock*3; j += nBlock*3, src += nBlock*4) @@ -3334,9 +3334,16 @@ struct RGB2Luv_b v_store_interleave(buf + j + k*3*fsize, f[0*4+k], f[1*4+k], f[2*4+k]); } } + for( ; j < dn*bufChannels; j += bufChannels, src += 4 ) + { + buf[j ] = (float)(src[0]*((float)f255inv)); + buf[j+1] = (float)(src[1]*((float)f255inv)); + buf[j+2] = (float)(src[2]*((float)f255inv)); + } } else // scn == 3 { + int j = 0; static const int nBlock = fsize*2; for( ; j <= dn*bufChannels - nBlock; j += nBlock, src += nBlock) @@ -3348,17 +3355,23 @@ struct RGB2Luv_b v_store_aligned(buf + j + 0*fsize, v_cvt_f32(q0)*v255inv); v_store_aligned(buf + j + 1*fsize, v_cvt_f32(q1)*v255inv); } + for( ; j < dn*bufChannels; j++, src++ ) + { + buf[j] = (float)(src[0]*((float)f255inv)); + } } -#endif - for( ; j < dn*bufChannels; j += bufChannels, src += scn ) +#else + for(int j = 0; j < dn*bufChannels; j += bufChannels, src += scn ) { buf[j ] = (float)(src[0]*((float)f255inv)); buf[j+1] = (float)(src[1]*((float)f255inv)); buf[j+2] = (float)(src[2]*((float)f255inv)); } +#endif + fcvt(buf, buf, dn); - j = 0; + int j = 0; #if CV_SIMD for( ; j <= dn*3 - fsize*3*4; j += fsize*3*4) @@ -3389,7 +3402,7 @@ struct RGB2Luv_b #endif for( ; j < dn*3; j += 3 ) { - dst[j] = saturate_cast(buf[j]*(float)fL); + dst[j+0] = saturate_cast(buf[j+0]*(float)fL); dst[j+1] = saturate_cast(buf[j+1]*(float)fu + (float)su); dst[j+2] = saturate_cast(buf[j+2]*(float)fv + (float)sv); } From 8c698262eae113660d19c924b27fe147a497d05e Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Mon, 27 May 2019 16:19:52 +0300 Subject: [PATCH 16/21] rgb2hls_b: out of bounds read fixed --- modules/imgproc/src/color_hsv.simd.hpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/modules/imgproc/src/color_hsv.simd.hpp b/modules/imgproc/src/color_hsv.simd.hpp index d0a93c9aa8..7501f4b113 100644 --- a/modules/imgproc/src/color_hsv.simd.hpp +++ b/modules/imgproc/src/color_hsv.simd.hpp @@ -724,7 +724,7 @@ struct RGB2HLS_b { CV_INSTRUMENT_REGION(); - int i, j, scn = srccn; + int scn = srccn; #if CV_SIMD float CV_DECL_ALIGNED(CV_SIMD_WIDTH) buf[bufChannels*BLOCK_SIZE]; @@ -744,15 +744,15 @@ struct RGB2HLS_b } #endif - for( i = 0; i < n; i += BLOCK_SIZE, dst += BLOCK_SIZE*3 ) + for(int i = 0; i < n; i += BLOCK_SIZE, dst += BLOCK_SIZE*3 ) { int dn = std::min(n - i, (int)BLOCK_SIZE); - j = 0; #if CV_SIMD v_float32 v255inv = vx_setall_f32(1.f/255.f); if (scn == 3) { + int j = 0; static const int nBlock = fsize*2; for ( ; j <= (dn * bufChannels - nBlock); j += nBlock, src += nBlock) @@ -763,9 +763,14 @@ struct RGB2HLS_b v_store_aligned(buf + j + 0*fsize, v_cvt_f32(qrgb0)*v255inv); v_store_aligned(buf + j + 1*fsize, v_cvt_f32(qrgb1)*v255inv); } + for( ; j < dn*3; j++, src++ ) + { + buf[j] = src[0]*(1.f/255.f); + } } else // if (scn == 4) { + int j = 0; static const int nBlock = fsize*4; for ( ; j <= dn*bufChannels - nBlock*bufChannels; j += nBlock*bufChannels, src += nBlock*4) @@ -795,17 +800,24 @@ struct RGB2HLS_b v_store_interleave(buf + j + k*bufChannels*fsize, f[0*4+k], f[1*4+k], f[2*4+k]); } } + for( ; j < dn*3; j += 3, src += 4 ) + { + buf[j+0] = src[0]*(1.f/255.f); + buf[j+1] = src[1]*(1.f/255.f); + buf[j+2] = src[2]*(1.f/255.f); + } } -#endif - for( ; j < dn*3; j += 3, src += scn ) +#else + for(int j = 0; j < dn*3; j += 3, src += scn ) { buf[j+0] = src[0]*(1.f/255.f); buf[j+1] = src[1]*(1.f/255.f); buf[j+2] = src[2]*(1.f/255.f); } +#endif cvt(buf, buf, dn); - j = 0; + int j = 0; #if CV_SIMD for( ; j <= dn*3 - fsize*3*4; j += fsize*3*4) { From 44d21e5a7998af89f60298754373faafa7651a44 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Fri, 24 May 2019 19:28:37 +0300 Subject: [PATCH 17/21] Enable Slice layer on Inference Engine backend --- modules/dnn/perf/perf_net.cpp | 3 +- modules/dnn/src/layers/slice_layer.cpp | 66 +++++++++++++++++--------- modules/dnn/test/test_backends.cpp | 2 +- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/modules/dnn/perf/perf_net.cpp b/modules/dnn/perf/perf_net.cpp index ce870df049..df92ed7b76 100644 --- a/modules/dnn/perf/perf_net.cpp +++ b/modules/dnn/perf/perf_net.cpp @@ -214,8 +214,7 @@ PERF_TEST_P_(DNNTestNetwork, EAST_text_detection) PERF_TEST_P_(DNNTestNetwork, FastNeuralStyle_eccv16) { if (backend == DNN_BACKEND_HALIDE || - (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16) || - (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)) + (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16)) throw SkipTestException(""); processNet("dnn/fast_neural_style_eccv16_starry_night.t7", "", "", Mat(cv::Size(320, 240), CV_32FC3)); } diff --git a/modules/dnn/src/layers/slice_layer.cpp b/modules/dnn/src/layers/slice_layer.cpp index 0821979376..11dd4ea9e2 100644 --- a/modules/dnn/src/layers/slice_layer.cpp +++ b/modules/dnn/src/layers/slice_layer.cpp @@ -110,15 +110,9 @@ public: virtual bool supportBackend(int backendId) CV_OVERRIDE { -#ifdef HAVE_INF_ENGINE - if (backendId == DNN_BACKEND_INFERENCE_ENGINE) - { - return INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2018R5) && - sliceRanges.size() == 1 && sliceRanges[0].size() == 4; - } - else -#endif - return backendId == DNN_BACKEND_OPENCV; + return backendId == DNN_BACKEND_OPENCV || + (backendId == DNN_BACKEND_INFERENCE_ENGINE && + sliceRanges.size() == 1 && sliceRanges[0].size() == 4); } bool getMemoryShapes(const std::vector &inputs, @@ -264,39 +258,65 @@ public: #ifdef HAVE_INF_ENGINE virtual Ptr initInfEngine(const std::vector >& inputs) CV_OVERRIDE { -#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2018R5) - InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]); - InferenceEngine::LayerParams lp; - lp.name = name; - lp.type = "Crop"; - lp.precision = InferenceEngine::Precision::FP32; - std::shared_ptr ieLayer(new InferenceEngine::CropLayer(lp)); - CV_Assert(sliceRanges.size() == 1); + std::vector axes, offsets, dims; int from, to, step; + int numDims = sliceRanges[0].size(); if (preferableTarget == DNN_TARGET_MYRIAD) { from = 1; - to = sliceRanges[0].size() + 1; + to = numDims; step = 1; } else { - from = sliceRanges[0].size() - 1; + from = numDims - 1; to = -1; step = -1; } for (int i = from; i != to; i += step) { - ieLayer->axis.push_back(i); - ieLayer->offset.push_back(sliceRanges[0][i].start); - ieLayer->dim.push_back(sliceRanges[0][i].end - sliceRanges[0][i].start); + axes.push_back(i); + offsets.push_back(sliceRanges[0][i].start); + dims.push_back(sliceRanges[0][i].size()); } + +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + std::vector outShape(numDims); + for (int i = 0; i < numDims; ++i) + outShape[numDims - 1 - i] = sliceRanges[0][i].size(); + + InferenceEngine::Builder::Layer ieLayer(name); + ieLayer.setName(name); + ieLayer.setType("Crop"); + ieLayer.getParameters()["axis"] = axes; + ieLayer.getParameters()["dim"] = dims; + ieLayer.getParameters()["offset"] = offsets; + ieLayer.setInputPorts(std::vector(2)); + ieLayer.setOutputPorts(std::vector(1)); + ieLayer.getInputPorts()[1].setParameter("type", "weights"); + + // Fake blob which will be moved to inputs (as weights). + auto shapeSource = InferenceEngine::make_shared_blob( + InferenceEngine::Precision::FP32, + InferenceEngine::Layout::ANY, outShape); + shapeSource->allocate(); + addConstantData("weights", shapeSource, ieLayer); + return Ptr(new InfEngineBackendNode(ieLayer)); #else - return Ptr(); + InferenceEngine::LayerParams lp; + lp.name = name; + lp.type = "Crop"; + lp.precision = InferenceEngine::Precision::FP32; + std::shared_ptr ieLayer(new InferenceEngine::CropLayer(lp)); + ieLayer->axis = axes; + ieLayer->offset = offsets; + ieLayer->dim = dims; + return Ptr(new InfEngineBackendNode(ieLayer)); #endif // IE < R5 + return Ptr(); } #endif }; diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index 564c4986a5..6de0657a1a 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -396,7 +396,7 @@ TEST_P(DNNTestNetwork, FastNeuralStyle_eccv16) Mat img = imread(findDataFile("dnn/googlenet_1.png", false)); Mat inp = blobFromImage(img, 1.0, Size(320, 240), Scalar(103.939, 116.779, 123.68), false, false); // Output image has values in range [-143.526, 148.539]. - float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.3 : 4e-5; + float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.4 : 4e-5; float lInf = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 7.28 : 2e-3; processNet("dnn/fast_neural_style_eccv16_starry_night.t7", "", inp, "", "", l1, lInf); } From 61d3222a229b5a407b2da26e75a63ab3bbea068a Mon Sep 17 00:00:00 2001 From: Lubov Batanina Date: Mon, 27 May 2019 19:17:07 +0300 Subject: [PATCH 18/21] Merge pull request #14537 from l-bat:fix_network_vizualizer Fix dnn visualizer (#14537) * Fixed print layer params * Fix print --- modules/dnn/src/dnn.cpp | 81 +++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index e7a363b9b0..83cd9d8d1a 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -2980,6 +2980,23 @@ int Net::getLayerId(const String &layer) return impl->getLayerId(layer); } +String parseLayerParams(const String& name, const LayerParams& lp) { + DictValue param = lp.get(name); + std::ostringstream out; + out << name << " "; + switch (param.size()) { + case 1: out << ": "; break; + case 2: out << "(HxW): "; break; + case 3: out << "(DxHxW): "; break; + default: CV_Error(Error::StsNotImplemented, format("Unsupported %s size = %d", name.c_str(), param.size())); + } + for (size_t i = 0; i < param.size() - 1; i++) { + out << param.get(i) << " x "; + } + out << param.get(param.size() - 1) << "\\l"; + return out.str(); +} + String Net::dump() { CV_Assert(!empty()); @@ -3065,39 +3082,47 @@ String Net::dump() out << " | "; } out << lp.name << "\\n" << lp.type << "\\n"; - if (lp.has("kernel_size")) { - DictValue size = lp.get("kernel_size"); - out << "kernel (HxW): " << size << " x " << size << "\\l"; - } else if (lp.has("kernel_h") && lp.has("kernel_w")) { - DictValue h = lp.get("kernel_h"); - DictValue w = lp.get("kernel_w"); - out << "kernel (HxW): " << h << " x " << w << "\\l"; - } - if (lp.has("stride")) { - DictValue stride = lp.get("stride"); - out << "stride (HxW): " << stride << " x " << stride << "\\l"; - } else if (lp.has("stride_h") && lp.has("stride_w")) { - DictValue h = lp.get("stride_h"); - DictValue w = lp.get("stride_w"); - out << "stride (HxW): " << h << " x " << w << "\\l"; - } - if (lp.has("dilation")) { - DictValue dilation = lp.get("dilation"); - out << "dilation (HxW): " << dilation << " x " << dilation << "\\l"; - } else if (lp.has("dilation_h") && lp.has("dilation_w")) { - DictValue h = lp.get("dilation_h"); - DictValue w = lp.get("dilation_w"); - out << "dilation (HxW): " << h << " x " << w << "\\l"; - } - if (lp.has("pad")) { - DictValue pad = lp.get("pad"); - out << "pad (LxTxRxB): " << pad << " x " << pad << " x " << pad << " x " << pad << "\\l"; + if (lp.has("kernel_size")) { + String kernel = parseLayerParams("kernel_size", lp); + out << kernel; + } else if (lp.has("kernel_h") && lp.has("kernel_w")) { + DictValue h = lp.get("kernel_h"); + DictValue w = lp.get("kernel_w"); + out << "kernel (HxW): " << h << " x " << w << "\\l"; + } + if (lp.has("stride")) { + String stride = parseLayerParams("stride", lp); + out << stride; + } else if (lp.has("stride_h") && lp.has("stride_w")) { + DictValue h = lp.get("stride_h"); + DictValue w = lp.get("stride_w"); + out << "stride (HxW): " << h << " x " << w << "\\l"; + } + if (lp.has("dilation")) { + String dilation = parseLayerParams("dilation", lp); + out << dilation; + } else if (lp.has("dilation_h") && lp.has("dilation_w")) { + DictValue h = lp.get("dilation_h"); + DictValue w = lp.get("dilation_w"); + out << "dilation (HxW): " << h << " x " << w << "\\l"; + } + if (lp.has("pad")) { + DictValue pad = lp.get("pad"); + out << "pad "; + switch (pad.size()) { + case 1: out << ": " << pad << "\\l"; break; + case 2: out << "(HxW): (" << pad.get(0) << " x " << pad.get(1) << ")" << "\\l"; break; + case 4: out << "(HxW): (" << pad.get(0) << ", " << pad.get(2) << ") x (" << pad.get(1) << ", " << pad.get(3) << ")" << "\\l"; break; + case 6: out << "(DxHxW): (" << pad.get(0) << ", " << pad.get(3) << ") x (" << pad.get(1) << ", " << pad.get(4) + << ") x (" << pad.get(2) << ", " << pad.get(5) << ")" << "\\l"; break; + default: CV_Error(Error::StsNotImplemented, format("Unsupported pad size = %d", pad.size())); + } } else if (lp.has("pad_l") && lp.has("pad_t") && lp.has("pad_r") && lp.has("pad_b")) { DictValue l = lp.get("pad_l"); DictValue t = lp.get("pad_t"); DictValue r = lp.get("pad_r"); DictValue b = lp.get("pad_b"); - out << "pad (LxTxRxB): " << l << " x " << t << " x " << r << " x " << b << "\\l"; + out << "pad (HxW): (" << t << ", " << b << ") x (" << l << ", " << r << ")" << "\\l"; } else if (lp.has("pooled_w") || lp.has("pooled_h")) { DictValue h = lp.get("pooled_h"); From 05563f5bc41c7751b70acbe4bf54d370d7d678c1 Mon Sep 17 00:00:00 2001 From: Alexey Nikolaev Date: Mon, 27 May 2019 19:18:37 +0300 Subject: [PATCH 19/21] Merge pull request #14592 from aleksey-nikolaev:master VideoCapture Direct Show. Added getter for CV_CAP_PROP_CONVERT_RGB, CAP_PROP_CHANNEL properties. (#14592) * Added getter for CV_CAP_PROP_CONVERT_RGB, CAP_PROP_CHANNEL properties. Some refactoring. * One space less. --- modules/videoio/src/cap_dshow.cpp | 55 +++++++++++++++++-------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/modules/videoio/src/cap_dshow.cpp b/modules/videoio/src/cap_dshow.cpp index 7e12cb0b29..f167e2ff65 100644 --- a/modules/videoio/src/cap_dshow.cpp +++ b/modules/videoio/src/cap_dshow.cpp @@ -527,6 +527,8 @@ class videoInput{ int getFourcc(int deviceID) const; double getFPS(int deviceID) const; + int getChannel(int deviceID) const; + // RGB conversion setting bool getConvertRGB(int deviceID); bool setConvertRGB(int deviceID, bool enable); @@ -962,6 +964,16 @@ videoDevice::~videoDevice(){ HRESULT HR = NOERROR; + //Check to see if the graph is running, if so stop it. + if( (pControl) ) + { + HR = pControl->Pause(); + if (FAILED(HR)) DebugPrintOut("ERROR - Could not pause pControl\n"); + + HR = pControl->Stop(); + if (FAILED(HR)) DebugPrintOut("ERROR - Could not stop pControl\n"); + } + //Stop the callback and free it if( (sgCallback) && (pGrabber) ) { @@ -978,16 +990,6 @@ videoDevice::~videoDevice(){ delete sgCallback; } - //Check to see if the graph is running, if so stop it. - if( (pControl) ) - { - HR = pControl->Pause(); - if (FAILED(HR)) DebugPrintOut("ERROR - Could not pause pControl\n"); - - HR = pControl->Stop(); - if (FAILED(HR)) DebugPrintOut("ERROR - Could not stop pControl\n"); - } - //Disconnect filters from capture device if( (pVideoInputFilter) )NukeDownstream(pVideoInputFilter); @@ -1479,6 +1481,12 @@ double videoInput::getFPS(int id) const } +int videoInput::getChannel(int deviceID) const +{ + if (!isDeviceSetup(deviceID)) + return 0; + return VDList[deviceID]->storeConn; +} // ---------------------------------------------------------------------- // @@ -3332,11 +3340,15 @@ double VideoCapture_DShow::getProperty(int propIdx) const return g_VI.getFourcc(m_index); case CV_CAP_PROP_FPS: return g_VI.getFPS(m_index); + case CV_CAP_PROP_CONVERT_RGB: + return g_VI.getConvertRGB(m_index); + case CAP_PROP_CHANNEL: + return g_VI.getChannel(m_index); case CV_CAP_PROP_AUTOFOCUS: // Flags indicate whether or not autofocus is enabled if (g_VI.getVideoSettingCamera(m_index, CameraControl_Focus, min_value, max_value, stepping_delta, current_value, flags, defaultValue)) return (double)flags; - return -1; + break; // video filter properties case CV_CAP_PROP_BRIGHTNESS: @@ -3351,7 +3363,7 @@ double VideoCapture_DShow::getProperty(int propIdx) const case CV_CAP_PROP_GAIN: if (g_VI.getVideoSettingFilter(m_index, g_VI.getVideoPropertyFromCV(propIdx), min_value, max_value, stepping_delta, current_value, flags, defaultValue)) return (double)current_value; - return -1; + break; // camera properties case CV_CAP_PROP_PAN: @@ -3363,14 +3375,12 @@ double VideoCapture_DShow::getProperty(int propIdx) const case CV_CAP_PROP_FOCUS: if (g_VI.getVideoSettingCamera(m_index, g_VI.getCameraPropertyFromCV(propIdx), min_value, max_value, stepping_delta, current_value, flags, defaultValue)) return (double)current_value; - return -1; - } - - if (propIdx == CV_CAP_PROP_SETTINGS ) - { + break; + case CV_CAP_PROP_SETTINGS: return g_VI.property_window_count(m_index); + default: + break; } - // unknown parameter or value not available return -1; } @@ -3470,12 +3480,6 @@ bool VideoCapture_DShow::setProperty(int propIdx, double propVal) return true; } - // show video/camera filter dialog - if (propIdx == CV_CAP_PROP_SETTINGS ) - { - return g_VI.showSettingsWindow(m_index); - } - //video Filter properties switch (propIdx) { @@ -3503,6 +3507,9 @@ bool VideoCapture_DShow::setProperty(int propIdx, double propVal) case CV_CAP_PROP_IRIS: case CV_CAP_PROP_FOCUS: return g_VI.setVideoSettingCamera(m_index, g_VI.getCameraPropertyFromCV(propIdx), (long)propVal); + // show video/camera filter dialog + case CV_CAP_PROP_SETTINGS: + return g_VI.showSettingsWindow(m_index); } return false; From a3be8d7cd790ee9b44b3bcf9a086f4142dc15568 Mon Sep 17 00:00:00 2001 From: LaurentBerger Date: Mon, 27 May 2019 21:57:10 +0200 Subject: [PATCH 20/21] dead link --- modules/dnn/include/opencv2/dnn/all_layers.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index 72064843b9..c0ed2e028c 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -608,7 +608,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN }; /** - * @brief Bilinear resize layer from https://github.com/cdmh/deeplab-public + * @brief Bilinear resize layer from https://github.com/cdmh/deeplab-public-ver2 * * It differs from @ref ResizeLayer in output shape and resize scales computations. */ From 38a3c1ce6b77c185cfbbbf60c85b866607745930 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 27 May 2019 15:14:18 +0300 Subject: [PATCH 21/21] dnn(test): update test tags for Debug build --- modules/dnn/test/test_backends.cpp | 12 ++++++++-- modules/dnn/test/test_caffe_importer.cpp | 28 +++++++++++++++++++----- modules/dnn/test/test_onnx_importer.cpp | 7 ++++-- modules/dnn/test/test_tf_importer.cpp | 14 +++++++++--- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index 6de0657a1a..92634d8e20 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -110,7 +110,10 @@ TEST_P(DNNTestNetwork, AlexNet) TEST_P(DNNTestNetwork, ResNet_50) { - applyTestTag(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + applyTestTag( + (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), + CV_TEST_TAG_DEBUG_LONG + ); processNet("dnn/ResNet-50-model.caffemodel", "dnn/ResNet-50-deploy.prototxt", Size(224, 224), "prob", target == DNN_TARGET_OPENCL ? "dnn/halide_scheduler_opencl_resnet_50.yml" : @@ -344,7 +347,10 @@ TEST_P(DNNTestNetwork, opencv_face_detector) TEST_P(DNNTestNetwork, Inception_v2_SSD_TensorFlow) { - applyTestTag(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + applyTestTag( + (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), + CV_TEST_TAG_DEBUG_LONG + ); #if defined(INF_ENGINE_RELEASE) if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD && getInferenceEngineVPUType() == CV_DNN_INFERENCE_ENGINE_VPU_TYPE_MYRIAD_X) @@ -382,6 +388,8 @@ TEST_P(DNNTestNetwork, DenseNet_121) TEST_P(DNNTestNetwork, FastNeuralStyle_eccv16) { + applyTestTag(CV_TEST_TAG_MEMORY_512MB, CV_TEST_TAG_DEBUG_VERYLONG); + if (backend == DNN_BACKEND_HALIDE || (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)) throw SkipTestException(""); diff --git a/modules/dnn/test/test_caffe_importer.cpp b/modules/dnn/test/test_caffe_importer.cpp index 3b1cc02a85..43236ff37f 100644 --- a/modules/dnn/test/test_caffe_importer.cpp +++ b/modules/dnn/test/test_caffe_importer.cpp @@ -114,6 +114,9 @@ TEST_P(Reproducibility_AlexNet, Accuracy) { Target targetId = get<1>(GetParam()); applyTestTag(targetId == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + if (!ocl::useOpenCL() && targetId != DNN_TARGET_CPU) + throw SkipTestException("OpenCL is disabled"); + bool readFromMemory = get<0>(GetParam()); Net net; { @@ -154,7 +157,8 @@ INSTANTIATE_TEST_CASE_P(/**/, Reproducibility_AlexNet, Combine(testing::Bool(), TEST(Reproducibility_FCN, Accuracy) { - applyTestTag(CV_TEST_TAG_LONG, CV_TEST_TAG_MEMORY_2GB); + applyTestTag(CV_TEST_TAG_LONG, CV_TEST_TAG_DEBUG_VERYLONG, CV_TEST_TAG_MEMORY_2GB); + Net net; { const string proto = findDataFile("dnn/fcn8s-heavy-pascal.prototxt", false); @@ -183,7 +187,7 @@ TEST(Reproducibility_FCN, Accuracy) TEST(Reproducibility_SSD, Accuracy) { - applyTestTag(CV_TEST_TAG_MEMORY_512MB); + applyTestTag(CV_TEST_TAG_MEMORY_512MB, CV_TEST_TAG_DEBUG_LONG); Net net; { const string proto = findDataFile("dnn/ssd_vgg16.prototxt", false); @@ -281,6 +285,9 @@ TEST_P(Reproducibility_ResNet50, Accuracy) { Target targetId = GetParam(); applyTestTag(targetId == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + if (!ocl::useOpenCL() && targetId != DNN_TARGET_CPU) + throw SkipTestException("OpenCL is disabled"); + Net net = readNetFromCaffe(findDataFile("dnn/ResNet-50-deploy.prototxt", false), findDataFile("dnn/ResNet-50-model.caffemodel", false)); @@ -541,7 +548,11 @@ INSTANTIATE_TEST_CASE_P(Test_Caffe, opencv_face_detector, TEST_P(Test_Caffe_nets, FasterRCNN_vgg16) { - applyTestTag(CV_TEST_TAG_LONG, (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB)); + applyTestTag( + (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB), + CV_TEST_TAG_LONG, + CV_TEST_TAG_DEBUG_VERYLONG + ); #if defined(INF_ENGINE_RELEASE) if (backend == DNN_BACKEND_INFERENCE_ENGINE && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) @@ -559,7 +570,10 @@ TEST_P(Test_Caffe_nets, FasterRCNN_vgg16) TEST_P(Test_Caffe_nets, FasterRCNN_zf) { - applyTestTag(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + applyTestTag( + (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), + CV_TEST_TAG_DEBUG_LONG + ); if ((backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16) || (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)) throw SkipTestException(""); @@ -571,7 +585,11 @@ TEST_P(Test_Caffe_nets, FasterRCNN_zf) TEST_P(Test_Caffe_nets, RFCN) { - applyTestTag(CV_TEST_TAG_LONG, (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_2GB)); + applyTestTag( + (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_2GB), + CV_TEST_TAG_LONG, + CV_TEST_TAG_DEBUG_VERYLONG + ); if ((backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16) || (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)) throw SkipTestException(""); diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index e66012c304..f926a43f97 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -343,7 +343,7 @@ TEST_P(Test_ONNX_nets, VGG16_bn) TEST_P(Test_ONNX_nets, ZFNet) { - applyTestTag(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + applyTestTag(CV_TEST_TAG_MEMORY_2GB); testONNXModels("zfnet512", pb); } @@ -418,7 +418,10 @@ TEST_P(Test_ONNX_nets, MobileNet_v2) TEST_P(Test_ONNX_nets, LResNet100E_IR) { - applyTestTag(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + applyTestTag( + (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), + CV_TEST_TAG_DEBUG_LONG + ); if (backend == DNN_BACKEND_INFERENCE_ENGINE && (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_OPENCL || target == DNN_TARGET_MYRIAD)) throw SkipTestException(""); diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index 1a70e8f471..afe9287dd0 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -437,7 +437,12 @@ TEST_P(Test_TensorFlow_nets, MobileNet_v1_SSD) TEST_P(Test_TensorFlow_nets, Faster_RCNN) { - applyTestTag(CV_TEST_TAG_LONG, (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB)); // FIXIT split test + // FIXIT split test + applyTestTag( + (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_1GB : CV_TEST_TAG_MEMORY_2GB), + CV_TEST_TAG_LONG, + CV_TEST_TAG_DEBUG_VERYLONG + ); static std::string names[] = {"faster_rcnn_inception_v2_coco_2018_01_28", "faster_rcnn_resnet50_coco_2018_01_28"}; @@ -535,7 +540,10 @@ TEST_P(Test_TensorFlow_nets, opencv_face_detector_uint8) // np.save('east_text_detection.geometry.npy', geometry) TEST_P(Test_TensorFlow_nets, EAST_text_detection) { - applyTestTag(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB); + applyTestTag( + (target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB), + CV_TEST_TAG_DEBUG_LONG + ); #if defined(INF_ENGINE_RELEASE) if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) @@ -765,7 +773,7 @@ TEST(Test_TensorFlow, two_inputs) TEST(Test_TensorFlow, Mask_RCNN) { - applyTestTag(CV_TEST_TAG_MEMORY_1GB); + applyTestTag(CV_TEST_TAG_MEMORY_1GB, CV_TEST_TAG_DEBUG_VERYLONG); std::string proto = findDataFile("dnn/mask_rcnn_inception_v2_coco_2018_01_28.pbtxt", false); std::string model = findDataFile("dnn/mask_rcnn_inception_v2_coco_2018_01_28.pb", false);