当前位置:网站首页>OpenCV每日函数 直方图相关(3)
OpenCV每日函数 直方图相关(3)
2022-06-22 08:59:00 【坐望云起】
一、EMD函数
1、函数原型
该函数计算推土机距离和/或两个加权点配置之间距离的下边界。 应用之一是用于图像检索的多维直方图比较。 EMD 是一个使用单纯形算法的一些修改来解决的运输问题,因此在最坏的情况下复杂度是指数级的,但平均而言它要快得多。 在真实度量的情况下,可以更快地计算下边界(使用线性时间算法),它可以用来粗略地确定两个签名是否足够远,以至于它们不能与同一个对象相关。
float cv::EMD (InputArray signature1, InputArray signature2, int distType, InputArray cost=noArray(), float *lowerBound=0, OutputArray flow=noArray())2、参数详解
| signature1 | 第一个签名,一个 size1×dims+1 浮点矩阵。 每行存储点权重,后跟点坐标。 如果使用用户定义的成本矩阵,则允许该矩阵具有单列(仅权重)。 权重必须是非负的并且至少有一个非零值。 |
| signature2 | 与 signature1 格式相同的第二个签名,尽管行数可能不同。 总重量可能不同。 在这种情况下,一个额外的“虚拟”点被添加到签名 1 或签名 2。 权重必须是非负的并且至少有一个非零值。 |
| distType | 使用的公制。 请参阅距离类型。 |
| cost | 用户定义的 size1×size2 成本矩阵。 此外,如果使用成本矩阵,则无法计算下边界 lowerBound 因为它需要一个度量函数。 |
| lowerBound | 可选输入/输出参数:两个签名之间距离的下边界,即质心之间的距离。 如果使用用户定义的成本矩阵,点配置的总权重不相等,或者签名仅由权重组成(签名矩阵只有一列),则可能无法计算下边界。 您必须**初始化 *lowerBound 。 如果计算的质心之间的距离大于或等于 *lowerBound(这意味着签名足够远),则该函数不计算 EMD。 在任何情况下,*lowerBound 都设置为返回时计算的质心之间的距离。 因此,如果要计算质心和 EMD 之间的距离,*lowerBound 应设置为 0。 |
| flow | 得到的 size1×size2 流矩阵: 是从 i-th 签名 1 的点到 j-th 签名 2 的点的流。 |
3、OpenCV源码
(1)源码路径
opencv\modules\imgproc\src\emd.cpp(2)源码代码
CV_IMPL float cvCalcEMD2( const CvArr* signature_arr1,
const CvArr* signature_arr2,
int dist_type,
CvDistanceFunction dist_func,
const CvArr* cost_matrix,
CvArr* flow_matrix,
float *lower_bound,
void *user_param )
{
cv::AutoBuffer<char> local_buf;
CvEMDState state;
float emd = 0;
memset( &state, 0, sizeof(state));
double total_cost = 0;
int result = 0;
float eps, min_delta;
CvNode2D *xp = 0;
CvMat sign_stub1, *signature1 = (CvMat*)signature_arr1;
CvMat sign_stub2, *signature2 = (CvMat*)signature_arr2;
CvMat cost_stub, *cost = &cost_stub;
CvMat flow_stub, *flow = (CvMat*)flow_matrix;
int dims, size1, size2;
signature1 = cvGetMat( signature1, &sign_stub1 );
signature2 = cvGetMat( signature2, &sign_stub2 );
if( signature1->cols != signature2->cols )
CV_Error( CV_StsUnmatchedSizes, "The arrays must have equal number of columns (which is number of dimensions but 1)" );
dims = signature1->cols - 1;
size1 = signature1->rows;
size2 = signature2->rows;
if( !CV_ARE_TYPES_EQ( signature1, signature2 ))
CV_Error( CV_StsUnmatchedFormats, "The array must have equal types" );
if( CV_MAT_TYPE( signature1->type ) != CV_32FC1 )
CV_Error( CV_StsUnsupportedFormat, "The signatures must be 32fC1" );
if( flow )
{
flow = cvGetMat( flow, &flow_stub );
if( flow->rows != size1 || flow->cols != size2 )
CV_Error( CV_StsUnmatchedSizes,
"The flow matrix size does not match to the signatures' sizes" );
if( CV_MAT_TYPE( flow->type ) != CV_32FC1 )
CV_Error( CV_StsUnsupportedFormat, "The flow matrix must be 32fC1" );
}
cost->data.fl = 0;
cost->step = 0;
if( dist_type < 0 )
{
if( cost_matrix )
{
if( dist_func )
CV_Error( CV_StsBadArg,
"Only one of cost matrix or distance function should be non-NULL in case of user-defined distance" );
if( lower_bound )
CV_Error( CV_StsBadArg,
"The lower boundary can not be calculated if the cost matrix is used" );
cost = cvGetMat( cost_matrix, &cost_stub );
if( cost->rows != size1 || cost->cols != size2 )
CV_Error( CV_StsUnmatchedSizes,
"The cost matrix size does not match to the signatures' sizes" );
if( CV_MAT_TYPE( cost->type ) != CV_32FC1 )
CV_Error( CV_StsUnsupportedFormat, "The cost matrix must be 32fC1" );
}
else if( !dist_func )
CV_Error( CV_StsNullPtr, "In case of user-defined distance Distance function is undefined" );
}
else
{
if( dims == 0 )
CV_Error( CV_StsBadSize,
"Number of dimensions can be 0 only if a user-defined metric is used" );
user_param = (void *) (size_t)dims;
switch (dist_type)
{
case CV_DIST_L1:
dist_func = icvDistL1;
break;
case CV_DIST_L2:
dist_func = icvDistL2;
break;
case CV_DIST_C:
dist_func = icvDistC;
break;
default:
CV_Error( CV_StsBadFlag, "Bad or unsupported metric type" );
}
}
result = icvInitEMD( signature1->data.fl, size1,
signature2->data.fl, size2,
dims, dist_func, user_param,
cost->data.fl, cost->step,
&state, lower_bound, local_buf );
if( result > 0 && lower_bound )
{
emd = *lower_bound;
return emd;
}
eps = CV_EMD_EPS * state.max_cost;
/* if ssize = 1 or dsize = 1 then we are done, else ... */
if( state.ssize > 1 && state.dsize > 1 )
{
int itr;
for( itr = 1; itr < MAX_ITERATIONS; itr++ )
{
/* find basic variables */
result = icvFindBasicVariables( state.cost, state.is_x,
state.u, state.v, state.ssize, state.dsize );
if( result < 0 )
break;
/* check for optimality */
min_delta = icvIsOptimal( state.cost, state.is_x,
state.u, state.v,
state.ssize, state.dsize, state.enter_x );
if( min_delta == CV_EMD_INF )
CV_Error( CV_StsNoConv, "" );
/* if no negative deltamin, we found the optimal solution */
if( min_delta >= -eps )
break;
/* improve solution */
if(!icvNewSolution( &state ))
CV_Error( CV_StsNoConv, "" );
}
}
/* compute the total flow */
for( xp = state._x; xp < state.end_x; xp++ )
{
float val = xp->val;
int i = xp->i;
int j = xp->j;
if( xp == state.enter_x )
continue;
int ci = state.idx1[i];
int cj = state.idx2[j];
if( ci >= 0 && cj >= 0 )
{
total_cost += (double)val * state.cost[i][j];
if( flow )
((float*)(flow->data.ptr + flow->step*ci))[cj] = val;
}
}
emd = (float) (total_cost / state.weight);
return emd;
}
float cv::EMD( InputArray _signature1, InputArray _signature2,
int distType, InputArray _cost,
float* lowerBound, OutputArray _flow )
{
CV_INSTRUMENT_REGION();
Mat signature1 = _signature1.getMat(), signature2 = _signature2.getMat();
Mat cost = _cost.getMat(), flow;
CvMat _csignature1 = cvMat(signature1);
CvMat _csignature2 = cvMat(signature2);
CvMat _ccost = cvMat(cost), _cflow;
if( _flow.needed() )
{
_flow.create(signature1.rows, signature2.rows, CV_32F);
flow = _flow.getMat();
flow = Scalar::all(0);
_cflow = cvMat(flow);
}
return cvCalcEMD2( &_csignature1, &_csignature2, distType, 0, cost.empty() ? 0 : &_ccost,
_flow.needed() ? &_cflow : 0, lowerBound, 0 );
}
float cv::wrapperEMD(InputArray _signature1, InputArray _signature2,
int distType, InputArray _cost,
Ptr<float> lowerBound, OutputArray _flow)
{
return EMD(_signature1, _signature2, distType, _cost, lowerBound.get(), _flow);
}4、参考代码
EMD(earth mover distance)方法是比较图像相似度的一种很好的方法。但是处理时间很慢。为了使用 EMD 比较,我们应该制作签名值。EMD 方法比较两个签名值。
首先,我们准备 2 张图像的直方图。并将直方图的值转换为签名。



