一、根据网上资料整理了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