test_rand.cpp 12.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
#include "test_precomp.hpp"

using namespace cv;
using namespace std;

class Core_RandTest : public cvtest::BaseTest
{
public:
    Core_RandTest();
protected:
    void run(int);
    bool check_pdf(const Mat& hist, double scale, int dist_type,
                   double& refval, double& realval);
};


Core_RandTest::Core_RandTest()
{
}

static double chi2_p95(int n)
{
    static float chi2_tab95[] = {
        3.841f, 5.991f, 7.815f, 9.488f, 11.07f, 12.59f, 14.07f, 15.51f,
        16.92f, 18.31f, 19.68f, 21.03f, 21.03f, 22.36f, 23.69f, 25.00f,
        26.30f, 27.59f, 28.87f, 30.14f, 31.41f, 32.67f, 33.92f, 35.17f,
        36.42f, 37.65f, 38.89f, 40.11f, 41.34f, 42.56f, 43.77f };
    static const double xp = 1.64;
    CV_Assert(n >= 1);
A
Andrey Kamaev 已提交
30

31 32 33 34 35 36 37 38 39
    if( n <= 30 )
        return chi2_tab95[n-1];
    return n + sqrt((double)2*n)*xp + 0.6666666666666*(xp*xp - 1);
}

bool Core_RandTest::check_pdf(const Mat& hist, double scale,
                            int dist_type, double& refval, double& realval)
{
    Mat hist0(hist.size(), CV_32F);
40 41
    const int* H = hist.ptr<int>();
    float* H0 = hist0.ptr<float>();
42
    int i, hsz = hist.cols;
A
Andrey Kamaev 已提交
43

44 45 46 47
    double sum = 0;
    for( i = 0; i < hsz; i++ )
        sum += H[i];
    CV_Assert( fabs(1./sum - scale) < FLT_EPSILON );
A
Andrey Kamaev 已提交
48

49 50 51 52 53 54 55 56
    if( dist_type == CV_RAND_UNI )
    {
        float scale0 = (float)(1./hsz);
        for( i = 0; i < hsz; i++ )
            H0[i] = scale0;
    }
    else
    {
A
Andrey Kamaev 已提交
57
        double sum2 = 0, r = (hsz-1.)/2;
58 59 60 61 62
        double alpha = 2*sqrt(2.)/r, beta = -alpha*r;
        for( i = 0; i < hsz; i++ )
        {
            double x = i*alpha + beta;
            H0[i] = (float)exp(-x*x);
A
Andrey Kamaev 已提交
63
            sum2 += H0[i];
64
        }
A
Andrey Kamaev 已提交
65
        sum2 = 1./sum2;
66
        for( i = 0; i < hsz; i++ )
A
Andrey Kamaev 已提交
67
            H0[i] = (float)(H0[i]*sum2);
68
    }
A
Andrey Kamaev 已提交
69

70 71 72 73 74 75 76 77 78
    double chi2 = 0;
    for( i = 0; i < hsz; i++ )
    {
        double a = H0[i];
        double b = H[i]*scale;
        if( a > DBL_EPSILON )
            chi2 += (a - b)*(a - b)/(a + b);
    }
    realval = chi2;
A
Andrey Kamaev 已提交
79

80 81 82 83 84 85 86 87 88 89
    double chi2_pval = chi2_p95(hsz - 1 - (dist_type == CV_RAND_NORMAL ? 2 : 0));
    refval = chi2_pval*0.01;
    return realval <= refval;
}

