瞎搞系列-边缘检测

边缘检测

从传统到如今深度学习稍微列举下:

传统:

canny

步骤:

1,用高斯滤波器平滑图像

2,用一阶偏导有限差分计算梯度和幅值和方向

3,对梯度幅值进行非极大抑制

4,用双阙值算法检测和连接边缘

细节:

边缘检测是基于对图像灰度差异运算而实现的,如果输入的是RGB,则需要先进行灰度图变换。RGB转灰度图的一个常用公式为:

Gray = R*0.299 + G*0.587 + B*0.114

//******************灰度转换函数*************************
//第一个参数image输入的彩色RGB图像;
//第二个参数imageGray是转换后输出的灰度图像;
//*************************************************************
void ConvertRGB2GRAY(const Mat &image,Mat &imageGray)
{
	if(!image.data||image.channels()!=3)
	{
		return ;
	}
	imageGray=Mat::zeros(image.size(),CV_8UC1);
	uchar *pointImage=image.data;
	uchar *pointImageGray=imageGray.data;
	int stepImage=image.step;
	int stepImageGray=imageGray.step;
	for(int i=0;i<imageGray.rows;i++)
	{
		for(int j=0;j<imageGray.cols;j++)
		{
			pointImageGray[i*stepImageGray+j]=0.114*pointImage[i*stepImage+3*j]+0.587*pointImage[i*stepImage+3*j+1]+0.299*pointImage[i*stepImage+3*j+2];
		}
	}
}

生成高斯滤波卷积核:

高斯滤波的过程是将灰度图像与高斯卷积核进行卷积,所以第一步是根据给定尺寸和sigma的高斯卷积核参数。

一维高斯卷积核:


二维高斯函数:



其中k+1为size/2

//******************高斯卷积核生成函数*************************
//第一个参数gaus是一个指向含有N个double类型数组的指针;
//第二个参数size是高斯卷积核的尺寸大小;
//第三个参数sigma是卷积核的标准差
//*************************************************************
void GetGaussianKernel(double **gaus, const int size,const double sigma)
{
	const double PI=4.0*atan(1.0); //圆周率π赋值
	int center=size/2;
	double sum=0;
	for(int i=0;i<size;i++)
	{
		for(int j=0;j<size;j++)
		{
			gaus[i][j]=(1/(2*PI*sigma*sigma))*exp(-((i-center)*(i-center)+(j-center)*(j-center))/(2*sigma*sigma));
			sum+=gaus[i][j];
		}
	}
	for(int i=0;i<size;i++)
	{
		for(int j=0;j<size;j++)
		{
			gaus[i][j]/=sum;
			cout<<gaus[i][j]<<"  ";
		}
		cout<<endl<<endl;
	}
	return ;
}

将生成的高斯卷积核与图像卷积,得到灰度图像的高斯滤波后的图像,抑制噪声

//******************高斯滤波*************************
//第一个参数imageSource是待滤波原始图像;
//第二个参数imageGaussian是滤波后输出图像;
//第三个参数gaus是一个指向含有N个double类型数组的指针;
//第四个参数size是滤波核的尺寸
//*************************************************************
void GaussianFilter(const Mat imageSource,Mat &imageGaussian,double **gaus,int size)
{
	imageGaussian=Mat::zeros(imageSource.size(),CV_8UC1);
	if(!imageSource.data||imageSource.channels()!=1)
	{
		return ;
	}
	double gausArray[100]; 
	for(int i=0;i<size*size;i++)
	{
		gausArray[i]=0;  //赋初值,空间分配
	}
	int array=0;
	for(int i=0;i<size;i++)
	{
		for(int j=0;j<size;j++)

		{
			gausArray[array]=gaus[i][j];//二维数组到一维 方便计算
			array++;
		}
	}
	//滤波
	for(int i=0;i<imageSource.rows;i++)
	{
		for(int j=0;j<imageSource.cols;j++)
		{
			int k=0;
			for(int l=-size/2;l<=size/2;l++)
			{
				for(int g=-size/2;g<=size/2;g++)
				{
					//以下处理针对滤波后图像边界处理,为超出边界的值赋值为边界值
					int row=i+l;
					int col=j+g;
					row=row<0?0:row;
					row=row>=imageSource.rows?imageSource.rows-1:row;
					col=col<0?0:col;
					col=col>=imageSource.cols?imageSource.cols-1:col;
					//卷积和
					imageGaussian.at<uchar>(i,j)+=gausArray[k]*imageSource.at<uchar>(row,col);
					k++;
				}
			}
		}
	}
}

利用sobel梯度算子计算梯度幅值与方向。图像的灰度值的梯度可以使用最简单的一阶有限差分来近似,若为下面的矩阵:


则计算公式为:


其中f为图像灰度值,p代表x方向梯度幅值,Q代表Y方向梯度幅值,M点位该点幅值,thea为梯度方向也就是角度。

一般常常采用的为sobel算子,sobel算子卷积核为:


使用sobel算子计算x,y方向梯度和梯度方向角代码实现:

