最大稳定极值区域MSER是一种不错的仿射不变提取算法,能够估计局部形状。基本原理就不概述了,作者本身已经开源,opencv也实现了相关的代码。MSER只是一个特征检测算法,如果想使用MSER匹配,那么还需要与其它描述符算法结合。但是尚未看到相关的开源代码。
OpenCV实践之MSER仿射匹配算法_AutoSleep的博客-CSDN博客MSER相关简述 极值稳定检测MSER算法是目前针对图像变形最为稳定的特征检测算法,主要采取分水岭算法来进行极值区域提取,具体细节就不多说了。大家可以参考MSER作者论文,当然也出现针对MSER算法的改进版本,分为两个方面:其一是对MSER算法进行加速,即计算效率提升;其二是针对彩色图像提出的MSCR算法,论文中述说对于彩色图像提取极值区域要优于MSER算法。但是,近两日我在OpenCV开放...https://blog.csdn.net/small_munich/article/details/80208248这一篇博客,将MSER和AffineFeature2d结合在一起使用是错误的。主要有两点:
1、MSER本身已经提供了局部形状,因此无需再使用二阶矩进行局部形状估计了。
2、AffineFeature2d本身的代码是有错误的,如果按照这篇博客的代码,会直接报错。
下面是本人关于MSER和SIFT结合的实现:
void AffineMSER_Impl::detectAndCompute(InputArray _img, InputArray mask, std::vector<KeyPoint>& keypoints, OutputArray _descriptors, bool useProvidedKeypoints)
{
Mat img = _img.getMat();
std::vector<std::vector<Point> > msers;
std::vector<Rect> bboxes;
feature2D->detectRegions(img, msers, bboxes);
float sigma = 1.6;
Mat gray, gray_fpt;
if (img.channels() == 3 || img.channels() == 4)
{
cvtColor(img, gray, COLOR_BGR2GRAY);
gray.convertTo(gray_fpt, DataType<float>::type, 1, 0);
}
else
img.convertTo(gray_fpt, DataType<float>::type, 1, 0);
float sig_diff = sqrtf(std::max(sigma * sigma - 0.5f * 0.5f, 0.01f));
GaussianBlur(gray_fpt, gray_fpt, Size(), sig_diff, sig_diff);
int ncomps = (int)msers.size();
keypoints.clear();
Size patchSize(31*sqrt(2)+2, 31*sqrt(2)+2);
Size2f blobSize(10, 10);
std::vector<cv::Mat> vNormalPatches;
const int n = MSER_ORI_HIST_BINS;
std::vector<std::vector<KeyPoint> > pkeypoints;
//截取归一化区域并计算主方向
#pragma omp parallel for
for (int i = 0; i < ncomps; i++)
{
Mat normalPatch;
KeyPoint kpt, pkpt;
Rect r = bboxes[i];
RotatedRect rect = fitEllipse(Mat(msers[i]));
float diam = std::sqrt(rect.size.height * rect.size.width);
if (diam > std::numeric_limits<float>::epsilon() && r.contains(rect.center))
{
//生成归一化Patch
extractNormalPatch(gray_fpt, normalPatch, rect, patchSize, blobSize);
//计算主方向和次主方向
int radius = cvRound(MSER_ORI_RADIUS * sigma * 1.25);
float hist[n];
float omax = calcOrientationHist(normalPatch, Point(patchSize.width / 2, patchSize.height / 2), radius, MSER_ORI_SIG_FCTR * sigma * 1.25, hist, n);
float mag_thr = (float)(omax * MSER_ORI_PEAK_RATIO);
for (int j = 0; j < n; j++)
{
int l = j > 0 ? j - 1 : n - 1;
int r2 = j < n - 1 ? j + 1 : 0;
if (hist[j] > hist[l] && hist[j] > hist[r2] && hist[j] >= mag_thr)
{
float bin = j + 0.5f * (hist[l] - hist[r2]) / (hist[l] - 2 * hist[j] + hist[r2]);
bin = bin < 0 ? n + bin : bin >= n ? bin - n : bin;
kpt.angle = 360.f - (float)((360.f / n) * bin);//此处的坐标系已经是图像坐标系下了
if (std::abs(kpt.angle - 360.f) < FLT_EPSILON)
kpt.angle = 0.f;
kpt.pt = rect.center;
kpt.size = diam;//size是直径
kpt.octave = 0;
pkpt = kpt;
pkpt.pt = Point2f((patchSize.width-1) / 2.0, (patchSize.height-1) / 2.0);
pkpt.size = sigma * 1.25;//如果是SIFT描述符,覆盖范围为31x31
Mat nPatch;
normalPatch.convertTo(nPatch, CV_8U);
#pragma omp critical
{
std::vector<KeyPoint> pk(1, pkpt);
pkeypoints.push_back(pk);
vNormalPatches.push_back(nPatch.clone());
keypoints.push_back(kpt);
}
}
}
}
}
//计算描述符
if (_descriptors.needed())
{
Ptr<SIFT> PtrSIFT = cv::SIFT::create();
int dsize = 128;
_descriptors.create((int)pkeypoints.size(), dsize, CV_32F);
Mat descriptors = _descriptors.getMat();
#pragma omp parallel for
for (int i = 0; i < pkeypoints.size(); i++)
{
std::vector<KeyPoint> pk = pkeypoints[i];
Mat normalPatch = vNormalPatches[i];
Mat tmpDesc;
PtrSIFT->compute(normalPatch, pk, tmpDesc);
tmpDesc.row(0).copyTo(descriptors.row(i));
}
}
}
如果输入的是彩色图像,那么opencv会自动调用MSCR,MSCR会合并重复提取的区域(差异性非常小的区域)。外点剔除同时采用单应矩阵和基础矩阵,阈值分别为8和6像素。匹配结果如下(牛津数据 graf 1-4):
使用默认参数,灰度图像有1K个匹配点,彩色图像只有不到100个。原因是:
1、灰度图像提取的时候,很多区域被重复提取,彩色图像基本不存在这个问题。
2、匹配过程中 ,使用的最近邻匹配,匹配点数相当于最大召回点数了。如果使用最近邻次近邻距离比,灰度图像的匹配数量也会下降很多。经测试,距离比值阈值设置为0.8时,匹配数量只有252个了。这说明,的确存在大量的区域被重复多次提取了。
另外,经过测试graf 1-6的匹配,使用灰度图像,匹配策略使用最近邻,有300+个匹配点了。
wall数据,使用默认参数,匹配数量很少,wall 1-6。
总结,
MSER算是一个比较不错的仿射不变特征了,但是对于纹理的要求比较高,要求大量的面积较大的斑点,斑点的边缘不能太模糊。
MSER对应的稳定区域的要求是非常严格的,并且原生的MSER会重复提取同一个区域,导致常用的最近次近距离比值并不是很适合。