void Core_RandTest::run( int )
{
    static int _ranges[][2] =
    {{ 0, 256 }, { -128, 128 }, { 0, 65536 }, { -32768, 32768 },
        { -1000000, 1000000 }, { -1000, 1000 }, { -1000, 1000 }};
A
Andrey Kamaev 已提交
90

91 92 93 94 95
    const int MAX_SDIM = 10;
    const int N = 2000000;
    const int maxSlice = 1000;
    const int MAX_HIST_SIZE = 1000;
    int progress = 0;
A
Andrey Kamaev 已提交
96

97 98 99
    RNG& rng = ts->get_rng();
    RNG tested_rng = theRNG();
    test_case_count = 200;
A
Andrey Kamaev 已提交
100

101 102 103 104
    for( int idx = 0; idx < test_case_count; idx++ )
    {
        progress = update_progress( progress, idx, test_case_count, 0 );
        ts->update_context( this, idx, false );
A
Andrey Kamaev 已提交
105

106 107 108 109 110 111
        int depth = cvtest::randInt(rng) % (CV_64F+1);
        int c, cn = (cvtest::randInt(rng) % 4) + 1;
        int type = CV_MAKETYPE(depth, cn);
        int dist_type = cvtest::randInt(rng) % (CV_RAND_NORMAL+1);
        int i, k, SZ = N/cn;
        Scalar A, B;
112 113 114 115

        double eps = 1.e-4;
        if (depth == CV_64F)
            eps = 1.e-7;
A
Andrey Kamaev 已提交
116

117 118 119
        bool do_sphere_test = dist_type == CV_RAND_UNI;
        Mat arr[2], hist[4];
        int W[] = {0,0,0,0};
A
Andrey Kamaev 已提交
120

121 122 123
        arr[0].create(1, SZ, type);
        arr[1].create(1, SZ, type);
        bool fast_algo = dist_type == CV_RAND_UNI && depth < CV_32F;
A
Andrey Kamaev 已提交
124

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
        for( c = 0; c < cn; c++ )
        {
            int a, b, hsz;
            if( dist_type == CV_RAND_UNI )
            {
                a = (int)(cvtest::randInt(rng) % (_ranges[depth][1] -
                                              _ranges[depth][0])) + _ranges[depth][0];
                do
                {
                    b = (int)(cvtest::randInt(rng) % (_ranges[depth][1] -
                                                  _ranges[depth][0])) + _ranges[depth][0];
                }
                while( abs(a-b) <= 1 );
                if( a > b )
                    std::swap(a, b);
A
Andrey Kamaev 已提交
140

141 142 143 144 145 146 147 148 149 150 151
                unsigned r = (unsigned)(b - a);
                fast_algo = fast_algo && r <= 256 && (r & (r-1)) == 0;
                hsz = min((unsigned)(b - a), (unsigned)MAX_HIST_SIZE);
                do_sphere_test = do_sphere_test && b - a >= 100;
            }
            else
            {
                int vrange = _ranges[depth][1] - _ranges[depth][0];
                int meanrange = vrange/16;
                int mindiv = MAX(vrange/20, 5);
                int maxdiv = MIN(vrange/8, 10000);
A
Andrey Kamaev 已提交
152

153 154 155 156 157 158 159
                a = cvtest::randInt(rng) % meanrange - meanrange/2 +
                (_ranges[depth][0] + _ranges[depth][1])/2;
                b = cvtest::randInt(rng) % (maxdiv - mindiv) + mindiv;
                hsz = min((unsigned)b*9, (unsigned)MAX_HIST_SIZE);
            }
            A[c] = a;
            B[c] = b;
A
Andrey Kamaev 已提交
160
            hist[c].create(1, hsz, CV_32S);
161
        }
A
Andrey Kamaev 已提交
162

163 164 165 166 167 168 169 170
        cv::RNG saved_rng = tested_rng;
        int maxk = fast_algo ? 0 : 1;
        for( k = 0; k <= maxk; k++ )
        {
            tested_rng = saved_rng;
            int sz = 0, dsz = 0, slice;
            for( slice = 0; slice < maxSlice; slice++, sz += dsz )
            {
171
                dsz = slice+1 < maxSlice ? (int)(cvtest::randInt(rng) % (SZ - sz + 1)) : SZ - sz;
172 173 174 175
                Mat aslice = arr[k].colRange(sz, sz + dsz);
                tested_rng.fill(aslice, dist_type, A, B);
            }
        }
A
Andrey Kamaev 已提交
176

177
        if( maxk >= 1 && cvtest::norm(arr[0], arr[1], NORM_INF) > eps)
178 179 180 181 182
        {
            ts->printf( cvtest::TS::LOG, "RNG output depends on the array lengths (some generated numbers get lost?)" );
            ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
            return;
        }
A
Andrey Kamaev 已提交
183

184 185
        for( c = 0; c < cn; c++ )
        {
186
            const uchar* data = arr[0].ptr();
187 188 189 190 191 192
            int* H = hist[c].ptr<int>();
            int HSZ = hist[c].cols;
            double minVal = dist_type == CV_RAND_UNI ? A[c] : A[c] - B[c]*4;
            double maxVal = dist_type == CV_RAND_UNI ? B[c] : A[c] + B[c]*4;
            double scale = HSZ/(maxVal - minVal);
            double delta = -minVal*scale;
A
Andrey Kamaev 已提交
193

194
            hist[c] = Scalar::all(0);
A
Andrey Kamaev 已提交
195

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
            for( i = c; i < SZ*cn; i += cn )
            {
                double val = depth == CV_8U ? ((const uchar*)data)[i] :
                depth == CV_8S ? ((const schar*)data)[i] :
                depth == CV_16U ? ((const ushort*)data)[i] :
                depth == CV_16S ? ((const short*)data)[i] :
                depth == CV_32S ? ((const int*)data)[i] :
                depth == CV_32F ? ((const float*)data)[i] :
                ((const double*)data)[i];
                int ival = cvFloor(val*scale + delta);
                if( (unsigned)ival < (unsigned)HSZ )
                {
                    H[ival]++;
                    W[c]++;
                }
                else if( dist_type == CV_RAND_UNI )
                {
                    if( (minVal <= val && val < maxVal) || (depth >= CV_32F && val == maxVal) )
                    {
                        H[ival < 0 ? 0 : HSZ-1]++;
                        W[c]++;
                    }
                    else
                    {
                        putchar('^');
                    }
                }
            }
A
Andrey Kamaev 已提交
224

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
            if( dist_type == CV_RAND_UNI && W[c] != SZ )
            {
                ts->printf( cvtest::TS::LOG, "Uniform RNG gave values out of the range [%g,%g) on channel %d/%d\n",
                           A[c], B[c], c, cn);
                ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
                return;
            }
            if( dist_type == CV_RAND_NORMAL && W[c] < SZ*.90)
            {
                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",
                           A[c], B[c], A[c], B[c], c, cn);
                ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
                return;
            }
            double refval = 0, realval = 0;
A
Andrey Kamaev 已提交
240

241 242 243 244 245 246 247 248 249
            if( !check_pdf(hist[c], 1./W[c], dist_type, refval, realval) )
            {
                ts->printf( cvtest::TS::LOG, "RNG failed Chi-square test "
                           "(got %g vs probable maximum %g) on channel %d/%d\n",
                           realval, refval, c, cn);
                ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
                return;
            }
        }
A
Andrey Kamaev 已提交
250

251 252 253 254 255
        // Monte-Carlo test. Compute volume of SDIM-dimensional sphere
        // inscribed in [-1,1]^SDIM cube.
        if( do_sphere_test )
        {
            int SDIM = cvtest::randInt(rng) % (MAX_SDIM-1) + 2;
A
Andrey Kamaev 已提交
256
            int N0 = (SZ*cn/SDIM), n = 0;
257
            double r2 = 0;
258
            const uchar* data = arr[0].ptr();
259 260 261 262 263 264
            double scale[4], delta[4];
            for( c = 0; c < cn; c++ )
            {
                scale[c] = 2./(B[c] - A[c]);
                delta[c] = -A[c]*scale[c] - 1;
            }
A
Andrey Kamaev 已提交
265

266 267 268 269 270 271 272 273 274 275 276 277 278
            for( i = k = c = 0; i <= SZ*cn - SDIM; i++, k++, c++ )
            {
                double val = depth == CV_8U ? ((const uchar*)data)[i] :
                depth == CV_8S ? ((const schar*)data)[i] :
                depth == CV_16U ? ((const ushort*)data)[i] :
                depth == CV_16S ? ((const short*)data)[i] :
                depth == CV_32S ? ((const int*)data)[i] :
                depth == CV_32F ? ((const float*)data)[i] : ((const double*)data)[i];
                c &= c < cn ? -1 : 0;
                val = val*scale[c] + delta[c];
                r2 += val*val;
                if( k == SDIM-1 )
                {
A
Andrey Kamaev 已提交
279
                    n += r2 <= 1;
280 281 282 283
                    r2 = 0;
                    k = -1;
                }
            }
A
Andrey Kamaev 已提交
284 285 286

            double V = ((double)n/N0)*(1 << SDIM);

287 288 289 290 291
            // the theoretically computed volume
            int sdim = SDIM % 2;
            double V0 = sdim + 1;
            for( sdim += 2; sdim <= SDIM; sdim += 2 )
                V0 *= 2*CV_PI/sdim;
A
Andrey Kamaev 已提交
292

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
            if( fabs(V - V0) > 0.3*fabs(V0) )
            {
                ts->printf( cvtest::TS::LOG, "RNG failed %d-dim sphere volume test (got %g instead of %g)\n",
                           SDIM, V, V0);
                ts->printf( cvtest::TS::LOG, "depth = %d, N0 = %d\n", depth, N0);
                ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
                return;
            }
        }
    }
}

