如何实现
为了衡量函数或者代码段的运行时间,Opencv有一个非常实用的函数,即为cv::getTickCount(),该函数返回从最近一次电脑开机到当前的始终周期数。也就是对系统时钟systick的一个测量,因为我们希望得到以秒为单位的代码运行时间,所以要使用另一个方法,即为cv::getTickFrequency(),这个方法返回去每秒的时钟周期数。为了获得某个函数或代码段的运行时间,通常需要使用这样的程序模板:
const int 64 start= cv::getTickCount();
colorReduce(image); //调用函数
//经过的时间(单位/秒)
double duration=(cv::getTickCount()=start)/cv::getTickFrequency();
实现原理:
在之前所说的colorReduce函数中有几种不同的实现方式,这里我们列出每种方式的运行时间,观察运行时间的相对差距。使用
了位运算符的方法用时9.5 ms,要比其他方法快得多;使用整数除法的方法用时26 ms;使用取模运算符的方法用时33 ms。由此可见,最快和最慢的速度相差竟超过3倍!因此,要在图像循环中计算出结果,花些时间找出效率最高的方法十分重要,其净影响会非常明显。如果需要重新分配输出图像而不是就地处理,运行时间就变为29ms。增加的时间代表内存分配的开销。
对于可以预先计算的数值,要避免在循环中做重复计算,这样很明显
会浪费时间。例如在减色函数中使用下面的内层循环:
int nc= image.cols * image.channels();
uchar div2= div>>1;
for (int i=0; i<nc; i++) {
然后改成这样:
for (int i=0; i<image.cols * image.channels(); i++) {
// . . .
*data++ += div>>1;
在前面的代码循环中,你需要反复计算一行的元素个数和div>>1的结果,其运行时间是52 ms,明显比修改前的版本(26 ms)要慢。但是要注意,有些编译器能够对此类循环进行优化,仍会生成高效的代码。
进而,我们可以考虑到上篇文章所述的迭代法的访问像素的函数:
for (int j=0; j<nl; j++) {
for (int i=0; i<nc; i++) {
// 处理每个像素 ---------------------
image.at<cv::Vec3b>(j,i)[0]=
image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
image.at<cv::Vec3b>(j,i)[1]=
image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
image.at<cv::Vec3b>(j,i)[2]=
image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
// 像素处理结束 ----------------
}
// 一行结束
}
这种实现方法用时53 ms,要慢很多。该方法应该在需要随机访问像素的时候使用,绝不要在扫描图像时使用。即使处理的元素总数相同,使用较短的循环和多条语句通常也要比使用较长的循环和单条语句的运行效率高。类似地,如果你要对一个像素执行N个不同的计算过程,那就在单个循环中执行全部计算,而不是写N个连续的循环,每个循环执行一个计算。
我们还做过连续性测试,针对连续图像生成一个循环,而不是对行和列运行常规的二重循环。例如,对于我们测试中用到的非常大的图像,这种优化效果不明显(从26 ms变为25 ms)。但通常情况下,采用这种策略是一个非常好的做法,因为它会明显提升速度。