From 71002e0e79ac6b03da7ceb09f066c20d41be495a Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 13 May 2015 22:00:02 +0300 Subject: [PATCH 01/13] some fixes from http://code.opencv.org/issues/3733 --- modules/imgproc/src/filter.cpp | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/modules/imgproc/src/filter.cpp b/modules/imgproc/src/filter.cpp index fb43eee0a1..f0b7ee79e5 100644 --- a/modules/imgproc/src/filter.cpp +++ b/modules/imgproc/src/filter.cpp @@ -3455,7 +3455,7 @@ struct SymmColumnSmallFilter : public SymmColumnFilter bool symmetrical = (this->symmetryType & KERNEL_SYMMETRICAL) != 0; bool is_1_2_1 = ky[0] == 2 && ky[1] == 1; bool is_1_m2_1 = ky[0] == -2 && ky[1] == 1; - bool is_m1_0_1 = ky[1] == 1 || ky[1] == -1; + bool is_m1_0_1 = ky[0] == 0 && (ky[1] == 1 || ky[1] == -1); ST f0 = ky[0], f1 = ky[1]; ST _delta = this->delta; CastOp castOp = this->castOp0; @@ -3486,13 +3486,12 @@ struct SymmColumnSmallFilter : public SymmColumnFilter D[i+2] = castOp(s0); D[i+3] = castOp(s1); } - #else + #endif for( ; i < width; i ++ ) { ST s0 = S0[i] + S1[i]*2 + S2[i] + _delta; D[i] = castOp(s0); } - #endif } else if( is_1_m2_1 ) { @@ -3509,17 +3508,16 @@ struct SymmColumnSmallFilter : public SymmColumnFilter D[i+2] = castOp(s0); D[i+3] = castOp(s1); } - #else + #endif for( ; i < width; i ++ ) { ST s0 = S0[i] - S1[i]*2 + S2[i] + _delta; D[i] = castOp(s0); } - #endif } else { - #if CV_ENABLE_UNROLLED + #if CV_ENABLE_UNROLLED for( ; i <= width - 4; i += 4 ) { ST s0 = (S0[i] + S2[i])*f1 + S1[i]*f0 + _delta; @@ -3532,16 +3530,13 @@ struct SymmColumnSmallFilter : public SymmColumnFilter D[i+2] = castOp(s0); D[i+3] = castOp(s1); } - #else + #endif for( ; i < width; i ++ ) { ST s0 = (S0[i] + S2[i])*f1 + S1[i]*f0 + _delta; D[i] = castOp(s0); } - #endif } - for( ; i < width; i++ ) - D[i] = castOp((S0[i] + S2[i])*f1 + S1[i]*f0 + _delta); } else { @@ -3549,7 +3544,7 @@ struct SymmColumnSmallFilter : public SymmColumnFilter { if( f1 < 0 ) std::swap(S0, S2); - #if CV_ENABLE_UNROLLED + #if CV_ENABLE_UNROLLED for( ; i <= width - 4; i += 4 ) { ST s0 = S2[i] - S0[i] + _delta; @@ -3562,19 +3557,18 @@ struct SymmColumnSmallFilter : public SymmColumnFilter D[i+2] = castOp(s0); D[i+3] = castOp(s1); } - #else + #endif for( ; i < width; i ++ ) { ST s0 = S2[i] - S0[i] + _delta; D[i] = castOp(s0); } - #endif if( f1 < 0 ) std::swap(S0, S2); } else { - #if CV_ENABLE_UNROLLED + #if CV_ENABLE_UNROLLED for( ; i <= width - 4; i += 4 ) { ST s0 = (S2[i] - S0[i])*f1 + _delta; @@ -3588,10 +3582,9 @@ struct SymmColumnSmallFilter : public SymmColumnFilter D[i+3] = castOp(s1); } #endif + for( ; i < width; i++ ) + D[i] = castOp((S2[i] - S0[i])*f1 + _delta); } - - for( ; i < width; i++ ) - D[i] = castOp((S2[i] - S0[i])*f1 + _delta); } } } From d3b0cb878a14430013fe00a8198f2bf9f486062d Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 13 May 2015 22:16:35 +0300 Subject: [PATCH 02/13] added test for http://code.opencv.org/issues/2736 --- modules/imgproc/test/test_filter.cpp | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index a203e60f5c..9253132186 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -1918,3 +1918,37 @@ TEST(Imgproc_Blur, borderTypes) EXPECT_EQ(expected_dst.size(), dst.size()); EXPECT_DOUBLE_EQ(0.0, cvtest::norm(expected_dst, dst, NORM_INF)); } + +TEST(Imgproc_Morphology, iterated) +{ + RNG& rng = theRNG(); + for( int iter = 0; iter < 30; iter++ ) + { + int width = rng.uniform(5, 33); + int height = rng.uniform(5, 33); + int cn = rng.uniform(1, 5); + int iterations = rng.uniform(1, 11); + int op = rng.uniform(0, 2); + Mat src(height, width, CV_8UC(cn)), dst0, dst1, dst2; + + randu(src, 0, 256); + if( op == 0 ) + dilate(src, dst0, Mat(), Point(-1,-1), iterations); + else + erode(src, dst0, Mat(), Point(-1,-1), iterations); + + for( int i = 0; i < iterations; i++ ) + if( op == 0 ) + dilate(i == 0 ? src : dst1, dst1, Mat(), Point(-1,-1), 1); + else + erode(i == 0 ? src : dst1, dst1, Mat(), Point(-1,-1), 1); + + Mat kern = getStructuringElement(MORPH_RECT, Size(3,3)); + if( op == 0 ) + dilate(src, dst2, kern, Point(-1,-1), iterations); + else + erode(src, dst2, kern, Point(-1,-1), iterations); + ASSERT_EQ(0.0, norm(dst0, dst1, NORM_INF)); + ASSERT_EQ(0.0, norm(dst0, dst2, NORM_INF)); + } +} From 2e7e754032e41830f591e45217c8db29fcdfdc79 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 08:06:46 +0300 Subject: [PATCH 03/13] added support for n-channel (n>4) images in warpAffine/warpPerspective/remap: http://code.opencv.org/issues/4190 --- modules/imgproc/src/imgwarp.cpp | 26 +++++++++++++++++++------- modules/imgproc/test/test_imgwarp.cpp | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 8a11515389..792ba1d959 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -3805,20 +3805,20 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, typedef typename CastOp::rtype T; typedef typename CastOp::type1 WT; Size ssize = _src.size(), dsize = _dst.size(); - int cn = _src.channels(); + int k, cn = _src.channels(); const AT* wtab = (const AT*)_wtab; const T* S0 = _src.ptr(); size_t sstep = _src.step/sizeof(S0[0]); - Scalar_ cval(saturate_cast(_borderValue[0]), - saturate_cast(_borderValue[1]), - saturate_cast(_borderValue[2]), - saturate_cast(_borderValue[3])); + T cval[CV_CN_MAX]; int dx, dy; CastOp castOp; VecOp vecOp; + for( k = 0; k < cn; k++ ) + cval[k] = saturate_cast(_borderValue[k & 3]); + unsigned width1 = std::max(ssize.width-1, 0), height1 = std::max(ssize.height-1, 0); - CV_Assert( cn <= 4 && ssize.area() > 0 ); + CV_Assert( ssize.area() > 0 ); #if CV_SSE2 if( _src.type() == CV_8UC3 ) width1 = std::max(ssize.width-2, 0); @@ -3882,7 +3882,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, WT t2 = S[2]*w[0] + S[5]*w[1] + S[sstep+2]*w[2] + S[sstep+5]*w[3]; D[0] = castOp(t0); D[1] = castOp(t1); D[2] = castOp(t2); } - else + else if( cn == 4 ) for( ; dx < X1; dx++, D += 4 ) { int sx = XY[dx*2], sy = XY[dx*2+1]; @@ -3895,6 +3895,18 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, t1 = S[3]*w[0] + S[7]*w[1] + S[sstep+3]*w[2] + S[sstep+7]*w[3]; D[2] = castOp(t0); D[3] = castOp(t1); } + else + for( ; dx < X1; dx++, D += cn ) + { + int sx = XY[dx*2], sy = XY[dx*2+1]; + const AT* w = wtab + FXY[dx]*4; + const T* S = S0 + sy*sstep + sx*cn; + for( k = 0; k < cn; k++ ) + { + WT t0 = S[k]*w[0] + S[k+cn]*w[1] + S[sstep+k]*w[2] + S[sstep+k+cn]*w[3]; + D[k] = castOp(t0); + } + } } else { diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index 176c9907f3..9fa48de007 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1632,4 +1632,26 @@ TEST(Resize, Area_half) } } +TEST(Imgproc_Warp, multichannel) +{ + RNG& rng = theRNG(); + for( int iter = 0; iter < 30; iter++ ) + { + int width = rng.uniform(3, 333); + int height = rng.uniform(3, 333); + int cn = rng.uniform(1, 10); + Mat src(height, width, CV_8UC(cn)), dst; + //randu(src, 0, 256); + src.setTo(0.); + + Mat rot = getRotationMatrix2D(Point2f(0.f, 0.f), 1, 1); + warpAffine(src, dst, rot, src.size()); + ASSERT_EQ(0.0, norm(dst, NORM_INF)); + Mat rot2 = Mat::eye(3, 3, rot.type()); + rot.copyTo(rot2.rowRange(0, 2)); + warpPerspective(src, dst, rot2, src.size()); + ASSERT_EQ(0.0, norm(dst, NORM_INF)); + } +} + /* End of file. */ From feb5b6aa93a12859ba0fa9d99eff5d0b1cccb08f Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 10:42:55 +0300 Subject: [PATCH 04/13] increased singularity epsilon in LU decomposition. This solved singular case from http://code.opencv.org/issues/3305. Added the respective test. --- modules/hal/src/matrix.cpp | 8 ++++---- modules/imgproc/test/test_imgwarp.cpp | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/modules/hal/src/matrix.cpp b/modules/hal/src/matrix.cpp index 9506aaf478..921b7783f7 100644 --- a/modules/hal/src/matrix.cpp +++ b/modules/hal/src/matrix.cpp @@ -49,7 +49,7 @@ namespace cv { namespace hal { \****************************************************************************************/ template static inline int -LUImpl(_Tp* A, size_t astep, int m, _Tp* b, size_t bstep, int n) +LUImpl(_Tp* A, size_t astep, int m, _Tp* b, size_t bstep, int n, _Tp eps) { int i, j, k, p = 1; astep /= sizeof(A[0]); @@ -63,7 +63,7 @@ LUImpl(_Tp* A, size_t astep, int m, _Tp* b, size_t bstep, int n) if( std::abs(A[j*astep + i]) > std::abs(A[k*astep + i]) ) k = j; - if( std::abs(A[k*astep + i]) < std::numeric_limits<_Tp>::epsilon() ) + if( std::abs(A[k*astep + i]) < eps ) return 0; if( k != i ) @@ -111,13 +111,13 @@ LUImpl(_Tp* A, size_t astep, int m, _Tp* b, size_t bstep, int n) int LU(float* A, size_t astep, int m, float* b, size_t bstep, int n) { - return LUImpl(A, astep, m, b, bstep, n); + return LUImpl(A, astep, m, b, bstep, n, FLT_EPSILON*10); } int LU(double* A, size_t astep, int m, double* b, size_t bstep, int n) { - return LUImpl(A, astep, m, b, bstep, n); + return LUImpl(A, astep, m, b, bstep, n, DBL_EPSILON*100); } diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index 9fa48de007..d15f72e409 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1654,4 +1654,18 @@ TEST(Imgproc_Warp, multichannel) } } +TEST(Imgproc_GetAffineTransform, singularity) +{ + Point2f A_sample[3]; + A_sample[0] = cv::Point2f(8, 9); + A_sample[1] = cv::Point2f(40, 41); + A_sample[2] = cv::Point2f(47, 48); + Point2f B_sample[3]; + B_sample[0] = cv::Point2f(7.37465, 11.8295); + B_sample[1] = cv::Point2f(15.0113, 12.8994); + B_sample[2] = cv::Point2f(38.9943, 9.56297); + Mat trans = cv::getAffineTransform(A_sample, B_sample); + ASSERT_EQ(0.0, norm(trans, NORM_INF)); +} + /* End of file. */ From b37aaa8303129dfeb6f2e7b151493055a0ea9ee6 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 15:39:42 +0300 Subject: [PATCH 05/13] significantly improved parallel non-local means by using granularity parameter in parallel_for_ loop. Because the algorithm deals with sliding sums, it's essential that each thread has enough work to do, otherwise the algorithm gets higher theoretical complexity and thus there is no speedup comparing to 1-thread code (at best). --- modules/photo/src/denoising.cpp | 44 ++++++++++++++++++--------- modules/photo/test/test_denoising.cpp | 11 +++++++ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/modules/photo/src/denoising.cpp b/modules/photo/src/denoising.cpp index c68d09b925..93d4b4ebbe 100644 --- a/modules/photo/src/denoising.cpp +++ b/modules/photo/src/denoising.cpp @@ -50,42 +50,50 @@ static void fastNlMeansDenoising_( const Mat& src, Mat& dst, const std::vector( - src, dst, templateWindowSize, searchWindowSize, &h[0])); + src, dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); break; case 2: if (hn == 1) parallel_for_(cv::Range(0, src.rows), FastNlMeansDenoisingInvoker, IT, UIT, D, int>( - src, dst, templateWindowSize, searchWindowSize, &h[0])); + src, dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); else parallel_for_(cv::Range(0, src.rows), FastNlMeansDenoisingInvoker, IT, UIT, D, Vec2i>( - src, dst, templateWindowSize, searchWindowSize, &h[0])); + src, dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); break; case 3: if (hn == 1) parallel_for_(cv::Range(0, src.rows), FastNlMeansDenoisingInvoker, IT, UIT, D, int>( - src, dst, templateWindowSize, searchWindowSize, &h[0])); + src, dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); else parallel_for_(cv::Range(0, src.rows), FastNlMeansDenoisingInvoker, IT, UIT, D, Vec3i>( - src, dst, templateWindowSize, searchWindowSize, &h[0])); + src, dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); break; case 4: if (hn == 1) parallel_for_(cv::Range(0, src.rows), FastNlMeansDenoisingInvoker, IT, UIT, D, int>( - src, dst, templateWindowSize, searchWindowSize, &h[0])); + src, dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); else parallel_for_(cv::Range(0, src.rows), FastNlMeansDenoisingInvoker, IT, UIT, D, Vec4i>( - src, dst, templateWindowSize, searchWindowSize, &h[0])); + src, dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); break; default: CV_Error(Error::StsBadArg, @@ -237,6 +245,7 @@ static void fastNlMeansDenoisingMulti_( const std::vector& srcImgs, Mat& ds int templateWindowSize, int searchWindowSize) { int hn = (int)h.size(); + double granularity = (double)std::max(1., (double)dst.total()/(1 << 16)); switch (srcImgs[0].type()) { @@ -244,43 +253,50 @@ static void fastNlMeansDenoisingMulti_( const std::vector& srcImgs, Mat& ds parallel_for_(cv::Range(0, srcImgs[0].rows), FastNlMeansMultiDenoisingInvoker( srcImgs, imgToDenoiseIndex, temporalWindowSize, - dst, templateWindowSize, searchWindowSize, &h[0])); + dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); break; case CV_8UC2: if (hn == 1) parallel_for_(cv::Range(0, srcImgs[0].rows), FastNlMeansMultiDenoisingInvoker, IT, UIT, D, int>( srcImgs, imgToDenoiseIndex, temporalWindowSize, - dst, templateWindowSize, searchWindowSize, &h[0])); + dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); else parallel_for_(cv::Range(0, srcImgs[0].rows), FastNlMeansMultiDenoisingInvoker, IT, UIT, D, Vec2i>( srcImgs, imgToDenoiseIndex, temporalWindowSize, - dst, templateWindowSize, searchWindowSize, &h[0])); + dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); break; case CV_8UC3: if (hn == 1) parallel_for_(cv::Range(0, srcImgs[0].rows), FastNlMeansMultiDenoisingInvoker, IT, UIT, D, int>( srcImgs, imgToDenoiseIndex, temporalWindowSize, - dst, templateWindowSize, searchWindowSize, &h[0])); + dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); else parallel_for_(cv::Range(0, srcImgs[0].rows), FastNlMeansMultiDenoisingInvoker, IT, UIT, D, Vec3i>( srcImgs, imgToDenoiseIndex, temporalWindowSize, - dst, templateWindowSize, searchWindowSize, &h[0])); + dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); break; case CV_8UC4: if (hn == 1) parallel_for_(cv::Range(0, srcImgs[0].rows), FastNlMeansMultiDenoisingInvoker, IT, UIT, D, int>( srcImgs, imgToDenoiseIndex, temporalWindowSize, - dst, templateWindowSize, searchWindowSize, &h[0])); + dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); else parallel_for_(cv::Range(0, srcImgs[0].rows), FastNlMeansMultiDenoisingInvoker, IT, UIT, D, Vec4i>( srcImgs, imgToDenoiseIndex, temporalWindowSize, - dst, templateWindowSize, searchWindowSize, &h[0])); + dst, templateWindowSize, searchWindowSize, &h[0]), + granularity); break; default: CV_Error(Error::StsBadArg, diff --git a/modules/photo/test/test_denoising.cpp b/modules/photo/test/test_denoising.cpp index 9808e9cddc..c3a69a2f76 100644 --- a/modules/photo/test/test_denoising.cpp +++ b/modules/photo/test/test_denoising.cpp @@ -156,3 +156,14 @@ TEST(Photo_White, issue_2646) ASSERT_EQ(0, nonWhitePixelsCount); } + +TEST(Photo_Denoising, speed) +{ + string imgname = string(cvtest::TS::ptr()->get_data_path()) + "shared/5MP.png"; + Mat src = imread(imgname, 0), dst; + + double t = (double)getTickCount(); + fastNlMeansDenoising(src, dst, 5, 7, 21); + t = (double)getTickCount() - t; + printf("execution time: %gms\n", t*1000./getTickFrequency()); +} From 05d888316a1f8ae93a15ba804a942b2d6d2f6bfd Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 16:25:18 +0300 Subject: [PATCH 06/13] added test for http://code.opencv.org/issues/2957 --- modules/imgproc/test/test_contours.cpp | 61 ++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/modules/imgproc/test/test_contours.cpp b/modules/imgproc/test/test_contours.cpp index b0b8c4fbb5..b94408d3b8 100644 --- a/modules/imgproc/test/test_contours.cpp +++ b/modules/imgproc/test/test_contours.cpp @@ -40,6 +40,7 @@ //M*/ #include "test_precomp.hpp" +#include "opencv2/highgui.hpp" using namespace cv; using namespace std; @@ -429,4 +430,64 @@ TEST(Core_Drawing, polylines) int cnt = countNonZero(img); ASSERT_EQ(cnt, 21); } + +//rotate/flip a quadrant appropriately +static void rot(int n, int *x, int *y, int rx, int ry) +{ + if (ry == 0) { + if (rx == 1) { + *x = n-1 - *x; + *y = n-1 - *y; + } + + //Swap x and y + int t = *x; + *x = *y; + *y = t; + } +} + +static void d2xy(int n, int d, int *x, int *y) +{ + int rx, ry, s, t=d; + *x = *y = 0; + for (s=1; s > contours; + findContours(img, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE); + printf("ncontours = %d, contour[0].npoints=%d\n", (int)contours.size(), (int)contours[0].size()); + img.setTo(Scalar::all(0)); + + drawContours(img, contours, 0, Scalar::all(255), 1); + //imshow("hilbert", img); + //waitKey(); + ASSERT_EQ(1, (int)contours.size()); + ASSERT_EQ(9832, (int)contours[0].size()); +} + /* End of file. */ From ca90667723648f9f27cc3badfcd12d49b44cc12c Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 16:42:51 +0300 Subject: [PATCH 07/13] fixed compile warnings on Linux and Windows --- modules/imgproc/src/imgwarp.cpp | 2 +- modules/imgproc/test/test_imgwarp.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 792ba1d959..760f3fb0a2 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -3960,7 +3960,7 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy, else for( ; dx < X1; dx++, D += cn ) { - int sx = XY[dx*2], sy = XY[dx*2+1], k; + int sx = XY[dx*2], sy = XY[dx*2+1]; if( borderType == BORDER_CONSTANT && (sx >= ssize.width || sx+1 < 0 || sy >= ssize.height || sy+1 < 0) ) diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index d15f72e409..bf3f6b3c4c 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1657,14 +1657,14 @@ TEST(Imgproc_Warp, multichannel) TEST(Imgproc_GetAffineTransform, singularity) { Point2f A_sample[3]; - A_sample[0] = cv::Point2f(8, 9); - A_sample[1] = cv::Point2f(40, 41); - A_sample[2] = cv::Point2f(47, 48); + A_sample[0] = Point2f(8.f, 9.f); + A_sample[1] = Point2f(40.f, 41.f); + A_sample[2] = Point2f(47.f, 48.f); Point2f B_sample[3]; - B_sample[0] = cv::Point2f(7.37465, 11.8295); - B_sample[1] = cv::Point2f(15.0113, 12.8994); - B_sample[2] = cv::Point2f(38.9943, 9.56297); - Mat trans = cv::getAffineTransform(A_sample, B_sample); + B_sample[0] = Point2f(7.37465f, 11.8295f); + B_sample[1] = Point2f(15.0113f, 12.8994f); + B_sample[2] = Point2f(38.9943f, 9.56297f); + Mat trans = getAffineTransform(A_sample, B_sample); ASSERT_EQ(0.0, norm(trans, NORM_INF)); } From ffabbfa77868089853617948ed41c7a57c0dd359 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 18:21:26 +0300 Subject: [PATCH 08/13] added test to prove that remap does not leak memory (http://code.opencv.org/issues/2502). disabled the test for now to save execution time. --- modules/imgproc/test/test_imgwarp.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index bf3f6b3c4c..4ffd50b41a 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1668,4 +1668,28 @@ TEST(Imgproc_GetAffineTransform, singularity) ASSERT_EQ(0.0, norm(trans, NORM_INF)); } +TEST(Imgproc_Remap, DISABLED_memleak) +{ + Mat src; + const int N = 400; + src.create(N, N, CV_8U); + randu(src, 0, 256); + Mat map_x, map_y, dst; + dst.create( src.size(), src.type() ); + map_x.create( src.size(), CV_32FC1 ); + map_y.create( src.size(), CV_32FC1 ); + randu(map_x, 0., N+0.); + randu(map_y, 0., N+0.); + + for( int iter = 0; iter < 10000; iter++ ) + { + if(iter % 100 == 0) + { + putchar('.'); + fflush(stdout); + } + remap(src, dst, map_x, map_y, CV_INTER_LINEAR); + } +} + /* End of file. */ From 3c7f7de477eb20debed0cb43dc0e7f10a3218ef0 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 19:32:25 +0300 Subject: [PATCH 09/13] avoid possible problems with zero values in tone mapping algorithms (http://code.opencv.org/issues/4020) --- modules/photo/src/tonemap.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/photo/src/tonemap.cpp b/modules/photo/src/tonemap.cpp index af930abb35..e475482d66 100644 --- a/modules/photo/src/tonemap.cpp +++ b/modules/photo/src/tonemap.cpp @@ -47,6 +47,12 @@ namespace cv { +inline void log_(const Mat& src, Mat& dst) +{ + max(src, Scalar::all(1e-4), dst); + log(dst, dst); +} + class TonemapImpl : public Tonemap { public: @@ -122,7 +128,7 @@ public: Mat gray_img; cvtColor(img, gray_img, COLOR_RGB2GRAY); Mat log_img; - log(gray_img, log_img); + log_(gray_img, log_img); float mean = expf(static_cast(sum(log_img)[0]) / log_img.total()); gray_img /= mean; log_img.release(); @@ -205,7 +211,7 @@ public: Mat gray_img; cvtColor(img, gray_img, COLOR_RGB2GRAY); Mat log_img; - log(gray_img, log_img); + log_(gray_img, log_img); Mat map_img; bilateralFilter(log_img, map_img, -1, sigma_color, sigma_space); @@ -289,7 +295,7 @@ public: Mat gray_img; cvtColor(img, gray_img, COLOR_RGB2GRAY); Mat log_img; - log(gray_img, log_img); + log_(gray_img, log_img); float log_mean = static_cast(sum(log_img)[0] / log_img.total()); double log_min, log_max; @@ -383,7 +389,7 @@ public: Mat gray_img; cvtColor(img, gray_img, COLOR_RGB2GRAY); Mat log_img; - log(gray_img, log_img); + log_(gray_img, log_img); std::vector x_contrast, y_contrast; getContrast(log_img, x_contrast, y_contrast); From 3c769edea0c51f7a74693f1ac747dfe9bcb7670e Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 19:54:48 +0300 Subject: [PATCH 10/13] added extra check in CalibrateDebevec to make sure the points are within the image: http://code.opencv.org/issues/4124 --- modules/photo/src/calibrate.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/photo/src/calibrate.cpp b/modules/photo/src/calibrate.cpp index eda3e1265b..63f1818e47 100644 --- a/modules/photo/src/calibrate.cpp +++ b/modules/photo/src/calibrate.cpp @@ -90,7 +90,9 @@ public: for(int i = 0, x = step_x / 2; i < x_points; i++, x += step_x) { for(int j = 0, y = step_y / 2; j < y_points; j++, y += step_y) { - sample_points.push_back(Point(x, y)); + if( 0 <= x && x < images[0].cols && + 0 <= y && y < images[0].rows ) + sample_points.push_back(Point(x, y)); } } } From 7dd67f4997ad871029dd73653945df0cfd6ebcee Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 20:33:53 +0300 Subject: [PATCH 11/13] use _mm_sad_epu8 instead of _mm_movemask_epi8 in countNonZero. Therefore, we do not need any popcount table and this solves http://code.opencv.org/issues/4333 --- modules/core/src/stat.cpp | 105 ++++++++------------------------------ 1 file changed, 20 insertions(+), 85 deletions(-) diff --git a/modules/core/src/stat.cpp b/modules/core/src/stat.cpp index 8a6bd54721..81f9a2484e 100644 --- a/modules/core/src/stat.cpp +++ b/modules/core/src/stat.cpp @@ -504,54 +504,21 @@ static int countNonZero_(const T* src, int len ) return nz; } -#if CV_SSE2 - -static const uchar * initPopcountTable() -{ - static uchar tab[256]; - static volatile bool initialized = false; - if( !initialized ) - { - // we compute inverse popcount table, - // since we pass (img[x] == 0) mask as index in the table. - unsigned int j = 0u; -#if CV_POPCNT - if (checkHardwareSupport(CV_CPU_POPCNT)) - { - for( ; j < 256u; j++ ) - tab[j] = (uchar)(8 - _mm_popcnt_u32(j)); - } -#endif - for( ; j < 256u; j++ ) - { - int val = 0; - for( int mask = 1; mask < 256; mask += mask ) - val += (j & mask) == 0; - tab[j] = (uchar)val; - } - initialized = true; - } - - return tab; -} - -#endif - static int countNonZero8u( const uchar* src, int len ) { int i=0, nz = 0; #if CV_SSE2 if(USE_SSE2)//5x-6x { - __m128i pattern = _mm_setzero_si128 (); - static const uchar * tab = initPopcountTable(); + __m128i v_zero = _mm_setzero_si128(); + __m128i sum = _mm_setzero_si128(); for (; i<=len-16; i+=16) { __m128i r0 = _mm_loadu_si128((const __m128i*)(src+i)); - int val = _mm_movemask_epi8(_mm_cmpeq_epi8(r0, pattern)); - nz += tab[val & 255] + tab[val >> 8]; + sum = _mm_add_epi32(sum, _mm_sad_epu8(_mm_sub_epi8(v_zero, _mm_cmpeq_epi8(r0, v_zero)), v_zero)); } + nz = i - _mm_cvtsi128_si32(_mm_add_epi32(sum, _mm_unpackhi_epi64(sum, sum))); } #elif CV_NEON int len0 = len & -16, blockSize1 = (1 << 8) - 16, blockSize0 = blockSize1 << 6; @@ -598,15 +565,15 @@ static int countNonZero16u( const ushort* src, int len ) if (USE_SSE2) { __m128i v_zero = _mm_setzero_si128 (); - static const uchar * tab = initPopcountTable(); + __m128i sum = _mm_setzero_si128(); for ( ; i <= len - 8; i += 8) { - __m128i v_src = _mm_loadu_si128((const __m128i*)(src + i)); - int val = _mm_movemask_epi8(_mm_packs_epi16(_mm_cmpeq_epi16(v_src, v_zero), v_zero)); - nz += tab[val]; + __m128i r0 = _mm_loadu_si128((const __m128i*)(src + i)); + sum = _mm_add_epi32(sum, _mm_sad_epu8(_mm_sub_epi8(v_zero, _mm_cmpeq_epi16(r0, v_zero)), v_zero)); } + nz = i - (_mm_cvtsi128_si32(_mm_add_epi32(sum, _mm_unpackhi_epi64(sum, sum))) >> 1); src += i; } #elif CV_NEON @@ -649,20 +616,15 @@ static int countNonZero32s( const int* src, int len ) if (USE_SSE2) { __m128i v_zero = _mm_setzero_si128 (); - static const uchar * tab = initPopcountTable(); + __m128i sum = _mm_setzero_si128(); - for ( ; i <= len - 8; i += 8) + for ( ; i <= len - 4; i += 4) { - __m128i v_src = _mm_loadu_si128((const __m128i*)(src + i)); - __m128i v_dst0 = _mm_cmpeq_epi32(v_src, v_zero); - - v_src = _mm_loadu_si128((const __m128i*)(src + i + 4)); - __m128i v_dst1 = _mm_cmpeq_epi32(v_src, v_zero); - - int val = _mm_movemask_epi8(_mm_packs_epi16(_mm_packs_epi32(v_dst0, v_dst1), v_zero)); - nz += tab[val]; + __m128i r0 = _mm_loadu_si128((const __m128i*)(src + i)); + sum = _mm_add_epi32(sum, _mm_sad_epu8(_mm_sub_epi8(v_zero, _mm_cmpeq_epi32(r0, v_zero)), v_zero)); } + nz = i - (_mm_cvtsi128_si32(_mm_add_epi32(sum, _mm_unpackhi_epi64(sum, sum))) >> 2); src += i; } #elif CV_NEON @@ -706,19 +668,17 @@ static int countNonZero32f( const float* src, int len ) #if CV_SSE2 if (USE_SSE2) { - __m128i v_zero_i = _mm_setzero_si128(); __m128 v_zero_f = _mm_setzero_ps(); - static const uchar * tab = initPopcountTable(); + __m128i v_zero = _mm_setzero_si128 (); + __m128i sum = _mm_setzero_si128(); - for ( ; i <= len - 8; i += 8) + for ( ; i <= len - 4; i += 4) { - __m128i v_dst0 = _mm_castps_si128(_mm_cmpeq_ps(_mm_loadu_ps(src + i), v_zero_f)); - __m128i v_dst1 = _mm_castps_si128(_mm_cmpeq_ps(_mm_loadu_ps(src + i + 4), v_zero_f)); - - int val = _mm_movemask_epi8(_mm_packs_epi16(_mm_packs_epi32(v_dst0, v_dst1), v_zero_i)); - nz += tab[val]; + __m128 r0 = _mm_loadu_ps(src + i); + sum = _mm_add_epi32(sum, _mm_sad_epu8(_mm_sub_epi8(v_zero, _mm_castps_si128(_mm_cmpeq_ps(r0, v_zero_f))), v_zero)); } + nz = i - (_mm_cvtsi128_si32(_mm_add_epi32(sum, _mm_unpackhi_epi64(sum, sum))) >> 2); src += i; } #elif CV_NEON @@ -758,32 +718,7 @@ static int countNonZero32f( const float* src, int len ) static int countNonZero64f( const double* src, int len ) { - int i = 0, nz = 0; -#if CV_SSE2 - if (USE_SSE2) - { - __m128i v_zero_i = _mm_setzero_si128(); - __m128d v_zero_d = _mm_setzero_pd(); - static const uchar * tab = initPopcountTable(); - - for ( ; i <= len - 8; i += 8) - { - __m128i v_dst0 = _mm_castpd_si128(_mm_cmpeq_pd(_mm_loadu_pd(src + i), v_zero_d)); - __m128i v_dst1 = _mm_castpd_si128(_mm_cmpeq_pd(_mm_loadu_pd(src + i + 2), v_zero_d)); - __m128i v_dst2 = _mm_castpd_si128(_mm_cmpeq_pd(_mm_loadu_pd(src + i + 4), v_zero_d)); - __m128i v_dst3 = _mm_castpd_si128(_mm_cmpeq_pd(_mm_loadu_pd(src + i + 6), v_zero_d)); - - v_dst0 = _mm_packs_epi32(v_dst0, v_dst1); - v_dst1 = _mm_packs_epi32(v_dst2, v_dst3); - - int val = _mm_movemask_epi8(_mm_packs_epi16(_mm_packs_epi32(v_dst0, v_dst1), v_zero_i)); - nz += tab[val]; - } - - src += i; - } -#endif - return nz + countNonZero_(src, len - i); + return countNonZero_(src, len); } typedef int (*CountNonZeroFunc)(const uchar*, int); From 66eb270cf72d555e7078b1d279f2c4c1d7f0111c Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 14 May 2015 23:29:09 +0300 Subject: [PATCH 12/13] significantly reduced sparse matrix footprint: http://code.opencv.org/issues/2206, http://code.opencv.org/issues/2924 --- modules/core/src/matrix.cpp | 7 ++++--- modules/core/test/test_mat.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index 9b77a4cf2c..42b271f1eb 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -4714,8 +4714,8 @@ SparseMat::Hdr::Hdr( int _dims, const int* _sizes, int _type ) refcount = 1; dims = _dims; - valueOffset = (int)alignSize(sizeof(SparseMat::Node) + - sizeof(int)*std::max(dims - CV_MAX_DIM, 0), CV_ELEM_SIZE1(_type)); + valueOffset = (int)alignSize(sizeof(SparseMat::Node) - MAX_DIM*sizeof(int) + + dims*sizeof(int), CV_ELEM_SIZE1(_type)); nodeSize = alignSize(valueOffset + CV_ELEM_SIZE(_type), (int)sizeof(size_t)); @@ -5114,7 +5114,8 @@ uchar* SparseMat::newNode(const int* idx, size_t hashval) if( !hdr->freeList ) { size_t i, nsz = hdr->nodeSize, psize = hdr->pool.size(), - newpsize = std::max(psize*2, 8*nsz); + newpsize = std::max(psize*3/2, 8*nsz); + newpsize = (newpsize/nsz)*nsz; hdr->pool.resize(newpsize); uchar* pool = &hdr->pool[0]; hdr->freeList = std::max(psize, nsz); diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 102bd99764..897ac40a43 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -1248,3 +1248,27 @@ TEST(Core_SVD, orthogonality) ASSERT_LT(norm(mat_U, Mat::eye(2, 2, type), NORM_INF), 1e-5); } } + + +TEST(Core_SparseMat, footprint) +{ + int n = 1000000; + int sz[] = { n, n }; + SparseMat m(2, sz, CV_64F); + + int nodeSize0 = (int)m.hdr->nodeSize; + double dataSize0 = ((double)m.hdr->pool.size() + (double)m.hdr->hashtab.size()*sizeof(size_t))*1e-6; + printf("before: node size=%d bytes, data size=%.0f Mbytes\n", nodeSize0, dataSize0); + + for (int i = 0; i < n; i++) + { + m.ref(i, i) = 1; + } + + double dataSize1 = ((double)m.hdr->pool.size() + (double)m.hdr->hashtab.size()*sizeof(size_t))*1e-6; + double threshold = (n*nodeSize0*1.6 + n*2.*sizeof(size_t))*1e-6; + printf("after: data size=%.0f Mbytes, threshold=%.0f MBytes\n", dataSize1, threshold); + + ASSERT_LE((int)m.hdr->nodeSize, 32); + ASSERT_LE(dataSize1, threshold); +} From 4a9b24457779352581c7878ed2734b31f103e00a Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Fri, 15 May 2015 16:15:10 +0300 Subject: [PATCH 13/13] hopefully, fixed crash in sparse mat test --- modules/core/src/matrix.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index 42b271f1eb..b273c8a7d8 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -4816,7 +4816,8 @@ void SparseMat::copyTo( SparseMat& m ) const void SparseMat::copyTo( Mat& m ) const { CV_Assert( hdr ); - m.create( dims(), hdr->size, type() ); + int ndims = dims(); + m.create( ndims, hdr->size, type() ); m = Scalar(0); SparseMatConstIterator from = begin(); @@ -4825,7 +4826,7 @@ void SparseMat::copyTo( Mat& m ) const for( i = 0; i < N; i++, ++from ) { const Node* n = from.node(); - copyElem( from.ptr, m.ptr(n->idx), esz); + copyElem( from.ptr, (ndims > 1 ? m.ptr(n->idx) : m.ptr(n->idx[0])), esz); } }