#include <iostream>
#include <vector>
#include <stdio.h>
#include <opencv2\opencv.hpp>
#ifdef _DEBUG
#pragma comment(lib, "opencv_core249d.lib")
#pragma comment(lib, "opencv_imgproc249d.lib") //MAT processing
#pragma comment(lib, "opencv_highgui249d.lib")
#else
#pragma comment(lib, "opencv_core249.lib")
#pragma comment(lib, "opencv_imgproc249.lib")
#pragma comment(lib, "opencv_highgui249.lib")
#endif
using namespace cv;
using namespace std;
int main()
{
//read 2 images for histogram comparing
///
Mat imgA, imgB;
imgA = imread(".\\image1.jpg");
imgB = imread(".\\image2.jpg");
imshow("img1", imgA);
imshow("img2", imgB);
//variables preparing
///
int hbins = 30, sbins = 32;
int channels[] = {0, 1};
int histSize[] = {hbins, sbins};
float hranges[] = { 0, 180 };
float sranges[] = { 0, 255 };
const float* ranges[] = { hranges, sranges};
Mat patch_HSV;
MatND HistA, HistB;
//cal histogram & normalization
///
cvtColor(imgA, patch_HSV, CV_BGR2HSV);
calcHist( &patch_HSV, 1, channels, Mat(), // do not use mask
HistA, 2, histSize, ranges,
true, // the histogram is uniform
false );
normalize(HistA, HistA, 0, 1, CV_MINMAX);
cvtColor(imgB, patch_HSV, CV_BGR2HSV);
calcHist( &patch_HSV, 1, channels, Mat(),// do not use mask
HistB, 2, histSize, ranges,
true, // the histogram is uniform
false );
normalize(HistB, HistB, 0, 1, CV_MINMAX);
//compare histogram
///
int numrows = hbins * sbins;
//make signature
Mat sig1(numrows, 3, CV_32FC1);
Mat sig2(numrows, 3, CV_32FC1);
//fill value into signature
for(int h=0; h< hbins; h++)
{
for(int s=0; s< sbins; ++s)
{
float binval = HistA.at< float>(h,s);
sig1.at<float>( h*sbins + s, 0) = binval;
sig1.at<float>( h*sbins + s, 1) = h;
sig1.at<float>( h*sbins + s, 2) = s;
binval = HistB.at< float>(h,s);
sig2.at<float>( h*sbins + s, 0) = binval;
sig2.at<float>( h*sbins + s, 1) = h;
sig2.at<float>( h*sbins + s, 2) = s;
}
}
//compare similarity of 2images using emd.
float emd = cv::EMD(sig1, sig2, CV_DIST_L2); //emd 0 is best matching.
printf("similarity %5.5f %%\n", (1-emd)*100 );
waitKey(0);
return 0;
} 边栏推荐
- Continuous training on tensorflow breakpoint (principle + code explanation)
- 版本问题导致“无法定位程序输入点OPENSSL_sk_new_reserve于动态链接库C:\Users...\libssl-1_1-x64.dll”
- What is defi and what mode is defi?
- 一文彻底搞懂My SQL索引知识点
- DOM编程
- Why can MySQL indexes improve query efficiency so much?
- Luogu p4292 [wc2010] reconstruction plan
- Basic knowledge of DDL operation database and operation table and explanation of use format
- Summary of microexpression data set (full)
- 开发报错记录
猜你喜欢

