概念:
圖像直方圖是反映一個(gè)圖像像素分布的統(tǒng)計(jì)表,其橫坐標(biāo)代表了圖像像素的種類(lèi),可以是灰度的,也可以是彩色的卵贱。縱坐標(biāo)代表了每一種顏色值在圖像中的像素總數(shù)或者占所有像素個(gè)數(shù)的百分比瓦侮。
圖像是由像素構(gòu)成艰赞,因?yàn)榉从诚袼胤植嫉闹狈綀D往往可以作為圖像一個(gè)很重要的特征佣谐。在實(shí)際工程中肚吏,圖像直方圖在特征提取、圖像匹配等方面都有很好的應(yīng)用狭魂。
OpenCV的直方圖計(jì)算:
OpenCV提供了一個(gè)簡(jiǎn)單的計(jì)算數(shù)據(jù)集(通常是圖像或分割后的通道)的直方圖函數(shù):
void calcHist(const cv::Mat *images,
int nimages,
const int *channels,
InputArray mask,
OutputArray hist,
int dims,
const int *histSize,
const float **ranges);
下面直接代碼演示使用該函數(shù)計(jì)算直方圖!
// 命名空間
using namespace cv;
using namespace std;
// 加載圖片
image = [UIImage imageNamed:@"yixiao"];
// UIImage -> Mat
UIImageToMat(image, cvImage);
if (!cvImage.data) {
return;
}
// 用來(lái)存放分割的單通道圖像:vector(向量): C++中的一種數(shù)據(jù)結(jié)構(gòu),確切的說(shuō)是一個(gè)類(lèi).它相當(dāng)于一個(gè)動(dòng)態(tài)的數(shù)組,當(dāng)程序員無(wú)法知道自己需要的數(shù)組的規(guī)模多大時(shí),用其來(lái)解決問(wèn)題可以達(dá)到最大節(jié)約空間的目的.
vector<Mat> rgb_planes;
// 分割成R, G, B 3個(gè)單通道圖像
split(cvImage, rgb_planes);
/*
void split(const cv::Mat &src, cv::Mat *mvbegin)
OpenCV中的通道分離函數(shù)
參數(shù)說(shuō)明:
const cv::Mat &src -- 要進(jìn)行分離的圖像矩陣
cv::Mat *mvbegin -- 輸出的則是Mat類(lèi)型的的向量
*/
// 設(shè)定bin數(shù)
int histSize = 255;
// 設(shè)定像素值范圍
float rang[] = {0,255};
// 設(shè)定每個(gè)區(qū)間的范圍
const float *histRange = {rang};
// calcHist函數(shù)的最后兩個(gè)參數(shù)值
bool uniform = true;
bool accumulate = false;
Mat r_hist,g_hist,b_hist;
// 計(jì)算直方圖
calcHist( &rgb_planes[0], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
calcHist( &rgb_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
calcHist( &rgb_planes[2], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );
/*
void calcHist(const cv::Mat *images,
int nimages,
const int *channels,
InputArray mask,
OutputArray hist,
int dims,
const int *histSize,
const float **ranges);
參數(shù)說(shuō)明:
const cv::Mat *images -- 輸入數(shù)組(或數(shù)組集)
int nimages -- 要計(jì)算直方圖的圖像的個(gè)數(shù),此函數(shù)可以為多圖像求直方圖,通常nimages=1罚攀。
const int *channels -- 圖像的通道党觅,它是一個(gè)數(shù)組,如果是灰度圖像則channels[1]={0};如果是彩色圖像則channels[3]={0,1,2}斋泄;如果是只是求彩色圖像第2個(gè)通道的直方圖杯瞻,則channels[1]={1};
InputArray mask -- 是一個(gè)遮罩圖像用于確定哪些點(diǎn)參與計(jì)算,實(shí)際應(yīng)用中是個(gè)很好的參數(shù)炫掐,默認(rèn)情況我們都設(shè)置為一個(gè)空?qǐng)D像魁莉,即:Mat()。
OutArray hist -- 計(jì)算得到的直方圖
int dim -- 得到的直方圖的維數(shù)募胃,灰度圖像為1維旗唁,彩色圖像為3維。
const int* histSize -- 每個(gè)維度的bin數(shù)目
const float** ranges -- 這是一個(gè)二維數(shù)組痹束,用來(lái)指出每個(gè)區(qū)間的范圍检疫。
后面兩個(gè)參數(shù)都有默認(rèn)值,uniform參數(shù)表明直方圖是否等距祷嘶,最后一個(gè)參數(shù)與多圖像下直方圖的顯示與存儲(chǔ)有關(guān)屎媳。
*/
// 創(chuàng)建直方圖畫(huà)布
int hist_w = 400; int hist_h = 400;
int bin_w = cvRound( (double) hist_w/histSize );
Mat histImage( hist_w, hist_h, CV_8UC3, Scalar( 0,0,0) );
// 將直方圖歸一化到范圍 [ 0, histImage.rows ]
normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
/*
歸一化函數(shù):http://blog.csdn.net/lanmeng_smile/article/details/49903865
為了作圖,原來(lái)很難在一張圖上作出來(lái)论巍,歸一化后就可以很方便的給出圖上的相對(duì)位置等
void normalize(InputArray src,OutputArray dst, double alpha = 1, double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray())
參數(shù)說(shuō)明:把需要處理的數(shù)據(jù)經(jīng)過(guò)處理后(通過(guò)某種算法)限制在你需要的一定范圍內(nèi)
InputArray src -- 輸入數(shù)組
OutputArray dst -- 輸出數(shù)組
double alpha = 1 -- range normalization模式的最小值
double beta = 0 -- range normalization模式的最大值烛谊,不用于norm normalization(范數(shù)歸一化)模式
int norm_type -- 歸一化的類(lèi)型,可以有以下的取值:
NORM_MINMAX:數(shù)組的數(shù)值被平移或縮放到一個(gè)指定的范圍嘉汰,線性歸一化晒来,一般較常用。
NORM_INF: 此類(lèi)型的定義沒(méi)有查到郑现,根據(jù)OpenCV 1的對(duì)應(yīng)項(xiàng)湃崩,可能是歸一化數(shù)組的C-范數(shù)(絕對(duì)值的最大值)
NORM_L1 : 歸一化數(shù)組的L1-范數(shù)(絕對(duì)值的和)
NORM_L2: 歸一化數(shù)組的(歐幾里德)L2-范數(shù)
int dtype -- type為負(fù)數(shù)時(shí),輸出數(shù)組的type與輸入數(shù)組的type相同接箫,否則攒读,輸出數(shù)組與輸入數(shù)組只是通道數(shù)相同,而tpye=CV_MAT_DEPTH(dtype)
InputArray mask -- 操作掩膜辛友,用于指示函數(shù)是否僅僅對(duì)指定的元素進(jìn)行操作
*/
// 在直方圖畫(huà)布上畫(huà)出直方圖
for( int i = 1; i < histSize; i++ )
{
line( histImage, cvPoint( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ) ,
cvPoint( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
Scalar( 0, 0, 255), 2, 8, 0 );
line( histImage, cvPoint( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) ,
cvPoint( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
Scalar( 0, 255, 0), 2, 8, 0 );
line( histImage, cvPoint( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) ,
cvPoint( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
Scalar( 255, 0, 0), 2, 8, 0 );
}
/*
int cvRound(double value):對(duì)一個(gè)double型的數(shù)進(jìn)行四舍五入薄扁,并返回一個(gè)整型數(shù)
void cvLine( CvArr* img,CvPoint pt1,CvPoint pt2,CvScalar color,int thickness=1,int line_type=8,int shift=0 )
參數(shù)說(shuō)明:
CvArr* img -- 要?jiǎng)澋木€所在的圖像
CvPoint pt1 -- 直線起點(diǎn)
CvPoint pt2 -- 直線終點(diǎn)
CvScalar color -- 直線的顏色
int thickness -- 線條粗細(xì)
int line_type -- 線段的類(lèi)型,可以取值8, 4废累, 和CV_AA邓梅, 分別代表8鄰接連接線,4鄰接連接線和反鋸齒連接線邑滨。默認(rèn)值為8鄰接日缨。為了獲得更好地效果可以選用CV_AA(采用了高斯濾波)。
int shift -- 坐標(biāo)點(diǎn)的小數(shù)點(diǎn)位數(shù)
*/
// 最后就是將畫(huà)布顯示了
UIImage *hist_image = MatToUIImage(histImage);
CGRect imgRect = CGRectMake(([UIScreen mainScreen].bounds.size.width - hist_image.size.width)*0.5, 200, hist_image.size.width, hist_image.size.height);
self.imageView.frame = imgRect;
self.imageView.image = hist_image;
最后放一張效果圖: