数字图像中深度和通道的概念,以及如何遍历各个通道

原博客:https://blog.csdn.net/u013355826/article/details/64905921

前沿
看了图像处理有一段时间了,但是图像的通道和深度一直不理解,毕竟是比较抽象的概念。现在好好总结下,希望能帮助理解图像的通道和图像的深度。
基于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。
灰度图的图像存储模型


灰度图像像素点的存储就是对应的原图从左到右,从上到下,依次排列,每个点的值就是就是像素点的值,每个点的地址就是像素像素点的地址。

RGB图的图像存储模型

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<uchar>(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<uchar>(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<Vec3b>(i, j)[0] = (src.at<Vec3b>(i, j)[0] / n)*n + n / 2;
            dst.at<Vec3b>(i, j)[1] = (src.at<Vec3b>(i, j)[1] / n)*n + n / 2;
            dst.at<Vec3b>(i, j)[2] = (src.at<Vec3b>(i, j)[2] / n)*n + n / 2;
        }
 
    }
 
}
Mat类中的成员函数 at(int x, int y) 可以用来储存图像元素,但是在编译期间,知道图像的数据类型,我们一定要确保指定的数据类型和矩阵中数据类型符合,因为at方法不会对任何数据类型进行转换。
对于彩色图像,每个像素由三部分组成,通道(BGR)。因此,对于一个包含彩色图像的 Mat ,会返回一个由8位数字组成的向量。OpenCV将此类型向量定义为: Vec3b。存储彩色图像像素代码可以写成:
  image.at<Vec3b>(i,j)[channel] = value; 
其中:(i,j)代表像素点位置,channel 代表 通道。
(3)迭代器操作像素
void colorReduce3(Mat &src, Mat &dst,int n= 8)
{
    dst = src.clone();
    Mat_<Vec3b>::iterator it = dst.begin<Vec3b>(); //初始位置迭代器
    Mat_<Vec3b>::iterator itend = dst.end<Vec3b>();//终止位置迭代器
 
    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的概念。
--------------------- 
作者:谦190 
来源:CSDN 
原文:https://blog.csdn.net/u013355826/article/details/64905921 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/qq_25974431/article/details/89553407