本篇博客主要介紹利用OpenCV工具提取一幅圖像中的顏色直方圖特征。所謂顏色直方圖,指的是一幅圖像中的顏色分布,與圖像中的特定的物體無關,只是用來表示人的眼睛觀察到的圖像中的顏色分布情況,例如說,一幅圖中紅色占了多少比例,綠色占了多少比例等。
我們知道,計算機色彩顯示器采用R、G、B相加混色的原理,通過發射出三種不同強度的電子束,使屏幕內側覆蓋的紅、綠、藍磷光材料發光而產生色彩。在RGB顏色空間中,任意色光F都可以用RGB三色不同分量的相加混合而成。也就是說,圖像中每個像素的顏色值都可以用一個三元值(R,G,B)來表示,例如(0,0,0)表示黑色,(0,0,255)表示藍色,……每個分量的大小從0~255.
那么,我們可以知道一張彩色圖像可以由R、G、B三個通道堆疊而成。可以想象成將三張大小相同的紙疊放,然后人眼從上往下看到的圖像就是原來的彩色圖像。這里,假設圖像的分辨率為720*576,那么每個通道的長為720,寬為576,單位是像素,總的像素個數就是3*720*576.
下面具體介紹顏色直方圖,橫軸表示bins,也就是顏色分為多少塊。假設bins=256,也就是橫坐標上每個點表示一個數值,縱坐標表示一幅圖中有多少個像素點的值是該數值。如果bins=16,也就是把顏色范圍0~255從小到大分為16塊,然后圖像中落在每塊對應的范圍的像素點的個數就是每塊對應的直方圖的高度。
因為opencv默認一張圖像是由R、G、B三個通道堆疊形成的,所以首先提取出每個通達的直方圖,最后將三個直方圖揉在一塊。
其中,最主要的就是一個calcHist函數,
void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false )
參數較多。
第一個參數表示輸入的圖像;
第二個參數表示輸入的圖像個數,一般是 1;
第三個參數表示需要處理的是圖像的第幾個通道;
第四個參數表示掩模,一般是Mat();
第五個參數是要輸出的直方圖數組;
第六個參數表示需要統計的直方圖通道個數,一般是1;
第七個參數histSize表示划分的區間數,即bins的個數;
第八個參數表示像素的變化范圍;后兩個參數分別表示是否歸一化,和是否累計計算像素個數。
重點關注我標紅的參數即可。
代碼展示:
1 #include "stdafx.h"
2 #include <iostream>
3 #include <opencv2/opencv.hpp>
4 using namespace cv; 5 using namespace std; 6
7 int main() 8 { 9 //讀取本地的一張圖片
10 Mat srcimage = imread("F:\\6030.png"); 11 imshow("原圖", srcimage); 12 int channels = 0; 13 int histsize[] = { 256 }; 14 float midranges[] = { 0,255 }; 15 const float *ranges[] = { midranges }; 16 MatND dsthist; //要輸出的直方圖 17 //重點關注calcHist函數,即為計算直方圖的函數
18 calcHist(&srcimage, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false); 19
20 Mat b_drawImage = Mat::zeros(Size(256, 256), CV_8UC3); 21 double g_dhistmaxvalue; 22 minMaxLoc(dsthist, 0, &g_dhistmaxvalue, 0, 0); 23 for (int i = 0;i < 256;i++) { 24 //這里的dsthist.at<float>(i)就是每個bins對應的縱軸的高度
25 int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue)); 26 line(b_drawImage, Point(i, b_drawImage.rows - 1), Point(i, b_drawImage.rows - 1 - value), Scalar(255, 0, 0)); 27 } 28 imshow("B通道直方圖", b_drawImage); 29
30 channels = 1; 31 calcHist(&srcimage, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false); 32 Mat g_drawImage = Mat::zeros(Size(256, 256), CV_8UC3); 33 for (int i = 0;i < 256;i++) { 34 int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue)); 35 line(g_drawImage, Point(i, g_drawImage.rows - 1), Point(i, g_drawImage.rows - 1 - value), Scalar(0, 255, 0)); 36 } 37 imshow("G通道直方圖", g_drawImage); 38
39 channels = 2; 40 calcHist(&srcimage, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false); 41 Mat r_drawImage = Mat::zeros(Size(256, 256), CV_8UC3); 42 for (int i = 0;i < 256;i++) { 43 int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue)); 44 line(r_drawImage, Point(i, r_drawImage.rows - 1), Point(i, r_drawImage.rows - 1 - value), Scalar(0, 0, 255)); 45 } 46 imshow("R通道直方圖", r_drawImage); 47
48 add(b_drawImage, g_drawImage, r_drawImage); //將三個直方圖疊在一塊
49 imshow("RGB直方圖", r_drawImage); 50 waitKey(0); 51 return 0; 52 }
結果展示:
接下來,繪制HSV顏色空間的直方圖。有了RGB顏色直方圖,還要HSV干什么。下面介紹HSV顏色空間模型。
HSV是一種將RGB色彩空間中的點在倒圓錐體中的表示方法。HSV即色相(Hue)、飽和度(Saturation)、亮度(Value),色相即顏色的基本屬性,飽和度指色彩的純度,越高則色彩越純,亮度指色彩的明亮程度。
在RGB顏色空間中,三種顏色分量的取值與所生成的顏色之間的聯系並不直觀。而HSV顏色空間,更類似於人類感覺顏色的方式,封裝了關於顏色的信息:“這是什么顏色?深淺如何?明暗如何?”HSV顏色空間規定的取值范圍: H:0~360,S:0~1,V:0~1
在OpenCV中,H:0~180,S:0~255,V:0~255, H/2,S*255,V*255
也就是說,這里H通道的取值是和其他兩個通道有所不同的,它的范圍是0~180,因為如果為了堆疊三個通道,強行設置256個bins,那么在第180~255個bins上,H通道上會出現為空的情況。
HSV顏色空間特征的提取方法和RGB類似,關鍵一點就是要將原圖像轉化為HSV顏色空間的圖像,之后再對三個通道分別進行直方圖繪制操作即可。
代碼展示:
1 #include "stdafx.h"
2 #include <iostream>
3 #include <opencv2/opencv.hpp>
4 using namespace cv; 5 using namespace std; 6
7 int main() 8 { 9 Mat srcimage = imread("F:\\6030.png"); 10 imshow("原圖", srcimage); 11 Mat srcimageHSV; 12 //圖像轉化HSV顏色空間圖像
13 cvtColor(srcimage, srcimageHSV, COLOR_BGR2HSV); 14 imshow("HSV空間圖像", srcimageHSV); 15 int channels = 0; 16 int histsize[] = { 256 }; 17 float midranges[] = { 0,255 }; 18 const float *ranges[] = { midranges }; 19 MatND dsthist; 20 calcHist(&srcimageHSV, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false); 21 Mat b_drawImage = Mat::zeros(Size(256, 256), CV_8UC3); 22
23 double g_dhistmaxvalue; 24 minMaxLoc(dsthist, 0, &g_dhistmaxvalue, 0, 0); 25 for (int i = 0;i < 256;i++) { 26 int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue)); 27 line(b_drawImage, Point(i, b_drawImage.rows - 1), Point(i, b_drawImage.rows - 1 - value), Scalar(255, 0, 0)); 28 } 29 imshow("H通道直方圖", b_drawImage); 30
31 channels = 1; 32 calcHist(&srcimageHSV, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false); 33 Mat g_drawImage = Mat::zeros(Size(256, 256), CV_8UC3); 34 for (int i = 0;i < 256;i++) { 35 int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue)); 36 line(g_drawImage, Point(i, g_drawImage.rows - 1), Point(i, g_drawImage.rows - 1 - value), Scalar(0, 255, 0)); 37 } 38 imshow("S通道直方圖", g_drawImage); 39
40 channels = 2; 41 calcHist(&srcimageHSV, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false); 42 Mat r_drawImage = Mat::zeros(Size(256, 256), CV_8UC3); 43 for (int i = 0;i < 256;i++) { 44 int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue)); 45 line(r_drawImage, Point(i, r_drawImage.rows - 1), Point(i, r_drawImage.rows - 1 - value), Scalar(0, 0, 255)); 46 } 47 imshow("V通道直方圖", r_drawImage); 48 add(b_drawImage, g_drawImage, r_drawImage); 49 imshow("HSV直方圖", r_drawImage); 50 waitKey(0); 51 return 0; 52 }
結果展示:
說明:HSV三個通道的直方圖繪制,我為了區分,用了紅綠藍三種顏色,和RGB通道沒有關系。顏色可以自己在程序中更改。其實每個通道都是從不同的角度(色相、飽和度、亮度)來分析圖像的。