索引地址:系列索引
键盘输入
默认情况下imread/imshow会直接退出,这个时候需要延时,OpenCV提供waitKey()函数来捕获键盘输入。
函数原型为:
1
| int waitKey(int delay=0);
|
参数说明:
- delay表示延时时间,单位为ms。
- 返回值为整数,表示获取到的键盘输入,没有键盘输入时返回-1。
延时
1 2 3 4 5 6 7 8 9 10 11 12
| #include <opencv2/opencv.hpp> #include <iostream>
using namespace std; using namespace cv;
int main(){ Mat img = cv::imread("lena.jpg"); imshow("lena",img); waitKey(5000); return 0; }
|
执行到waitkey函数运行开始计时,如果在5000毫秒之内获取到键盘输入,就执行waitkey之后的语句。如果没有输入就等到5000毫秒之后自动执行waitkey之后的语句。
如果delay为0,即waitKey(0)。那么就一直停留在waitkey语句中,表现为显示的图片一直都在,除非键盘输入什么(任意键)。
捕获键盘值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <opencv2/opencv.hpp> #include <iostream>
using namespace std; using namespace cv;
int main(){ Mat img = cv::imread("lena.jpg"); while(1){ imshow("lena",img); if(waitKey(30)==27){ break; } } return 0; }
|
循环显示图片,直到捕获到键值Esc
退出循环,因为循环之后是函数返回值,就是直接退出程序。
计时器
OpenCV自带计时函数。
函数原型:
1
| int64 cv::getTickCount ();
|
函数返回特定事件后的滴答数(例如,当机器打开时)。 它可以用来初始化RNG或通过读取函数调用前后的滴答计数来测量函数执行时间。
比如说第一次获取的滴答数为t1=1000,第二次获取的滴答数为t2=100000,两次获取的滴答数差为t2-t1=100000-1000=99000;
函数原型:
1
| double cv::getTickFrequency( ) ;
|
函数返回每秒的滴答数。
如果获取的滴答频率为f=1000滴答/秒,那么上面两次获取的滴答时间差为:(t2-t1)/f=99秒,以实现计时功能。
从官方文档说明可以看出,此函数只能用来计算时间差,不能用来进行日期计时,因为其没有统一的开始。
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <opencv2/opencv.hpp>
using namespace cv;
int main() { double tTime; tTime = (double)getTickCount(); const int nTimes = 100; for (int i = 0; i < nTimes; i++) { int sum = 0; for (int j = 0; j < 100; j++) { sum += j; } } tTime = ((double)getTickCount() - tTime) / getTickFrequency(); tTime /= nTimes; std::cout << tTime << std::endl; return 0; }
|
测试结果:
计算100以内整数求和的时间为7.3494e-07秒。
随机数
产生随机数是编程中经常用到的操作,特别在进行初始化的时候需要赋一些随机值。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); RNG::uniform(); RNG::gaussian(); RNG::uniform(a, b); 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 rng;
int rand1 = rng; cout<<"rand1="<<rand1<<endl;
double rand2 = rng.uniform(0, 1); cout<<"rand2="<<rand2<<endl;
double rand3 = rng.uniform((double)0, (double)1); cout<<"rand3="<<rand3<<endl;
double rand4 = rng.uniform(0.f, 1.f); cout<<"rand4="<<rand4<<endl;
double rand5 = rng.uniform(0., 1.); cout<<"rand5="<<rand5<<endl;
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(); 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();
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(); a[9] = rng.operator()(); a[10] = rng.operator()(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; 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;
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));
|
开始生成随机数啦
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
|
由Rect()可以确定一张矩形图,矩形涉及到长宽面积等等,而长宽是尺寸相关的概念。