Sift:(第一步)建立高斯差分金字塔

先导知识:高斯模糊

 疑问:上图中的c和d?

我本来比较疑惑这一点,就是c所示的灰色的边界是经怎样处理得到的,d中细细的黑边又是怎样得到的?然后我搜了一下,这篇博客解决了我的疑惑:

高斯模糊实现小结_高斯模糊边缘处理_zddhub的博客-CSDN博客

 代码如下(搬运,侵删)

高斯核确定(灰色边缘):

//高斯平滑
//未使用sigma,边缘无处理
void GaussianTemplateSmooth(const Mat &src, Mat &dst, double sigma)
{
	//高斯模板(7*7),sigma = 0.84089642,归一化后得到
	static const double gaussianTemplate[7][7] = 
	{
		{0.00000067, 0.00002292, 0.00019117, 0.00038771, 0.00019117, 0.00002292, 0.00000067},
		{0.00002292, 0.00078633, 0.00655965, 0.01330373, 0.00655965, 0.00078633, 0.00002292},
		{0.00019117, 0.00655965, 0.05472157, 0.11098164, 0.05472157, 0.00655965, 0.00019117},
		{0.00038771, 0.01330373, 0.11098164, 0.22508352, 0.11098164, 0.01330373, 0.00038771},
		{0.00019117, 0.00655965, 0.05472157, 0.11098164, 0.05472157, 0.00655965, 0.00019117},
		{0.00002292, 0.00078633, 0.00655965, 0.01330373, 0.00655965, 0.00078633, 0.00002292},
		{0.00000067, 0.00002292, 0.00019117, 0.00038771, 0.00019117, 0.00002292, 0.00000067}
	};
 
	dst.create(src.size(), src.type());
	uchar* srcData = src.data;
	uchar* dstData = dst.data;
 
	for(int j = 0; j < src.cols-7; j++)
	{
		for(int i = 0; i < src.rows-7; i++)
		{
			double acc = 0;
			double accb = 0, accg = 0, accr = 0; 
			for(int m = 0; m < 7; m++)
			{
				for(int n = 0; n < 7; n++)
				{
					if(src.channels() == 1)
						acc += *(srcData + src.step * (i+n) + src.channels() * (j+m)) * gaussianTemplate[m][n];
					else
					{
						accb += *(srcData + src.step * (i+n) + src.channels() * (j+m) + 0) * gaussianTemplate[m][n];
						accg += *(srcData + src.step * (i+n) + src.channels() * (j+m) + 1) * gaussianTemplate[m][n];
						accr += *(srcData + src.step * (i+n) + src.channels() * (j+m) + 2) * gaussianTemplate[m][n];
					}
				}
			}
			if(src.channels() == 1)
				*(dstData + dst.step * (i+3) + dst.channels() * (j+3))=(int)acc;
			else
			{
				*(dstData + dst.step * (i+3) + dst.channels() * (j+3) + 0)=(int)accb;
				*(dstData + dst.step * (i+3) + dst.channels() * (j+3) + 1)=(int)accg;
				*(dstData + dst.step * (i+3) + dst.channels() * (j+3) + 2)=(int)accr;
			}
		}
	}
	
}

由sigma求高斯核(灰色边缘):

void GaussianSmooth2D(const Mat &src, Mat &dst, double sigma)
{
	if(src.channels() != 1)
		return;
 
	//确保sigma为正数 
	sigma = sigma > 0 ? sigma : 0;
	//高斯核矩阵的大小为(6*sigma+1)*(6*sigma+1)
	//ksize为奇数
	int ksize = cvRound(sigma * 3) * 2 + 1;
	
//	dst.create(src.size(), src.type());
	if(ksize == 1)
	{
		src.copyTo(dst);	
		return;
	}
 
	dst.create(src.size(), src.type());
 
	//计算高斯核矩阵
	double *kernel = new double[ksize*ksize];
 
	double scale = -0.5/(sigma*sigma);
	const double PI = 3.141592653;
	double cons = -scale/PI;
 
	double sum = 0;
 
	for(int i = 0; i < ksize; i++)
	{
		for(int j = 0; j < ksize; j++)
		{
			int x = i-(ksize-1)/2;
			int y = j-(ksize-1)/2;
			kernel[i*ksize + j] = cons * exp(scale * (x*x + y*y));
 
			sum += kernel[i*ksize+j];
//			cout << " " << kernel[i*ksize + j];
		}
//		cout <<endl;
	}
	//归一化
	for(int i = ksize*ksize-1; i >=0; i--)
	{
		*(kernel+i) /= sum;
	}
	//图像卷积运算,无边缘处理
	for(int j = 0; j < src.cols-ksize; j++)
	{
		for(int i = 0; i < src.rows-ksize; i++)
		{
			double acc = 0;
 
			for(int m = 0; m < ksize; m++)
			{
				for(int n = 0; n < ksize; n++)
				{
					acc += *(srcData + src.step * (i+n) + src.channels() * (j+m)) * kernel[m*ksize+n]; 
				}
			}
 
		
			*(dstData + dst.step * (i + (ksize - 1)/2) + (j + (ksize -1)/2)) = (int)acc;
		}
	}
	delete []kernel;
}

