标签: 机器视觉
图像是由一个个像素组成的,各种算法和处理都是基于像素实现的,所以访问像素的操作是最常见的操作方式。常见的像素访问方式有Mat::at访问,指针访问和迭代器访问。
为了详细的说明各个方式访问像素的方法,本文将使用不同的访问像素的方法实现Color Reduce(颜色缩减)的功能。Color Reduce可以将图像的颜色数降低,比如可以将64阶的灰度图降为8阶的灰度图,主要通过整数的除法实现,因为整数的除法可以去尾。比如某像素点的值为M,降阶之后的值就为M/8*8,原先0~63连续值就变为{0, 8, 16, 24, 32, 40, 48, 56}的离散值。本文将处理3通道的彩色图,颜色缩减的倍数为32。
OpenCV2中存储图像的变量类型是Mat (强烈建议放弃OpenCV1中的数据结构和函数),访问像素其实就是访问Mat的元素。
Mat::at方法官方说明如下:
Returns a reference to the specified array element.
(返回指定元素的引用)
该方法有各种参数的重载,使用该方法实现的Color Reduce功能代码如下:
void color_reduce1(Mat &image)
{
for (int i=0; i<image.rows; i++)
for (int j=0; j<image.cols; j++)
{
if (image.channels() == 1)//如果是单通道图
image.at<uchar>(i, j) = image.at<uchar>(i, j) /32 * 32;
else if (image.channels() == 3)//如果是三通道图
{
image.at<Vec3b>(i, j)[0] = image.at<Vec3b>(i, j)[0] / 32 * 32;
image.at<Vec3b>(i, j)[1] = image.at<Vec3b>(i, j)[1] / 32 * 32;
image.at<Vec3b>(i, j)[2] = image.at<Vec3b>(i, j)[2] / 32 * 32;
}
}
}
Mat::ptr方法官方说明如下:
Returns a pointer to the specified matrix row.
(返回指定行的指针)
该方法可以返回尖括号指定元素类型的行指针,该指针指向该行第一个元素
void color_reduce2(Mat &image)
{
int rows = image.rows;//行数
int rowsCount = image.cols * image.channels();//每行的元素个数
for (int i=0; i<rows; i++)
{
uchar* data = image.ptr<uchar>(i);//每行首元素地址
for (int j=0; j<rowsCount; j++)
data[j] = data[j] / 32 * 32;
}
}
uchar* data = image.data+i*image.step+j*image.elemSize()
void color_reduce3(Mat &image)
{
Mat_<Vec3b>::iterator iter = image.begin<Vec3b>();
for ( ; iter != image.end<Vec3b>(); iter++)
{
(*iter)[0] = (*iter)[0] / 32 * 32;//[]的优先级高,所以要先解引用
(*iter)[1] = (*iter)[1] / 32 * 32;
(*iter)[2] = (*iter)[2] / 32 * 32;
}
}
Mat_<Vec3b>::const_iterator iter;
Mat_<Vec3b> image;
Mat_<Vec3b>::iterator beg = image.begin();
Mat_<Vec3b>::iterator end = image.end();
既然访问像素的方法有多种,自然而然就想比较一下。OpenCV提供了测量代码运行时间的函数:
double getTickFrequency()//返回每秒钟的时钟数,即时钟频率
int64 getTickCount()//返回当前时钟数
代码如下:
int main()
{
Mat image;
image = imread("img.jpg");
double time;
time = (double)getTickCount();
for (int i=0; i<10; i++)//多次运行以减小误差
color_reduce1(image);//更换测试的函数即可
time = (double)getTickCount() - time;
time /= getTickFrequency();
cout<<time/10<<endl;//除以10输出
}
各方法运行时间如下表所示(单位:秒):
测试方法 | Mat::at 访问 |
指针访问 | 迭代器访问 |
---|---|---|---|
Debug | 0.316153 | 0.003027 | 0.414347 |
Release | 0.00158968 | 0.00070958 | 0.00610587 |