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