换成边缘处理(细细的黑边):

	int center = (ksize-1) /2;
	//图像卷积运算,处理边缘
	for(int j = 0; j < src.cols; j++)
	{
		for(int i = 0; i < src.rows; i++)
		{
			double acc = 0;
 
			for(int m = -center, c = 0; m <= center; m++, c++)
			{
				for(int n = -center, r = 0; n <= center; n++, r++)
				{
					if((i+n) >=0 && (i+n) < src.rows && (j+m) >=0 && (j+m) < src.cols)
					{
						
						acc += *(srcData + src.step * (i+n) + src.channels() * (j+m)) * kernel[r*ksize+c]; 
				
					}
				}
			}
 
 
			*(dstData + dst.step * (i) + (j)) = (int)acc;
		}
	}

我基础不好,也不怎么聪明,直接看看不明白,就写了写画了画:

以上是关于高斯模糊,实际用opencv就一句代码就解决了。

Sift第一步—— 建立高斯差分金字塔

 1.尺度空间

2.高斯金字塔

尺度空间在实现时,可用高斯金字塔来表示。

高斯金字塔的两步构建:对图像做不同尺度的高斯模糊;对图像进行降采样(隔点取点)

  • 同一组 或者叫 同一大层(Octave):(不同层)图像间进行高斯模糊的sigma不同;由下到上,sigma变大,图像变模糊。
  • 不同组:下一组图像对上一层图像进行降采样(又叫下采样)得到。

 那么,为什么要构建这么一个尺度空间呢?

答:高斯金字塔可以模拟人眼,人眼看东西是近大远小,近处清晰 远处模糊的。上图中,从下面看这些图像,发现离得越近越清晰,越远越模糊,自然也是近处图像大,远处图像小的。那么为什么要模拟人眼呢?人眼可以发现不同姿态、处于不同光照等条件下的物体是同一个,因为这个物体具有同样的局部特征,人眼可以迅速匹配,sift正是要找图像的局部特征的。

 (图片来源于网络,侵删)

 接下来要思考的,便是这个高斯金字塔要设多少组,每组要设多少层,用于高斯模糊的高斯核sigma要设为多少?

一些参数:

  • 组数(大层数):

       其中,M、N为原图像的大小,t为塔顶图像的最小维数的对数值。如,对于512*512大小的图像,如果塔顶图像为4*4,t为2,则n=9-2=7,分为7组(大层)图像。

  •  每组的层数:s+3(s 是 高斯差分金字塔 想要从多少张图像中检测极值点)
  •  sigma:

 3.高斯差分金字塔

两层直接减:

特征点就在高斯差分金字塔里找,这就是第二步的内容了

--------------------------------------------------------------------------------------------------------------------------------

Lowe,D 的SIFT代码(初始图像\rightarrow高斯金字塔最底层图像)

我们想要得到的高斯金字塔最底层图像是自然图像经1.6的高斯核模糊得到的,而我们手头上的是相机捕捉到的初始图像(可看作自然图像经0.5的高斯核模糊得到)。没错,就是这个样子。所以,我们想要得到高斯金字塔最底层图像,还要经过处理。

