Day 6 - Opencv用指针扫描图像

1.前言

在大多数图像处理任务中,执行计算时你都需要对图像的所有像素进行扫描。需要访问的像素数量非常庞大,一次必须采用高效的方式来执行这个任务。下面学习的是使用指针运算来遍历图像像素。

2.指针扫描图像

以减色算法为例,算法步骤如下:

  1. N为减色因子,将image中的每个像素值除以N(整除),如:0/8=0, 1/8=0,8/8=1;
  2. 将步骤1得到的结果乘以N,得到N的倍数,并且不超过原始像素的值,如: (0/8)*8=0,(1/8)*8=0,(8/8)*8=8;
  3. 加上N/2,就得到相邻的N倍数之间的中间值;
  4. 对每个像素重复上述步骤,得到(256/N)(256/N)(256/N)种可能的颜色值;
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <random>
#include <iostream>

//减色函数
void reduceColor(cv::Mat image, int n);

int main(int argc, char** argv)
{
    //1.读取图像
	cv::Mat image = cv::imread("15011.jpg");
	if (image.empty())
	{
		std::cout << "\n Durn, couldn't read image filename " << std::endl;
		return 1;
	}
	//2.显示图像
	cv::namedWindow("Original Image");
	cv::imshow("Original Image", image);
    //3.计时
	auto startTime = std::chrono::system_clock::now();
	//4.调用减色算法
	reduceColor(image, 24);
	auto endTime = std::chrono::system_clock::now();
	//5.输出减色算法运行时间
	std::cout << "time:" << std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime).count() << std::endl;
	//6.显示结果图像
	cv::namedWindow("ReducedColr Image");
	cv::imshow("ReducedColr Image", image);
	cv::waitKey(0);
	return 0;
}
//image: 输入图像
//n    : 减色因子
void reduceColor(cv::Mat image, int n)
{
	int rows = image.rows; //行数
	int cols = image.cols; //列数
	int allCols = cols * image.channels(); //因为BGR图像为3通道
	//按行处理
	for (int i = 0; i < rows; i++)
	{
		//取每行的首地址
		uchar* data = image.ptr<uchar>(i);
		//按列处理
		for (int j = 0; j < allCols; j++)
		{
			//处理每个像素,通过首地址加上索引来读取相应内存中像素值
			data[j] = data[j] / n * n + n / 2;
			//另一种实现方法
			//*data++ = *data / n * n + n / 2;
		}
	}
}

原图:
在这里插入图片描述
在这里插入图片描述

3.其他减色算法

3.1 取模运算

前面一部分,同样可以得到n的倍数

data[i] = data[i] - data[i] % n + n / 2;

3.2 位运算符

如果把减色因子限定为2的只是,即div=pow(2,n),那么把像素值的前n位掩码后就能得到最接近的div的倍数。

//用来截取像素值的掩码
uchar mask = 0xFF<<n;

如果减色因子div=16,则n=4, 即0xFF需要左移4位,则mask=0xF0;

//计算过程
//1.先计算0xFF的值
0xFF = 15*16^1 + 15*16^0 = 255;
//2.将255转换成2进制
255 = 11111111;
//3.将2进制左移4位
11111111 << 4 = 11110000 = 2^7 + 2^6 + 2^5 + 2^4 = 240;
//4.将左移后的二进制转换为16进制
11110000 = 240 = 0xF0;

当前像素值关于减色因子的倍数可以如下实现:

//掩码
*data &= mask; //这一处是与mask进行位运算,可以想象成图像中的mask

那么,它的运算为何能够得到div的倍数呢?我们来通过二进制看一下:

假设当前像素值为255,对255执行掩码运算:
11111111 & 11110000 = 11110000 = 240 
240 / 16 = 15;
若当前像素值为254,254执行掩码运算:
11111110 & 11110000 = 11110000 = 240
240 / 16 = 15;
即,我们将像素值处于240-255之间的像素点的灰度值统一变换为240 + div / 2,实现了我们的减色算法

同样,div / 2 也可以使用位移运算

div / 2 = div>>1;

因此,减色运算的计算就转换为:

uchar mask = 0xFF<<n;
*data &= mask;
*data++ += div>>1;

一般来说,使用位运算的代码运行效率很高,因此在效率为重时,位运算是不二之选。

经实际测试来看,第一种方式是2287us, 第二种方式1255us,提速了将近一半的样子。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首次接触图像处理,通过次来记录自己的学习记录,以方便回忆。 //指针访问像素 void colorReduce(Mat& temImage, int div) { //行数 int rowNumber = temImage.rows; cout << "图像通道数:" << temImage.channels() << endl; //列数*通道数=每一行的元素个数 int colNumber = temImage.cols * temImage.channels(); for (int row = 0; row < rowNumber;row++) { uchar* data = temImage.ptr<uchar>(row); for (int col = 0; col < colNumber;col++) { data[col] = data[col] / div*div + div / 2; } } } //迭代器iterator操作像素 void iterColorReduce(Mat& temImage,int div) { Mat_<Vec3b>::iterator it = temImage.begin<Vec3b>(); Mat_<Vec3b>::iterator itend = temImage.end<Vec3b>(); //存取彩色图像的像素 while (it != itend) { //开始处理每个像素 (*it)[0] = (*it)[0] / div*div + div / 2; (*it)[1] = (*it)[1] / div*div + div / 2; (*it)[2] = (*it)[2] / div*div + div / 2; ++it; } } //动态地址计算像素 void atColorReduce(Mat& temImage, int div) { int rowNumber = temImage.rows; int colNumber = temImage.cols; //存取彩色图像 for (int row = 0; row < rowNumber; row++) { for (int col = 0; col < colNumber; col++) { //开始处理每个图像 //蓝色通道 temImage.at<Vec3b>(row, col)[0] = temImage.at<Vec3b>(row, col)[0] / div*div + div / 2; //绿色通道 temImage.at<Vec3b>(row, col)[1] = temImage.at<Vec3b>(row, col)[1] / div*div + div / 2; //红色通道 temImage.at<Vec3b>(row, col)[2] = temImage.at<Vec3b>(row, col)[2] / div*div + div / 2; } } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值