图像的通道和深度以及图像的像素点操作完全解析

前沿

看了图像处理有一段时间了,但是图像的通道和深度一直不理解,毕竟是比较抽象的概念。现在好好总结下,希望能帮助理解图像的通道和图像的深度。
基于OpenCV3.1.0版本
感谢贾志刚老师的视频以及QQ群
看了 xiaowei_cqu 的博客
看了毛星云的OpenCV3编程入门

图像的深度和通道

图像的深度:
图像中像素点占得bit位数,就是图像的深度,比如:
二值图像:图像的像素点不是0 就是1 (图像不是黑色就是白色),图像像素点占的位数就是 1 位,图像的深度就是1,也称作位图。
灰度图像:图像的像素点位于0-255之间,(0:全黑,255代表:全白,在0-255之间插入了255个等级的灰度)。2^8=255,图像的深度是8。
依次轮推,我们把计算机中存储单个像素点所用的 bit 位称为图像的深度。
图像的通道
有了图像深度的概念,我们知道如果是24位的图像,则这个像素点的颜色的取值范围是:从0到2^24。这个范围特别大,如果我们知道了某店的像素值怎么判断像素点的颜色呢?
我们知道 RGB是基本的三原色,如果我们用8位代表一种颜色,每种颜色最大是255,这样每个像素点的颜色值的范围就是(0-255,0-255,0-255)。这样图像的通道就是3。
灰度图的图像存储模型

图像的通道和深度以及图像的像素点操作完全解析_第1张图片
灰度图像像素点的存储就是对应的原图从左到右,从上到下,依次排列,每个点的值就是就是像素点的值,每个点的地址就是像素像素点的地址。

RGB图的图像存储模型
图像的通道和深度以及图像的像素点操作完全解析_第2张图片
RGB彩色图像和灰度图相比,每个像素点都有3个通道。每个通道占的内存空间都是8位。在内存中,RGB 图像的存储是以二维数组的形式。
学习图像的存储就是为了理解图像中像素点的存储情况,有助于我们对每个像素点的操作。

图像中像素点的遍历

注意:我们对图像像素的遍历其实对每个像素点中通道的遍历。以后我们对像素以及通道有关的操作时候,能够更好的理解像素以及通道的概念。
我们以经典的图像的颜色空间压缩为例来进行图像的遍历
什么是颜色空间的颜色呢?
        我们知道,对于3通道的深度是8的 RGB 图像,一共可以有255^3中颜色,如此庞大的颜色对我们的处理很不方便,我们可以对图像的像素进行量化。减小图像的颜色种类,同样也可以达到同样的效果。比如我们把图像的像素减少8倍,则每个通道只能有256/8=32中颜色,这样的话,原来图像的0-7像素点对应量化后的0,原来的图像的8-15对应量化后的图像的1,......原来图像中的248-255对应量化后的32。这样就能实现对图像的压缩。
那么这种办法在编程怎么实现?
很简答的,直接利用C/C++中 int 变量的 “/” 运算,这样的话,像素(0-7)/ 8 =0。同理依次可以得到压缩后像素值。

(1)利用基本的 行和列概念实现像素遍历
   上面的分析知道了,图像的存储就是以二维数组的形式,那我们很好理解如果取一个二维数组中元素?常规的方法就是先确定行,然后再确定列。这样就能把这个元素取出来。OpenCV中Mat类中定义的指针,可以获取某一行的地址,然后确定列数就可以获取我们所需要的地址。
Mat 类中有:Mat.ptr(int i=0) 获取像素矩阵的指针,i 是从第零行开始的。这块有点像二维数据的存储那样,二维数组可以当做是若干个一维指针,如果知道了每个行的第一个元素就能遍历这个行所有数据。
具体的程序代码
void colorReduce(Mat& srcImage,Mat& desImage, int n) //srcImage:输入图像,desImage:输出图像,n:减少的倍数
{
    desImage = srcImage.clone();
	int channels = desImage.channels();
	int rows = desImage.rows;
	int cols = desImage.cols*channels;//真正的列数是像素的列数乘以通道数,具体见RGB图像的存储
	for (int i = 0;i < rows;i++)        //双重循环的外循环,遍历图片的行数
	{
		uchar* pt = desImage.ptr(i); //获取第 i 行的像素矩阵指针
		for (int j = 0; j < cols; j++)   //双重循环的内循环,遍历图像的列数(包括每个通道数)
		{
			pt[j]=(pt[j]/n)*n+n/2; //通常我们会在后面加上 n/2

		}
	}

}
修改:
内层循环可以用指针实现移动到下一列:
			*pt++=(*pt/n)*n+n/2; 

(2)利用动态地址遍历像素点

void colorReduce2(Mat &src, Mat &dst,int n = 8)
{
	dst = src.clone();
	int cols = src.cols;
	int rows = src.rows;
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			dst.at(i, j)[0] = (src.at(i, j)[0] / n)*n + n / 2;
			dst.at(i, j)[1] = (src.at(i, j)[1] / n)*n + n / 2;
			dst.at(i, j)[2] = (src.at(i, j)[2] / n)*n + n / 2;
		}

	}

}
Mat类中的成员函数 at(int x, int y) 可以用来储存图像元素,但是在编译期间,知道图像的数据类型,我们一定要确保指定的数据类型和矩阵中数据类型符合,因为at方法不会对任何数据类型进行转换。
对于彩色图像,每个像素由三部分组成,通道(BGR)。因此,对于一个包含彩色图像的 Mat ,会返回一个由8位数字组成的向量。OpenCV将此类型向量定义为: Vec3b。存储彩色图像像素代码可以写成:
  image.at(i,j)[channel] = value; 
其中:(i,j)代表像素点位置,channel 代表 通道。

(3)迭代器操作像素

void colorReduce3(Mat &src, Mat &dst,int n= 8)
{
	dst = src.clone();
	Mat_::iterator it = dst.begin(); //初始位置迭代器
	Mat_::iterator itend = dst.end();//终止位置迭代器

	for (;it != itend; it++)
	{
         (*it)[0] = ((*it)[0]/n)*n+n/2;
	     (*it)[1] = ((*it)[1]/n)*n+n/2;
		 (*it)[2] = ((*it)[2]/n)*n+n/2;	
	}

}

其中:迭代器是C++中STL的概念。











你可能感兴趣的:(C++语言,OpenCV学习)