//生成高斯金字塔最底层图像 
/*
  Converts an image to 8-bit grayscale and Gaussian-smooths it.  The image is
  optionally doubled in size prior to smoothing.

  @param img input image(初始图像,经相机捕捉得到) 
  @param img_dbl if true, image is doubled in size prior to smoothing(控制高斯金字塔最低层图像由哪种方式得到)
   img_dbl不为0的话,将初始图像放大两倍再进行高斯模糊(这里调用时,就将其设为1) 
   img_del为0的话,对初始图像进行高斯系数为1.52的高斯模糊 
  @param sigma total std of Gaussian smoothing(sigma为1.6,想要高斯金字塔最底层图像的模糊系数) 
*/
static IplImage* create_init_img( IplImage* img, int img_dbl, double sigma )
{
  IplImage* gray;//灰度图 
  IplImage* dbl;//放大灰度图 
  double sig_diff;// 

  gray = convert_to_gray32( img );  //调用函数convert_to_gray32。将原始图像转化为32位灰度图 
  if( img_dbl )  //初始图像处理方法1 
    {
      sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4 );  //SIFT_INIT_SIGMA为0.5,sqrt(1.6^2+(2*0.5)^2)=1.25
      dbl = cvCreateImage( cvSize( img->width*2, img->height*2 ),
			   IPL_DEPTH_32F, 1 );  //创建双倍img大小 单通道 32位灰度图空间 
      cvResize( gray, dbl, CV_INTER_CUBIC );//放大图像,输入图像gray,输出dbl,放大方法为双立方插值 
      cvSmooth( dbl, dbl, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff );//分离高斯模糊,输入图像为dbl,输出图像为dbl,高斯核为sig_diff 
      cvReleaseImage( &gray );//释放灰度图空间 
      return dbl; //dbl就视为高斯金字塔最底层图像 
    }
  else   //初始图像处理方法2 
    {
      sig_diff = sqrt( sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA );//sqrt(1.6^2-0.5^2) 
      cvSmooth( gray, gray, CV_GAUSSIAN, 0, 0, sig_diff, sig_diff );//分离高斯模糊,输入图像为dbl,输出图像为dbl,高斯核为sig_diff
      return gray;//gray就视为高斯金字塔最底层图像 
    }
}


//转为32位灰度图 
/*
  Converts an image to 32-bit grayscale

  @param img a 3-channel 8-bit color (BGR) or 8-bit gray image

  @return Returns a 32-bit grayscale image
*/
static IplImage* convert_to_gray32( IplImage* img )
{
  IplImage* gray8, * gray32;
  
  gray32 = cvCreateImage( cvGetSize(img), IPL_DEPTH_32F, 1 );//创建img大小 单通道 32位灰度图空间 
  if( img->nChannels == 1 ) //如果初始图像的通道数为1,说明输入图像为8位灰度图 
    gray8 = cvClone( img );//图像克隆,直接复制 
  else //初始图像的通道数为3,说明输入图像位RGB图像 
    {
      gray8 = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 1 );//创建img大小 单通道 8位灰度图空间
      cvCvtColor( img, gray8, CV_BGR2GRAY );//将RGB图像转为8位灰度图,输入图像为img,输入图像为gray8 
    }
    
  cvConvertScale( gray8, gray32, 1.0 / 255.0, 0 ); //将8位灰度图转为32位灰度图,比例因子是1/255,偏移量为0 

  cvReleaseImage( &gray8 );//释放8位灰度图空间 
  return gray32; //返回32位灰度图 
}

Lowe,D 的SIFT代码(建立高斯金字塔)

