如何衡量算法的性能?
我们如何测量时间?OpenCV提供了两个简单的函数来实现cv::getTickCount()和cv::getTickFrequency()。第一个返回某个事件(比如自启动系统以来)中系统CPU的节拍数。第二个返回您的CPU在一秒内发出一个tick的次数。因此,测量两个操作之间的时间代码如下:
double t = (double)getTickCount();
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Times passed in seconds: " << t << endl;
如何遍历每一个图像的像素?
在学习如何遍历每一个图像的像素前,先了解一下图像矩阵是如何存储在内存中的? 矩阵的大小取决于所使用的颜色系统。更准确地说,它取决于所使用的通道数量。对于灰度图像: 对于多通道图像,列包含与通道数量相同的子列。以BGR颜色系统为例: 注意,通道的顺序是相反的:BGR而不是RGB。
在OpenCV中,目前有三种主要的方法来逐个像素地处理图像。
1.高效的遍历方式
说到性能,便是经典的C风格操作符访问。 在之前文章:高效的遍历opencv Mat图像像素中已经测试过。
2. 迭代器(安全)方法
迭代器方法被认为是一种更安全的方法,因为它从用户那里接管了这些任务。你所需要做的就是询问图像矩阵的开始和结束,然后增加开始迭代器,直到你到达结束。使用*操作符获取迭代器所指向的值。
cv::Mat matKernel = cv::Mat::zeros(7000, 7000, CV_32F);
nums = 10;
while (nums)
{
double t = (double)cv::getTickCount();
cv::MatIterator_<float> it, end;
for( it = matKernel.begin<float>(), end = matKernel.end<float>(); it != end; ++it)
*it = 255;
t = ((double)cv::getTickCount() - t) / cv::getTickFrequency()*1000;
cout << "迭代器遍历:" << t<<"ms"<<endl;
nums--;
}
另外,对于彩色通道:
cv::Mat rgbImage = cv::Mat::zeros(7000, 7000, CV_32FC3);
nums = 10;
while (nums)
{
double t = (double)cv::getTickCount();
cv::MatIterator_<cv::Vec3b> it, end;
for( it = rgbImage.begin<cv::Vec3b>(), end = rgbImage.end<cv::Vec3b>(); it != end; ++it)
{
(*it)[0] = 255;
(*it)[1] = 255;
(*it)[2] = 255;
}
t = ((double)cv::getTickCount() - t) / cv::getTickFrequency()*1000;
cout << "彩色图像迭代器遍历:" << t<<"ms"<<endl;
nums--;
}
3.动态地址计算 cv::Mat::at() 函数
该方法不推荐用于遍历图像。它被用来获取或修改图像中的随机元素。它的基本用途是指定要访问的项目的行号和列号。 在文章cv::Mat::at()函数遍历cv::Mat图像像素已经测试过。
4.查找表
在图像处理中,将所有给定的图像值修改为其他值是很常见的。OpenCV提供了一个修改图像值的功能cv::LUT()函数,而不需要写入图像的扫描逻辑。说白了该函数就是实现一个灰度值重映射的功能,例如对于灰度图像有0~255个灰阶,可以将图像灰度0-100的映射成0,101-200的映射成100,201-256的映射成255 函数原型:
CV_EXPORTS_W void LUT(InputArray src, InputArray lut, OutputArray dst);
该函数只能输入深度为8bit的图像类型。 首先,我们构建一个Mat类型的查找表:
cv::Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for( int i = 0; i < 256; ++i)
{
if(i<101)
{
p[i] = 0;
}
else if(i>=101 && i<201)
{
p[i] = 100;
}
else
{
p[i] = 255;
}
}
循环调用测试:
cv::Mat dst;
nums = 10;
while (nums)
{
double t = (double)cv::getTickCount();
cv::LUT(matKernel, lookUpTable, dst);
t = ((double)cv::getTickCount() - t) / cv::getTickFrequency()*1000;
cout << "LUT:" << t<<"ms"<<endl;
nums--;
}
5.四种方式性能对比
总结
我们可以得出一些结论。最快的方法是LUT函数。这是因为OpenCV库是通过Intel线程构建块实现多线程的。但是,如果您需要遍历整个图像,最好使用指针方法。迭代器是一种更安全的选择,但是会比较慢。在调试模式下,使用动态地址访问方法是成本最高的。在发布模式下,它可能会打败迭代器方法。
|