【图像处理】OpenCV系列三十六--- EMD函数详解
1、函数原型
float EMD(InputArray signature1,
InputArray signature2,
int distType,
InputArray cost = noArray(),
float *lowerBound = 0,
OutputArray flow = noArray())
2、函数功能
计算两个加权点配置之间的“最小工作”距离;
该函数计算两个加权点配置之间的土方移动距离和/或距离的下边界;EMD是一个通过修改单纯形算法来解决的运输问题,因此在最坏的情况下,其复杂度是指数级的,但是平均来说,它要快得多;在实际度量的情况下,可以甚至更快地计算下边界(使用线性时间算法),并且可以使用它来粗略确定两个特征是否足够远以使得它们不能与相同的对象相关;
3、参数详解
-
第一个参数,InputArray signature1,第一个特征,大小size1×dims+1的浮点矩阵。每行存储点权重,后跟点坐标。如果使用用户定义的成本矩阵,则允许矩阵具有单列(仅限权重)。权重必须为非负值,并且至少有一个非零值;
-
第二个参数,InputArray signature2,第二个特征的格式与特征1相同,但行数可能不同。总权重可能不同。在这种情况下,将向特征1或特征2添加一个额外的“虚拟”点。权重必须为非负值,并且至少有一个非零值;
-
第三个参数,int distType,使用的度量方法;
具体方法如下:
(1) DIST_USER,用户自定义的距离;
(2) DIST_L1,distance = |x1-x2| + |y1-y2|;
(3) DIST_L2,简单欧氏距离;
(4) DIST_C,distance = max(|x1-x2|,|y1-y2|);
(5) DIST_L12,L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1));
(6) DIST_FAIR,distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998;
(7) DIST_WELSCH,distance = c2/2(1-exp(-(x/c)2)), c = 2.9846;
(8) DIST_HUBER,distance = |x|<c ? x^2/2 : c(|x|-c/2), c=1.345; -
第四个参数,InputArray cost = noArray(),用户定义的size1×size2成本矩阵。此外,如果使用成本矩阵,则无法计算下边界lowerBound ,因为它需要一个度量函数;
-
第五个参数,float *lowerBound = 0,可选的输入/输出参数:两个特征之间距离的下界,即质量中心之间的距离;如果使用用户定义的成本矩阵,则不能计算下边界,点配置的总权重不相等,或者如果特征仅由权重组成(特征矩阵具有单个列);你必须初始化lowerBound;
如果计算出的质心之间的距离大于或等于lowerBound(这意味着特征足够远),则该函数不计算EMD;在任何情况下,lowerBound都设置为返回时质心之间的计算距离;因此,如果要同时计算质心和EMD之间的距离,lowerBound应设置为0;
- 第六个参数,OutputArray flow = noArray(),合成size1×size2的flow矩阵;flow(i,j)是flow从signature1的第i个点到signature2的第j个点;
4、实验实例
#include<iostream>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
//直方图相似度比较
vector<Mat> src;//迭代器push_back
Mat temp = imread("lena.png", 1);
int m=temp.rows / 2;
int n = temp.cols;
//将一幅图分割为上下两部分
Mat image_cut =Mat(temp, Rect(0, 0, n, m)).clone();
Mat image_cut2 = Mat(temp, Rect(0, m, n, m)).clone();
src.push_back(image_cut);
src.push_back(image_cut2);
temp = imread("1.png", 1);
src.push_back(temp);
temp = imread("2.png", 1);
src.push_back(temp);
// 计算直方图的参数配置
vector<Mat> hsv(4), hist(4),hist_img(4);
int scale=10,histSize[] = { 8,8 }, ch[] = { 0,1 };
float h_ranges[] = { 0,180 };
float s_ranges[] = { 0,255 };
const float* ranges[] = { h_ranges,s_ranges };
for (int i = 0; i < 4 ; i++)
{
// 将BGR颜色空间转换为HSV颜色空间
cvtColor(src[i], hsv[i], COLOR_BGR2HSV);
// 计算直方图
calcHist(&hsv[i], 1, ch, noArray(),
hist[i], 2, histSize, ranges, true);
// 归一化处理
normalize(hist[i], hist[i], 0, 255, NORM_MINMAX);
// 创建需要显示的图像
hist_img[i]=Mat::zeros(histSize[0] * scale,
histSize[1] * scale, CV_8UC3);
for (int h = 0; h < histSize[0]; h++)
{
for (int s = 0; s < histSize[1]; s++)
{
// 得到像素值
float hval = hist[i].at<float>(h, s);
// 根据像素值得颜色填充像素值位置
rectangle(hist_img[i], Rect(h * scale,
s * scale, 10, 10), Scalar::all(hval), -1);
}
}
}
// 显示
imshow("0", src[0]);
imshow("1", src[1]);
imshow("2", src[2]);
imshow("3", src[3]);
imshow("hist0", hist_img[0]);
imshow("hist1", hist_img[1]);
imshow("hist2", hist_img[2]);
imshow("hist3", hist_img[3]);
for (int i = 0; i < 4; i++)
{
cout << "hist[0] vs hist[" << i <<"]"<< endl;
for (int j = 0; j < 4; j++)
{
// 比较两幅直方图的相似度
cout << "method[" << j <<"]"<<
compareHist(hist[0], hist[i], j)<<endl;
}
}
// EMD算法
vector<Mat> sig(4);
for (int i = 0; i < 4; i++)
{
vector<Vec3f> sigv;
// 归一化处理
normalize(hist[i], hist[i], 1, 0, NORM_L1);
for (int h = 0; h < histSize[0]; h++)
{
for (int s = 0; s < histSize[1]; s++)
{
float hval = hist[i].at<float>(h, s);
// 像素值不为0的点存储在sigv
if (hval != 0)
sigv.push_back(Vec3f(hval, (float)h, (float)s));
}
}
// 将图像改为单通道,保持原有的行数列数不变
sig[i] = Mat(sigv).clone().reshape(1);
// 计算EMD
if (i > 0)
cout << EMD(sig[0], sig[i], DIST_L2) << endl;
}
waitKey(0);
return 0;
}
5、实验结果
我是奕双,现在已经毕业将近两年了,从大学开始学编程,期间学习了C语言编程,C++语言编程,Win32编程,MFC编程,毕业之后进入一家图像处理相关领域的公司,掌握了用OpenCV对图像进行处理,如果大家对相关领域感兴趣的话,可以关注我,我这边会为大家进行解答哦!如果大家需要相关学习资料的话,可以私聊我哦!