np.arange与np.linspace细微区别(数据溢出问题)

模板引擎,让交互变得优雅

【node】快收下爬虫,我们不再为数据发愁

Xshell远程服务器tensorboard/visdom的本地可视化方法【亲测一步有效】

Why can MySQL indexes improve query efficiency so much?

Solidity from introduction to practice (V)

The third-party libraries commonly used in golang development are not the most complete, but more complete

13 proxy mode

希望越来越多的女性从事科技工作

Installation and use of Jupiter notebook
随机推荐
CF1267G Game Relics
IP address (IPv4)
10 decoration mode
Flask博客实战 - 实现文章列表页及详情页
12 享元模式
Flask博客实战 - 集成富文本编辑器Quill
面试突击59:一个表中可以有多个自增列吗?
Solidity from introduction to practice (IV)
07 适配器模式
Instanceinforeplicator class of Eureka (service registration assistant)
Webrtc series - iceconfig and stunping failure handling for network transmission
Daily learning-01
[qnx hypervisor 2.2 user manual]5.6 close guest
Win10 add group policy command
09 组合模式
Spark yard memory resource calculation and analysis (Reference) -- optimized configuration of executor cores, nums and memory
Deeply analyze the usage of final keyword
深入解析final关键字的用法
Flask blog practice - realize the classified management of blogs
[target detection] | detection error mechanism why object detectors fail: investigating the influence of the dataset
是从 i-th 签名 1 的点到 j-th 签名 2 的点的流。