图像的每个通道都可看作一个二维矩阵,矩阵中的元素,是我们进行运算时的基本单位,所以遍历矩阵每个元素的操作是我们经常要用到的。
本文用四种方式实现对OpenCV的Mat类矩阵元素的遍历。
以下四个代码通过对矩阵元素的遍历实现图像的反色操作。
四个代码中用到的图像的下载链接如下:
https://pan.baidu.com/s/1JEy2tiuwCKDi4n9TfuVyTA 提取码:201q
方法一:下标遍历法,格式为M.at(i,j)
image.at(i,j):取出灰度图像image中第i行第j列点的像素值;
image.at(i,j)[k]:取出彩色图像image第k通道中第i行第j列点的像素值。
Vec3b表示数据类型为:uchar类型、长度为3的向量,其定义如下:
typedef Vec< uchar, 3 >cv::Vec3b
//opencv版本:OpenCV3.0
//VS版本:VS2013
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
#include
#include
#include
#include
#include
cv::Mat inverseColor1(cv::Mat srcImage)
{
cv::Mat tempImage = srcImage.clone();
int row = tempImage.rows;
int col = tempImage.cols;
// 对各个像素点遍历进行取反操作
for (int i = 0;
i < row;
i++)
{
for (int j = 0;
j < col;
j++)
{
// 分别对各个通道进行反色处理
tempImage.at(i, j)[0] = 255 - tempImage.at(i, j)[0];
tempImage.at(i, j)[1] = 255 - tempImage.at(i, j)[1];
tempImage.at(i, j)[2] = 255 - tempImage.at(i, j)[2];
}
}
return tempImage;
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("F:/material/images/P0028-flower-02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
dstImage = srcImage.clone();
dstImage = inverseColor1(srcImage);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
方法二:利用行指针遍历
//opencv版本:OpenCV3.0
//VS版本:VS2013
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
#include
#include
#include
#include
#include cv::Mat inverseColor2(cv::Mat srcImage)
{
cv::Mat tempImage = srcImage.clone();
int row = tempImage.rows;
// 由于图像是三通道图像,所以要作下面这样的处理
int nStep = tempImage.cols * tempImage.channels();
for(int i = 0;
i < row;
i++)
{
// 取源图像的第i行指针
const uchar* pSrcData = https://www.it610.com/article/srcImage.ptr(i);
// 取目标图像的第i行指针
uchar* pResultData = https://www.it610.com/article/tempImage.ptr(i);
for(int j=0;
j < nStep;
j++)
{
pResultData[j]= cv::saturate_cast(255 - pSrcData[j]);
}
}
return tempImage;
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("F:/material/images/P0028-flower-02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
dstImage = srcImage.clone();
dstImage = inverseColor2(srcImage);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
说明:
image.ptr(i):取出图像中第i行数据的指针
要使用这种方法有一个前提:那就是图像每一行的数据在内存里是连续存储的,并且每个像素的三个通道数据按顺序存储。
如果不仅每一行的数据在内存里是连续存储的,行与行间也是连续的,那么就可以用下面的第三种方法。
方法三:利用函数isContinuous()先判断矩阵数据是否连续存储
//opencv版本:OpenCV3.0
//VS版本:VS2013
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
#include
#include
#include
#include
#include cv::Mat inverseColor3(cv::Mat srcImage)
{
int row = srcImage.rows;
int col = srcImage.cols;
cv::Mat tempImage = srcImage.clone();
// 判断是否是连续图像,即是否有像素填充
if( srcImage.isContinuous() && tempImage.isContinuous() )
{
row = 1;
// 按照行展开
col = col * srcImage.rows * srcImage.channels();
}
// 遍历图像的每个像素
for(int i = 0;
i < row;
i++)
{
// 设定图像数据源指针及输出图像数据指针
const uchar* pSrcData = https://www.it610.com/article/srcImage.ptr(i);
uchar* pResultData = https://www.it610.com/article/tempImage.ptr(i);
for(int j = 0;
j < col;
j++)
{
*pResultData++ = 255 - *pSrcData++;
}
}
return tempImage;
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("F:/material/images/P0028-flower-02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
dstImage = srcImage.clone();
dstImage = inverseColor3(srcImage);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
如果矩阵满足isContinuous的条件,即不仅每一行的数据在内存里是连续存储的,行与行间也是连续的,我们就可以把图像完全展开,看成是一行。这种方法的运行效率要比方法二高。
方法四:使用OpenCV的迭代器来遍历
OpenCV的迭代器使用很简单,大家看了大家的代码就知道怎么用了,这里就不多叙述了。
//opencv版本:OpenCV3.0
//VS版本:VS2013
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
#include
#include
#include
#include
#include cv::Mat inverseColor4(cv::Mat srcImage)
{
cv::Mat tempImage = srcImage.clone();
// 初始化源图像迭代器
cv::MatConstIterator_ srcIterStart= srcImage.begin();
cv::MatConstIterator_ srcIterEnd = srcImage.end();
// 初始化输出图像迭代器
cv::MatIterator_ resIterStart = tempImage.begin();
cv::MatIterator_ resIterEnd = tempImage.end();
// 遍历图像反色处理
while( srcIterStart != srcIterEnd )
{
(*resIterStart)[0] = 255 - (*srcIterStart)[0];
(*resIterStart)[1] = 255 - (*srcIterStart)[1];
(*resIterStart)[2] = 255 - (*srcIterStart)[2];
// 迭代器递增
srcIterStart++;
resIterStart++;
}
return tempImage;
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("F:/material/images/P0028-flower-02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
dstImage = srcImage.clone();
dstImage = inverseColor4(srcImage);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
四种方法的运行结果都是一样的,如下:
【图像处理原理|四种方式实现对OpenCV的MAT类矩阵元素的遍历】 用我在博文 https://blog.csdn.net/wenhao_ir/article/details/51546399 中提到的程序运行时间测量方法,可以测试出运行时间最短的是方法三(isContinuous判别法),其次依次是方法二(行指针法)、方法一(at法)。用时最多的是方法四(迭代器法)。
推荐阅读