OpenCV—访问图像中的像素

序言: OpenCV3编程入门已经进入第五章 (Core组件进阶) 学习了,后面也越来越多对图像的处理,这一节主要是对像素的认识。


文章目录

      • 一、访问图形中的像素
        • 1、图像在内存之中的存储方式
        • 2、颜色空间缩减
        • 3、LUT函数:Look up table操作
        • 4、计时函数
        • 5、访问图像像素的三类方法
          • (1)用指针访问像素
          • (2)用迭代器操作像素
          • (3)动态地址计算

一、访问图形中的像素

1、图像在内存之中的存储方式

图像矩阵的大小取决于所用的颜色模型,也就是说取决于所用的通道数。如下灰度图像:

OpenCV—访问图像中的像素_第1张图片

对于多通道的图像,矩阵中的列会包含多个子列,子列个数与通道数相等。

OpenCV—访问图像中的像素_第2张图片

2、颜色空间缩减

(1)问题提出:对于单通道像素,有256个不同的值,若三通道图像,颜色的存储值就有一千六百万多种。

解决: 如此之多的颜色数值,但是达到同样效果的只占很小一部分。颜色空间缩减(color space reduction)可以大大降低运算复杂度。它的做法:将现有颜色空间值除以某个输入值,以获得较少的颜色数,比如:0-9可以取0,10-19取10,以此类推。

(2)uchar类型的三通道图像,每个通道取值0~255,也就是256x256不同的值。

  • 0~9范围的像素值为0
  • 10~19范围的像素值是10
  • 20~29范围的像素值是20

这样操作,将颜色取值降低为26x26x26种情况。即:Inew=(Iold/10)*10。因为uchar类型的值除以int值,结果仍是char类型,所以求出来的小数也要向下取整。

(3)为了防止处理图像像素时,每一步计算都会花销时间,于是可以把计算好的结果提前存入表table中,这样不需要计算,直接取结果即可。

// 1、遍历图像矩阵的每一个像素
// 2、对像素应用上述公式
int divideWith=10;
uchar table[256];
for(int i=0;i<256;i++){
    table[i]=divideWith*(i/divideWith);//table[i]存放值为i像素减小颜色空间结果
}
//p[j]=table[p[j]];

总结:对于较大的图像,有效的方法是预先计算所有可能的值,然后需要这些值的时候,利用查找表直接赋值即可。

3、LUT函数:Look up table操作

说明:函数原型为:operationsOnArrays:LUT()的函数来进行,用于批量进行图像元素查找、扫描与操作图像。

//首先建立一个Mat型用于查表
Mat lookUpTable(1,256,CV_8U);
uchar* p=lookUpTable.data;
for(int i=0;i<256;i++){
	p[i]=table[i];
}
//然后调用函数(I输入,J输出)
for(int i=0;i<times;i++){
	LUT(I,lookUpTable,J);
}

这个地方书上讲的有点模糊,不是很懂,就去参考其他大佬

(1)对于单通道图像,其像素灰度值为0-255,LUT函数可以将函数的一个灰度值转换成其他灰度值。如:将一张图片0-100的像素的灰度值变成0,101-200的像素变成100,201-255的像素变成255。

(2)对于多通道图像,其内部每一个通道都做这样的映射,这样每一个通道的处理就和单通道差不多了。

(3)LUT函数为:

void LUT(InputArray src,InpitArray lut,OutputArray dst);
  • src: 输入图像(单通道或者3通道)
  • lut: 表示查找表,下面会用具体示例介绍
  • dst: 输出图像

(4)单通道图像处理

#include
using namespace cv;
int main()
{
	uchar lutData[256];
	for (int i = 0; i < 256; i++)
	{
		if (i <= 100)
			lutData[i] = 0;
		if (i > 100 && i <= 200)
			lutData[i] = 100;
		if (i > 200)
			lutData[i] = 255;
	}
	Mat lut(1, 256, CV_8UC1);
	Mat a = imread("12.jpg", CV_LOAD_IMAGE_GRAYSCALE),b;
	namedWindow("原灰度图像", CV_WINDOW_AUTOSIZE);
	namedWindow("转灰度图像", CV_WINDOW_AUTOSIZE);
	imshow("原灰度图像", a);
	LUT(a, lut, b);
	imshow("转灰度图像", b);
	waitKey(0);
}

OpenCV—访问图像中的像素_第3张图片

4、计时函数

说明:计时函数:getTickCount()和getTickFrequency()

  • getTickCount()函数返回CPU自某个事件(如启动电脑)以来走过的时钟周期数
  • getTickFrequency()函数返回CPU一秒钟走的时钟周期数,我们就可以以秒为单位对某运算计时。
