1 摘要
在分析圖像狐胎、物體和視頻信息的時(shí)候特姐,我們通常使用直方圖來表示我們關(guān)注的信息同木。直方圖可以描述很多不同的信息浮梢,如物體的顏色分布,物體的邊緣梯度模板彤路,假設(shè)的物體位置的概率分布秕硝。下圖簡單演示了如何使用直方圖進(jìn)行快速的手勢(shì)識(shí)別。
在該示例中模型手的邊緣梯度分布被分為5個(gè)不同的組洲尊,分別表示上远豺、下、左坞嘀、右和OK手勢(shì)躯护。通過網(wǎng)絡(luò)攝像頭分析用戶的手勢(shì)可以實(shí)現(xiàn)用戶通過手勢(shì)控制視頻的播放。在獲取到每一幀圖像后丽涩,首先分析感興趣的顏色區(qū)域棺滞,這里是用戶的手,然計(jì)算其邊緣的梯度直方圖矢渊,再將其和已知的5類手勢(shì)直方圖模型進(jìn)行匹配检眯,找到匹配程度最高的模型,從而達(dá)到手勢(shì)識(shí)別的目的昆淡。
圖中的豎直細(xì)長矩形條表示當(dāng)前幀的興趣區(qū)域邊緣直方圖和已有手勢(shì)模型直方圖的匹配程度锰瘸,水平灰色線條表示匹配程度的閾值,即只有當(dāng)匹配程度高于此值時(shí)這種匹配關(guān)系才是有效的昂灵。
在很多計(jì)算機(jī)視覺程序中都使用到了直方圖避凝,如視頻數(shù)據(jù)中每幀畫面的邊緣和顏色統(tǒng)計(jì)直方圖發(fā)生顯著變化表示場(chǎng)景的切換舞萄。你也可以通過興趣點(diǎn)附近特征的直方圖為其添加標(biāo)簽,從而識(shí)別興趣點(diǎn)管削。邊緣倒脓、顏色和角點(diǎn)等直方圖形成了目標(biāo)的通用特征,這些特征可以被分類器使用從而識(shí)別目標(biāo)對(duì)象含思。顏色和邊緣直方圖的序列也可以被用于鑒定視頻是否復(fù)制于網(wǎng)絡(luò)崎弃。類似的應(yīng)用實(shí)例還有很多,總之直方圖是計(jì)算機(jī)視覺的經(jīng)典工具含潘。
直方圖只是簡單的將原始數(shù)據(jù)分到預(yù)先定義好的多個(gè)組中饲做,并計(jì)算每個(gè)組內(nèi)的樣本數(shù)量。也可以先計(jì)算出原始數(shù)據(jù)中的梯度幅度遏弱、方向和顏色等特征盆均,然后再對(duì)這些特征計(jì)數(shù)。不管是哪一種情況漱逸,直方圖獲得的都是原始數(shù)據(jù)分布的統(tǒng)計(jì)圖像泪姨。通常直方圖的維度都比原始數(shù)據(jù)更低,如下圖中左上圖展示了一個(gè)二維分布的樣本集合饰抒,通過右上圖中定義的組統(tǒng)計(jì)每個(gè)組內(nèi)樣本的數(shù)量肮砾,則得到右下圖的一維統(tǒng)計(jì)直方圖。
由于直方圖可以處理任意含義的原始數(shù)據(jù)袋坑,因此它是一個(gè)用于表示圖像信息的便利工具仗处。
在生成直方圖時(shí)可能會(huì)遇到下圖演示的問題。左側(cè)的兩幅圖像的分組過寬咒彤,則得到的結(jié)果會(huì)過于粗糙疆柔,并且丟失了數(shù)據(jù)的分布細(xì)節(jié)信息咒精。右側(cè)的兩幅圖像的分組過細(xì)镶柱,每個(gè)組中就沒有足夠的數(shù)據(jù)點(diǎn)來準(zhǔn)確的表示分布信息,并且會(huì)出現(xiàn)一些細(xì)小的突刺單元模叙。
2 直方圖
新版本的OpenCV使用矩陣cv::Mat
和稀疏矩陣cv::SparseMat
對(duì)象來表示一維或者多維的直方圖歇拆,同時(shí)支持在每個(gè)維度上組的分布都可以是均勻的或者非均勻的,另外還提供了一些有用的函數(shù)來執(zhí)行常見的直方圖操作范咨。
盡管直方圖對(duì)象使用了和圖像數(shù)據(jù)同類型的對(duì)象故觅,盡管他們底層的數(shù)據(jù)結(jié)構(gòu)是相同的,但是它們內(nèi)部的數(shù)據(jù)含義卻不相同渠啊。對(duì)于n行矩陣表示的直方圖(N×1的矩陣除外)输吏,每個(gè)數(shù)據(jù)表示的都是在某個(gè)維度(以行索引標(biāo)識(shí))的某組數(shù)據(jù)(以列索引標(biāo)識(shí))的統(tǒng)計(jì)結(jié)果。數(shù)據(jù)組的索引和其實(shí)際含義是分離的替蛉,在使用直方圖時(shí)需要在它們之間轉(zhuǎn)換贯溅。例如拄氯,在表示人體重的直方圖中,數(shù)據(jù)組可能被分為20到40它浅、40到60译柏、60到80和80到100四組,它們的索引是0姐霍、1鄙麦、2、3镊折,幸運(yùn)的是OpenCV中的很多函數(shù)都會(huì)在內(nèi)部執(zhí)行這種轉(zhuǎn)換邏輯胯府。
如果需要使用高維的直方圖時(shí),通常情況下大部分元素的值都是0腌乡,此時(shí)可以選擇更合適的數(shù)據(jù)類型cv::SparseMat
盟劫。實(shí)際上該類型的設(shè)計(jì)原因就是為了更好的處理直方圖數(shù)據(jù)。稠密矩陣的大部分函數(shù)都適用于稀疏矩陣,當(dāng)然我們也會(huì)介紹一些值得關(guān)注的特例勤庐。
2.1 創(chuàng)建直方圖
創(chuàng)建直方圖的函數(shù)原型如下发绢,需要注意直方圖的維度和輸入矩陣數(shù)組的維度無關(guān),僅和其數(shù)量及矩陣的數(shù)量相關(guān)影所,直方圖的每個(gè)維度反應(yīng)的是某個(gè)輸入矩陣的某個(gè)通道上的數(shù)據(jù)統(tǒng)計(jì)結(jié)果。當(dāng)然你也可以指定應(yīng)當(dāng)統(tǒng)計(jì)哪些輸入矩陣的哪些通道僚碎。
// images:C語言風(fēng)格的待處理的矩陣數(shù)組猴娩,基本數(shù)據(jù)類型為8U或者32F
// nimages:輸入矩陣的個(gè)數(shù)
// channels:C語言風(fēng)格的待處理通道列表,下文介紹
// mask:蒙版矩陣勺阐,只統(tǒng)計(jì)其中非0值對(duì)應(yīng)的原始數(shù)據(jù)中的點(diǎn)
// hist:統(tǒng)計(jì)結(jié)果直方圖
// dims:直方圖的維度卷中,必須小于cv::MAX_DIMS(32)
// histSize:C語言風(fēng)格數(shù)組,直方圖在每個(gè)維度應(yīng)當(dāng)分配的數(shù)據(jù)組數(shù)
// ranges:C語言風(fēng)格的數(shù)組渊抽,每個(gè)元素都是一個(gè)數(shù)組蟆豫,表示在對(duì)應(yīng)維度上的分組策略,
// 具體細(xì)節(jié)和uniform相關(guān)懒闷,下文介紹
// uniform:直方圖分組策略是均勻分組還是非均勻分組十减,下文介紹
// accumulate:在生成直方圖時(shí)是累加(false)還是清空(true)hist內(nèi)部的數(shù)據(jù)
void cv::calcHist(const cv::Mat* images, int nimages, const int* channels,
cv::InputArray mask, cv::OutputArray hist, int dims,
const int* histSize, const float** ranges, bool uniform = true,
bool accumulate = false);
// 和前一個(gè)函數(shù)類似,但是輸出數(shù)據(jù)類型為稀疏矩陣
void cv::calcHist(const cv::Mat* images, int nimages, const int* channels,
cv::InputArray mask, cv::SparseMat& hist, int dims,
const int* histSize, const float** ranges, bool uniform = true,
bool accumulate =false);
函數(shù)cv::calcHist()
有三種形式愤估,除了上文列出的形式使用C語言風(fēng)格的數(shù)組作為輸入矩陣外帮辟,第三種使用STL向量模板容器作為參數(shù)類型。
函數(shù)的第一個(gè)參數(shù)images
包含了構(gòu)建直方圖需要的nimages
個(gè)數(shù)據(jù)矩陣玩焰。所有的矩陣尺寸必須相同由驹,但是通道數(shù)可以不同,基本事件類型可以是8位或者32位昔园,但是它們應(yīng)該是相同的蔓榄。channels
指定了輸入數(shù)組中的哪些通道應(yīng)當(dāng)被處理的闹炉,通道的索引是按順序編號(hào)的,即images[0]
中的第一個(gè)通道的索引是0润樱,然后遞增渣触,images[1]
的通道索引在此基礎(chǔ)上繼續(xù)遞增∫既簦可以明顯看出channels
的元素個(gè)數(shù)和最后得到的直方圖的維度相同嗅钻。
參數(shù)ranges
表示直方圖各個(gè)維度上的分組策略,數(shù)組的具體含義和參數(shù)uniform
的取值相關(guān)店展。如果參數(shù)uniform
設(shè)置為true
养篓,則每個(gè)組的區(qū)間是相同的,此時(shí)需要在參數(shù)ranges中指定每個(gè)組的下邊界和上邊界(不包含)赂蕴,例如ranges[i] = [0, 100.0)
柳弄。如果參數(shù)uniform
設(shè)置為false
,如果在第i
維度存在N個(gè)分組概说,則ranges[i]
應(yīng)該包含N+1個(gè)元素碧注,其中的第j
個(gè)元素表示第j-1
分組的上界,以及低j分組的下界糖赔。如果使用的參數(shù)類型為STL的vector <float>
萍丐,則和c語言數(shù)組的區(qū)別在于向量類型的參數(shù)中數(shù)據(jù)都是一維的。Ranges
內(nèi)數(shù)據(jù)的含義如下圖放典。
2.2 基礎(chǔ)直方圖操作
盡管直方圖使用的數(shù)據(jù)類型是圖像數(shù)據(jù)使用的cv::Mat
逝变,但是在OpenCV中對(duì)于直方圖含義的矩陣支持一些新的操作,在本小節(jié)將會(huì)介紹這些操作奋构,另外也會(huì)介紹如何使用矩陣本身已經(jīng)支持的函數(shù)來完成一些重要的直方圖操作壳影。
2.2.1 直方圖標(biāo)準(zhǔn)化
通常我們得到直方圖數(shù)據(jù)后需要將其標(biāo)準(zhǔn)化,使直方圖的每個(gè)維度上的數(shù)值都表示的是占整個(gè)直方圖的比值弥臼,其實(shí)現(xiàn)方式如下宴咧。
// 使用運(yùn)算符
cv::Mat normalized = my_hist / sum(my_hist)[0];
// 使用函數(shù)
cv::normalize(my_hist, my_hist, 1, 0, NORM_L1);
2.2.2 直方圖閾值處理
另外一個(gè)很常見的操作是你希望閾值化處理一個(gè)直方圖并丟棄其中所以值低于閾值的元素,此時(shí)你可以使用處理標(biāo)準(zhǔn)矩陣的閾值函數(shù)醋火,示例代碼如下悠汽。
cv::threshold(my_hist, my_thresholded_hist, threshold, 0, cv::THRESH_TOZERO);
2.2.3 尋找最大最小值
通常在處理概率分布直方圖時(shí)箱吕,你可能并不想做閾值處理芥驳,而是像找到其中的最大或者最小值,OpenCV提供一種函數(shù)來實(shí)現(xiàn)這個(gè)功能茬高,其中處理二維矩陣函數(shù)void cv::minMaxLoc()
的原型如下兆旬。
// 處理二維矩陣的函數(shù)
// src:待查詢的矩陣
// minVal:最小值,設(shè)置為NULL時(shí)不會(huì)計(jì)算
// maxVal:最大值怎栽,設(shè)置為NULL時(shí)不會(huì)計(jì)算
// minLoc:最小值的位置丽猬,設(shè)置為NULL時(shí)不會(huì)計(jì)算
// maxLoc:最大值的位置宿饱,設(shè)置為NULL時(shí)不會(huì)計(jì)算
// mask:蒙板矩陣,數(shù)值為0的點(diǎn)對(duì)應(yīng)的原始矩陣在統(tǒng)計(jì)時(shí)將被忽略
void cv::minMaxLoc(cv::InputArray src, double* minVal, double* maxVal = 0,
cv::Point* minLoc = 0, cv::Point* maxLoc = 0,
cv::InputArray mask = cv::noArray());
// 處理任意維度稀疏矩陣的函數(shù)
void cv::minMaxLoc(const cv::SparseMat& src, double* minVal, double* maxVal = 0,
int* minLoc = 0, int* maxLoc = 0,
cv::InputArray mask = cv::noArray());
該函數(shù)的使用實(shí)例如下脚祟。提示:對(duì)于vector<>
數(shù)組谬以,可以通過cv::Mat(vec).reshape(1)
將其轉(zhuǎn)換為通道數(shù)為1的矩陣。
// 尋找二維矩陣表示的直方圖中的最大值及其位置
double max_val;
cv::Point max_pt;
cv::minMaxLoc(my_hist, NULL, &max_val, NULL, &max_pt);
// 尋找任意維度稀疏矩陣表示的直方圖中的最大值及其位置
double maxval;
int max_pt[CV_MAX_DIM];
cv::minMaxLoc(my_hist, NULL, &max_val, NULL, max_pt);
函數(shù)cv::minMaxIdx()
用于尋找任意維度矩陣中的最大最小值以及它們的位置由桌,其函數(shù)原型如下为黎。
void cv::minMaxIdx(cv::InputArray src, double* minVal, double* maxVal = 0,
int* minLoc = 0, int* maxLoc = 0,
cv::InputArray mask = cv::noArray());
需要注意的是如果使用了一維矩陣作為輸入函數(shù),參數(shù)minLoc
和maxLoc
分配的矩陣仍需要包含兩個(gè)元素行您,因?yàn)樵摵瘮?shù)內(nèi)部會(huì)將一維矩陣轉(zhuǎn)換為二維矩陣铭乾。如果尋找到的元素索引為k,當(dāng)輸入矩陣尺寸為N??1娃循,返回值為(k, 0)炕檩,當(dāng)輸入矩陣尺寸為1??N,返回值為(0, k)捌斧。
2.2.4 直方圖比較
在某種指定的標(biāo)準(zhǔn)下比較直方圖的相似性是一個(gè)必不可缺的圖像處理工具笛质,該方法由Swain和Ballard發(fā)明,并由Schiele和Crowley加以推廣捞蚂。該方法可以有很多應(yīng)用場(chǎng)景经瓷,可以通過該函數(shù)比較兩幅圖像的直方圖的相似性完成圖像匹配任務(wù),也可以通過直方圖比較來搜索模型洞难。后者的做法是比較圖像不同子區(qū)域和目標(biāo)模型的直方圖舆吮,通過匹配程度判斷該子區(qū)域是否包含模型。其函數(shù)原型如下队贱。
// H1:待比較的直方圖1
// H2:待比較的直方圖2色冀,尺寸需要和H1相同
// method:比較方法,下文介紹
double cv::compareHist(cv::InputArray H1, cv::InputArray H2,
int method);
double cv::compareHist(const cv::SparseMat& H1, const cv::SparseMat& H2,
int method);
參數(shù)method
指定了直方圖相似的判斷標(biāo)準(zhǔn)柱嫌,可以選擇的方法有四種锋恬。
相關(guān)性方法
定義相關(guān)性方法的值為cv::COMP_CORREL
,該方法基于皮爾遜(Pearson)相關(guān)性系數(shù)實(shí)現(xiàn)编丘,當(dāng)H1和H2表示概率分布時(shí)与学,使用該方法非常合適。距離計(jì)算的公式如下嘉抓。
其中
N表示直方圖的分組數(shù)量索守,直方圖的匹配程度越高,使用該方法的匹配函數(shù)返回值越大抑片。1表示完全匹配卵佛,-1表示完全不匹配,而0表示兩個(gè)分布無關(guān)系,也就是隨機(jī)組合截汪。
Chi-square方法
定義Chi-square方法的值為cv::COMP_CHISQR_ALT
疾牲,該方法基于chi-squared測(cè)試統(tǒng)計(jì)方法實(shí)現(xiàn),它同樣可以檢測(cè)兩個(gè)分布的相關(guān)性衙解,其函數(shù)原型如下阳柔。
直方圖的匹配程度越高,該方法的到的值越低蚓峦,完全匹配的結(jié)果為0盔沫,而完全不匹配的值取決于直方圖的大小。
交集法
定義交集法的值為cv::COMP_INTERSECT
枫匾,該方法基于兩個(gè)直方圖的簡單交集實(shí)現(xiàn)架诞,其計(jì)算公式如下。
直方圖的匹配程度越高干茉,該方法計(jì)算得到的值越高谴忧,如果H1和H2是兩個(gè)經(jīng)過標(biāo)準(zhǔn)化處理的直方圖,則完全匹配的結(jié)果為1角虫,完全不匹配的結(jié)果為0沾谓。
巴氏距離
定義巴氏距離(Bhattacharyya)的值為cv::COMP_BHATTACHARYYA
,也是基于兩個(gè)分布的重疊部分實(shí)現(xiàn)的一種距離計(jì)算方法戳鹅,其公式如下均驶。
直方圖的匹配程度越高,該方法計(jì)算得到的值越低枫虏,完全匹配的結(jié)果為0妇穴,完全不匹配的結(jié)果為1。盡管該方法內(nèi)部會(huì)有一個(gè)因子來對(duì)直方圖進(jìn)行標(biāo)準(zhǔn)化處理隶债,但是通常情況像你應(yīng)當(dāng)自行對(duì)輸入?yún)?shù)進(jìn)行標(biāo)準(zhǔn)化處理腾它,因?yàn)轭愃朴?jì)算直方圖交集的概念對(duì)于未標(biāo)準(zhǔn)化的直方圖是完全沒有意義的。
考慮簡單的只包含兩個(gè)分組的一維標(biāo)準(zhǔn)化直方圖死讹,模型左側(cè)分組的值為1.0瞒滴,右側(cè)分組的值為0,則使用上述4種方法計(jì)算出的匹配結(jié)果如下圖所示赞警。仔細(xì)觀察下圖會(huì)發(fā)現(xiàn)當(dāng)直方圖匹配模型只是簡單反轉(zhuǎn)時(shí)妓忍,即下圖的第2和第4行圖像,前4種方法計(jì)算得到的都是完全不匹配的結(jié)果愧旦,即使在某種程度上這兩種分布仍然有一定相似性世剖。
EMD也是一種距離算法忘瓦,在處理半匹配時(shí)該方法能夠得到更準(zhǔn)確的結(jié)果搁廓,下文還會(huì)詳細(xì)的介紹到EMD(Earth Mover‘s Distance)算法,這里暫時(shí)先不再討論耕皮。根據(jù)該書原作者的經(jīng)驗(yàn)境蜕,使用交集法處理快速粗糙的直方圖匹配效果更好,而chi-square方法和巴氏方法精度更高凌停,但是計(jì)算成本更高粱年,EMD算法給出的結(jié)果是最符合視覺感受的,但是它的計(jì)算速度更慢罚拟。
2.2.5 直方圖創(chuàng)建示例
示例Computation讀取了一副圖像台诗,將其轉(zhuǎn)換為HSV顏色空間,然后統(tǒng)計(jì)器色度分量H和飽和度分量S的分布直方圖赐俗,其核心代碼如下拉队。
int main(int argc, const char * argv[]) {
// 讀取原圖
cv::Mat src = cv::imread(argv[1], cv::IMREAD_COLOR);
// 構(gòu)建HSV顏色空間數(shù)據(jù)
cv::Mat hsv;
cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV);
// 設(shè)定二維直方圖中的分組策略
// HSV標(biāo)準(zhǔn)定義色調(diào)H取值區(qū)域?yàn)閇0, 360),S和V取值區(qū)域?yàn)閇0, 1]阻逮,而在OpenCV中為了能夠用8位
// 統(tǒng)一表示粱快,H減半處理及,OpenCV中HSV的H值僅為實(shí)際值的一半叔扼,因此其取值范圍為[0, 180)事哭,
// 而S和V的取值為[0, 255]
float h_ranges[] = {0, 180};
float s_ranges[] = {0, 256};
const float * ranges[] = {h_ranges, s_ranges};
int histSize[] = {30, 32};
int ch[] = {0, 1};
// 計(jì)算二維直方圖
cv::Mat hist;
cv::calcHist(&hsv, 1, ch, cv::noArray(), hist, 2, histSize, ranges, true);
// 標(biāo)準(zhǔn)化處理直方圖
cv::normalize(hist, hist, 0, 255, cv::NORM_MINMAX);
// 定義單個(gè)數(shù)據(jù)點(diǎn)表示的方塊直徑
int scale = 10;
// 創(chuàng)建顯示二維直方圖的圖像
cv::Mat hist_img(histSize[0] * scale, histSize[1] * scale, CV_8UC3);
// 在圖像hist_img中繪制scale??scale像素的方塊表示二維直方圖hist中的每個(gè)數(shù)據(jù)點(diǎn)
for (int h = 0; h < histSize[0]; h++) {
for (int s = 0; s < histSize[1]; s++) {
float hval = hist.at<float>(h, s);
cv::rectangle(hist_img,
cv::Rect(h * scale, s * scale, scale, scale),
cv::Scalar::all(hval), -1);
}
}
return 0;
}
程序的運(yùn)行結(jié)果如下圖。其中左圖為原始圖像瓜富,右圖為其色度和飽和度分布直方圖鳍咱,不用為圖中的大部分黑色區(qū)域感到好奇,這是因?yàn)樯群惋柡投鹊姆植歼^于集中引起的与柑。
在很多實(shí)際的應(yīng)用中膚色的顏色直方圖很有用谤辜,下圖包含了不同光照環(huán)境下的手掌照片及整張圖片的顏色直方圖,最左側(cè)一列是手掌的照片价捧,中間列是BGR顏色空間的直方圖每辟,而右側(cè)一列是HSV顏色空間的直方圖。從圖中可以看出隨著光照環(huán)境的改變干旧,手掌的膚色也會(huì)發(fā)生一些變化渠欺。
為了測(cè)試直方圖比較的結(jié)果,從某個(gè)圖片中選擇部分區(qū)域(如室內(nèi)圖片的上部分)椎眯,計(jì)算其顏色直方圖并將其和室內(nèi)環(huán)境的下半部分圖片及另外兩個(gè)光照環(huán)境下的整幅手掌圖片的顏色直方圖比較挠将。這里選擇使用HSV顏色空間的直方圖,它可以使用色度H和飽和度S的直方圖進(jìn)行膚色匹配编整,盡管還有變量亮度V的值未使用舔稀,但是這已經(jīng)足夠我們完成即使是跨種族的膚色匹配任務(wù)。
一個(gè)實(shí)現(xiàn)了上述膚色匹配任務(wù)的比較結(jié)果如下表掌测。其中内贮,室內(nèi)圖像的上半幅圖片和下半幅圖片對(duì)顏色直方圖匹配程度很高,和另外兩種光照環(huán)境下的顏色直方圖匹配程度較低。
比較目標(biāo) | 相關(guān)性法 | Chi-Square方法 | 交集法 | 巴氏距離 |
---|---|---|---|---|
完全匹配參考目標(biāo) | (1.0) | (0.0) | (1.0) | (0.0) |
下半幅室內(nèi)圖片 | 0.96 | 0.14 | 0.82 | 0.2 |
室外陰影圖片 | 0.09 | 1.57 | 0.13 | 0.8 |
室外明亮圖片 | 0.0 | 1.98 | 0.01 | 0.99 |
2.3 復(fù)雜直方圖方法
在介紹了一些基礎(chǔ)的直方圖操作后夜郁,接下來將會(huì)介紹一些高級(jí)的直方圖方法什燕,這些方法包括比較直方圖,計(jì)算或者可視化圖像的哪部分對(duì)指定部分的直方圖有貢獻(xiàn)竞端。
2.3.1 EMD距離
在前面的文章中我們已經(jīng)看見光照條件的改變會(huì)導(dǎo)致明顯的顏色位移屎即,如在前文的手掌照片及直方圖展示中就能觀察到這個(gè)現(xiàn)象。盡管這些位移不會(huì)改變顏色直方圖的形狀事富,但是會(huì)改變它的位置技俐,從而使得直方圖匹配度較低。這也是直方圖匹配度一個(gè)難點(diǎn)统台,我們經(jīng)常想要找到一個(gè)距離度量標(biāo)準(zhǔn)使得形狀相同但是發(fā)生位移的直方圖分布能夠得到一個(gè)匹配的比較結(jié)果雕擂,EMD距離(Earth Mover‘s Distance)就是這樣的距離度量標(biāo)準(zhǔn)。
該方法的基本思想是通過移動(dòng)部分或者整個(gè)直方圖到新的位置將其“搬到”另外一個(gè)直方圖中贱勃,并度量該操作耗費(fèi)的成本捂刺。如在上文演示函數(shù)cv::compareHist()
使用的簡單匹配模型結(jié)果中,該方法的完美匹配直方圖計(jì)算得到的距離為0募寨,而對(duì)于半匹配的模型距離為0.5族展,而對(duì)于其中完全不匹配的情況需要將整個(gè)直方圖向由移動(dòng)一步,因此得到的距離為1拔鹰。
實(shí)際上EMD是一個(gè)相同通用的算法仪缸,它允許用戶自定義距離度量衡以及移動(dòng)成本矩陣。你可以通過記錄數(shù)據(jù)從一個(gè)直方圖的何處移動(dòng)到了另一個(gè)直方圖中的何處列肢,并且基于數(shù)據(jù)的先驗(yàn)信息(Prior Information)的到一個(gè)非線性的距離恰画。OpenCV提供的EMD函數(shù)原型如下。
// 參數(shù)詳細(xì)含義下文介紹
// signature1:待比較的直方圖轉(zhuǎn)換的簽名格式瓷马,尺寸為sz1??dms+1
// signature2:待比較的直方圖轉(zhuǎn)換的簽名格式拴还,尺寸為sz2??dms+1
// distType:距離類型,如cv::DIST_L1
// cost:移動(dòng)成本矩陣欧聘,尺寸為sz1??sz2片林,當(dāng)參數(shù)選擇cv::DIST_USER需要設(shè)置此參數(shù)
// lowerBound:兩個(gè)直方圖重心距離下界,可以同時(shí)作為輸入和輸出
// flow:尺寸為sz1??sz2怀骤,表示直方圖1中第I個(gè)元素流向直方圖2中第j個(gè)元素的質(zhì)量
float cv::EMD(cv::InputArray signature1, cv::InputArray signature2,
int distType, cv::InputArray cost = noArray(),
float* lowerBound = 0, cv::OutputArray flow = noArray());
在使用該函數(shù)比較直方圖距離的時(shí)候费封,必須將直方圖轉(zhuǎn)換為一種稱為簽名的格式,該格式需要傳入一個(gè)sz1??dms+1的矩陣蒋伦,每一行數(shù)據(jù)都包含直方圖在某個(gè)坐標(biāo)上的統(tǒng)計(jì)結(jié)果以及對(duì)應(yīng)的坐標(biāo)弓摘。如在三維直方圖中在點(diǎn)(7, 43, 11)的分組的值為537,則簽名矩陣對(duì)應(yīng)的某行數(shù)據(jù)為[537, 7, 43, 11]痕届。
參數(shù)distType
表示EMD距離的度量衡韧献,其取值在前文很多地方都已經(jīng)有過介紹末患,可以是曼哈頓街區(qū)距離(Manhattan Distance),取值為cv::DIST_L1
锤窑,歐式距離(Euclidean Distance)璧针,取值為cv::DIST_L2
,也可以是棋盤距離(Checkerboard Distance)果复,取值為cv::DIST_C
陈莽,或者是用戶自定義的距離衡渤昌,取值為cv::DIST_USER
虽抄。當(dāng)選擇用戶自定義距離時(shí),需要通過參數(shù)cost指定移動(dòng)成本独柑,即自定義的移動(dòng)距離迈窟。其尺寸為sz1??sz2
,該矩陣的每個(gè)元素(坐標(biāo)為ij)表示從直方圖1的第i個(gè)元素表示的位置到直方圖2第j個(gè)元素表示的位置之間的距離忌栅。
lowerBound
可以同時(shí)作為輸入和輸出參數(shù)车酣。作為輸出參數(shù),它表示兩個(gè)直方圖的重心(Center of Mass)距離的下界索绪。為了計(jì)算這個(gè)下屆湖员,必須使用標(biāo)準(zhǔn)的距離度量衡,即參數(shù)distType不能設(shè)置為cv::DIST_USER
瑞驱,并且兩個(gè)直方圖的總權(quán)重必須相同(經(jīng)過標(biāo)準(zhǔn)化處理的兩個(gè)直方圖總權(quán)重就是相同的娘摔,都為1)。做為輸入?yún)?shù)唤反,它必須被賦予有意義的值凳寺,即如果兩個(gè)直方圖的重心距離低于等于該值才會(huì)計(jì)算EMD距離,這種機(jī)制很有用彤侍,因?yàn)橛?jì)算重心距離會(huì)比計(jì)算EMD距離快很多肠缨,如果重心距離都已經(jīng)大于指定的值了,則我們認(rèn)為兩個(gè)直方圖已經(jīng)不匹配了盏阶,就沒有必要再計(jì)算其準(zhǔn)確的EMD距離绑谣。如果想跳過此邏輯,只需將參數(shù)lowerBound
的值設(shè)置為0即可兄裂。
參數(shù)flow
是一個(gè)可選的sz1??sz2矩陣钞楼,其中的元素E(i, j)表示直方圖1中第i個(gè)元素流向直方圖2中第j個(gè)元素的質(zhì)量,其實(shí)該參數(shù)就是表示數(shù)據(jù)流動(dòng)的過程蒸眠。
示例程序EMD使用已經(jīng)介紹過的5種距離比較了前文室內(nèi)手掌圖片上半幅圖與下半幅圖漾橙,以及與其他光照條件下的整幅手掌圖,以及一副完全無關(guān)圖像的顏色直方圖的相似程度楞卡。程序運(yùn)行后得到的原始圖像及其顏色直方圖表示如下霜运。
距離比較結(jié)果如下表脾歇。這里在進(jìn)行前四種方法比較直方圖時(shí)標(biāo)準(zhǔn)化的區(qū)域?yàn)?到255,使用EMD比較直方圖距離時(shí)的標(biāo)準(zhǔn)化方式是直方圖的所有元素?cái)?shù)據(jù)權(quán)重和為1淘捡。這里和前文原書中給出的比較結(jié)果不同藕各,推測(cè)應(yīng)當(dāng)是標(biāo)準(zhǔn)化策略以及圖像選取的范圍有差異。
比較目標(biāo) | 相關(guān)性法 | Chi-Square方法 | 交集法 | 巴氏距離 | EMD距離 |
---|---|---|---|---|---|
下半幅室內(nèi)圖片 | 0.501425 | 1648.11 | 301.039 | 0.55207 | 1.52593 |
室外陰影圖片 | 0.459135 | 244318 | 633.146 | 0.449522 | 2.73921 |
室外明亮圖片 | 0.676008 | 35634.4 | 756.639 | 0.408152 | 1.80114 |
完全無關(guān)的水果圖片 | 0.0938923 | 3380830 | 389.745 | 0.689889 | 2.8382 |
2.3.2 反向投影
反向投影(Back Projection)可以判斷像素集合與指定的直方圖表示的顏色分布的匹配程度焦除。例如假定我們有膚色的顏色直方圖激况,可以通過該技術(shù)尋找圖像中和膚色匹配的區(qū)域。從統(tǒng)計(jì)學(xué)的角度看膘魄,如果將已知的直方圖分布看作是在特定目標(biāo)上的顏色分布的先驗(yàn)概率分布(Prior Probability Distribution)乌逐,則反向投影就是計(jì)算圖像中的任意區(qū)域符合該先驗(yàn)概率分布的概率,也就是說屬于目標(biāo)物體的概率创葡。實(shí)際上反向投影就是計(jì)算每個(gè)像素在直方圖分布對(duì)應(yīng)分組中的計(jì)數(shù)值浙踢。
OpenCV提供了兩個(gè)函數(shù)分別用于處理密集矩陣和稀疏矩陣,其函數(shù)原型如下灿渴。直方圖中只包含了維度洛波、維度分組及每個(gè)分組的統(tǒng)計(jì)結(jié)果,但是不包含分組的區(qū)間骚露,所以需要額外的參數(shù)ranges
確定蹬挤。
// images:反向投影的目標(biāo)查詢數(shù)組矩陣,單通道8U或者三通道32F棘幸,所以矩陣大小類型必須相同焰扳,
// 通道數(shù)可以不同
// nimages:images中包含矩陣的個(gè)數(shù)
// channels:需要比較的通道索引,索引的編號(hào)規(guī)則和函數(shù)cv::calcHist()相同够话,該數(shù)組的格式和
// 參數(shù)hist表示的直方圖的維度相同
// hist:比較的直方圖矩陣
// backProject:反向投影的結(jié)果蓝翰,和參數(shù)images中的矩陣大小和數(shù)據(jù)類型相同,單通道
// ranges:直方圖每個(gè)維度的分組策略女嘲,和函數(shù)cv::calcHist()相同
// scale:輸出結(jié)果的縮放系數(shù)畜份,通常反向投影得到的數(shù)據(jù)值都較低,有時(shí)適當(dāng)放大結(jié)果可視化效果更好
// uniform:分組策略是否為均勻分布欣尼,和函數(shù)cv::calcHist()相同
void cv::calcBackProject(const cv::Mat* images, int nimages, const int* channels,
cv::InputArray hist, cv::OutputArray backProject,
const float** ranges,
double scale = 1, bool uniform = true);
void cv::calcBackProject(const cv::Mat* images, int nimages, const int* channels,
const cv::SparseMat& hist, cv::OutputArray backProject,
const float** ranges,
double scale = 1, bool uniform = true);
void cv::calcBackProject(cv::InputArrayOfArrays images,
const vector<int>& channels,
cv::InputArray hist, cv::OutputArray backProject,
const vector<float>& ranges,
double scale = 1, bool uniform = true);
如果調(diào)用該函數(shù)使用的直方圖是經(jīng)過標(biāo)準(zhǔn)化處理的爆雹,則反向投影的結(jié)果就是某個(gè)像素是直方圖表示的分布中的一部分的概率。即對(duì)于前文講到的膚色示例而言愕鼓,如果C是某個(gè)像素的顏色值钙态,而F表示該像素屬于皮膚的概率。則可以通過p(C|F)表示在皮膚中像素C出現(xiàn)的概率菇晃,這和p(F|C)表示的含義不同册倒,后者表示顏色C屬于皮膚的概率,也是反向投影在某種程度上表示的含義磺送。但是這兩個(gè)概率可以通過如下貝葉斯公式計(jì)算驻子,其中p(F)表示場(chǎng)景中指定目標(biāo)的累計(jì)概率灿意,p(C)表示場(chǎng)景中顏色C的累計(jì)概率,當(dāng)然對(duì)于直方圖而言這是顏色區(qū)間的累計(jì)概率崇呵。
下圖使用膚色直方圖計(jì)算了一副圖像中像素顏色屬于皮膚的概率缤剧,直方圖計(jì)算的是HSV顏色空間中的色度H和飽和度S。其中左上角圖片為皮膚模型的顏色直方圖域慷,可以用于計(jì)算上述公式中的p(C|F)荒辕,右上側(cè)圖像為測(cè)試圖像,左下圖為測(cè)試圖像的顏色直方圖犹褒,右下角圖像是根據(jù)該顏色直方圖進(jìn)行反向投影得到的結(jié)果抵窒,也就是上述公式中的p(F|C)。需要注意這里進(jìn)行反向投影時(shí)傳入的直方圖為皮膚模型的顏色直方圖化漆。
通常情況下你可以通過如下三步來尋找感興趣的區(qū)域估脆。首先創(chuàng)建需要尋找的目標(biāo)或者區(qū)域的直方圖钦奋。然后使用該直方圖計(jì)算待查詢圖像的后向投影座云,其中較高的值表示和感興趣區(qū)域匹配程度更高。最后根據(jù)后向投影中的高值取原圖的部分區(qū)域付材,再計(jì)算其直方圖和目標(biāo)區(qū)域的直方圖進(jìn)行比較朦拖。
需要注意的是如果后向投影圖的基本數(shù)據(jù)類型為cv::U8
,不要對(duì)直方圖進(jìn)行標(biāo)準(zhǔn)化厌衔,對(duì)于已經(jīng)標(biāo)準(zhǔn)化的直方圖要進(jìn)行放大處理璧帝,因?yàn)闃?biāo)準(zhǔn)后后的直方圖中的最大值為1,在cv::U8
數(shù)據(jù)類型中除了1都會(huì)被轉(zhuǎn)換為0富寿。
3 模版匹配
模版匹配不依賴于直方圖睬隶,相反其實(shí)現(xiàn)方式是通過一個(gè)圖像塊在輸入圖像上滑動(dòng),匹配的方法下文介紹页徐,一個(gè)模版匹配的示例如下圖苏潜。其中左上角圖片表示了待匹配目標(biāo)HSV顏色模型下的HS直方圖。而右側(cè)圖像是需要查詢的圖片变勇,其中白色方塊表示滑動(dòng)圖像塊的大小恤左,這里需要查找的是咖啡杯。左下圖為待查詢圖像的HS直方圖搀绣,而右下圖為測(cè)試圖像應(yīng)用模版匹配的結(jié)果飞袋,可以明顯看出咖啡杯已經(jīng)被挑選出來。
另外一個(gè)模版匹配的場(chǎng)景如下圖链患,這里用的是一副包含人臉的圖像滑塊巧鸭,通過在輸入圖像滑動(dòng)該滑塊可以在整個(gè)圖像中尋找到最到最好的匹配效果來確定人臉的存在。
OpenCV提供的模版匹配函數(shù)如下麻捻。
// image:待查詢的圖片纲仍,8U或者32F的灰度或者彩色圖像览闰,大小為W??H
// templ:圖像滑塊,大小為w??h
// result:模版匹配的結(jié)果巷折,單通道压鉴,數(shù)據(jù)類型為32F,大小為W-w+1??H-h+1
// method:模版匹配使用的方法锻拘,下文介紹
void cv::matchTemplate(cv::InputArray image, cv::InputArray templ,
cv::OutputArray result, int method);
參數(shù)method
指定了模版匹配的方法油吭,可選值如下,在下面的公式中將使用I表示輸入圖像署拟,T表示圖像滑塊婉宰,R表示模版匹配的結(jié)果。每一種方法都有一個(gè)標(biāo)準(zhǔn)化版本推穷,因?yàn)椴煌庹窄h(huán)境下的數(shù)據(jù)標(biāo)準(zhǔn)化后系數(shù)相同心包,可以排除光照條件帶來的干擾。
3.1 方差匹配方法
該方法取值為cv::TM_SQDIFF
馒铃,比較的是兩個(gè)矩陣的方差蟹腾,得到的值越小表示越相似,其公式如下区宇。
3.2 歸一化方差匹配
歸一化方差匹配的取值為cv::TM_SQDIFF_NORMED
娃殖,完全匹配情況下計(jì)算得到的值為0,其公式如下议谷。
3.3 相關(guān)性匹配
相關(guān)性匹配方法的取值為cv::TM_CCORR
,該方法以乘法的方式匹配模版波附,越匹配的矩陣計(jì)算結(jié)果越大狈究,完全不匹配的矩陣計(jì)算結(jié)果為0,其公式如下拯勉。
3.4 歸一化相關(guān)性匹配
歸一化相關(guān)性匹配的取值為cv::TM_CCORR_NORMED
妥曲,極度不匹配的矩陣計(jì)算結(jié)果趨于0,其公式如下波丰。
3.5 相關(guān)性系數(shù)匹配
相關(guān)性系數(shù)匹配的取值為cv::TM_CCOEFF
掰烟,它比較的是兩個(gè)矩陣中元素和矩陣本身均值差值的相關(guān)性爽蝴,完美匹配矩陣的計(jì)算結(jié)果為1,完全不匹配矩陣的計(jì)算結(jié)果為-1纫骑,0表示無相關(guān)性蝎亚,其公式如下。
3.6 歸一化相關(guān)系數(shù)匹配
歸一化相關(guān)系數(shù)匹配的取值為cv::TM_CCOEFF_NORMED
先馆,較好的匹配矩陣計(jì)算結(jié)果是較大正值发框,而匹配程度很差的矩陣計(jì)算結(jié)果是較大的負(fù)值,其公式如下煤墙,其中T’和I’計(jì)算方式通相關(guān)性系數(shù)匹配方法梅惯。
使用相對(duì)復(fù)雜的匹配方法(相關(guān)性系數(shù)匹配)替換相對(duì)簡單的匹配方法(如方差匹配)將會(huì)得到更準(zhǔn)確的匹配結(jié)果宪拥,但是會(huì)花費(fèi)更多的計(jì)算成本。最后嘗試所有的方法铣减,并在最終的應(yīng)用程序中權(quán)衡計(jì)算成本和結(jié)果精度選擇最合適的方法她君。需要注意的是除了方差匹配和歸一化方差匹配對(duì)于越好的匹配得到的計(jì)算結(jié)果越小,其他方法都是相反的葫哗。
當(dāng)模版匹配完成后犁河,可以通過函數(shù)cv::minMaxLoc()
或者cv::minMaxIdx()
尋找最佳匹配結(jié)果的位置。一個(gè)好的匹配模式應(yīng)當(dāng)是一個(gè)局部區(qū)域的像素點(diǎn)都取得較好的匹配結(jié)果魄梯,因?yàn)槟0嬖谙袼攸c(diǎn)臨域內(nèi)滑動(dòng)時(shí)桨螺,模版覆蓋區(qū)域內(nèi)像素點(diǎn)輕微改變通常不會(huì)使得匹配結(jié)果相差太多。同時(shí)為了避免圖像噪聲引起的異常高匹配酿秸,可以輕微的平滑模版匹配得到的結(jié)果再尋找極值灭翔。在這種場(chǎng)景下,前文介紹的圖像形態(tài)學(xué)的方法能夠發(fā)揮很好的作用辣苏。
示例程序Template實(shí)現(xiàn)了不同方法的模版匹配肝箱,其核心代碼如下。
int main(int argc, const char * argv[]) {
// 讀取匹配模版圖像
cv::Mat templ = cv::imread(argv[1], 1);
// 讀取待查找圖像
cv::Mat src = cv::imread(argv[2], 1);
// 使用6種不同的方法執(zhí)行模版匹配操作
cv::Mat ftmp[6];
for (int i = 0; i < 6; ++i) {
cv::matchTemplate( src, templ, ftmp[i], i);
cv::normalize(ftmp[i],ftmp[i],1,0,cv::NORM_MINMAX);
}
return 0;
}
示例程序使用的模版圖像和待查找的圖像如下圖稀蟋,其中左側(cè)圖像是使用的模版滑塊煌张,右側(cè)圖像是待查找的圖像。
6種不同的模版匹配方法得到的效果圖如下退客,需要注意方差法和歸一化方差法的最佳匹配計(jì)算結(jié)果為0骏融,其他方法則剛好相反。因此在下圖中第一列的兩幅圖片中的黑色區(qū)域?yàn)槠ヅ浜玫膮^(qū)域萌狂,而右側(cè)兩列圖片中亮色區(qū)域?yàn)槠ヅ涑潭容^高的區(qū)域档玻。
4 小結(jié)
本章介紹了在OpenCV中如何使用矩陣和稀疏矩陣對(duì)象表示直方圖,在實(shí)際應(yīng)用中直方圖通常表示為概率密度函數(shù)茫藏,即其內(nèi)部的每個(gè)元素表示其對(duì)應(yīng)分組在整個(gè)隨機(jī)變量分布中的概率误趴。此外還介紹了如何使用直方圖識(shí)別物體和興趣區(qū)域。另外本章也介紹了直方圖的基本操作务傲,當(dāng)直方圖被看作是概率密度函數(shù)時(shí)這些操作通常能發(fā)揮較大的作用凉当。例如直方圖的標(biāo)準(zhǔn)化,以及比較直方圖售葡。在本章的最后看杭,我們討論了模版匹配,該技術(shù)能很好的處理高度結(jié)構(gòu)化的圖片天通。