若該文爲原創文章,未經允許不得轉載
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105966116
各位讀者,知識無窮而人力有窮,要麼改需求,要麼找專業人士,要麼自己研究
目錄
紅胖子(紅模仿)的博文大全:開發技術集合(包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機、軟硬結合等等)持續更新中...(點擊傳送門)
OpenCV開發專欄(點擊傳送門)
OpenCV開發筆記(五十二):紅胖子8分鐘帶你深入瞭解直方圖對比匹配(圖文並茂+淺顯易懂+程序源碼)
前言
紅胖子來也!!!
識別的方式多種多樣,其中在衆多圖像中挑選出與自己圖片匹配的圖片,或者準確說知道一張圖片a,一個圖片集合A,a在A中,使用直方圖匹配能迅速識別該圖片。
Demo
直方圖
直方圖的理解請閱讀博文:《OpenCV開發筆記(十):OpenCV圖像顏色通道分離和圖像顏色多通道混合》
直方圖對比
概述
計算兩副圖像的直方圖,然後比較得出相似度。
直方圖可選擇不同的維度,如灰度空間計算直方圖,其他色彩空間計算灰度圖,或者其他任何維度用來計算直方圖作爲比較的依據。
其計算兩張直方圖的對比度的方法有四種。
原理
計算相關性
值越大,相關度越高,最大值爲1,最小值爲0。
計算卡方
值越小,相關度越高,最大值無上界,最小值0。
計算直方圖相交
值越大,相關度越高,最大值爲9.455319,最小值爲0。
計算Bhattacharyya距離
值越小,相關度越高,最大值爲1,最小值爲0。
函數原型
double compareHist( InputArray H1,
InputArray H2,
int method );
double compareHist( const SparseMat& H1,
const SparseMat& H2,
int method );
- 參數一:InputArray類型的H1,輸入圖像直方圖1;
- 參數二:InputArray類型的H2,輸入圖像直方圖2,與H1的大小一致;
- 參數三:int類型的method,匹配的方法,如下表:
序號 |
枚舉 |
值 |
描述 |
1 |
HISTCMP_CORREL |
0 |
相關性比較 |
2 |
HISTCMP_CHISQR |
1 |
卡方比較 |
3 |
HISTCMP_INTERSECT |
2 |
十字交叉性 |
4 |
HISTCMP_BHATTACHARYYA |
3 |
巴氏距離 |
5 |
HISTCMP_HELLINGER |
3 |
等同於HISTCMP_BHATTACHARYYA |
6 |
HISTCMP_CHISQR_ALT |
4 |
替代開放:通常用於紋理比較 |
7 |
HISTCMP_KL_DIV |
5 |
KL散度 |
HSV顏色空間請查看博文《OpenCV開發筆記(六):OpenCV基礎數據結構、顏色轉換函數和顏色空間》中的“HSV顏色空間”。
Demo源碼
void OpenCVManager::testCompareHist()
{
QString fileName1 =
"E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/17.jpg";
QString fileName2 =
"E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/17.jpg";
cv::Mat srcMat = cv::imread(fileName1.toStdString());
cv::Mat srcMat2 = cv::imread(fileName2.toStdString());
int width = 300;
int height = 200;
cv::resize(srcMat, srcMat, cv::Size(width, height));
cv::resize(srcMat2, srcMat2, cv::Size(width, height));
cv::String windowName = _windowTitle.toStdString();
cvui::init(windowName);
cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2,
srcMat.rows * 4),
srcMat.type());
cv::Mat mat;
while(true)
{
// 刷新全圖黑色
windowMat = cv::Scalar(0, 0, 0);
// 原圖複製
mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, srcMat, 1.0f, 0.0f, mat);
// 原圖複製
mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, srcMat2, 1.0f, 0.0f, mat);
cv::MatND dstGrayHist1;
cv::MatND dstGrayHist2;
{
// 計算直方圖,直方圖存放,需要有東西,所以使用cv::MatND,其等於createHist
int channels[] = {0};
// 直方圖的條數
int hueBinNum = 256;
int histSize[] = {hueBinNum};
// 變化範圍
float range[] = {0, 256};
const float *ranges[] = {range};
// 灰度直方圖
cv::Mat grayMat;
cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
cv::calcHist(&grayMat, // 只有1個mat
1, // 只有1個mat
channels, // 只有1個mat的3個通道,bgr
cv::Mat(), // 不使用掩碼
dstGrayHist1, // 輸出的目標直方圖
1, // 計算直方圖的維度
histSize, // 每個維度的直方圖條數(例如灰度爲一維,多少條)
ranges, // 每個維度的範圍
true, // 直方圖是否均勻
false); // 累計標識符,false表示直方圖在配置階段會被清零
// 灰度圖顯示
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::cvtColor(grayMat, grayMat, cv::COLOR_GRAY2BGR);
cv::addWeighted(mat, 0.0f, grayMat, 1.0f, 0.0f, mat);
}
{
// 計算直方圖,直方圖存放,需要有東西,所以使用cv::MatND,其等於createHist
int channels[] = {0};
// 直方圖的條數
int hueBinNum = 256;
int histSize[] = {hueBinNum};
// 變化範圍
float range[] = {0, 256};
const float *ranges[] = {range};
// 灰度直方圖
cv::Mat grayMat;
cv::cvtColor(srcMat2, grayMat, cv::COLOR_BGR2GRAY);
cv::calcHist(&grayMat, // 只有1個mat
1, // 只有1個mat
channels, // 只有1個mat的3個通道,bgr
cv::Mat(), // 不使用掩碼
dstGrayHist2, // 輸出的目標直方圖
1, // 計算直方圖的維度
histSize, // 每個維度的直方圖條數(例如灰度爲一維,多少條)
ranges, // 每個維度的範圍
true, // 直方圖是否均勻
false); // 累計標識符,false表示直方圖在配置階段會被清零
// 灰度圖顯示
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::cvtColor(grayMat, grayMat, cv::COLOR_GRAY2BGR);
cv::addWeighted(mat, 0.0f, grayMat, 1.0f, 0.0f, mat);
}
{
// 直方圖四種比較 未歸一化
double correl, chisqr, intersect, bhattacharyya;
correl = cv::compareHist(dstGrayHist1, dstGrayHist2, CV_COMP_CORREL);
chisqr = cv::compareHist(dstGrayHist1, dstGrayHist2, CV_COMP_CHISQR);
intersect = cv::compareHist(dstGrayHist1, dstGrayHist2, CV_COMP_INTERSECT);
bhattacharyya = cv::compareHist(dstGrayHist1, dstGrayHist2, CV_COMP_BHATTACHARYYA);
cvui::printf(windowMat, width * 0 + 20, height * 2 + 0,
"===========================");
cvui::printf(windowMat, width * 0 + 20, height * 2 + 10,
"| GRAY HIST COMPARE ------");
cvui::printf(windowMat, width * 0 + 20, height * 2 + 50,
"| CV_COMP_CORREL = %f", correl);
cvui::printf(windowMat, width * 0 + 20, height * 2 + 90,
"| CV_COMP_CHISQR = %f", chisqr);
cvui::printf(windowMat, width * 0 + 20, height * 2 + 130,
"| CV_COMP_INTERSECT = %f", intersect);
cvui::printf(windowMat, width * 0 + 20, height * 2 + 170,
"| CV_COMP_BHATTACHARYYA = %f", bhattacharyya);
}
{
// 直方圖四種比較 歸一化(注意歸一化第四個參數beta爲1)
cv::normalize(dstGrayHist1, dstGrayHist1, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
cv::normalize(dstGrayHist2, dstGrayHist2, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
double correl, chisqr, intersect, bhattacharyya;
correl = cv::compareHist(dstGrayHist1, dstGrayHist2, CV_COMP_CORREL);
chisqr = cv::compareHist(dstGrayHist1, dstGrayHist2, CV_COMP_CHISQR);
intersect = cv::compareHist(dstGrayHist1, dstGrayHist2, CV_COMP_INTERSECT);
bhattacharyya = cv::compareHist(dstGrayHist1, dstGrayHist2, CV_COMP_BHATTACHARYYA);
cvui::printf(windowMat, width * 0 + 20, height * 2 + 0,
"===========================");
cvui::printf(windowMat, width * 1 + 20, height * 2 + 10,
"| GRAY HIST NORMAL COMPARE ------");
cvui::printf(windowMat, width * 1 + 20, height * 2 + 50,
"| CV_COMP_CORREL = %f", correl);
cvui::printf(windowMat, width * 1 + 20, height * 2 + 90,
"| CV_COMP_CHISQR = %f", chisqr);
cvui::printf(windowMat, width * 1 + 20, height * 2 + 130,
"| CV_COMP_INTERSECT = %f", intersect);
cvui::printf(windowMat, width * 1 + 20, height * 2 + 170,
"| CV_COMP_BHATTACHARYYA = %f", bhattacharyya);
}
cv::Mat hsvHist1;
cv::Mat hsvHist2;
{
// 計算直方圖,直方圖存放,需要有東西,所以使用cv::MatND,其等於createHist
int channels[] = {0};
// 直方圖的條數
int hBins = 50, sBins = 60;
int histSize[] = {hBins, sBins};
// 變化範圍
float hRanges[] = {0, 256}; // hue
float sRanges[] = {0, 180}; // saturation
const float *ranges[] = {hRanges, sRanges};
// hsv直方圖
cv::Mat hsvMat;
cv::cvtColor(srcMat, hsvMat, cv::COLOR_BGR2HSV);
cv::calcHist(&hsvMat, // 只有1個mat
1, // 只有1個mat
channels, // 只有1個mat的3個通道,bgr
cv::Mat(), // 不使用掩碼
hsvHist1, // 輸出的目標直方圖
1, // 計算直方圖的維度
histSize, // 每個維度的直方圖條數(例如灰度爲一維,多少條)
ranges, // 每個維度的範圍
true, // 直方圖是否均勻
false); // 累計標識符,false表示直方圖在配置階段會被清零
}
{
// 計算直方圖,直方圖存放,需要有東西,所以使用cv::MatND,其等於createHist
int channels[] = {0};
// 直方圖的條數
int hBins = 50, sBins = 60;
int histSize[] = {hBins, sBins};
// 變化範圍
float hRanges[] = {0, 256}; // hue
float sRanges[] = {0, 180}; // saturation
const float *ranges[] = {hRanges, sRanges};
// hsv直方圖
cv::Mat hsvMat;
cv::cvtColor(srcMat2, hsvMat, cv::COLOR_BGR2HSV);
cv::calcHist(&hsvMat, // 只有1個mat
1, // 只有1個mat
channels, // 只有1個mat的3個通道,bgr
cv::Mat(), // 不使用掩碼
hsvHist2, // 輸出的目標直方圖
1, // 計算直方圖的維度
histSize, // 每個維度的直方圖條數(例如灰度爲一維,多少條)
ranges, // 每個維度的範圍
true, // 直方圖是否均勻
false); // 累計標識符,false表示直方圖在配置階段會被清零
}
{
// 直方圖四種比較 未歸一化
double correl, chisqr, intersect, bhattacharyya;
correl = cv::compareHist(hsvHist1, hsvHist2, CV_COMP_CORREL);
chisqr = cv::compareHist(hsvHist1, hsvHist2, CV_COMP_CHISQR);
intersect = cv::compareHist(hsvHist1, hsvHist2, CV_COMP_INTERSECT);
bhattacharyya = cv::compareHist(hsvHist1, hsvHist2, CV_COMP_BHATTACHARYYA);
cvui::printf(windowMat, width * 0 + 20, height * 3 + 0,
"===========================");
cvui::printf(windowMat, width * 0 + 20, height * 3 + 10,
"| HSV HIST COMPARE ------");
cvui::printf(windowMat, width * 0 + 20, height * 3 + 50,
"| CV_COMP_CORREL = %f", correl);
cvui::printf(windowMat, width * 0 + 20, height * 3 + 90,
"| CV_COMP_CHISQR = %f", chisqr);
cvui::printf(windowMat, width * 0 + 20, height * 3 + 130,
"| CV_COMP_INTERSECT = %f", intersect);
cvui::printf(windowMat, width * 0 + 20, height * 3 + 170,
"| CV_COMP_BHATTACHARYYA = %f", bhattacharyya);
}
{
// 直方圖四種比較 歸一化(注意歸一化第四個參數beta爲1)
cv::normalize(dstGrayHist1, dstGrayHist1, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
cv::normalize(dstGrayHist2, dstGrayHist2, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
double correl, chisqr, intersect, bhattacharyya;
correl = cv::compareHist(hsvHist1, hsvHist2, CV_COMP_CORREL);
chisqr = cv::compareHist(hsvHist1, hsvHist2, CV_COMP_CHISQR);
intersect = cv::compareHist(hsvHist1, hsvHist2, CV_COMP_INTERSECT);
bhattacharyya = cv::compareHist(hsvHist1, hsvHist2, CV_COMP_BHATTACHARYYA);
cvui::printf(windowMat, width * 1 + 20, height * 3 + 0,
"===========================");
cvui::printf(windowMat, width * 1 + 20, height * 3 + 10,
"| HSV HIST NORMAL COMPARE ------");
cvui::printf(windowMat, width * 1 + 20, height * 3 + 50,
"| CV_COMP_CORREL = %f", correl);
cvui::printf(windowMat, width * 1 + 20, height * 3 + 90,
"| CV_COMP_CHISQR = %f", chisqr);
cvui::printf(windowMat, width * 1 + 20, height * 3 + 130,
"| CV_COMP_INTERSECT = %f", intersect);
cvui::printf(windowMat, width * 1 + 20, height * 3 + 170,
"| CV_COMP_BHATTACHARYYA = %f", bhattacharyya);
}
// 更新
cvui::update();
// 顯示
cv::imshow(windowName, windowMat);
// esc鍵退出
if(cv::waitKey(25) == 27)
{
break;
}
}
}
工程模板:對應版本號v1.47.0
對應版本號v1.47.0
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客導航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105966116