OpenCV4入门系列教程7:RNG()

索引地址:系列教程索引地址

上一篇:OpenCV4入门系列教程6:Size()

产生随机数是编程中经常用到的操作,特别在进行初始化的时候需要赋一些随机值。C和C++中产生随机数的方法如rand()、srand()等在OpenCV中仍可以用。此外,OpenCV还特地编写了C++的随机数类RNG,还有一些相关的函数,使用起来更加方便。下面一一介绍。

说明

  • 计算机产生的随机数都是伪随机数,是根据种子seed和特定算法计算出来的。所以,只要种子一定,算法一定,产生的随机数是相同的
  • 要想产生完全重复的随机数,可以用系统时间做种子。OpenCV中用GetTickCount(),C 中用time()

RNG

RNG类是opencv里C++的随机数产生器。它可产生一个64位的int随机数。目前可按均匀分布和高斯分布产生随机数。随机数的产生采用的是Multiply-With-Carry算法和Ziggurat算法。

函数原型:

1
2
3
4
5
6
RNG(int seed);//使用种子seed产生一个64位随机整数,默认-1
RNG::uniform();//产生一个均匀分布的随机数
RNG::gaussian();//产生一个高斯分布的随机数
RNG::uniform(a, b);//返回一个[a,b)范围的均匀分布的随机数,a,b的数据类型要一致,而且必须是int、float、double中的一种,默认是int。
RNG::gaussian(σ);//返回一个均值为0,标准差为σ的随机数。
//如果要产生均值为λ,标准差为σ的随机数,可以λ+ RNG::gaussian(σ)

测试代码:

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
30
31
32
33
34
35
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main() {
//创建RNG对象,使用默认种子“-1”
RNG rng;

//产生64位整数
int rand1 = rng;
cout<<"rand1="<<rand1<<endl;

//总是得到double类型数据0.000000,因为会调用uniform(int,int),只会取整数,所以只产生0
double rand2 = rng.uniform(0, 1);
cout<<"rand2="<<rand2<<endl;

//产生[0,1)范围内均匀分布的double类型数据
double rand3 = rng.uniform((double)0, (double)1);
cout<<"rand3="<<rand3<<endl;

//产生[0,1)范围内均匀分布的float类型数据,注意被自动转换为double了。
double rand4 = rng.uniform(0.f, 1.f);
cout<<"rand4="<<rand4<<endl;

//产生[0,1)范围内均匀分布的double类型数据。
double rand5 = rng.uniform(0., 1.);
cout<<"rand5="<<rand5<<endl;

//产生符合均值为0,标准差为2的高斯分布的随机数
double rand6 = rng.gaussian(2);
cout<<"rand6="<<rand6<<endl;

return 0;
}

输出为:

1
2
3
4
5
6
rand1=130063606
rand2=0
rand3=0.901059
rand4=0.937133
rand5=0.74879
rand6=0.610327

返回下一个随机数

上面代码一次只能返回一个随机数,实际上系统已经生成一个随机数组。如果我们要连续获得随机数,没有必要重新定义一个RNG类,只需要取出随机数组的下一个随机数即可。

1
2
3
RNG::next();//返回下一个64位随机整数
RNG::double();//
RNG::operator()();//返回下一个指定类型的随机数

测试代码:

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
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main() {
int a[11]={0};
RNG rng;
a[0] = rng.next(); //返回下一个随机整数,即N1.next();

//返回下一个指定类型的随机数
a[1] = rng.operator uchar(); //返回下一个无符号字符数
a[2] = rng.operator schar(); //返回下一个有符号字符数
a[3] = rng.operator ushort(); //返回下一个无符号短型
a[4] = rng.operator short int(); //返回下一个短整型数
a[5] = rng.operator int(); //返回下一个整型数
a[6] = rng.operator unsigned int(); //返回下一个无符号整型数
a[7] = rng.operator float(); //返回下一个浮点数
a[8] = rng.operator double(); //返回下一个double型数
a[9] = rng.operator()(); //和rng.next()等价
a[10] = rng.operator()(100); //返回[0,100)范围内的随机数

for(int i=0;i<11;i++){
cout<<"a["<<i<<"]="<<a[i]<<endl;
}

return 0;
}

测试结果:

1
2
3
4
5
6
7
8
9
10
11
a[0]=130063606
a[1]=165
a[2]=-25
a[3]=35421
a[4]=-3479
a[5]=-1078939986
a[6]=1172977286
a[7]=0
a[8]=0
a[9]=-92568545
a[10]=38

用随机数填充矩阵

1
void fill( InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange=false );

参数说明:

  • InputOutputArray 输入输出矩阵,最多支持4通道,超过4通道先用reshape()改变结构
  • int distType UNIFORM 或 NORMAL,表示均匀分布和高斯分布
  • InputArray a disType是UNIFORM,a表示为下界(闭区间);disType是NORMAL,a均值
  • InputArray b disType是UNIFORM,b表示为上界(开区间);disType是NORMAL,b标准差
  • bool saturateRange=false 只针对均匀分布有效。当为真的时候,会先把产生随机数的范围变换到数据类型的范围,再产生随机数;如果为假,会先产生随机数,再进行截断到数据类型的有效区间。

测试代码:

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 <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main() {
RNG rng;
//产生[1,1000)均匀分布的int随机数填充fillM
Mat_<int> fillM(3, 3);
rng.fill(fillM, RNG::UNIFORM, 1, 1000);
cout << "filM = " << fillM << endl << endl;

Mat fillM1(3, 3, CV_8U);
rng.fill(fillM1, RNG::UNIFORM, 1, 1000, true);
cout << "filM1 = " << fillM1 << endl << endl;

Mat fillM2(3, 3, CV_8U);
rng.fill(fillM2, RNG::UNIFORM, 1, 1000, false);
cout << "filM2 = " << fillM2 << endl << endl;
// fillM1产生的数据都在[0,,255)内,且小于255;
// fillM2产生的数据虽然也在同样范围内,但是由于用了截断操作,所以很多数据都是255,

//产生均值为1,标准差为3的随机double数填进fillN
Mat_<double> fillN(3, 3);
rng.fill(fillN, RNG::NORMAL, 1, 3);
cout << "filN = " << fillN << endl << endl;

return 0;
}

测试结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
filM = [800, 699, 734;
255, 482, 557;
438, 804, 800]

filM1 = [240, 27, 249;
208, 219, 46;
184, 159, 102]

filM2 = [205, 1, 206;
255, 255, 255;
255, 255, 255]

filN = [-3.098058104515076, 0.8102578073740005, 5.054980039596558; 3.397326588630676, 0.2112799882888794, -0.5630163550376892; 6.498937964439392, 2.099618792533875, 4.876072406768799]

问题来了,如果进行多次运行,每次运行的结果是一样的,
可以采取另一种方式,用系统时间作为种子初始化rng

1
RNG rng((unsigned)time(NULL));//当然,用这个要记得加上头函数<time.h>

开始生成随机数啦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <time.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main() {
RNG rng((unsigned)time(NULL));

double x=rng.uniform((double)0,(double)255);
cout<<"x="<<x<<endl;
float y=rng.uniform(0.f,255.f);
cout<<"y="<<y<<endl;
int z=rng.uniform((int)0, (int)255 );
cout<<"z="<<z<<endl;

return 0;
}

每次运行生成的随机数都不一样哦~

1
2
3
4
5
6
7
8
9
10
11
x=54.1418
y=62.9061
z=120

x=38.6976
y=251.299
z=183

x=23.2534
y=184.693
z=247

下一篇:OpenCV4入门系列教程8:Mat()