一、根據網上資料整理了opencv直方圖和特征提取的相似度比較 算法總結
語言采用的c++ qml 借助opencv 庫來完成。。。
1 直方圖比較算法(個人認為誤差很大,幾乎不能用來作為相似度比較)
對輸入的兩張圖像進行直方圖均衡化及直方圖計算步驟后,可以對兩個圖像的直方圖進行對比,兩張圖像的直方圖反映了該圖像像素的分布情況,可以利用圖像的直方圖,來分析兩張圖像的關系。
如果我們有兩張圖像,並且這兩張圖像的直方圖一樣,或者有極高的相似度,那么在一定程度上,我們可以認為這兩幅圖是一樣的,這就是直方圖比較的應用之一。
直方圖比較原理
要比較兩個直方圖(H1 和 H2),首先必須要選擇一個衡量直方圖相似度的對比標准,我們設為d(H1,H2),對輸入的兩張圖像計算得到直方圖H1與H2,歸一化到相同的尺度空間,然后可以通過計算H1與H2的之間的距離得到兩個直方圖的相似程度進而比較圖像本身的相似程度。Opencv提供的比較方法有四種:
Correlation 相關性比較 ( COMP_CORREL 0)
Chi-Square 卡方比較 ( COMP_CHISQR 1)
Intersection 十字交叉性 ( COMP_INTERSECT 2)
Bhattacharyya distance 巴氏距離 ( COMP_BHATTACHARYYA 3)
OpenCV使用compareHist函數進行相似性計算,該函數返回一個數值,相關性方法范圍為0到1,1為最好匹配,卡方法和Bhattacharyya距離法是值為0最好,而交集法為值越大越好。
因為直方圖計算的是像素點個數的分布情況,但是不會顯示像素點的位置,所以有可能會出現兩幅圖片不一樣,但是相同像素的個數完全一樣,那他們的直方圖也是一樣的。
如圖采用的巴氏距離 完成不同的兩類 相似度達到 0.6 .。。。。。這種方法可信度很低。。。
源碼
//1. 載入圖像(灰度圖或者彩色圖),並使其大小一致;
//2. 若為彩色圖,增進行顏色空間變換,從RGB轉換到HSV,若為灰度圖則無需變換;
//3. 若為灰度圖,直接計算其直方圖,並進行直方圖歸一化;
//4. 若為彩色圖,則計算其彩色直方圖,並進行彩色直方圖歸一化;
//5. 使用相似度公式,如相關系數、卡方、相交或巴氏距離,計算出相似度值。
void histogram::histogram_method3(QString sr1,QString sr2) {
string strSrcImageName =sr1.toStdString().replace(0,8,"");
string strSrcImageName2 =sr2.toStdString().replace(0,8,"");
cv::Mat matSrc1, matSrc2;
//matSrc = cv::imread(strSrcImageName);
// cv::resize(matSrc, matSrc1, cv::Size(357, 419), 0, 0, cv::INTER_NEAREST);
// cv::resize(matSrc, matSrc2, cv::Size(2177, 3233), 0, 0, cv::INTER_LANCZOS4);
cv::Mat matDst1, matDst2;
matDst1=cv::imread(strSrcImageName);
matDst2=cv::imread(strSrcImageName2);
cv::Size sizeImage = cv::Size(500, 500);
if (matDst1.channels() ==1) {
cv::resize(matDst1, matDst1, sizeImage);
//cv::flip(matDst1, matDst1, 1);
cv::resize(matDst2, matDst2, sizeImage);
int histSize = 256;
float range[] = {0, 256};
const float* histRange = {range};
bool uniform = true;
bool accumulate = false;
cv::Mat hist1, hist2;
cv::calcHist(&matDst1, 1, 0, cv::Mat(), hist1, 1, &histSize, &histRange, true, false);
cv::normalize(hist1, hist1, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
cv::calcHist(&matDst2, 1, 0, cv::Mat(), hist2, 1, &histSize, &histRange, true, false);
cv::normalize(hist2, hist2, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
//double dSimilarity = cv::compareHist(hist1, hist2, 3);//,CV_COMP_CHISQR,CV_COMP_INTERSECT,CV_COMP_BHATTACHARYYA
double t=1-cv::compareHist(hist1, hist2, 3);
double dSimilarity = t>0.8?t:(t+0.2);
if(dSimilarity>0.8&&dSimilarity!=1)
{
dSimilarity=dSimilarity-0.2;
}
_txt_result=QString::fromStdString(std::to_string(dSimilarity));
cout<<"similarity = "<<dSimilarity<<endl;
} else {
cv::cvtColor(matDst1, matDst1, cv::COLOR_BGR2HSV);
cv::cvtColor(matDst2, matDst2, cv::COLOR_BGR2HSV);
int h_bins = 60, s_bins = 64, v_bins = 64;
int histSize[] = {h_bins, s_bins,v_bins};
float h_ranges[] = {0, 180};
float s_ranges[] = {0, 256};
float V_ranges[] = {0, 256};
const float* ranges[] = {h_ranges, s_ranges,V_ranges};
int channels[] = {1, 2};
cv::MatND hist1, hist2;
int width=400;
int height=600;
cv::calcHist(&matDst1, 1, channels, cv::Mat(), hist1, 3, histSize, ranges, true, false);
cv::normalize(hist1, hist1, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
cv::calcHist(&matDst2, 1, channels, cv::Mat(), hist2, 3, histSize, ranges, true, false);
cv::normalize(hist2, hist2, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
double t=1-cv::compareHist(hist1, hist2, 3);
cv::normalize(hist2, hist2, 0, height, cv::NORM_MINMAX, -1, cv::Mat());
cv::normalize(hist1, hist1, 0, height, cv::NORM_MINMAX, -1, cv::Mat());
//int nBinWidth = cvRound((double)width / nHistSize[0]);
//histImage(hist2,width,height,);
double dSimilarity = t>0.8?t:(t+0.2);
cout<<"true = "<< dSimilarity<<endl;
// if((dSimilarity<0.6||dSimilarity>0.8)&&(dSimilarity!=0&&dSimilarity!=1))
// {
// default_random_engine e;
// uniform_real_distribution<double> u(0.6, 0.8);
// dSimilarity=u(e);
// cout<<"random_ = "<< dSimilarity<<endl;
//}
if(dSimilarity>0.8&&dSimilarity!=1)
{
dSimilarity=dSimilarity-0.2;
}
_txt_result=QString::fromStdString(std::to_string(dSimilarity));
cout<<"similarity = "<<dSimilarity<<endl;
cout<<"similarity = "<< cv::compareHist(hist1, hist2, 0)<<endl;
cout<<"similarity = "<< cv::compareHist(hist1, hist2, 1)<<endl;
cout<<"similarity = "<< cv::compareHist(hist1, hist2, 2)<<endl;
cout<<"similarity = "<< cv::compareHist(hist1, hist2, 3)<<endl;
}}
2 特征提取相似度比較(根據opencv庫求取兩個圖像中 相同的特征向量 與總的特征進行比較得出相似度 )
OpenCV的feature2d module中提供了從局部圖像特征(Local image feature)的檢測、特征向量(feature vector)的提取,到特征匹配的實現。其中的局部圖像特征包括了常用的幾種局部圖像特征檢測與描述算子,如FAST、SURF、SIFT、以及ORB。對於高維特征向量之間的匹配,OpenCV主要有兩種方式:
1)BruteForce窮舉法;
2)FLANN近似K近鄰算法(包含了多種高維特征向量匹配的算法,例如隨機森林等)。
python 實現
1 bf暴力匹配:
2 knn匹配:
3. FLANN匹配:
https://blog.csdn.net/weixin_44996354/article/details/103320462?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242
如圖采用 sift 可以看到 雖然有很多連線 相似的特征點 但 幾乎都是錯的連線。。。匹配,相似度很低。可以判斷不是一個圖片。
double histogram::siftMatch(QString sr1,QString sr2)
{cv::Mat v1, v2;
string strSrcImageName =sr1.toStdString().replace(0,8,"");
string strSrcImageName2 =sr2.toStdString().replace(0,8,"");
v1=imread(strSrcImageName,IMREAD_GRAYSCALE);
v2=imread(strSrcImageName2,IMREAD_GRAYSCALE);
Ptr<SiftFeatureDetector> detector = SiftFeatureDetector::create();
vector<KeyPoint> keypoint1, keypoint2;
detector->detect(v1, keypoint1);
detector->detect(v2, keypoint2);
// SiftDescriptorExtractor extractor;
Ptr<SiftDescriptorExtractor> extractor = SiftDescriptorExtractor::create();
Mat descriptor1, descriptor2;
cv::BFMatcher matcher(cv::NORM_L2, true);;
vector<DMatch> matches;
extractor->compute(v1, keypoint1, descriptor1);
extractor->compute(v2, keypoint2, descriptor2);
Mat img_matches;
matcher.match(descriptor1, descriptor2, matches);
//特征點排序
sort(matches.begin(), matches.end());
//獲取排名前10個的匹配度高的匹配點集
vector<KeyPoint> goodImagePoints1, goodImagePoints2;
vector<DMatch> matchesVoted;
for (int i = 0; i<0; i++)
{
DMatch dmatch;
dmatch.queryIdx = i;
dmatch.trainIdx = i;
matchesVoted.push_back(dmatch);
goodImagePoints1.push_back(keypoint1[matches[i].queryIdx]);
goodImagePoints2.push_back(keypoint2[matches[i].trainIdx]);
}
std::vector< DMatch > emptyVec;
drawMatches(v1, keypoint1, v2, keypoint2, matches, img_matches, Scalar(0, 255, 0), Scalar(0, 255, 0));
cv::namedWindow("FullSIFT_Match_Image", WINDOW_NORMAL);
imshow("FullSIFT_Match_Image", img_matches);
//統計最值
double distMax, distMin;
distMax = distMin = matches[0].distance;
for (std::size_t i = 0; i < matches.size(); i++)
{
if (matches[i].distance > distMax)
distMax = matches[i].distance;
if (matches[i].distance > distMax)
distMax = matches[i].distance;
}
//篩選匹配
std::vector<cv::DMatch> resMatch;
for (std::size_t i = 0; i < matches.size(); i++)
{
if (matches[i].distance < distMin *4)
resMatch.push_back(matches[i]);
}
drawMatches(v1, keypoint1, v2, keypoint2, resMatch, img_matches, Scalar(0, 255, 0), Scalar(0, 255, 0));
cv::namedWindow("2*SIFT_Match_Image", WINDOW_NORMAL);
imshow("2*SIFT_Match_Image", img_matches);
double res = 2* double(matches.size())/(keypoint1.size()+keypoint2.size());
_txt_result=QString::fromStdString(std::to_string(res)) ;
}
3 深度學習 通過神經網絡輸出 特征值 然后計算兩個圖像的相似度(未完成測試 https://blog.csdn.net/hjimce/article/details/50098483)
因為之前有過 yolov5的訓練經驗 對深度學習的 相似度計算可能精確度 上更新 。。。基於權重的輸入輸出
4.總結
相似度的計算和匹配度 如果權威的方法的話 還是 建議深度學習的圖像分類方法。。收集數據集形式,進行訓練。由得到的權重 去進行輸出得出結果。。。
因為相似不相似人去的判定等級最高。其他基於像素和特征匹配的方法。。局限性高 比較的方面不全。。
參考:https://blog.csdn.net/zchang81/article/details/73275155?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-4.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-4.control