test_rand.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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. class Core_RandTest : public cvtest::BaseTest
  7. {
  8. public:
  9. Core_RandTest();
  10. protected:
  11. void run(int);
  12. bool check_pdf(const Mat& hist, double scale, int dist_type,
  13. double& refval, double& realval);
  14. };
  15. Core_RandTest::Core_RandTest()
  16. {
  17. }
  18. static double chi2_p95(int n)
  19. {
  20. static float chi2_tab95[] = {
  21. 3.841f, 5.991f, 7.815f, 9.488f, 11.07f, 12.59f, 14.07f, 15.51f,
  22. 16.92f, 18.31f, 19.68f, 21.03f, 21.03f, 22.36f, 23.69f, 25.00f,
  23. 26.30f, 27.59f, 28.87f, 30.14f, 31.41f, 32.67f, 33.92f, 35.17f,
  24. 36.42f, 37.65f, 38.89f, 40.11f, 41.34f, 42.56f, 43.77f };
  25. static const double xp = 1.64;
  26. CV_Assert(n >= 1);
  27. if( n <= 30 )
  28. return chi2_tab95[n-1];
  29. return n + sqrt((double)2*n)*xp + 0.6666666666666*(xp*xp - 1);
  30. }
  31. bool Core_RandTest::check_pdf(const Mat& hist, double scale,
  32. int dist_type, double& refval, double& realval)
  33. {
  34. Mat hist0(hist.size(), CV_32F);
  35. const int* H = hist.ptr<int>();
  36. float* H0 = hist0.ptr<float>();
  37. int i, hsz = hist.cols;
  38. double sum = 0;
  39. for( i = 0; i < hsz; i++ )
  40. sum += H[i];
  41. CV_Assert( fabs(1./sum - scale) < FLT_EPSILON );
  42. if( dist_type == CV_RAND_UNI )
  43. {
  44. float scale0 = (float)(1./hsz);
  45. for( i = 0; i < hsz; i++ )
  46. H0[i] = scale0;
  47. }
  48. else
  49. {
  50. double sum2 = 0, r = (hsz-1.)/2;
  51. double alpha = 2*sqrt(2.)/r, beta = -alpha*r;
  52. for( i = 0; i < hsz; i++ )
  53. {
  54. double x = i*alpha + beta;
  55. H0[i] = (float)exp(-x*x);
  56. sum2 += H0[i];
  57. }
  58. sum2 = 1./sum2;
  59. for( i = 0; i < hsz; i++ )
  60. H0[i] = (float)(H0[i]*sum2);
  61. }
  62. double chi2 = 0;
  63. for( i = 0; i < hsz; i++ )
  64. {
  65. double a = H0[i];
  66. double b = H[i]*scale;
  67. if( a > DBL_EPSILON )
  68. chi2 += (a - b)*(a - b)/(a + b);
  69. }
  70. realval = chi2;
  71. double chi2_pval = chi2_p95(hsz - 1 - (dist_type == CV_RAND_NORMAL ? 2 : 0));
  72. refval = chi2_pval*0.01;
  73. return realval <= refval;
  74. }
  75. void Core_RandTest::run( int )
  76. {
  77. static int _ranges[][2] =
  78. {{ 0, 256 }, { -128, 128 }, { 0, 65536 }, { -32768, 32768 },
  79. { -1000000, 1000000 }, { -1000, 1000 }, { -1000, 1000 }};
  80. const int MAX_SDIM = 10;
  81. const int N = 2000000;
  82. const int maxSlice = 1000;
  83. const int MAX_HIST_SIZE = 1000;
  84. int progress = 0;
  85. RNG& rng = ts->get_rng();
  86. RNG tested_rng = theRNG();
  87. test_case_count = 200;
  88. for( int idx = 0; idx < test_case_count; idx++ )
  89. {
  90. progress = update_progress( progress, idx, test_case_count, 0 );
  91. ts->update_context( this, idx, false );
  92. int depth = cvtest::randInt(rng) % (CV_64F+1);
  93. int c, cn = (cvtest::randInt(rng) % 4) + 1;
  94. int type = CV_MAKETYPE(depth, cn);
  95. int dist_type = cvtest::randInt(rng) % (CV_RAND_NORMAL+1);
  96. int i, k, SZ = N/cn;
  97. Scalar A, B;
  98. double eps = 1.e-4;
  99. if (depth == CV_64F)
  100. eps = 1.e-7;
  101. bool do_sphere_test = dist_type == CV_RAND_UNI;
  102. Mat arr[2], hist[4];
  103. int W[] = {0,0,0,0};
  104. arr[0].create(1, SZ, type);
  105. arr[1].create(1, SZ, type);
  106. bool fast_algo = dist_type == CV_RAND_UNI && depth < CV_32F;
  107. for( c = 0; c < cn; c++ )
  108. {
  109. int a, b, hsz;
  110. if( dist_type == CV_RAND_UNI )
  111. {
  112. a = (int)(cvtest::randInt(rng) % (_ranges[depth][1] -
  113. _ranges[depth][0])) + _ranges[depth][0];
  114. do
  115. {
  116. b = (int)(cvtest::randInt(rng) % (_ranges[depth][1] -
  117. _ranges[depth][0])) + _ranges[depth][0];
  118. }
  119. while( abs(a-b) <= 1 );
  120. if( a > b )
  121. std::swap(a, b);
  122. unsigned r = (unsigned)(b - a);
  123. fast_algo = fast_algo && r <= 256 && (r & (r-1)) == 0;
  124. hsz = min((unsigned)(b - a), (unsigned)MAX_HIST_SIZE);
  125. do_sphere_test = do_sphere_test && b - a >= 100;
  126. }
  127. else
  128. {
  129. int vrange = _ranges[depth][1] - _ranges[depth][0];
  130. int meanrange = vrange/16;
  131. int mindiv = MAX(vrange/20, 5);
  132. int maxdiv = MIN(vrange/8, 10000);
  133. a = cvtest::randInt(rng) % meanrange - meanrange/2 +
  134. (_ranges[depth][0] + _ranges[depth][1])/2;
  135. b = cvtest::randInt(rng) % (maxdiv - mindiv) + mindiv;
  136. hsz = min((unsigned)b*9, (unsigned)MAX_HIST_SIZE);
  137. }
  138. A[c] = a;
  139. B[c] = b;
  140. hist[c].create(1, hsz, CV_32S);
  141. }
  142. cv::RNG saved_rng = tested_rng;
  143. int maxk = fast_algo ? 0 : 1;
  144. for( k = 0; k <= maxk; k++ )
  145. {
  146. tested_rng = saved_rng;
  147. int sz = 0, dsz = 0, slice;
  148. for( slice = 0; slice < maxSlice && sz < SZ; slice++, sz += dsz )
  149. {
  150. dsz = slice+1 < maxSlice ? (int)(cvtest::randInt(rng) % (SZ - sz) + 1) : SZ - sz;
  151. Mat aslice = arr[k].colRange(sz, sz + dsz);
  152. tested_rng.fill(aslice, dist_type, A, B);
  153. }
  154. }
  155. if( maxk >= 1 && cvtest::norm(arr[0], arr[1], NORM_INF) > eps)
  156. {
  157. ts->printf( cvtest::TS::LOG, "RNG output depends on the array lengths (some generated numbers get lost?)" );
  158. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
  159. return;
  160. }
  161. for( c = 0; c < cn; c++ )
  162. {
  163. const uchar* data = arr[0].ptr();
  164. int* H = hist[c].ptr<int>();
  165. int HSZ = hist[c].cols;
  166. double minVal = dist_type == CV_RAND_UNI ? A[c] : A[c] - B[c]*4;
  167. double maxVal = dist_type == CV_RAND_UNI ? B[c] : A[c] + B[c]*4;
  168. double scale = HSZ/(maxVal - minVal);
  169. double delta = -minVal*scale;
  170. hist[c] = Scalar::all(0);
  171. for( i = c; i < SZ*cn; i += cn )
  172. {
  173. double val = depth == CV_8U ? ((const uchar*)data)[i] :
  174. depth == CV_8S ? ((const schar*)data)[i] :
  175. depth == CV_16U ? ((const ushort*)data)[i] :
  176. depth == CV_16S ? ((const short*)data)[i] :
  177. depth == CV_32S ? ((const int*)data)[i] :
  178. depth == CV_32F ? ((const float*)data)[i] :
  179. ((const double*)data)[i];
  180. int ival = cvFloor(val*scale + delta);
  181. if( (unsigned)ival < (unsigned)HSZ )
  182. {
  183. H[ival]++;
  184. W[c]++;
  185. }
  186. else if( dist_type == CV_RAND_UNI )
  187. {
  188. if( (minVal <= val && val < maxVal) || (depth >= CV_32F && val == maxVal) )
  189. {
  190. H[ival < 0 ? 0 : HSZ-1]++;
  191. W[c]++;
  192. }
  193. else
  194. {
  195. putchar('^');
  196. }
  197. }
  198. }
  199. if( dist_type == CV_RAND_UNI && W[c] != SZ )
  200. {
  201. ts->printf( cvtest::TS::LOG, "Uniform RNG gave values out of the range [%g,%g) on channel %d/%d\n",
  202. A[c], B[c], c, cn);
  203. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
  204. return;
  205. }
  206. if( dist_type == CV_RAND_NORMAL && W[c] < SZ*.90)
  207. {
  208. ts->printf( cvtest::TS::LOG, "Normal RNG gave too many values out of the range (%g+4*%g,%g+4*%g) on channel %d/%d\n",
  209. A[c], B[c], A[c], B[c], c, cn);
  210. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
  211. return;
  212. }
  213. double refval = 0, realval = 0;
  214. if( !check_pdf(hist[c], 1./W[c], dist_type, refval, realval) )
  215. {
  216. ts->printf( cvtest::TS::LOG, "RNG failed Chi-square test "
  217. "(got %g vs probable maximum %g) on channel %d/%d\n",
  218. realval, refval, c, cn);
  219. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
  220. return;
  221. }
  222. }
  223. // Monte-Carlo test. Compute volume of SDIM-dimensional sphere
  224. // inscribed in [-1,1]^SDIM cube.
  225. if( do_sphere_test )
  226. {
  227. int SDIM = cvtest::randInt(rng) % (MAX_SDIM-1) + 2;
  228. int N0 = (SZ*cn/SDIM), n = 0;
  229. double r2 = 0;
  230. const uchar* data = arr[0].ptr();
  231. double scale[4], delta[4];
  232. for( c = 0; c < cn; c++ )
  233. {
  234. scale[c] = 2./(B[c] - A[c]);
  235. delta[c] = -A[c]*scale[c] - 1;
  236. }
  237. for( i = k = c = 0; i <= SZ*cn - SDIM; i++, k++, c++ )
  238. {
  239. double val = depth == CV_8U ? ((const uchar*)data)[i] :
  240. depth == CV_8S ? ((const schar*)data)[i] :
  241. depth == CV_16U ? ((const ushort*)data)[i] :
  242. depth == CV_16S ? ((const short*)data)[i] :
  243. depth == CV_32S ? ((const int*)data)[i] :
  244. depth == CV_32F ? ((const float*)data)[i] : ((const double*)data)[i];
  245. c &= c < cn ? -1 : 0;
  246. val = val*scale[c] + delta[c];
  247. r2 += val*val;
  248. if( k == SDIM-1 )
  249. {
  250. n += r2 <= 1;
  251. r2 = 0;
  252. k = -1;
  253. }
  254. }
  255. double V = ((double)n/N0)*(1 << SDIM);
  256. // the theoretically computed volume
  257. int sdim = SDIM % 2;
  258. double V0 = sdim + 1;
  259. for( sdim += 2; sdim <= SDIM; sdim += 2 )
  260. V0 *= 2*CV_PI/sdim;
  261. if( fabs(V - V0) > 0.3*fabs(V0) )
  262. {
  263. ts->printf( cvtest::TS::LOG, "RNG failed %d-dim sphere volume test (got %g instead of %g)\n",
  264. SDIM, V, V0);
  265. ts->printf( cvtest::TS::LOG, "depth = %d, N0 = %d\n", depth, N0);
  266. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
  267. return;
  268. }
  269. }
  270. }
  271. }
  272. TEST(Core_Rand, quality) { Core_RandTest test; test.safe_run(); }
  273. class Core_RandRangeTest : public cvtest::BaseTest
  274. {
  275. public:
  276. Core_RandRangeTest() {}
  277. ~Core_RandRangeTest() {}
  278. protected:
  279. void run(int)
  280. {
  281. Mat a(Size(1280, 720), CV_8U, Scalar(20));
  282. Mat af(Size(1280, 720), CV_32F, Scalar(20));
  283. theRNG().fill(a, RNG::UNIFORM, -DBL_MAX, DBL_MAX);
  284. theRNG().fill(af, RNG::UNIFORM, -DBL_MAX, DBL_MAX);
  285. int n0 = 0, n255 = 0, nx = 0;
  286. int nfmin = 0, nfmax = 0, nfx = 0;
  287. for( int i = 0; i < a.rows; i++ )
  288. for( int j = 0; j < a.cols; j++ )
  289. {
  290. int v = a.at<uchar>(i,j);
  291. double vf = af.at<float>(i,j);
  292. if( v == 0 ) n0++;
  293. else if( v == 255 ) n255++;
  294. else nx++;
  295. if( vf < FLT_MAX*-0.999f ) nfmin++;
  296. else if( vf > FLT_MAX*0.999f ) nfmax++;
  297. else nfx++;
  298. }
  299. CV_Assert( n0 > nx*2 && n255 > nx*2 );
  300. CV_Assert( nfmin > nfx*2 && nfmax > nfx*2 );
  301. }
  302. };
  303. TEST(Core_Rand, range) { Core_RandRangeTest test; test.safe_run(); }
  304. TEST(Core_RNG_MT19937, regression)
  305. {
  306. cv::RNG_MT19937 rng;
  307. int actual[61] = {0, };
  308. const size_t length = (sizeof(actual) / sizeof(actual[0]));
  309. for (int i = 0; i < 10000; ++i )
  310. {
  311. actual[(unsigned)(rng.next() ^ i) % length]++;
  312. }
  313. int expected[length] = {
  314. 177, 158, 180, 177, 160, 179, 143, 162,
  315. 177, 144, 170, 174, 165, 168, 168, 156,
  316. 177, 157, 159, 169, 177, 182, 166, 154,
  317. 144, 180, 168, 152, 170, 187, 160, 145,
  318. 139, 164, 157, 179, 148, 183, 159, 160,
  319. 196, 184, 149, 142, 162, 148, 163, 152,
  320. 168, 173, 160, 181, 172, 181, 155, 153,
  321. 158, 171, 138, 150, 150 };
  322. for (size_t i = 0; i < length; ++i)
  323. {
  324. ASSERT_EQ(expected[i], actual[i]);
  325. }
  326. }
  327. TEST(Core_Rand, Regression_Stack_Corruption)
  328. {
  329. int bufsz = 128; //enough for 14 doubles
  330. AutoBuffer<uchar> buffer(bufsz);
  331. size_t offset = 0;
  332. cv::Mat_<cv::Point2d> x(2, 3, (cv::Point2d*)(buffer.data()+offset));
  333. offset += x.total()*x.elemSize();
  334. double& param1 = *(double*)(buffer.data()+offset);
  335. offset += sizeof(double);
  336. double& param2 = *(double*)(buffer.data()+offset);
  337. param1 = -9; param2 = 2;
  338. cv::theRNG().fill(x, cv::RNG::NORMAL, param1, param2);
  339. ASSERT_EQ(param1, -9);
  340. ASSERT_EQ(param2, 2);
  341. }
  342. class RandRowFillParallelLoopBody : public cv::ParallelLoopBody
  343. {
  344. public:
  345. RandRowFillParallelLoopBody(Mat& dst) : dst_(dst) {}
  346. ~RandRowFillParallelLoopBody() {}
  347. void operator()(const cv::Range& r) const
  348. {
  349. cv::RNG rng = cv::theRNG(); // copy state
  350. for (int y = r.start; y < r.end; y++)
  351. {
  352. cv::theRNG() = cv::RNG(rng.state + y); // seed is based on processed row
  353. cv::randu(dst_.row(y), Scalar(-100), Scalar(100));
  354. }
  355. // theRNG() state is changed here (but state collision has low probability, so we don't check this)
  356. }
  357. protected:
  358. Mat& dst_;
  359. };
  360. TEST(Core_Rand, parallel_for_stable_results)
  361. {
  362. cv::RNG rng = cv::theRNG(); // save rng state
  363. Mat dst1(1000, 100, CV_8SC1);
  364. parallel_for_(cv::Range(0, dst1.rows), RandRowFillParallelLoopBody(dst1));
  365. cv::theRNG() = rng; // restore rng state
  366. Mat dst2(1000, 100, CV_8SC1);
  367. parallel_for_(cv::Range(0, dst2.rows), RandRowFillParallelLoopBody(dst2));
  368. ASSERT_EQ(0, countNonZero(dst1 != dst2));
  369. }
  370. }} // namespace