//******************Sobel卷积因子计算X、Y方向梯度和梯度方向角********************
//第一个参数imageSourc原始灰度图像;
//第二个参数imageSobelX是X方向梯度图像;
//第三个参数imageSobelY是Y方向梯度图像;
//第四个参数pointDrection是梯度方向角数组指针
//*************************************************************
void SobelGradDirction(const Mat imageSource,Mat &imageSobelX,Mat &imageSobelY,double *&pointDrection)
{
	pointDrection=new double[(imageSource.rows-1)*(imageSource.cols-1)];
	for(int i=0;i<(imageSource.rows-1)*(imageSource.cols-1);i++)
	{
		pointDrection[i]=0;
	}
	imageSobelX=Mat::zeros(imageSource.size(),CV_32SC1);
	imageSobelY=Mat::zeros(imageSource.size(),CV_32SC1);
	uchar *P=imageSource.data;  
	uchar *PX=imageSobelX.data;  
	uchar *PY=imageSobelY.data;  

	int step=imageSource.step;  
	int stepXY=imageSobelX.step; 
	int k=0;
	int m=0;
	int n=0;
	for(int i=1;i<(imageSource.rows-1);i++)  
	{  
		for(int j=1;j<(imageSource.cols-1);j++)  
		{  
			//通过指针遍历图像上每一个像素 
			double gradY=P[(i-1)*step+j+1]+P[i*step+j+1]*2+P[(i+1)*step+j+1]-P[(i-1)*step+j-1]-P[i*step+j-1]*2-P[(i+1)*step+j-1];
			PY[i*stepXY+j*(stepXY/step)]=abs(gradY);
			double gradX=P[(i+1)*step+j-1]+P[(i+1)*step+j]*2+P[(i+1)*step+j+1]-P[(i-1)*step+j-1]-P[(i-1)*step+j]*2-P[(i-1)*step+j+1];
            PX[i*stepXY+j*(stepXY/step)]=abs(gradX);
			if(gradX==0)
			{
				gradX=0.00000000000000001;  //防止除数为0异常
			}
			pointDrection[k]=atan(gradY/gradX)*57.3;//弧度转换为度
			pointDrection[k]+=90;
			k++;
		}  
	} 
	convertScaleAbs(imageSobelX,imageSobelX);
	convertScaleAbs(imageSobelY,imageSobelY);
}

求梯度图的幅值

为了使得x,y方向的梯度和梯度角之后来计算x和y方向融合的梯度幅值,计算公式为:


//******************计算Sobel的X和Y方向梯度幅值*************************
//第一个参数imageGradX是X方向梯度图像;
//第二个参数imageGradY是Y方向梯度图像;
//第三个参数SobelAmpXY是输出的X、Y方向梯度图像幅值
//*************************************************************
void SobelAmplitude(const Mat imageGradX,const Mat imageGradY,Mat &SobelAmpXY)
{
	SobelAmpXY=Mat::zeros(imageGradX.size(),CV_32FC1);
	for(int i=0;i<SobelAmpXY.rows;i++)
	{
		for(int j=0;j<SobelAmpXY.cols;j++)
		{
			SobelAmpXY.at<float>(i,j)=sqrt(imageGradX.at<uchar>(i,j)*imageGradX.at<uchar>(i,j)+imageGradY.at<uchar>(i,j)*imageGradY.at<uchar>(i,j));
		}
	}
	convertScaleAbs(SobelAmpXY,SobelAmpXY);
}

对梯度进行非极大抑制

对幅值图像进行非极大抑制,可以进一步消除非边缘的噪点,更重要的是,可以细化边缘。

抑制逻辑:沿着该点梯度方向,比较前后两个点的幅值大小,若该点大于前后两点,则保留,若该点小于则置为0.

双阈值处理:指定一个低阈值A,一个高阈值B,一般取B为图像整体灰度级分布的70%,灰度值大于B,置为255,灰度值小于A,置于0.

灰度值置于A和B之间的,考察该像素点邻近的8像素点是否有灰度值为255的,若没有255的,表示这是一个孤立的局部极大值点,可以排除,否则为0.若有255的,表示邻近,依次重复。

指标:

1,好的信噪比,非边缘点判定为边缘点的概率要低,将边缘点判为非边缘点的概率要低

2,高的定位性能,检测出的边缘点要尽可能在实际边缘点的中心

3,对单一边缘仅有唯一的响应,单个边缘产生多个响应的概率要低。

在提高对景物边缘的敏感性的同时,又能抑制噪声的方法才能更加好的提取。

参考:

https://blog.csdn.net/dcrmg/article/details/52344902

http://www.cnblogs.com/techyan1990/p/7291771.html

深度学习:

我只罗列了三大会的一些文章:

Holistically-Nested Edge Detection(ICCV 2015)

Richer Convolutional Features for Edge Detection(CVPR 2017)

CASENet: Deep Category-Aware Semantic Edge Detection(CVPR 2017)

Learning Relaxed Deep Supervision for Better Edge Detection.(CVPR 2016)

深度学习在边缘检测好像不那么热,所以文章都比较少。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值