TEST(Core_Rand, quality) { Core_RandTest test; test.safe_run(); }


308 309 310 311
class Core_RandRangeTest : public cvtest::BaseTest
{
public:
    Core_RandRangeTest() {}
A
Andrey Kamaev 已提交
312
    ~Core_RandRangeTest() {}
313 314 315 316 317 318 319 320 321
protected:
    void run(int)
    {
        Mat a(Size(1280, 720), CV_8U, Scalar(20));
        Mat af(Size(1280, 720), CV_32F, Scalar(20));
        theRNG().fill(a, RNG::UNIFORM, -DBL_MAX, DBL_MAX);
        theRNG().fill(af, RNG::UNIFORM, -DBL_MAX, DBL_MAX);
        int n0 = 0, n255 = 0, nx = 0;
        int nfmin = 0, nfmax = 0, nfx = 0;
A
Andrey Kamaev 已提交
322

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
        for( int i = 0; i < a.rows; i++ )
            for( int j = 0; j < a.cols; j++ )
            {
                int v = a.at<uchar>(i,j);
                double vf = af.at<float>(i,j);
                if( v == 0 ) n0++;
                else if( v == 255 ) n255++;
                else nx++;
                if( vf < FLT_MAX*-0.999f ) nfmin++;
                else if( vf > FLT_MAX*0.999f ) nfmax++;
                else nfx++;
            }
        CV_Assert( n0 > nx*2 && n255 > nx*2 );
        CV_Assert( nfmin > nfx*2 && nfmax > nfx*2 );
    }
};

