c++和opencv小知识:遍历图像像素的常用方法(详细,很全)

c++遍历图像像素的常用方法(详细,很全)

本文基本上把常用的遍历方法都讲解了。在图像处理时经常会用到遍历图像像素点的方式,同样是遍历图像像素点,共有很多中方法可以做到;在这些方法中,有相对高效的,也有低效的;不同场景使用不同方法。
数据格式千万不要搞错:
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;
}

  • 19
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值