fixed bug #1318. extended RNG::fill with the optional saturateRange parameter
authorVadim Pisarevsky <no@email>
Thu, 29 Mar 2012 12:00:34 +0000 (12:00 +0000)
committerVadim Pisarevsky <no@email>
Thu, 29 Mar 2012 12:00:34 +0000 (12:00 +0000)
modules/core/doc/operations_on_arrays.rst
modules/core/include/opencv2/core/core.hpp
modules/core/src/rand.cpp
modules/core/test/test_rand.cpp

index b2048cae6bf1b007cf04ce8baddb030117163481..c32c35f86d5763e3be7212ef4a4b169a5d14fe21 100644 (file)
@@ -2564,7 +2564,7 @@ RNG::fill
 -------------
 Fills arrays with random numbers.
 
-.. ocv:function:: void RNG::fill( InputOutputArray mat, int distType, InputArray a, InputArray b )
+.. ocv:function:: void RNG::fill( InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange=false )
 
     :param mat: 2D or N-dimensional matrix. Currently matrices with more than 4 channels are not supported by the methods. Use  :ocv:func:`Mat::reshape`  as a possible workaround.
 
@@ -2573,6 +2573,8 @@ Fills arrays with random numbers.
     :param a: First distribution parameter. In case of the uniform distribution, this is an inclusive lower boundary. In case of the normal distribution, this is a mean value.
 
     :param b: Second distribution parameter. In case of the uniform distribution, this is a non-inclusive upper boundary. In case of the normal distribution, this is a standard deviation (diagonal of the standard deviation matrix or the full standard deviation matrix).
+    
+    :param saturateRange: Pre-saturation flag; for uniform distribution only. If it is true, the method will first convert a and b to the acceptable value range (according to the mat datatype) and then will generate uniformly distributed random numbers within the range ``[saturate(a), saturate(b))``. If ``saturateRange=false``, the method will generate uniformly distributed random numbers in the original range ``[a, b)`` and then will saturate them. It means, for example, that ``theRNG().fill(mat_8u, RNG::UNIFORM, -DBL_MAX, DBL_MAX)`` will likely produce array mostly filled with 0's and 255's, since the range ``(0, 255)`` is significantly smaller than ``[-DBL_MAX, DBL_MAX)``.
 
 Each of the methods fills the matrix with the random values from the specified distribution. As the new numbers are generated, the RNG state is updated accordingly. In case of multiple-channel images, every channel is filled independently, which means that RNG cannot generate samples from the multi-dimensional Gaussian distribution with non-diagonal covariance matrix directly. To do that, the method generates samples from multi-dimensional standard Gaussian distribution with zero mean and identity covariation matrix, and then transforms them using :ocv:func:`transform` to get samples from the specified Gaussian distribution.
 
index 0d3662b8844fbb9fcc4d65d0f4ac6e1e125ccad2..decf6c907d1870c3c5fe4f9083e448934eec52c8 100644 (file)
@@ -1979,7 +1979,7 @@ public:
     float uniform(float a, float b);
     //! returns uniformly distributed double-precision floating-point random number from [a,b) range
     double uniform(double a, double b);
-    void fill( InputOutputArray mat, int distType, InputArray a, InputArray b );
+    void fill( InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange=false );
     //! returns Gaussian random variate with mean zero.
     double gaussian(double sigma);
 
index e143256c88290cc181764098a74d45a9309cbb84..2289950152de83c62d4913a0041d0f70074740dd 100644 (file)
@@ -443,7 +443,8 @@ static RandnScaleFunc randnScaleTab[] =
     (RandnScaleFunc)randnScale_64f, 0 
 };
     
-void RNG::fill( InputOutputArray _mat, int disttype, InputArray _param1arg, InputArray _param2arg )
+void RNG::fill( InputOutputArray _mat, int disttype,
+                InputArray _param1arg, InputArray _param2arg, bool saturateRange )
 {
     Mat mat = _mat.getMat(), _param1 = _param1arg.getMat(), _param2 = _param2arg.getMat();
     int depth = mat.depth(), cn = mat.channels();
@@ -505,6 +506,13 @@ void RNG::fill( InputOutputArray _mat, int disttype, InputArray _param1arg, Inpu
             {
                 double a = min(p1[j], p2[j]);
                 double b = max(p1[j], p2[j]);
+                if( saturateRange )
+                {
+                    a = max(a, depth == CV_8U || depth == CV_16U ? 0. :
+                            depth == CV_8S ? -128. : depth == CV_16S ? -32768. : (double)INT_MIN);
+                    b = min(b, depth == CV_8U ? 256. : depth == CV_16U ? 65536. :
+                            depth == CV_8S ? 128. : depth == CV_16S ? 32768. : (double)INT_MAX);
+                }
                 ip[j][1] = cvCeil(a);
                 int idiff = ip[j][0] = cvFloor(b) - ip[j][1] - 1;
                 double diff = b - a;
@@ -512,6 +520,13 @@ void RNG::fill( InputOutputArray _mat, int disttype, InputArray _param1arg, Inpu
                 fast_int_mode &= diff <= 4294967296. && (idiff & (idiff+1)) == 0;
                 if( fast_int_mode )
                     smallFlag &= idiff <= 255;
+                else
+                {
+                    if( diff > INT_MAX )
+                        ip[j][0] = INT_MAX;
+                    if( a < INT_MIN/2 )
+                        ip[j][1] = INT_MIN/2;
+                }
             }
             
             if( !fast_int_mode )
@@ -537,6 +552,7 @@ void RNG::fill( InputOutputArray _mat, int disttype, InputArray _param1arg, Inpu
             double scale = depth == CV_64F ?
                 5.4210108624275221700372640043497e-20 : // 2**-64
                 2.3283064365386962890625e-10;           // 2**-32
+            double maxdiff = saturateRange ? (double)FLT_MAX : DBL_MAX;
 
             // for each channel i compute such dparam[0][i] & dparam[1][i],
             // so that a signed 32/64-bit integer X is transformed to
@@ -547,7 +563,7 @@ void RNG::fill( InputOutputArray _mat, int disttype, InputArray _param1arg, Inpu
                 fp = (Vec2f*)(parambuf + cn*2);
                 for( j = 0; j < cn; j++ )
                 {
-                    fp[j][0] = (float)((p2[j] - p1[j])*scale);
+                    fp[j][0] = (float)(std::min(maxdiff, p2[j] - p1[j])*scale);
                     fp[j][1] = (float)((p2[j] + p1[j])*0.5);
                 }
             }
@@ -556,7 +572,7 @@ void RNG::fill( InputOutputArray _mat, int disttype, InputArray _param1arg, Inpu
                 dp = (Vec2d*)(parambuf + cn*2);
                 for( j = 0; j < cn; j++ )
                 {
-                    dp[j][0] = ((p2[j] - p1[j])*scale);
+                    dp[j][0] = std::min(DBL_MAX, p2[j] - p1[j])*scale;
                     dp[j][1] = ((p2[j] + p1[j])*0.5);
                 }
             }
index d05b16a8680c2764663d3ffe79b75266652590c2..d4427a5a6e6f80c87c1362a7a5c7c065c9ae951f 100644 (file)
@@ -301,3 +301,37 @@ void Core_RandTest::run( int )
 TEST(Core_Rand, quality) { Core_RandTest test; test.safe_run(); }
 
 
+class Core_RandRangeTest : public cvtest::BaseTest
+{
+public:
+    Core_RandRangeTest() {}
+    ~Core_RandRangeTest() {}   
+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;
+        
+        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(); }
+