RFD描述子简介—Receptive Fields Selection for Binary Feature Description

最近在看这篇Binary descriptor的论文,由于从来没有做过descriptor的相关工作,这篇东西看了很久才看懂一点点。在这里写篇博文记录一下。

这篇文章的工作是提出一种新的方法,用二进制的向量(不同于以往SIFT这种浮点向量)对图像特征点进行描述。特征点对应的区域就是标题中的Receptive Fields。

首先作者基于gradient map的方法(参见论文BGM :Learning Image Descriptors with the Boosting-Trick)定义了一个Receptive Fields的响应:


如果公式看不懂,还可以参考k123han123大神的这篇文章。Response公式(1)(3)的意思是计算区域γ中所有像素梯度在某个方向上的强度。如果还是

对这个响应g(x,y)做一个阈值 t 的截断,这个就得到了这个Receptive Fields对应的binary code。


如上图,本文下一步要做的就是要在一个patch(不是一张图,是一张图的众多patch里的其中一个patch)里选取合适的Receptive Fields,来生成一系列的Binary Code,用来描述这个patch。

文章后面介绍的是公式(5)中 t 的学习方法,其实就是一个简单的一维搜索:


作者对公式中的符号解释如下:


简单的说就是,每一个Receptive Fields(就是γ)对应一个阈值t。Receptive Fields没有类标,而patch是有类标的,所以要选取t值使得有同类的patch的binary code相似,不同类的patch的binary code不相似。

阈值t选完后,下一步是Receptive Fields Selection。作者提出了三个判据来选取Receptive Fields:


最后作者用的是第二个方法。对所有的Receptive Fields的得分排序,依次选取,再去掉相关性太强的区域,便完成了整个算法:



公式(1)(2)(3)(4)确实不好懂,在这里附上作者的代码供大家参考。

下面是计算梯度的函数,只要看到dir代表梯度方向,mag代表梯度幅值,就基本上能看懂了

void Utils::getHoG(Mat& im, int nDir, Mat* &PatchHoG)
{
	int GRAD_RADIUS = 1;
	int height = im.rows;
	int width = im.cols;
	double sigma0 = 1.6;
	Mat smooth_patch;
	GaussianBlur(im, smooth_patch, Size(0,0), sigma0, sigma0);

	float* hog = new float[width * height * nDir];
	memset(hog, 0, width*height*nDir*sizeof(float));

	for (int y = GRAD_RADIUS;y <= height-1-GRAD_RADIUS;y++)
	{
		for (int x = GRAD_RADIUS;x <= width-1-GRAD_RADIUS;x++)
		{
			float gray2 = smooth_patch.at<float>(y, x+GRAD_RADIUS);
			float gray1 = smooth_patch.at<float>(y, x-GRAD_RADIUS);
			float dx = gray2 - gray1;
			gray2 = smooth_patch.at<float>(y+GRAD_RADIUS, x);
			gray1 = smooth_patch.at<float>(y-GRAD_RADIUS, x);
			float dy = gray2 - gray1;

			double dir = atan2(dy,dx);
			double mag = sqrt(dx * dx + dy * dy);

			double idxDir = (dir + CV_PI) * nDir / (2.0 * CV_PI);
			if ((int)idxDir == nDir)	idxDir -= nDir;
			int dirIdx[2];
			float dirWeight[2];
			dirIdx[0] = (int)idxDir;
			dirIdx[1] = (dirIdx[0] + 1) % nDir;
			dirWeight[0] = 1.0 - (idxDir - dirIdx[0]);
			dirWeight[1] = idxDir - dirIdx[0];

			int pos_idx = y * width + x;
			hog[dirIdx[0] * width * height + pos_idx] = dirWeight[0] * mag;
			hog[dirIdx[1] * width * height + pos_idx] = dirWeight[1] * mag;
		}
	}

	for(int y = 0;y < height;y++)
	{
		for(int x = 0;x < width;x++)
		{
			float Total = 0;
			for(int i = 0;i < nDir;i++)
			{
				PatchHoG[i].at<float>(y,x) = hog[i * width * height + y * width + x];
				Total += hog[i * width * height + y * width + x];
			}
			PatchHoG[nDir].at<float>(y,x) = Total;
		}
	}

	delete [] hog;

}

下面的代码用于计算公式(1)(2),只要注意到Value, Total_Value,  thr对应的值就能看懂了。

void RFDR::compute( OxKey* pKeys, int nKey, IplImage* in, float scale, Mat& descriptors ) 
{
	descriptors = Mat::zeros(nKey, ceil(nDim/64.0)*8, descriptorType());
	Mat *gradMap, *integralMap;
	gradMap = new Mat[orientQuant+1];
	integralMap = new Mat[orientQuant+1];
	for (int j = 0;j < orientQuant+1;j++)
		gradMap[j] = Mat::zeros(patchSize, patchSize, CV_32FC1);
		
	for (int i = 0; i < nKey; ++i)
    {
		Mat Patch;
		// rectify the patch around a given keypoint
		Utils::rectifyPatch(pKeys[i], in, scale, patchSize, Patch);
		Utils::getHoG(Patch, orientQuant, gradMap);
			
		for (int j = 0;j < orientQuant+1;j++)
			integral(gradMap[j], integralMap[j], CV_32F);

		uchar* desc = descriptors.ptr<uchar>(i);
		for (int j = 0;j < nDim;j++)
		{
			uchar LT_x = pRFs[j].leftTopX,				LT_y = pRFs[j].leftTopY;
			uchar RB_x = pRFs[j].rightBottomX + 1,		RB_y = pRFs[j].rightBottomY + 1;
			uchar idx = pRFs[j].idx;
			float thr = pRFs[j].thr;

			float Value = 0;
			Value  = integralMap[idx].at<float>(RB_y, RB_x); 
			Value -= integralMap[idx].at<float>(RB_y, LT_x); 
			Value -= integralMap[idx].at<float>(LT_y, RB_x); 
			Value += integralMap[idx].at<float>(LT_y, LT_x); 

			float Total_Value = 0;
			Total_Value  = integralMap[orientQuant].at<float>(RB_y, RB_x); 
			Total_Value -= integralMap[orientQuant].at<float>(RB_y, LT_x); 
			Total_Value -= integralMap[orientQuant].at<float>(LT_y, RB_x); 
			Total_Value += integralMap[orientQuant].at<float>(LT_y, LT_x); 

			if( Total_Value >= 1 )
				Value /= Total_Value;

			Value -= thr;
			desc[j/8] |= (Value >= 0) ? binLookUp[j%8] : 0;
		}
		// clean-up
		Patch.release();
	}

	for (int j = 0;j < orientQuant+1;j++)
	{
		gradMap[j].release();
		integralMap[j].release();
	}
	delete [] gradMap;
	delete [] integralMap;

}

在这里只是简单地介绍了这篇论文的一些思路以及实现方法,作者的程序是命令行调用形式运行的,编译了之后打开cmd,按readme格式调用exe文件就可以了。

作者主页                     

代码下载地址

参考文献:

Fan, Bin, et al. "Receptive Fields Selection for Binary Feature Description." (2014): 1-1.

发布了29 篇原创文章 · 获赞 28 · 访问量 13万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览