【從零學習openCV】使用直方圖統計畫素
阿新 • • 發佈:2019-02-15
1. 計算影象直方圖
影象是由畫素組成的,在一個單通道的灰度影象中,每個畫素的值介於0到255之間,而直方圖就是一個簡單的表,給出了一幅或者一組影象中擁有給定數值的畫素數量。當然直方圖也可以歸一化,歸一化後的所有項的和為1,在這種情況下,每一項給出的都是擁有特定數值的畫素在影象中佔的比例。
下面我們看看如何用opencv計算單通道影象的直方圖,我用一個Histogram1D封裝了與單通道直方圖操作相關的變數和函式:
main函式如下,直接呼叫getHistogramImage即可得到直方圖:#ifndef HISTOGRAM1D_H #define HISTOGRAM1D_H #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace cv; class Histogram1D{ private: int histSize[1]; //項的數量 float hranges[2]; //畫素的最小及最大值 const float* ranges[1]; int channels[1]; //僅用到一個通道 public: Histogram1D(){ //準備1D直方圖的引數 histSize[0] =256; hranges[0] = 0.0; hranges[1] = 255.0; ranges[0] = hranges; channels[0] = 0; //預設情況,我們考察0號通道 } //計算1D直方圖 MatND getHistogram(const Mat&image) { MatND hist; //計算直方圖 calcHist(&image, 1, //計算單張影象的直方圖 channels, //通道的數量 Mat(), //不使用影象作為掩碼 hist, //返回的直方圖 1, //這是1D的直方圖 histSize, //項的數量 ranges //畫素值的範圍 ); return hist; } //計算1D直方圖,並返回一幅影象 Mat getHistogramImage(const Mat &image) { //首先計算直方圖 MatND hist = getHistogram(image); //獲取最大值和最小值 double maxVal = 0; double minVal = 0; minMaxLoc(hist,&minVal,&maxVal,0,0); //顯示直方圖的影象 Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255)); //設定最高點為nbins的90% int hpt = static_cast<int>(0.9*histSize[0]); //每個條目都繪製一條垂直線 for(int h = 0;h<histSize[0];h++){ float binVal =hist.at<float>(h); int intensity = static_cast<int>(binVal*hpt/maxVal); //兩點之間繪製一條線 line(histImg,Point(h,histSize[0]), Point(h,histSize[0]-intensity), Scalar::all(0)); } return histImg; } }; #endif // HISTOGRAM1D_H
效果如下:#include <QCoreApplication> #include "Histogram1D.h" int main(int argc, char *argv[]) { namedWindow( "src1", WINDOW_AUTOSIZE ); namedWindow( "src2", WINDOW_AUTOSIZE ); Mat image = imread( "test.jpg",0 ); //讀取灰度圖 Histogram1D h; while(1) { imshow( "src1", image); imshow( "src2", h.getHistogramImage(image)); char c = waitKey(30); if( 27==c ) return 0; } }
原始灰度影象
直方圖
當然我們還可以用calcHist函式來計算彩色BGR影象的直方圖,這時候直方圖變成三維的了:
這時候getHistogram返回的是一個256*256*256的矩陣,包含了超過1600萬個元素,這計算代價是非常大的,可以參考opencv操作畫素中所述的方法減少顏色的數量,或者可以使用cv::SparseMat資料結構,用於儲存大型的稀疏矩陣,這樣可以極大地減少記憶體的消耗。#ifndef COLORHISTOGRAM_H #define COLORHISTOGRAM_H #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include<QDebug> using namespace cv; class ColorHistogram{ private: int histSize[3]; //項的數量 float hranges[2]; //畫素的最小及最大值 const float* ranges[3]; int channels[3]; //用到三個通道 public: ColorHistogram(){ //準備彩色直方圖的引數 histSize[0] = histSize[1]= histSize[2]=256; hranges[0] = 0.0; hranges[1] = 255.0; ranges[0] =ranges[1]=ranges[2]=hranges; //所有通道都有相同的範圍 channels[0] = 0; //3個通道 channels[1] = 1; channels[2] = 2; } //計算三維直方圖 MatND getHistogram(const Mat&image) { MatND hist; //計算直方圖 calcHist(&image, 1, //計算單張影象的直方圖 channels, //通道的數量 Mat(), //不使用影象作為掩碼 hist, //返回的直方圖 3, //這是三維的直方圖 histSize, //項的數量 ranges //畫素值的範圍 ); qDebug()<<hist.dims; return hist; } }; #endif // COLORHISTOGRAM_H
2. 查詢表的使用
查詢表實際上就是一個簡單的一對一(或者多對一)的函式,定義瞭如何將畫素值轉換為新的值,本質上就是一個一維陣列。
openCV中用cv::LUT的方法對影象應用查詢表生成新影象,我們將這個功能加到Histogram1D類中:
//應用查詢表,image為輸入影象,lookup是1*256的unchar矩陣,代表查詢表
Mat applyLookUp(const Mat&image,const Mat&lookup)
{
Mat result;
//應用查詢表
LUT(image,lookup,result);
return result;
}
這時我們可以做個簡單的實驗,比如將原先每個畫素強度進行反轉,即x變成255-x:
int dim(256);
Mat lut(1,&dim,CV_8U);
for(int i=0;i<256;i++)
{
lut.at<uchar>(i) = 255-i;
}
Mat reverse = h.applyLookUp(image,lut);
效果如下:
3. 直方圖均衡化
直方圖均衡化其實就是為了讓直方圖更加的平坦,能夠大幅改善影象的外觀。
openCV中提供了一個簡單易用的函式cv::equalizeHist來執行直方圖均衡化。
//直方圖均衡化
Mat equalize(const Mat &image)
{
Mat result;
equalizeHist(image,result);
return result;
}
應用與之前的影象,效果如下:
、
可以看出影象的對比度增強了,直方圖也更加的平坦。
參考書籍 《openCV2計算機視覺程式設計手冊》