一幅圖像由不一樣灰度值的像素組成,圖像中灰度的分佈狀況是該圖像的一個重要特徵。圖像的灰度直方圖就描述了圖像中灰度分佈狀況,可以很直觀的展現出圖像中各個灰度級所佔的多少。
圖像的灰度直方圖是灰度級的函數,描述的是圖像中具備該灰度級的像素的個數:其中,橫座標是灰度級,縱座標是該灰度級出現的頻率。html
不過一般會將縱座標歸一化到[0,1][0,1]區間內,也就是將灰度級出現的頻率(像素個數)除以圖像中像素的總數。灰度直方圖的計算公式以下:ios
其中算法
直方圖的計算是很簡單的,無非是遍歷圖像的像素,統計每一個灰度級的個數。OpenCV提供了一個可靠的直方圖函數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 );
該函數可以同時計算多個圖像,多個通道,不一樣灰度範圍的灰度直方圖.函數
其參數說明以下:post
images,輸入圖像的數組,這些圖像要有相同大大小,相同的深度(CV_8U CV_16U CV_32F).
nimages ,輸入圖像的個數
channels,要計算直方圖的通道個數。
mask,可選的掩碼,不使用時可設爲空。要和輸入圖像具備相同的大小,在進行直方圖計算的時候,只會統計該掩碼不爲0的對應像素
hist,輸出的直方圖
dims,直方圖的維度
histSize,直方圖每一個維度的大小
ranges,直方圖每一個維度要統計的灰度級的範圍
uniform,是否進行歸一化,默認爲true
accumulate,累積標誌,默認值爲false。ui
下面是一個使用caclHist的一個示例,這裏對其進行了一個封裝,只繪製灰度直方圖url
#include "stdafx.h" #include <vector> #include <iostream> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> using namespace cv; using namespace std; 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.0f; hranges[1] = 255.0f; ranges[0] = hranges; channels[0] = 0; } MatND getHistogram(const Mat &image) { MatND hist; // 計算直方圖 calcHist(&image,// 要計算圖像的 1, // 只計算一幅圖像的直方圖 channels, // 通道數量 Mat(), // 不使用掩碼 hist, // 存放直方圖 1, // 1D直方圖 histSize, // 統計的灰度的個數 ranges); // 灰度值的範圍 return hist; } Mat getHistogramImage(const Mat &image) { MatND hist = getHistogram(image); // 最大值,最小值 double maxVal = 0.0f; double minVal = 0.0f; minMaxLoc(hist, &minVal, &maxVal); //顯示直方圖的圖像 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; } }; int main() { //圖像的獲取 Mat srcImage = imread("111.jpg"); if (!srcImage.data) { cout << "讀入圖片失敗!" << endl; return -1; } Histogram1D hist; Mat histImg; histImg = hist.getHistogramImage(srcImage); imshow("Image", srcImage); imshow("Histogram", histImg); waitKey(); return 0; }
運行結果以下:spa
假如圖像的灰度分佈不均勻,其灰度分佈集中在較窄的範圍內,使圖像的細節不夠清晰,對比度較低。一般採用直方圖均衡化及直方圖規定化兩種變換,使圖像的灰度範圍拉開或使灰度均勻分佈,從而增大反差,使圖像細節清晰,以達到加強的目的。.net
直方圖均衡化就是對圖像進行非線性拉伸,從新分配圖像的灰度值,使必定範圍內圖像的灰度值大體相等,它是以累計分佈函數變化法爲基礎的直方圖修正法。它將當前的灰度分佈經過一個變換函數,變換爲範圍更寬、灰度分佈更均勻的圖像。也就是將原圖像的直方圖修改成在整個灰度區間內大體均勻分佈,所以擴大了圖像的動態範圍,加強圖像的對比度。
方圖均衡化算法的步驟以下所示:
其中n爲像素總數,nk爲灰度級Sk的像素個數
其中Dj是目的圖像的像素,CDF(Si)是源圖像灰度爲i的累積分佈,L是圖像中最大灰度級(灰度圖爲255)
其代碼實現以下:
代碼實現以下所示:
void equalization_self(const Mat &src, Mat &dst) { Histogram1D hist1D; MatND hist = hist1D.getHistogram(src); hist /= (src.rows * src.cols); // 對獲得的灰度直方圖進行歸一化 float cdf[256] = { 0 }; // 灰度的累積機率 Mat lut(1, 256, CV_8U); // 灰度變換的查找表 for (int i = 0; i < 256; i++) { // 計算灰度級的累積機率 if (i == 0) cdf[i] = hist.at<float>(i); else cdf[i] = cdf[i - 1] + hist.at<float>(i); lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 建立灰度的查找表 } LUT(src, lut, dst); // 應用查找表,進行灰度變化,獲得均衡化後的圖像 }
實際在OpenCV中也提供了灰度均衡化的函數equalizeHist
,該函數的使用很簡單,只有兩個參數:輸入圖像,輸出圖像。
下面是一個直方圖均衡化的示例
#include "stdafx.h" #include <vector> #include <iostream> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> using namespace cv; using namespace std; 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.0f; hranges[1] = 255.0f; ranges[0] = hranges; channels[0] = 0; } MatND getHistogram(const Mat &image) { MatND hist; // 計算直方圖 calcHist(&image,// 要計算圖像的 1, // 只計算一幅圖像的直方圖 channels, // 通道數量 Mat(), // 不使用掩碼 hist, // 存放直方圖 1, // 1D直方圖 histSize, // 統計的灰度的個數 ranges); // 灰度值的範圍 return hist; } Mat getHistogramImage(const Mat &image) { MatND hist = getHistogram(image); // 最大值,最小值 double maxVal = 0.0f; double minVal = 0.0f; minMaxLoc(hist, &minVal, &maxVal); //顯示直方圖的圖像 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; } }; void equalization_self(const Mat &src, Mat &dst) { Histogram1D hist1D; MatND hist = hist1D.getHistogram(src); hist /= (src.rows * src.cols); // 對獲得的灰度直方圖進行歸一化 float cdf[256] = { 0 }; // 灰度的累積機率 Mat lut(1, 256, CV_8U); // 灰度變換的查找表 for (int i = 0; i < 256; i++) { // 計算灰度級的累積機率 if (i == 0) cdf[i] = hist.at<float>(i); else cdf[i] = cdf[i - 1] + hist.at<float>(i); lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 建立灰度的查找表 } LUT(src, lut, dst); // 應用查找表,進行灰度變化,獲得均衡化後的圖像 } int main() { //圖像的獲取 Mat srcImage = imread("111.jpg"); Mat srcgray; cvtColor(srcImage, srcgray, CV_BGR2GRAY); if (!srcImage.data) { cout << "讀入圖片失敗!" << endl; return -1; } Histogram1D hist; Mat histImg; histImg = hist.getHistogramImage(srcImage); //直方圖均衡化 Mat equalize, equalizeImg, equalize1, equalizeImg1, srcgrayhist; //函數均衡 equalization_self(srcImage, equalize); equalizeImg= hist.getHistogramImage(equalize); //OpenCV自帶函數均衡 srcgrayhist = hist.getHistogramImage(srcgray); equalizeHist(srcgray, equalize1); equalizeImg1 = hist.getHistogramImage(equalize1); imshow("Image", srcImage); imshow("Histogram", histImg); imshow("equalizeImg", equalizeImg); imshow("equalize", equalize); imshow("srcgray", srcgray); imshow("srcgrayhist", srcgrayhist); imshow("equalizeImg1", equalizeImg1); imshow("equalize1", equalize1); waitKey(); return 0; }
程序運行結果以下,左邊兩張圖片是原圖及咱們寫的函數的效果,右邊兩張圖是灰度圖及OpenCV自帶函數的效果。
須要注意的是,OpenCV自帶的函數只能對單通道進行直方圖均衡化,若是對原圖進行均衡化,因爲原圖是三通道的,此時會報錯。可是這並不意味着沒法用equalizeHist對彩色圖像進行處理,詳情請看參考資料OpenCV直方圖(直方圖、直方圖均衡,直方圖匹配,原理、實現)
直方圖均衡化能夠自動的肯定變換函數,該函數尋求產生又均勻直方圖的輸出圖像,須要自動加強時,這是一種好方法,由於這種方法獲得的結果能夠預知,而且這種方法的實現也很簡單,可是在有些應用中這種自動的加強並非最好的方法。有時候,須要圖像具備
某一特定的直方圖形狀(也就是灰度分佈),而不是均勻分佈的直方圖,這時候可使用直方圖規定化。
直方圖規定化,也叫作直方圖匹配,用於將圖像變換爲某一特定的灰度分佈,也就是其目的的灰度直方圖是已知的。這其實和均衡化很相似,均衡化後的灰度直方圖也是已知的,是一個均勻分佈的直方圖;而規定化後的直方圖能夠隨意的指定,也就是在執行規定化操
做時,首先要知道變換後的灰度直方圖,這樣才能肯定變換函數。規定化操做可以有目的的加強某個灰度區間,相比於均衡化操做,規定化多了一個輸入,可是其變換後的結果也更靈活。
直方圖的規定化也較爲簡單。能夠利用均衡化後的直方圖做爲一箇中間過程,而後求取規定化的變換函數。具體步驟以下:
直方圖規定化在原理上很簡單,在實踐中,常見的困難是尋找T(r)和G-1的有意義表達式。幸運的是,在處理離散量時,問題能夠大大獲得簡化。
爲了方便,這裏將公式方法改寫
其中MN是圖像的像素總數,nj是具備灰度值rj的像素數,L是圖像中可能的灰度級數。
相似的,給出個規定的Sk值
對一個q值,有
則咱們用反變換找到指望的值
即對每個S值給出一個Z值,這樣就實現了從S到Z的一個映射,即在實踐中,咱們不須要計算G的反變換。
即直方圖的規定化過程以下:
直方圖規定化的實現能夠分爲一下幾步:
void hist_specify(const Mat &src, const Mat &dst,Mat &result) { Histogram1D hist1D; MatND src_hist = hist1D.getHistogram(src); MatND dst_hist = hist1D.getHistogram(dst); float src_cdf[256] = { 0 }; float dst_cdf[256] = { 0 }; // 源圖像和目標圖像的大小不同,要將獲得的直方圖進行歸一化處理 src_hist /= (src.rows * src.cols); dst_hist /= (dst.rows * dst.cols); // 計算原始直方圖和規定直方圖的累積機率 for (int i = 0; i < 256; i++) { if (i == 0) { src_cdf[i] = src_hist.at<float>(i); dst_cdf[i] = dst_hist.at<float>(i); } else { src_cdf[i] = src_cdf[i - 1] + src_hist.at<float>(i); dst_cdf[i] = dst_cdf[i - 1] + dst_hist.at<float>(i); } } // 累積機率的差值 float diff_cdf[256][256]; for (int i = 0; i < 256; i++) for (int j = 0; j < 256; j++) diff_cdf[i][j] = fabs(src_cdf[i] - dst_cdf[j]); // 構建灰度級映射表 Mat lut(1, 256, CV_8U); for (int i = 0; i < 256; i++) { // 查找源灰度級爲i的映射灰度 // 和i的累積機率差值最小的規定化灰度 float min = diff_cdf[i][0]; int index = 0; for (int j = 1; j < 256; j++) { if (min > diff_cdf[i][j]) { min = diff_cdf[i][j]; index = j; } } lut.at<uchar>(i) = static_cast<uchar>(index); } // 應用查找表,作直方圖規定化 LUT(src, lut, result); }
#include "stdafx.h" #include <vector> #include <iostream> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> using namespace cv; using namespace std; 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.0f; hranges[1] = 255.0f; ranges[0] = hranges; channels[0] = 0; } MatND getHistogram(const Mat &image) { MatND hist; // 計算直方圖 calcHist(&image,// 要計算圖像的 1, // 只計算一幅圖像的直方圖 channels, // 通道數量 Mat(), // 不使用掩碼 hist, // 存放直方圖 1, // 1D直方圖 histSize, // 統計的灰度的個數 ranges); // 灰度值的範圍 return hist; } Mat getHistogramImage(const Mat &image) { MatND hist = getHistogram(image); // 最大值,最小值 double maxVal = 0.0f; double minVal = 0.0f; minMaxLoc(hist, &minVal, &maxVal); //顯示直方圖的圖像 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; } }; void equalization_self(const Mat &src, Mat &dst) { Histogram1D hist1D; MatND hist = hist1D.getHistogram(src); hist /= (src.rows * src.cols); // 對獲得的灰度直方圖進行歸一化 float cdf[256] = { 0 }; // 灰度的累積機率 Mat lut(1, 256, CV_8U); // 灰度變換的查找表 for (int i = 0; i < 256; i++) { // 計算灰度級的累積機率 if (i == 0) cdf[i] = hist.at<float>(i); else cdf[i] = cdf[i - 1] + hist.at<float>(i); lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 建立灰度的查找表 } LUT(src, lut, dst); // 應用查找表,進行灰度變化,獲得均衡化後的圖像 } void hist_specify(const Mat &src, const Mat &dst, Mat &result) { Histogram1D hist1D; MatND src_hist = hist1D.getHistogram(src); MatND dst_hist = hist1D.getHistogram(dst); float src_cdf[256] = { 0 }; float dst_cdf[256] = { 0 }; // 源圖像和目標圖像的大小不同,要將獲得的直方圖進行歸一化處理 src_hist /= (src.rows * src.cols); dst_hist /= (dst.rows * dst.cols); // 計算原始直方圖和規定直方圖的累積機率 for (int i = 0; i < 256; i++) { if (i == 0) { src_cdf[i] = src_hist.at<float>(i); dst_cdf[i] = dst_hist.at<float>(i); } else { src_cdf[i] = src_cdf[i - 1] + src_hist.at<float>(i); dst_cdf[i] = dst_cdf[i - 1] + dst_hist.at<float>(i); } } // 累積機率的差值 float diff_cdf[256][256]; for (int i = 0; i < 256; i++) for (int j = 0; j < 256; j++) diff_cdf[i][j] = fabs(src_cdf[i] - dst_cdf[j]); // 構建灰度級映射表 Mat lut(1, 256, CV_8U); for (int i = 0; i < 256; i++) { // 查找源灰度級爲i的映射灰度 // 和i的累積機率差值最小的規定化灰度 float min = diff_cdf[i][0]; int index = 0; for (int j = 1; j < 256; j++) { if (min > diff_cdf[i][j]) { min = diff_cdf[i][j]; index = j; } } lut.at<uchar>(i) = static_cast<uchar>(index); } // 應用查找表,作直方圖規定化 LUT(src, lut, result); } int main() { //圖像的獲取 Mat srcImage = imread("111.jpg"); Mat srcImage1 = imread("222.jpg"); Mat srcgray, srcgray1; cvtColor(srcImage, srcgray, CV_BGR2GRAY); cvtColor(srcImage1, srcgray1, CV_BGR2GRAY); Histogram1D hist; Mat histImg, histImg1, histImg_result; histImg = hist.getHistogramImage(srcImage); histImg1 = hist.getHistogramImage(srcImage1); //直方圖規定化 Mat result, equalizeImg, equalize1, equalizeImg1; hist_specify(srcgray, srcgray1, result); histImg_result = hist.getHistogramImage(result); imshow("Image", srcImage); imshow("Histogram", histImg); imshow("Histogram1", histImg1); imshow("histImg_result", histImg_result); imshow("srcgray", srcgray); imshow("srcgray1", srcgray1); imshow("result", result); waitKey(); return 0; }
圖像的表示方法除了咱們一般使用的RGB方法外,還能夠經過HSV空間來進行表示,HSL和HSV都是一種將RGB色彩模型中的點在圓柱座標系中的表示法。這兩種表示法試圖作到比RGB基於笛卡爾座標系的幾何結構更加直觀。HSV即色相、飽和度、明度(英語:Hue, Saturation, Value),又稱HSB,其中B即英語:Brightness。所以在分析圖像的H-S直方圖時,須要先將源RGB的圖像轉換到HSV顏色空間內,而後再將對應的H和S通道進行單元劃分,再其二維空間上計算對應的直方圖,最後再次經過計算直方圖空間上的最大值,歸一化繪製相應的直方圖信息。
H-S直方圖一般是應用在目標檢測、特徵分析以及目標特徵跟蹤等場景中。其主要關注點爲圖像中的位置信息。
//實現直方圖的反向投影 #include "stdafx.h" #include <iostream> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> using namespace cv; using namespace std; int main() { Mat srcImage = imread("111.jpg"); if (!srcImage.data) { cout << "讀入圖片失敗!" << endl; return -1; } Mat hsvImage; //將圖像轉換到HSV空間 cvtColor(srcImage, hsvImage, CV_BGR2HSV); //初始化灰度階參數 int hbins = 30, sbins = 32; int histSize[] = { hbins, sbins }; //灰度變化範圍設置 float hranges[] = { 0, 180 }; //飽和度變化範圍 float sranges[] = { 0, 256 }; const float *ranges[] = { hranges, sranges }; MatND hist; //選取計算直方圖通道 int channels[] = { 0, 1 }; //計算當前通道直方圖 calcHist(&hsvImage, 1, channels, Mat(), hist, 2, histSize, ranges, true, false); double maxValue; //找到直方圖的最大值 minMaxLoc(hist, 0, &maxValue, 0, 0); int scale = 10; Mat histImage = Mat::zeros(sbins*scale, hbins * 10, CV_8UC3); //遍歷H和S通道 for (int h = 0; h < hbins; h++) { for (int s = 0; s < sbins; s++) { float binVal = hist.at<float>(h, s); //根據最大值計算變換範圍 int intensity = cvRound(binVal * 255 / maxValue); //進行繪圖 rectangle(histImage, Point(h*scale, s*scale), Point((h + 1)*scale - 1, (s + 1)*scale - 1), Scalar::all(intensity), CV_FILLED); } } imshow("原圖像", srcImage); imshow("H-S直方圖", histImage); waitKey(); return 0; }
若是一幅圖像的區域中顯示的是一種紋理結構或者一個獨特的物體,那麼這個區域的直方圖能夠看做是一個機率函數,其表現形式是某個像素屬於該紋理或物體的機率。直方圖的反向投影是利用直方圖模型計算給定的像素點的特徵。反向投影在某一位置的值是源圖像在對應位置的像素值的累計。簡單的講就是首先計算某一特徵的直方圖模型,而後使用模型去尋找圖像中存在的該特徵的方法。主要用於在輸入圖像中查找與特定圖像最匹配的點或者區域,即定位模板圖像出如今圖像中的位置。
爲了方便地計算直方圖的反向投影,OpenCV中提供了一個用來簡單計算hue通道的直方圖反向投影的函數calcBackProject,下面對這個函數進行說明,函數原型爲:
void calcBackProject( const Mat* images, int nimages,const int* channels, InputArray hist,OutputArray backProject,const float** ranges,double scale=1, bool uniform=true );
第一個參數 images表示輸入圖像源指針,須要注意的是,圖像的源必須具備一樣的深度信息,也就是說,能夠是CV_8U或CV_32U,圖像能夠有任意的通道數。
第二個參數nimages表示的是待計算圖像源中圖像的個數,一般單幅圖像計算直方圖時這個參數的值爲1.
第三個參數channels指的是須要同級的圖像的通道維數數組索引,第一個數組的通道由0到arrays[0].channels()-1,第二個數組的通道從array[0].channels()到arrays[0].channels() + array[1].channels()-1。並以此類推。
第四個參數hist表示輸入源圖像的直方圖
第五個參數backProject表示的是目標圖像的反向投影圖,這個圖能夠是單通道,與Image[0]具備一樣的尺度和深度
第六個參數ranges表示用於指出直方圖每一維的每一個bin上下界範圍的數組,對於均勻直方圖這個參數是一個包含兩個元素的數組。
第七個參數scale表示可選的輸出反向投影的尺寸的參數
第八個參數uniform是直方圖統一顯示的標誌。
在給出具體程序以前,先簡單介紹一下會用到的一個OpenCV中的函數,函數聲明以下:
void mixChannels(const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts, const int* fromTo, size_t npairs); CV_EXPORTS void mixChannels(const vector<Mat>& src, vector<Mat>& dst, const int* fromTo, size_t npairs); void mixChannels(InputArrayOfArrays src, InputArrayOfArrays dst,const vector<int>& fromTo);
此函數爲重排圖像通道提供了比較先進的機制,這裏函數實現的功能是通道複製到特定的通道
參數src表示的是輸入圖像源組,被複制通道的輸入圖像數據
參數nsrc指的是待輸入圖像源中圖像的圖像的個數
參數dst表示的是輸出目標圖像數組,存儲複製後的通道,全部的數組必需要事先進行分配,並且要和輸入數組的大小和深度相同。
參數ndsts指的是目標數組中圖像的總數
參數fromTo指的是通道索引對的數組,表示的是將輸入圖像數組複製到目標數組通道,若是是偶數表示輸入矩陣索引,若是是奇數表示的是輸出矩陣索引,若是是偶數並且其下標爲負,則相應輸出矩陣爲0
參數npairs表示fromTo中的索引對
//實現直方圖的反向投影 #include "stdafx.h" #include <iostream> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> using namespace cv; using namespace std; int main() { Mat srcImage = imread("111.jpg"); if (!srcImage.data) { cout << "圖像打開失敗!" << endl; return -1; } // 將圖像轉換到HSV顏色空間 Mat hsvImage; cvtColor(srcImage, hsvImage, CV_BGR2HSV); //進行 hue 通道分離 Mat hueImage; hueImage.create(hsvImage.size(), hsvImage.depth()); int ch[] = { 0, 0 }; mixChannels(&hsvImage, 1, &hueImage, 1, ch, 1); //初始化直方圖計算參數 int bins = 25; MatND hist; int histSize = MAX(bins, 2); float hue_range[] = { 0, 100 }; const float*ranges = { hue_range }; //計算直方圖並進行歸一化操做 calcHist(&hueImage, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false); normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat()); //計算反向投影 MatND backproj; calcBackProject(&hueImage, 1, 0, hist, backproj, &ranges, 1, true); //定義輸出圖像 int w = 320, h = 360; int bin_w = cvRound((double)w / histSize); Mat histImage = Mat::zeros(w, h, CV_8UC3); for (int i = 0; i < bins; i++) { //繪製直方圖 rectangle(histImage, Point(i*bin_w, h), Point((i + 1)*bin_w, h - cvRound(hist.at<float>(i)*h / 255.0)), Scalar(0, 0, 255), -1); } //顯示原圖像和反向投影圖像 imshow("反向投影圖", backproj); imshow("原圖像", srcImage); imshow("直方圖", histImage); //進行直方圖均衡化 equalizeHist(backproj, backproj); imshow("直方圖均衡化後的直方圖", backproj); waitKey(); return 0; }
程序運行結果以下所示: