本文基本上把常用的遍历方法都讲解了。在图像处理时经常会用到遍历图像像素点的方式,同样是遍历图像像素点,共有很多中方法可以做到;在这些方法中,有相对高效的,也有低效的;不同场景使用不同方法。
数据格式千万不要搞错:
uchar对应的是CV_8U,char对应的是CV_8S,int对应的是CV_32S,float对应的是CV_32F,double对应的是CV_64F。
1、遍历图像像素方法一:通过at(i,j)坐标指针
说明:就是把图像看成二维矩阵,at(i,j)索引坐标位置,单通道直接得到坐标位置对应的像素值,三通道就这个位置代表了像素值的一维数组;
//遍历图像像素方法一:通过at<type>(i,j)坐标指针
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
if (src.channels() == 1)
{
dst.at<uchar>(i, j) = src.at<uchar>(i, j) + 100;
}
else if (src.channels() ==3)
{
dst1.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i, j)[0] + 100;
dst1.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i, j)[1] + 100;
dst1.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i, j)[2] + 100;
}
}
}
2、遍历图像像素方法二:data遍历索引法
说明:data方法就是对于单通道就是把灰度图像看成二维数组,数组的排列先行后列,一行索引后进入此行的列,每个位置就是一个像素值;三通道彩色图像,就是在灰度图像的基础,索引的位置不是单个值而是一个一维数组。切记data是数据真正存储位置的指针
//遍历图像像素方法二:data遍历索引法
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
if (src.channels() == 1)
{
//图像数组是逐行逐列顺序排列的,也就是第一行,全列,第二行全列的走
int indexs = i * src.cols + j;
dst.data[indexs] = src.data[indexs] + 100;
}
else if (src.channels() == 3)
{
int indexs = i * src.cols + j;
//在彩色图像中,每个indexs又是三个通过的数组
dst1.data[3 * indexs + 0] = src.data[3 * indexs + 0] + 100;
dst1.data[3 * indexs + 1] = src.data[3 * indexs + 1] + 100;
dst1.data[3 * indexs + 2] = src.data[3 * indexs + 2] + 100;
}
}
}
3、遍历图像像素方法三:采用ptr指针方法
说明:ptr指针尤其固定格式,就是先把图像看成(src.rows,1)的图像,ptr获取每个位置的地址,地址位置隐藏了列的数据,由于列表名就是列表的地址,所以ptr获取的地址就是此行中列这样一维数据的列表名称。这样通过下标就可以获取像素值
for (int i = 0; i < src.rows; i++)
{
//获取第i行首地址,ptr与at结果差不多
uchar* src_rows_ptr = src.ptr<uchar>(i);
uchar* dst_rows_ptr = dst.ptr<uchar>(i);
for (int j = 0; j < src.cols; j++)
{
//每行指针的地址,又代表一行数组名称,因此下面就是遍历一行中的列数组数据
dst_rows_ptr[j] = src_rows_ptr[j] + 100;
}
}
4、遍历彩色图像像素方法三:采用ptr指针方法
说明:彩色图像的ptr就是在灰度的基础上,把(src.rows,1)处的地址看成是(src.cols,3)的二维数组,也是通过数组名(ptr获取的地址)来索引下标获取像素值。
for (int i = 0; i < src.rows; i++)
{
//获取第i行首地址,首地址又是第一个数据,是三通道,所以是Vec3b
// 可以理解为三维数组,i个(j,3)维数组
Vec3b* src_rows_ptr = src.ptr<Vec3b>(i);
Vec3b* dst1_rows_ptr = dst1.ptr<Vec3b>(i);
for (int j = 0; j < src.cols; j++)
{
//每行指针的地址,又代表一行数组名称和第一个数据,因此下面就是遍历一行中的列数组数据
//可以理解,每行是一个二维数组(j,3)维度
dst1_rows_ptr[j][0]= src_rows_ptr[j][0]+100;
dst1_rows_ptr[j][1] = src_rows_ptr[j][1]+100;
dst1_rows_ptr[j][2] = src_rows_ptr[j][2]+100;
}
}
5、针对at和ptr有很多人容易理解at,却理解不了ptr,下面讲一个用at生成ptr模式的解析例子
说明:这是为了对比at和ptr而增加的,主要是获取at(i,0)位置处的地址,将其看成数值名称,通过下标索引像素值,和ptr原理一样,只是获取地址的方式不一样(数组名)
5.1 遍历灰度图像像素方法五:采用at方法,使用ptr模式
for (int i = 0; i < src.rows; i++)
{
//将灰度图片看成(src.rows,1)维度的二维矩阵,获取(i,0)数据的地址
uchar* src_rows_ptr = &(src.at<uchar>(i, 0));
uchar* dst_rows_ptr = &(dst.at<uchar>(i, 0));
for (int j = 0; j < src.cols; j++)
{
//将(i,0)数据的地址下的内容看成是一维数组,(i,0)数据的地址是一维数组的名字
dst_rows_ptr[j] = src_rows_ptr[j] + 100;
}
}
5.2 遍历灰度图像像素方法六:采用at方法,使用ptr模式
for (int i = 0; i < src.rows; i++)
{
//将彩色图片看成(src.rows,1)维度的二维矩阵,获取(i,0)数据的地址
Vec3b* src_rows_ptr = &(src.at<Vec3b>(i, 0));
Vec3b* dst1_rows_ptr = &(dst1.at<Vec3b>(i, 0));
for (int j = 0; j < src.cols; j++)
{
//将(i,0)数据的地址下的内容看成是二维数组,(i,0)数据的地址是二维数组的名字
dst1_rows_ptr[j][0] = src_rows_ptr[j](0) + 100;
dst1_rows_ptr[j][1] = src_rows_ptr[j](1) + 100;
dst1_rows_ptr[j][2] = src_rows_ptr[j](2) + 100;
}
}
综上所述:使用ptr指针效率非常高,大家普遍使用的是at和ptr方法;使用的时候,一定要规范格式;
其中:at<类型>(i,j)
ptr<类型>(i)
有不理解的,欢迎留言交流
附上整个流程main.cpp
使用方式:需要测试哪个方法,取消注释就行;
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat src = imread("C:\\Users\\tudejiang\\Desktop\\1.jpg",1);
if (src.empty())
{
cout << "没有加载成功图片" << endl;
return -1;
}
imshow("src",src);
Mat dst = Mat(src.size(), CV_8UC1);
Mat dst1 = Mat(src.size(), CV_8UC3);
//遍历图像像素方法一:通过at<type>(i,j)坐标指针
/*for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
if (src.channels() == 1)
{
dst.at<uchar>(i, j) = src.at<uchar>(i, j) + 100;
}
else if (src.channels() ==3)
{
dst1.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i, j)[0] + 100;
dst1.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i, j)[1] + 100;
dst1.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i, j)[2] + 100;
}
}
}*/
//遍历图像像素方法二:data遍历索引法
//for (int i = 0; i < src.rows; i++)
//{
// for (int j = 0; j < src.cols; j++)
// {
// if (src.channels() == 1)
// {
// //图像数组是逐行逐列顺序排列的,也就是第一行,全列,第二行全列的走
// int indexs = i * src.cols + j;
// dst.data[indexs] = src.data[indexs] + 100;
// }
// else if (src.channels() == 3)
// {
// int indexs = i * src.cols + j;
// //在彩色图像中,每个indexs又是三个通过的数组
// dst1.data[3 * indexs + 0] = src.data[3 * indexs + 0] + 100;
// dst1.data[3 * indexs + 1] = src.data[3 * indexs + 1] + 100;
// dst1.data[3 * indexs + 2] = src.data[3 * indexs + 2] + 100;
//
// }
// }
//}
//遍历灰度图像像素方法三:采用ptr指针方法
//for (int i = 0; i < src.rows; i++)
//{
// //获取第i行首地址,ptr与at结果差不多
// uchar* src_rows_ptr = src.ptr<uchar>(i);
// uchar* dst_rows_ptr = dst.ptr<uchar>(i);
// for (int j = 0; j < src.cols; j++)
// {
// //每行指针的地址,又代表一行数组名称,因此下面就是遍历一行中的列数组数据
// dst_rows_ptr[j] = src_rows_ptr[j] + 100;
// }
//}
//遍历彩色图像像素方法四:采用ptr指针方法
//for (int i = 0; i < src.rows; i++)
//{
// //获取第i行首地址,首地址又是第一个数据,是三通道,所以是Vec3b
// // 可以理解为三维数组,i个(j,3)维数组
// Vec3b* src_rows_ptr = src.ptr<Vec3b>(i);
// Vec3b* dst1_rows_ptr = dst1.ptr<Vec3b>(i);
// for (int j = 0; j < src.cols; j++)
// {
// //每行指针的地址,又代表一行数组名称和第一个数据,因此下面就是遍历一行中的列数组数据
// //可以理解,每行是一个二维数组(j,3)维度
// dst1_rows_ptr[j][0]= src_rows_ptr[j][0]+100;
// dst1_rows_ptr[j][1] = src_rows_ptr[j][1]+100;
// dst1_rows_ptr[j][2] = src_rows_ptr[j][2]+100;
// }
//}
//遍历灰度图像像素方法五:采用at方法,使用ptr模式
//for (int i = 0; i < src.rows; i++)
//{
// //将灰度图片看成(src.rows,1)维度的二维矩阵,获取(i,0)数据的地址
// uchar* src_rows_ptr = &(src.at<uchar>(i, 0));
// uchar* dst_rows_ptr = &(dst.at<uchar>(i, 0));
// for (int j = 0; j < src.cols; j++)
// {
// //将(i,0)数据的地址下的内容看成是一维数组,(i,0)数据的地址是一维数组的名字
// dst_rows_ptr[j] = src_rows_ptr[j] + 100;
// }
//}
//imshow("dst1", dst);
//遍历灰度图像像素方法六:采用at方法,使用ptr模式
for (int i = 0; i < src.rows; i++)
{
//将彩色图片看成(src.rows,1)维度的二维矩阵,获取(i,0)数据的地址
Vec3b* src_rows_ptr = &(src.at<Vec3b>(i, 0));
Vec3b* dst1_rows_ptr = &(dst1.at<Vec3b>(i, 0));
for (int j = 0; j < src.cols; j++)
{
//将(i,0)数据的地址下的内容看成是二维数组,(i,0)数据的地址是二维数组的名字
dst1_rows_ptr[j][0] = src_rows_ptr[j](0) + 100;
dst1_rows_ptr[j][1] = src_rows_ptr[j](1) + 100;
dst1_rows_ptr[j][2] = src_rows_ptr[j](2) + 100;
}
}
imshow("dst1", dst1);
waitKey(0);
return 0;
}