基于纹理边缘抑制的轮廓和边界检测(Contour and Boundary Detection)
一幅复杂的自然场景图像中包含丰富的信息,视觉不可能对空间中的每一点赋予相同的关注程度。对人类视觉系统的实验表明:图像中的轮廓特征特别重要,它们在保留关于物体的边界有用的结构信息的同时,极大地降低了数据量,从而简化了信息的表达形式,使视觉能对各种瞬息万变的输入可以及时有效地处理。在很多情况下,根据画出了的物体轮廓就可以识别出物体。
轮廓在计算机视觉中属于中层视觉,它是基于形状目标识别的一个重要特征。形状与结构的视觉分析在人类的视觉感知中起着重要的作用,物体往往是通过它的形状大小、颜色信息和几何结构等特征来描述的。在许多视觉任务中,由于形状具有很强的鲁棒性,因此一般物体的识别主要是根据形状特征。轮廓检测的一个首要问题是如何处理通过简单边缘检测机制产生的大量的候选成分。由于许多的边缘仅仅是一种对比度的突变,它们可能不属于任何有意思的和相关物理轮廓。通过来自周边环境上下文的信息影响 边缘检测器的响应来增强对相关边缘的敏感性,并根据上下文的信息将孤立的边缘点连接起来,构成某一个物体在图像上形成的一个区域的完整边界线,去除那些不具有实际物理意义的背景纹理边缘。
本文根据Cosmin Grigorescu等人的论文“Contour and boundary detection improved by surround suppression of texture edges"的内容和自己的理解而成,如果对其中的细节感兴趣,敬请参考原文。 在Cosmin Grigorescu的论文中,提出了一个重要的计算环节(Computational Step),叫做环境抑制(Surround Suppression)来提高自然场景中图像的目标轮廓和区域边界的检测。该灵感主要是受到人的初级视皮层(Primary Visual Cortex)中的那些具有方向选择性的神经元(Orientation Selective Neurons)激发,这些细胞对周围的非经典感受野区域具有抑制作用,并且影响对边缘和线条的聚集的感知能力(Perception of Group of Edges or Lines)。接下来,本文将详细的介绍下这篇论文的基本内容。
(1)计算梯度(Gradient Computation)
这里的操作类似Canny算子的第一步,也即首先将原图与一个高斯函数进行卷积,对图像进行平滑滤波,然后对滤波后的图像进行有限差分运算,求梯度。在Canny中,其计算公式如下:
然而,通过学者的研究得出,采用上面的公式来计算是病态的(ill-posed),因此,在该文中,采用如下的公式来计算梯度:
其中,是高斯函数的一阶微分。接着,进一步来计算X、Y方向的梯度,计算公式如下:
计算完梯度,那么就可以进一步得到幅值和方向,公式如下:
接着,沿着梯度方向采用非极大值抑制来对图像中的边缘像素进行定位,非极大值抑制可以参考博文:Canny算子中的非极大值抑制(Non-Maximum Suppression)分析。
(2)环境抑制(Surround Suppression)
通过第一步的计算,可以得到边缘图像,在这一步,需要对检测到的边缘图像进行非轮廓边缘的抑制,非轮廓边缘包括背景纹理以及噪声等。在该文中,通过高斯差分来模拟人的初级视皮层细胞对环境抑制机制,DOG的表达式如下:
权重函数的定义如下:
对图像中的每一个点,采用上面的权重函数进行处理。根据处理方式的不同,可以分为各向同性抑制和各向异性抑制。下面来简单介绍下这两种抑制。
(3)各向同性抑制(Isotropic Surround Suppression)
通过前面的两步计算,各向同性抑制的准备工作已经就绪了。该种方式的抑制作用只考虑距离因素,因此其表达公式如下:
最后,可以得到轮廓检测算子,用公式表达如下:
其中,alpha是一个控制因子(control factor),H(*)是一个操作算子,类似于Canny算子的后处理部分,包括轮廓的细化、滞后阈值化以及轮廓连接等,具体的可以参考Canny算子。
(4)各向异性抑制(Anisotropic Surround Suppression)
各向异性抑制与各向同性抑制相比,增加了一个抑制因素,也即方向因素。对于两个点(x,y)和(x-u,y-v),其方向因素表示如下:
通过上式可知,如果这两个点的方向一致,那么抑制最用最大,因为cos(0)=1,随着夹角的增大,抑制作用逐渐减少,当两个点相互垂直时,抑制作用最小(cos(90)=0)。
增加了方向的环境抑制因素后,抑制作用表示如下:
公式中的符号与前面相同,不再介绍。
下面给出两段代码:
(1)非极大值抑制
// non-maximum suppression void NonMaxSuppress(float*pMag, float* pGradX, float*pGradY, Size sz, unsigned char* pNSRst) { long x,y; int nPos; // the component of the gradient float gx,gy; // the temp varialbe float g1,g2,g3,g4; float weight; float dTemp,dTemp1,dTemp2; //设置图像边缘为不可能的分界点 for(x=0;x<sz.width;x++) { pNSRst[x] = 0; pNSRst[(sz.height-1)*sz.width+x] = 0; } for(y=0;y<sz.height;y++) { pNSRst[y*sz.width] = 0; pNSRst[y*sz.width + sz.width-1] = 0; } for (y=1;y<sz.height-1;y++) { for (x=1;x<sz.width-1;x++) { nPos=y*sz.width+x; // if pMag[nPos]==0, then nPos is not the edge point if (pMag[nPos]==0) { pNSRst[nPos]=0; } else { // the gradient of current point dTemp=pMag[nPos]; // x,y 方向导数 gx=pGradX[nPos]; gy=pGradY[nPos]; //如果方向导数y分量比x分量大,说明导数方向趋向于y分量 if (fabs(gy)>fabs(gx)) { // calculate the factor of interplation weight=fabs(gx)/fabs(gy); g2 = pMag[nPos-sz.width]; // 上一行 g4 = pMag[nPos+sz.width]; // 下一行 //如果x,y两个方向导数的符号相同 //C 为当前像素,与g1-g4 的位置关系为: //g1 g2 // C // g4 g3 if(gx*gy>0) { g1 = pMag[nPos-sz.width-1]; g3 = pMag[nPos+sz.width+1]; } //如果x,y两个方向的方向导数方向相反 //C是当前像素,与g1-g4的关系为: // g2 g1 // C // g3 g4 else { g1 = pMag[nPos-sz.width+1]; g3 = pMag[nPos+sz.width-1]; } } else { //插值比例 weight = fabs(gy)/fabs(gx); g2 = pMag[nPos+1]; //后一列 g4 = pMag[nPos-1]; // 前一列 //如果x,y两个方向的方向导数符号相同 //当前像素C与 g1-g4的关系为 // g3 // g4 C g2 // g1 if(gx * gy > 0) { g1 = pMag[nPos+sz.width+1]; g3 = pMag[nPos-sz.width-1]; } //如果x,y两个方向导数的方向相反 // C与g1-g4的关系为 // g1 // g4 C g2 // g3 else { g1 = pMag[nPos-sz.width+1]; g3 = pMag[nPos+sz.width-1]; } } dTemp1 = weight*g1 + (1-weight)*g2; dTemp2 = weight*g3 + (1-weight)*g4; //当前像素的梯度是局部的最大值 //该点可能是边界点 if(dTemp>=dTemp1 && dTemp>=dTemp2) pNSRst[nPos] = 128; else //不可能是边界点 pNSRst[nPos] = 0; } } } }
(2)高斯差分DoG
// sigma2 = 4*sigma1 void getDoGKernel( Mat& kernel, int kSize, double sigma1, double sigma2) { double sigma1x = sigma1*sigma1; double sigma2x = sigma2*sigma2; Mat mkernel (kSize, kSize, CV_64F); int center = (kSize-1)/2; int i,j; double dsum = 0; for ( i=0; i<kSize; ++i ) { double* data = (double*)(mkernel.data+ mkernel.step[0]*i); int di = (i-center); for ( j=0; j<kSize; ++j ) { int dj = (j-center); double d1 = (1.0/sigma1x)*exp(-(di*di+dj*dj)/(2*sigma1x)); double d2 = (1.0/sigma2x)*exp(-(di*di+dj*dj)/(2*sigma2x)); double dis = d2 - d1; if ( dis>0.0) data[j] = dis; else data[j]=0.0; dsum += fabs(dis); } } // 归一化 mkernel = mkernel/dsum; mkernel.copyTo( kernel); return; }
最后,再附上一小节代码:
Sobel( gray, sobelX, CV_32F, 1, 0, 3); Sobel( gray, sobelY, CV_32F, 0, 1, 3); magnitude( sobelX, sobelY, mag); //cartToPolar( sobelX, sobelY, mag, dir, true); // 非极大值值抑制 filter2D( mag, mag, mag.depth(), kernel); // 环境抑制 surround suppression
另外,推荐几篇相关论文:
1、Cosmin Grigorescu. contour detection based on nonclassical receptive field inhibition(2003 TIP).
2、唐奇伶. 基于初级视皮层感知机制的轮廓与边界检测(2007华中科技大学博士论文)
作者:kezunhai出处:http://blog.csdn.net/kezunhai欢迎转载或分享,但请务必声明文章出处。