opencv之图像元素遍历(反色) 笔记

opencv版本号: opencv3.4.1

目录

1. 下标M.at(i, j)

2. 指针遍历Mat::ptr

3. 迭代器Matlterator_

 4. isContinuout方法

 5. LUT查表法

图像处理中需要对图像的像素点进行遍历,遍历图像像素的算法影响运行速度;以图像反色为例,有以下几种算法;

1. 下标M.at<float>(i, j)

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<cv::Vec3b>(i,j)[0] = 255 - srcImage.at<cv::Vec3b>(i,j)[0];
			tempImage.at<cv::Vec3b>(i,j)[1] = 255 - srcImage.at<cv::Vec3b>(i,j)[1];
			tempImage.at<cv::Vec3b>(i,j)[2] = 255 - srcImage.at<cv::Vec3b>(i,j)[2];
		}
	}

	return tempImage;
}

2. 指针遍历Mat::ptr<type>

cv::Mat inverseColor2(cv::Mat srcImage)
{
	cv::Mat tempImage = srcImage.clone();
	int row = tempImage.rows;

	//将3通道转换为单通道
	//std::cout << " row=" << row << " cols=" << tempImage.cols << " channels=" << tempImage.channels() << endl;
	int nStep = tempImage.cols * tempImage.channels();

	for(int i = 0; i < row; i++){
		//取源图像指针
		const uchar *pSrcData = srcImage.ptr<uchar>(i);

		uchar *pResultData = tempImage.ptr<uchar>(i);
		for(int j = 0; j < nStep; j++){
			pResultData[j] = cv::saturate_cast<uchar>(255-pSrcData[j]);
		}
	}

	return tempImage;
}

3. 迭代器Matlterator_

cv::Mat inverseColor3(cv :: Mat srcImage)
{
	cv::Mat tempImage = srcImage.clone();

	//初始化源图像迭代器
	cv::MatConstIterator_<cv::Vec3b> srcIterStart = srcImage.begin<cv::Vec3b>();
	cv::MatConstIterator_<cv::Vec3b> srcIterEnd   = srcImage.end<cv::Vec3b>();

	//初始化输出图像迭代器
	cv::MatIterator_<cv::Vec3b> resIterStart = tempImage.begin<cv::Vec3b>();
	cv::MatIterator_<cv::Vec3b> resIterEnd   = tempImage.end<cv::Vec3b>();

	while(srcIterStart != srcIterEnd)

	{
		(*resIterStart)[0] = 255 - (*srcIterStart)[0];
		(*resIterStart)[1] = 255 - (*srcIterStart)[1];
		(*resIterStart)[2] = 255 - (*srcIterStart)[2];
		
		srcIterStart++;
		resIterStart++;
	}

	return tempImage;
}

 4. isContinuout方法

图像数据的存储行与行间可能是空白单元,即图像行与行间的存储可能是不连续的,所以在使用上述方法进行图像的遍历时,很大程度上会造成指针的移动浪费;所以用isContunuout函数判断图像元素存储是连续的;

cv::Mat inverseColor4(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 = srcImage.ptr<uchar>(i);
		uchar *pResultData = tempImage.ptr<uchar>(i);
		for(int j =0 ; j < col; j++){
			*pResultData = 255 - *pSrcData;
			
			pResultData++;
			pSrcData++;
		}
	}

	return tempImage;
}

 5. LUT查表法

为了减少图像映射的时间复杂度,提供了查找表LUT;

cv::Mat inverseColor5(cv :: Mat srcImage)
{
	int row = srcImage.rows;
	int col = srcImage.cols;
	cv::Mat tempImage = srcImage.clone();

	//建立LUT反色table
	uchar LutTab[256];
	for(int i =0 ; i < 256; i++){
		LutTab[i] = 255 - i;
	}

	cv::Mat lookUpTable(1, 256, CV_8U);
	uchar *pData = lookUpTable.data;

	//建立映射表
	for(int i = 0; i < 256; i++){
		pData[i] = LutTab[i];
	}

	//利用索引查找
	cv::LUT(srcImage, lookUpTable, tempImage);

	return tempImage;
}

测试代码:

int main()
{	
	cv::Mat srcImage = cv::imread("./lena.jpg");
	if(srcImage.empty()){
		std::cout << "imread failed" << endl;
		return -1;
	}

	double tTime;
	tTime = (double)getTickCount();
	
	//cv::Mat dstImage = inverseColor1(srcImage); //运行时间Max 7.30422
	//cv::Mat dstImage = inverseColor2(srcImage); //运行时间Max 1.88004
	//cv::Mat dstImage = inverseColor3(srcImage); //运行时间Max 8.38001
	//cv::Mat dstImage = inverseColor4(srcImage);  //运行时间Max     1.66753
	cv::Mat dstImage = inverseColor5(srcImage);  //运行时间Max     1.26254

	tTime = 1000 * ((double)getTickCount() - tTime) / getTickFrequency();
	std::cout << tTime << std::endl;

	cv::imwrite("./lena_inverse5.jpg", dstImage);
	
	return  0;
}

getTickCount()和getTickFrequency()函数用来计时测试代码的运行时间;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天未及海宽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值