TEST(Core_Rand, range) { Core_RandRangeTest test; test.safe_run(); }

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367

TEST(Core_RNG_MT19937, regression)
{
    cv::RNG_MT19937 rng;
    int actual[61] = {0, };
    const size_t length = (sizeof(actual) / sizeof(actual[0]));
    for (int i = 0; i < 10000; ++i )
    {
        actual[(unsigned)(rng.next() ^ i) % length]++;
    }

    int expected[length] = {
        177, 158, 180, 177,  160, 179, 143, 162,
        177, 144, 170, 174,  165, 168, 168, 156,
        177, 157, 159, 169,  177, 182, 166, 154,
        144, 180, 168, 152,  170, 187, 160, 145,
        139, 164, 157, 179,  148, 183, 159, 160,
        196, 184, 149, 142,  162, 148, 163, 152,
        168, 173, 160, 181,  172, 181, 155, 153,
        158, 171, 138, 150,  150 };

    for (size_t i = 0; i < length; ++i)
    {
        ASSERT_EQ(expected[i], actual[i]);
    }
}
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384


TEST(Core_Rand, Regression_Stack_Corruption)
{
    int bufsz = 128; //enough for 14 doubles
    AutoBuffer<uchar> buffer(bufsz);
    size_t offset = 0;
    cv::Mat_<cv::Point2d> x(2, 3, (cv::Point2d*)(buffer+offset)); offset += x.total()*x.elemSize();
    double& param1 = *(double*)(buffer+offset); offset += sizeof(double);
    double& param2 = *(double*)(buffer+offset); offset += sizeof(double);
    param1 = -9; param2 = 2;

    cv::theRNG().fill(x, cv::RNG::NORMAL, param1, param2);

    ASSERT_EQ(param1, -9);
    ASSERT_EQ(param2,  2);
}