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 b2048ca..c32c35f 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 0d3662b..decf6c9 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 e143256..2289950 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 d05b16a..d4427a5 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(); }
+