本节主要解决以下几个问题:
- 如何遍历图像的每一个像素
- 如何存储opencv的矩阵
- 如何衡量算法的性能
- 查询表是什么并且为何要使用该表
举一个例子
如果是使用RGB的格式,数据格式采用unsigned char来进行储存,则每个像素点有256个不同的值,对于有三个通道的图像来说(RGB三个),其可以呈现超过一千六百万种颜色(256256256)。然而很多时候我们其实并不需要这么多种颜色也可以达到同样的结果。
在这种情况下,颜色空间减少就是一种常见的解决措施,举个例子,把0到9之间的值都设为0,10到19之间的值都设为10,以此类推。如下所示:
一个简单的颜色空间减少算法由两个部分组成,遍历图像矩阵的每一个像素并且应用这个方案。当然,此处使用的是除法操作和乘法操作,对系统来说是一个并不合适的昂贵操作。因此,如果可以的话,尽量使用加减法操作,或直接赋值操作。
因此,对于较大的图像,明智的做法是事先计算所有可能的值,并在赋值过程中使用查找表进行赋值。查找表是简单的数组(具有一个或多个维度),对于给定的输入值变化,它保存最终输出值。它的优点是我们不需要进行计算,我们只需要读取结果。
原文中所描述的代码链接
在此贴一段实现的代码,来自上文所述的链接
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// 确保图像数据类型为8位无符号字符类型
CV_Assert(I.depth() == CV_8U);
int channels = I.channels(); // 获取图像的通道数
int nRows = I.rows; // 获取图像的行数
int nCols = I.cols * channels; // 获取图像的列数乘以通道数
// 如果图像数据在内存中是连续存储的,可以将其视为单行处理
if (I.isContinuous())
{
nCols *= nRows; // 将列数乘以行数,得到总的像素数
nRows = 1; // 设置行数为1
}
int i, j;
uchar* p;
for (i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i); // 获取第i行的指针
for (j = 0; j < nCols; ++j)
{
p[j] = table[p[j]]; // 使用查找表对每个像素值进行转换
}
}
return I; // 返回处理后的图像
}
这段代码的核心思想是通过查找表对图像的每个像素值进行高效的批量处理,适用于许多图像处理任务,如颜色变换、亮度调整等。
另外需要注意的一点是时间统计,Opencv提供了两个简单的功能来实现,分别是 cv::getTickCount() 和cv::getTickFrequency()。
第一个返回系统CPU从某个事件(比如自启动系统以来)的滴答数。第二个返回CPU在一秒钟内发出tick的次数。
double t = (double)getTickCount();
// do something ...
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Times passed in seconds: " << t << endl;