double time0=static_cast<double>(getTickCount());//记录起始时间
//进行图像处理操作。。。。
time0=((double)getTickCount()-time0)/getTickFrequency();
printf("输出运行时间%f\n",time0);

5、访问图像像素的三类方法

说明:任何图像处理算法,都是从操作每个像素开始的。

提供三种访问每个像素的方法

  • 指针访问:C操作符[ ];
  • 迭代器iterator;
  • 动态地址计算

这三种方法在访问速度上略有差异,在debug模式下,比较明显。程序的目的是减少图像中颜色的数量,比如原来的图像是256种颜色,变成64只需要将原来的颜色除以4再乘以4.

示例程序:主程序中调用colorReduce函数来完成减少颜色的工作。

#include
#include
#include
using namespace cv;
using namespace std;
//全局函数声明
void colorReduce(Mat &inputImage, Mat& outputImage, int div);

int main()
{
	Mat srcImage = imread("12.jpg");
	imshow("原始图像",srcImage);
	
	//将原始图的参数规格来创建效果图
	Mat dstImage;
	dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());//效果图的大小、类型和原图片相同

	//记录起始时间
	double time0 = static_cast<double>(getTickCount());

	//调用颜色空间缩减函数
	colorReduce(srcImage, dstImage, 128);

	//计算运行时间并输出
	time0 = ((double)getTickCount() - time0) / getTickFrequency();
	printf("此方法运行时间为:%f\n", time0);
	imshow("效果图",dstImage);
	waitKey(0);
}
(1)用指针访问像素

说明:用指针访问像素发这种方法利用的是C语言操作符[ ],这种方法比较快。

void colorReduce(Mat &inputImage, Mat& outputImage, int div)
{
	//参数准备
	outputImage = inputImage.clone();//复制实参到临时变量
	int rowNumber = outputImage.rows;//行数
	int colNumber = outputImage.cols*outputImage.channels();//列数x通道数=每一行元素的个数
	//双重循环,遍历所有的像素值
	for (int i = 0; i < rowNumber; i++) {//行循环
		uchar* data = outputImage.ptr<uchar>(i);//获取第i行的首地址
		for (int j = 0; j < colNumber; j++) { //列循环
			//处理每一个像素
			data[j] = data[j] / div * div + div / 2;
		}
	}
}

补充:简化指针运算,Mat类提供了ptr函数可以得到图像任意行的首地址。

uchar* data = outputImage.ptr(i);//获取第i行的首地址
(2)用迭代器操作像素

说明:在迭代法中,获得图像矩阵的begin和end,然后增加迭代直至从begin到end。将*操作符添加在迭代指针前。

/*
	迭代器
*/
void colorReduce(Mat &inputImage, Mat& outputImage, int div)
{
	//参数准备
	outputImage = inputImage.clone();//复制实参到临时变量
	//获取迭代器
	Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();//初始位置迭代器
	Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();//终止位置迭代器
	//获取彩色图像像素
	for (; it != itend; it++) {
		//处理每一个像素
		(*it)[0] = (*it)[0] / div * div + div / 2;//蓝
		(*it)[1] = (*it)[1] / div * div + div / 2;//绿
		(*it)[2] = (*it)[2] / div * div + div / 2;//红
	}
}
(3)动态地址计算

说明:使用动态地址运算配合at方法的colorReduce函数的代码,这种方法简洁,便于对像素的直观认识。

/*
	动态地址法
*/
void colorReduce(Mat &inputImage, Mat& outputImage, int div)
{
	//参数准备
	outputImage = inputImage.clone();//复制实参到临时变量
	int rowNumber = outputImage.rows;//行数
	int colNumber = outputImage.cols;//行数

	//获取彩色图像像素
	for (int i = 0; i < rowNumber; i++) {
		for (int j = 0; j < colNumber; j++) {
			//开始处理每一个像素,0/1/2表示通道数
			outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div * div + div / 2;//蓝色通道
			outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div * div + div / 2;//绿色通道
			outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div * div + div / 2;//红色通道
		}
	}
}

补充:

  • Vec3b:由三个unsigned char组成的向量
  • image.at(i,j):取出灰度图像中i行j列的点。
  • image.at(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点,k=[0,1,2],分别代表B,G,R。

实际效果:

OpenCV—访问图像中的像素_第4张图片

你可能感兴趣的:(OpenCV编程入门,opencv,计算机视觉,人工智能)