數字圖像中深度和通道的概念,以及如何遍歷各個通道

原博客: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 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章