//建立高斯金子塔 
/*
  Builds Gaussian scale space pyramid from an image

  @param base base image of the pyramid(最底层图像) 
  @param octvs number of octaves of scale space(高斯金字塔的组数/大层数) 
  @param intvls number of intervals per octave(想要从高斯差分金字塔的多少张图片中提取特征,高斯金字塔每组的层数intvls+3) 
  @param sigma amount of Gaussian smoothing per octave(sigma为1.6) 

  @return Returns a Gaussian scale space pyramid as an octvs x (intvls + 3)
    array
*/
static IplImage*** build_gauss_pyr( IplImage* base, int octvs,
			     int intvls, double sigma )
{
  IplImage*** gauss_pyr;  //指向高斯金字塔 
  const int _intvls = intvls;
  double sig[_intvls+3], sig_total, sig_prev, k;
  int i, o;

  gauss_pyr = calloc( octvs, sizeof( IplImage** ) );//开辟高斯金字塔空间,octvs个 IplImage**  大小的空间 
  for( i = 0; i < octvs; i++ )
    gauss_pyr[i] = calloc( intvls + 3, sizeof( IplImage *) ); //开辟高斯金字塔每组的空间,intvls+3个 IplImage* 大小的空间 

  /*
    precompute Gaussian sigmas using the following formula:

    \sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2

    sig[i] is the incremental sigma value needed to compute 
    the actual sigma of level i. Keeping track of incremental
    sigmas vs. total sigmas keeps the gaussian kernel small.
  */
  //高斯模糊的参数 
  k = pow( 2.0, 1.0 / intvls );
  sig[0] = sigma;
  sig[1] = sigma * sqrt( k*k- 1 );  
  for (i = 2; i < intvls + 3; i++)
      sig[i] = sig[i-1] * k;

  for( o = 0; o < octvs; o++ )
    for( i = 0; i < intvls + 3; i++ )
      {
	if( o == 0  &&  i == 0 )  //对于高斯金字塔最底层的图像 
	  gauss_pyr[o][i] = cvCloneImage(base);  //函数的输入图像直接复制过来 

	/* base of new octvave is halved image from end of previous octave */
	else if( i == 0 )  //其余组最底层的图像 
	  gauss_pyr[o][i] = downsample( gauss_pyr[o-1][intvls] );//调用下采样函数downsample,直接对其上一组的倒数第三层进行下采样 
	  
	/* blur the current octave's last image to create the next one */
	else //剩余非各组底层的图像 
	  {
	    gauss_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i-1]),
					     IPL_DEPTH_32F, 1 );//创建大小同其同组上一层图像大小相同 单通道 32位灰图的图像空间 
	    cvSmooth( gauss_pyr[o][i-1], gauss_pyr[o][i],
		      CV_GAUSSIAN, 0, 0, sig[i], sig[i] ); //分离高斯滤波,输入图像gauss_pyr[o][i-1],输出图像gauss_pyr[o][i]
	  }
      }

  return gauss_pyr;//返回指向高斯金字塔的指针 
}

这里在构建高斯金字塔各组非底层的图像时,是在它同组下一层图像的基础上,进行的高斯模糊。关于参数,说明如下:

//下采样 
/*
  Downsamples an image to a quarter of its size (half in each dimension)
  using nearest-neighbor interpolation

  @param img an image(需要进行下采样的图像) 

  @return Returns an image whose dimensions are half those of img
*/
static IplImage* downsample( IplImage* img )
{
  IplImage* smaller = cvCreateImage( cvSize(img->width / 2, img->height / 2),
				     img->depth, img->nChannels ); //创建大小为原图像的一半 位深和通道数同原图像的图像空间 
  cvResize( img, smaller, CV_INTER_NN );//下采样(缩小图像),输入图像img,输出图像smaller,缩减方法为最近邻居插补 

  return smaller;
}

Lowe,D 的SIFT代码(建立高斯差分金字塔)

//建立高斯差分金字塔 
/*
  Builds a difference of Gaussians scale space pyramid by subtracting adjacent
  intervals of a Gaussian pyramid

  @param gauss_pyr Gaussian scale-space pyramid(高斯金字塔) 
  @param octvs number of octaves of scale space(高斯金字塔的组数/大层数)
  @param intvls number of intervals per octave(想要从高斯差分金字塔的多少张图片中提取特征,高斯金字塔每组的层数intvls+3)

  @return Returns a difference of Gaussians scale space pyramid as an
    octvs x (intvls + 2) array
*/
static IplImage*** build_dog_pyr( IplImage*** gauss_pyr, int octvs, int intvls )
{
  IplImage*** dog_pyr;
  int i, o;

  dog_pyr = calloc( octvs, sizeof( IplImage** ) );//开辟高斯差分金字塔空间,octvs个 IplImage**  大小的空间 
  for( i = 0; i < octvs; i++ )
    dog_pyr[i] = calloc( intvls + 2, sizeof(IplImage*) );//开辟高斯差分金字塔每组的空间,intvls+2个 IplImage* 大小的空间 

  for( o = 0; o < octvs; o++ ) 
    for( i = 0; i < intvls + 2; i++ )
      {
	dog_pyr[o][i] = cvCreateImage( cvGetSize(gauss_pyr[o][i]),
				       IPL_DEPTH_32F, 1 );//创建同高斯金字塔对应组、层的图像大小 单通道 32位灰度图像空间 
	cvSub( gauss_pyr[o][i+1], gauss_pyr[o][i], dog_pyr[o][i], NULL );//产生差分图像,gauss_pyr[o][i+1]-gauss_pyr[o][i]=dog_pyr[o][i]
      }

  return dog_pyr;

下一步:Sift:(第二步)关键点位置的确定_zzz_zzzz_的博客-CSDN博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值