test_smooth_bitexact.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. #include "test_precomp.hpp"
  5. namespace opencv_test { namespace {
  6. static const int fixedShiftU8 = 8;
  7. static const int64_t fixedOneU8 = (1L << fixedShiftU8);
  8. static const int fixedShiftU16 = 16;
  9. static const int64_t fixedOneU16 = (1L << fixedShiftU16);
  10. int64_t vU8[][9] = {
  11. { fixedOneU8 }, // size 1, sigma 0
  12. { fixedOneU8 >> 2, fixedOneU8 >> 1, fixedOneU8 >> 2 }, // size 3, sigma 0
  13. { fixedOneU8 >> 4, fixedOneU8 >> 2, 6 * (fixedOneU8 >> 4), fixedOneU8 >> 2, fixedOneU8 >> 4 }, // size 5, sigma 0
  14. { fixedOneU8 >> 5, 7 * (fixedOneU8 >> 6), 7 * (fixedOneU8 >> 5), 9 * (fixedOneU8 >> 5), 7 * (fixedOneU8 >> 5), 7 * (fixedOneU8 >> 6), fixedOneU8 >> 5 }, // size 7, sigma 0
  15. { 4, 13, 30, 51, 60, 51, 30, 13, 4 }, // size 9, sigma 0
  16. #if 1
  17. #define CV_TEST_INACCURATE_GAUSSIAN_BLUR
  18. { 81, 94, 81 }, // size 3, sigma 1.75
  19. { 65, 126, 65 }, // size 3, sigma 0.875
  20. { 0, 7, 242, 7, 0 }, // size 5, sigma 0.375
  21. { 4, 56, 136, 56, 4 } // size 5, sigma 0.75
  22. #endif
  23. };
  24. int64_t vU16[][9] = {
  25. { fixedOneU16 }, // size 1, sigma 0
  26. { fixedOneU16 >> 2, fixedOneU16 >> 1, fixedOneU16 >> 2 }, // size 3, sigma 0
  27. { fixedOneU16 >> 4, fixedOneU16 >> 2, 6 * (fixedOneU16 >> 4), fixedOneU16 >> 2, fixedOneU16 >> 4 }, // size 5, sigma 0
  28. { fixedOneU16 >> 5, 7 * (fixedOneU16 >> 6), 7 * (fixedOneU16 >> 5), 9 * (fixedOneU16 >> 5), 7 * (fixedOneU16 >> 5), 7 * (fixedOneU16 >> 6), fixedOneU16 >> 5 }, // size 7, sigma 0
  29. { 4<<8, 13<<8, 30<<8, 51<<8, 60<<8, 51<<8, 30<<8, 13<<8, 4<<8 } // size 9, sigma 0
  30. };
  31. template <typename T, int fixedShift>
  32. T eval(Mat src, vector<int64_t> kernelx, vector<int64_t> kernely)
  33. {
  34. static const int64_t fixedRound = ((1LL << (fixedShift * 2)) >> 1);
  35. int64_t val = 0;
  36. for (size_t j = 0; j < kernely.size(); j++)
  37. {
  38. int64_t lineval = 0;
  39. for (size_t i = 0; i < kernelx.size(); i++)
  40. lineval += src.at<T>((int)j, (int)i) * kernelx[i];
  41. val += lineval * kernely[j];
  42. }
  43. return saturate_cast<T>((val + fixedRound) >> (fixedShift * 2));
  44. }
  45. struct testmode
  46. {
  47. int type;
  48. Size sz;
  49. Size kernel;
  50. double sigma_x;
  51. double sigma_y;
  52. vector<int64_t> kernel_x;
  53. vector<int64_t> kernel_y;
  54. };
  55. int bordermodes[] = {
  56. BORDER_CONSTANT | BORDER_ISOLATED,
  57. BORDER_REPLICATE | BORDER_ISOLATED,
  58. BORDER_REFLECT | BORDER_ISOLATED,
  59. BORDER_WRAP | BORDER_ISOLATED,
  60. BORDER_REFLECT_101 | BORDER_ISOLATED
  61. // BORDER_CONSTANT,
  62. // BORDER_REPLICATE,
  63. // BORDER_REFLECT,
  64. // BORDER_WRAP,
  65. // BORDER_REFLECT_101
  66. };
  67. template <int fixedShift>
  68. void checkMode(const testmode& mode)
  69. {
  70. int type = mode.type, depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
  71. int dcols = mode.sz.width, drows = mode.sz.height;
  72. Size kernel = mode.kernel;
  73. int rows = drows + 20, cols = dcols + 20;
  74. Mat src(rows, cols, type), refdst(drows, dcols, type), dst;
  75. for (int j = 0; j < rows; j++)
  76. {
  77. uint8_t* line = src.ptr(j);
  78. for (int i = 0; i < cols; i++)
  79. for (int c = 0; c < cn; c++)
  80. {
  81. RNG rnd(0x123456789abcdefULL);
  82. double val = j < rows / 2 ? (i < cols / 2 ? ((sin((i + 1)*CV_PI / 256.)*sin((j + 1)*CV_PI / 256.)*sin((cn + 4)*CV_PI / 8.) + 1.)*128.) :
  83. (((i / 128 + j / 128) % 2) * 250 + (j / 128) % 2)) :
  84. (i < cols / 2 ? ((i / 128) * (85 - j / 256 * 40) * ((j / 128) % 2) + (7 - i / 128) * (85 - j / 256 * 40) * ((j / 128 + 1) % 2)) :
  85. ((uchar)rnd));
  86. if (depth == CV_8U)
  87. line[i*cn + c] = (uint8_t)val;
  88. else if (depth == CV_16U)
  89. ((uint16_t*)line)[i*cn + c] = (uint16_t)val;
  90. else if (depth == CV_16S)
  91. ((int16_t*)line)[i*cn + c] = (int16_t)val;
  92. else if (depth == CV_32S)
  93. ((int32_t*)line)[i*cn + c] = (int32_t)val;
  94. else
  95. CV_Assert(0);
  96. }
  97. }
  98. Mat src_roi = src(Rect(10, 10, dcols, drows));
  99. for (int borderind = 0, _bordercnt = sizeof(bordermodes) / sizeof(bordermodes[0]); borderind < _bordercnt; ++borderind)
  100. {
  101. Mat src_border;
  102. cv::copyMakeBorder(src_roi, src_border, kernel.height / 2, kernel.height / 2, kernel.width / 2, kernel.width / 2, bordermodes[borderind]);
  103. for (int c = 0; c < src_border.channels(); c++)
  104. {
  105. int fromTo[2] = { c, 0 };
  106. int toFrom[2] = { 0, c };
  107. Mat src_chan(src_border.size(), CV_MAKETYPE(src_border.depth(),1));
  108. Mat dst_chan(refdst.size(), CV_MAKETYPE(refdst.depth(), 1));
  109. mixChannels(src_border, src_chan, fromTo, 1);
  110. for (int j = 0; j < drows; j++)
  111. for (int i = 0; i < dcols; i++)
  112. {
  113. if (depth == CV_8U)
  114. dst_chan.at<uint8_t>(j, i) = eval<uint8_t, fixedShift>(src_chan(Rect(i,j,kernel.width,kernel.height)), mode.kernel_x, mode.kernel_y);
  115. else if (depth == CV_16U)
  116. dst_chan.at<uint16_t>(j, i) = eval<uint16_t, fixedShift>(src_chan(Rect(i, j, kernel.width, kernel.height)), mode.kernel_x, mode.kernel_y);
  117. else if (depth == CV_16S)
  118. dst_chan.at<int16_t>(j, i) = eval<int16_t, fixedShift>(src_chan(Rect(i, j, kernel.width, kernel.height)), mode.kernel_x, mode.kernel_y);
  119. else if (depth == CV_32S)
  120. dst_chan.at<int32_t>(j, i) = eval<int32_t, fixedShift>(src_chan(Rect(i, j, kernel.width, kernel.height)), mode.kernel_x, mode.kernel_y);
  121. else
  122. CV_Assert(0);
  123. }
  124. mixChannels(dst_chan, refdst, toFrom, 1);
  125. }
  126. cv::GaussianBlur(src_roi, dst, kernel, mode.sigma_x, mode.sigma_y, bordermodes[borderind]);
  127. EXPECT_GE(0, cvtest::norm(refdst, dst, cv::NORM_L1))
  128. << "GaussianBlur " << cn << "-chan mat " << drows << "x" << dcols << " by kernel " << kernel << " sigma(" << mode.sigma_x << ";" << mode.sigma_y << ") failed with max diff " << cvtest::norm(refdst, dst, cv::NORM_INF);
  129. }
  130. }
  131. TEST(GaussianBlur_Bitexact, Linear8U)
  132. {
  133. testmode modes[] = {
  134. { CV_8UC1, Size( 1, 1), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  135. { CV_8UC1, Size( 2, 2), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  136. { CV_8UC1, Size( 3, 1), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  137. { CV_8UC1, Size( 1, 3), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  138. { CV_8UC1, Size( 3, 3), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  139. { CV_8UC1, Size( 3, 3), Size(5, 5), 0, 0, vector<int64_t>(vU8[2], vU8[2]+5), vector<int64_t>(vU8[2], vU8[2]+5) },
  140. { CV_8UC1, Size( 3, 3), Size(7, 7), 0, 0, vector<int64_t>(vU8[3], vU8[3]+7), vector<int64_t>(vU8[3], vU8[3]+7) },
  141. { CV_8UC1, Size( 5, 5), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  142. { CV_8UC1, Size( 5, 5), Size(5, 5), 0, 0, vector<int64_t>(vU8[2], vU8[2]+5), vector<int64_t>(vU8[2], vU8[2]+5) },
  143. { CV_8UC1, Size( 3, 5), Size(5, 5), 0, 0, vector<int64_t>(vU8[2], vU8[2]+5), vector<int64_t>(vU8[2], vU8[2]+5) },
  144. { CV_8UC1, Size( 5, 5), Size(5, 5), 0, 0, vector<int64_t>(vU8[2], vU8[2]+5), vector<int64_t>(vU8[2], vU8[2]+5) },
  145. { CV_8UC1, Size( 5, 5), Size(7, 7), 0, 0, vector<int64_t>(vU8[3], vU8[3]+7), vector<int64_t>(vU8[3], vU8[3]+7) },
  146. { CV_8UC1, Size( 7, 7), Size(7, 7), 0, 0, vector<int64_t>(vU8[3], vU8[3]+7), vector<int64_t>(vU8[3], vU8[3]+7) },
  147. { CV_8UC1, Size( 256, 128), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  148. { CV_8UC2, Size( 256, 128), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  149. { CV_8UC3, Size( 256, 128), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  150. { CV_8UC4, Size( 256, 128), Size(3, 3), 0, 0, vector<int64_t>(vU8[1], vU8[1]+3), vector<int64_t>(vU8[1], vU8[1]+3) },
  151. { CV_8UC1, Size( 256, 128), Size(5, 5), 0, 0, vector<int64_t>(vU8[2], vU8[2]+5), vector<int64_t>(vU8[2], vU8[2]+5) },
  152. { CV_8UC1, Size( 256, 128), Size(7, 7), 0, 0, vector<int64_t>(vU8[3], vU8[3]+7), vector<int64_t>(vU8[3], vU8[3]+7) },
  153. { CV_8UC1, Size( 256, 128), Size(9, 9), 0, 0, vector<int64_t>(vU8[4], vU8[4]+9), vector<int64_t>(vU8[4], vU8[4]+9) },
  154. #ifdef CV_TEST_INACCURATE_GAUSSIAN_BLUR
  155. { CV_8UC1, Size( 256, 128), Size(3, 3), 1.75, 0.875, vector<int64_t>(vU8[5], vU8[5]+3), vector<int64_t>(vU8[6], vU8[6]+3) },
  156. { CV_8UC2, Size( 256, 128), Size(3, 3), 1.75, 0.875, vector<int64_t>(vU8[5], vU8[5]+3), vector<int64_t>(vU8[6], vU8[6]+3) },
  157. { CV_8UC3, Size( 256, 128), Size(3, 3), 1.75, 0.875, vector<int64_t>(vU8[5], vU8[5]+3), vector<int64_t>(vU8[6], vU8[6]+3) },
  158. { CV_8UC4, Size( 256, 128), Size(3, 3), 1.75, 0.875, vector<int64_t>(vU8[5], vU8[5]+3), vector<int64_t>(vU8[6], vU8[6]+3) },
  159. { CV_8UC1, Size( 256, 128), Size(5, 5), 0.375, 0.75, vector<int64_t>(vU8[7], vU8[7]+5), vector<int64_t>(vU8[8], vU8[8]+5) }
  160. #endif
  161. };
  162. for (int modeind = 0, _modecnt = sizeof(modes) / sizeof(modes[0]); modeind < _modecnt; ++modeind)
  163. {
  164. checkMode<fixedShiftU8>(modes[modeind]);
  165. }
  166. }
  167. TEST(GaussianBlur_Bitexact, Linear16U)
  168. {
  169. testmode modes[] = {
  170. { CV_16UC1, Size( 1, 1), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  171. { CV_16UC1, Size( 2, 2), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  172. { CV_16UC1, Size( 3, 1), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  173. { CV_16UC1, Size( 1, 3), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  174. { CV_16UC1, Size( 3, 3), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  175. { CV_16UC1, Size( 3, 3), Size(5, 5), 0, 0, vector<int64_t>(vU16[2], vU16[2]+5), vector<int64_t>(vU16[2], vU16[2]+5) },
  176. { CV_16UC1, Size( 3, 3), Size(7, 7), 0, 0, vector<int64_t>(vU16[3], vU16[3]+7), vector<int64_t>(vU16[3], vU16[3]+7) },
  177. { CV_16UC1, Size( 5, 5), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  178. { CV_16UC1, Size( 5, 5), Size(5, 5), 0, 0, vector<int64_t>(vU16[2], vU16[2]+5), vector<int64_t>(vU16[2], vU16[2]+5) },
  179. { CV_16UC1, Size( 3, 5), Size(5, 5), 0, 0, vector<int64_t>(vU16[2], vU16[2]+5), vector<int64_t>(vU16[2], vU16[2]+5) },
  180. { CV_16UC1, Size( 5, 5), Size(5, 5), 0, 0, vector<int64_t>(vU16[2], vU16[2]+5), vector<int64_t>(vU16[2], vU16[2]+5) },
  181. { CV_16UC1, Size( 5, 5), Size(7, 7), 0, 0, vector<int64_t>(vU16[3], vU16[3]+7), vector<int64_t>(vU16[3], vU16[3]+7) },
  182. { CV_16UC1, Size( 7, 7), Size(7, 7), 0, 0, vector<int64_t>(vU16[3], vU16[3]+7), vector<int64_t>(vU16[3], vU16[3]+7) },
  183. { CV_16UC1, Size( 256, 128), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  184. { CV_16UC2, Size( 256, 128), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  185. { CV_16UC3, Size( 256, 128), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  186. { CV_16UC4, Size( 256, 128), Size(3, 3), 0, 0, vector<int64_t>(vU16[1], vU16[1]+3), vector<int64_t>(vU16[1], vU16[1]+3) },
  187. { CV_16UC1, Size( 256, 128), Size(5, 5), 0, 0, vector<int64_t>(vU16[2], vU16[2]+5), vector<int64_t>(vU16[2], vU16[2]+5) },
  188. { CV_16UC1, Size( 256, 128), Size(7, 7), 0, 0, vector<int64_t>(vU16[3], vU16[3]+7), vector<int64_t>(vU16[3], vU16[3]+7) },
  189. { CV_16UC1, Size( 256, 128), Size(9, 9), 0, 0, vector<int64_t>(vU16[4], vU16[4]+9), vector<int64_t>(vU16[4], vU16[4]+9) },
  190. };
  191. for (int modeind = 0, _modecnt = sizeof(modes) / sizeof(modes[0]); modeind < _modecnt; ++modeind)
  192. {
  193. checkMode<16>(modes[modeind]);
  194. }
  195. }
  196. TEST(GaussianBlur_Bitexact, regression_15015)
  197. {
  198. Mat src(100,100,CV_8UC3,Scalar(255,255,255));
  199. Mat dst;
  200. GaussianBlur(src, dst, Size(5, 5), 0);
  201. ASSERT_EQ(0.0, cvtest::norm(dst, src, NORM_INF));
  202. }
  203. TEST(GaussianBlur_Bitexact, overflow_20121)
  204. {
  205. Mat src(100, 100, CV_16UC1, Scalar(65535));
  206. Mat dst;
  207. GaussianBlur(src, dst, cv::Size(9, 9), 0.0);
  208. double min_val;
  209. minMaxLoc(dst, &min_val);
  210. ASSERT_EQ(cvRound(min_val), 65535);
  211. }
  212. static void checkGaussianBlur_8Uvs32F(const Mat& src8u, const Mat& src32f, int N, double sigma)
  213. {
  214. Mat dst8u; GaussianBlur(src8u, dst8u, Size(N, N), sigma); // through bit-exact path
  215. Mat dst8u_32f; dst8u.convertTo(dst8u_32f, CV_32F);
  216. Mat dst32f; GaussianBlur(src32f, dst32f, Size(N, N), sigma); // without bit-exact computations
  217. double normINF_32f = cv::norm(dst8u_32f, dst32f, NORM_INF);
  218. EXPECT_LE(normINF_32f, 1.0);
  219. }
  220. TEST(GaussianBlur_Bitexact, regression_9863)
  221. {
  222. Mat src8u = imread(cvtest::findDataFile("shared/lena.png"));
  223. Mat src32f; src8u.convertTo(src32f, CV_32F);
  224. checkGaussianBlur_8Uvs32F(src8u, src32f, 151, 30);
  225. }
  226. TEST(GaussianBlur_Bitexact, overflow_20792)
  227. {
  228. Mat src(128, 128, CV_16UC1, Scalar(255));
  229. Mat dst;
  230. double sigma = theRNG().uniform(0.0, 0.2); // a peaky kernel
  231. GaussianBlur(src, dst, Size(7, 7), sigma, 0.9);
  232. int count = (int)countNonZero(dst);
  233. int nintyPercent = (int)(src.rows*src.cols * 0.9);
  234. EXPECT_GT(count, nintyPercent);
  235. }
  236. }} // namespace