【opencv实践】边缘检测
边缘检测:
一、canny算子
Canny边缘检测根据对信噪比与定位乘积进行测度,得到最优化逼近算子,也就是Canny算子。类似与 LoG 边缘检测方法,也属于先平滑后求导数的方法。
二、canny算法描述
1.首先进行高斯平滑滤波;
2、然后计算像素点的梯度(利用sobel算子)
3、计算幅值和夹角
4、非极大值抑制(NMS)幅值和夹角,如下图所示,就是比较梯度方向前后像素梯度的大小。
上图中左右图:g1、g2、g3、g4都代表像素点,很明显它们是c的八领域中的4个,左图中c点是我们需要判断的点,蓝色的直线是它的梯度方向,也就是说c如果是局部极大值,它的梯度幅值M需要大于直线与g1g2和g2g3的交点,dtmp1和dtmp2处的梯度幅值。但是dtmp1和dtmp2不是整像素,而是亚像素,也就是坐标是浮点的,那怎么求它们的梯度幅值呢?线性插值,例如dtmp1在g1、g2之间,g1、g2的幅值都知道,我们只要知道dtmp1在g1、g2之间的比例,就能得到它的梯度幅值,而比例是可以靠夹角计算出来的,夹角又是梯度的方向。
写个线性插值的公式:设g1的幅值M(g1),g2的幅值M(g2),则dtmp1可以很得到:
M(dtmp1)=w*M(g2)+(1-w)*M(g1)
其中w=distance(dtmp1,g2)/distance(g1,g2)
distance(g1,g2) 表示两点之间的距离。实际上w是一个比例系数,这个比例系数可以通过梯度方向(幅角的正切和余切)得到。
5、双阈值选取
-
将低于阈值的所有值赋零,得到图像的边缘阵列
- 阈值τ取得太低->假边缘
- 阈值τ取得太高->部分轮廊丢失
Canny算法中减少假边缘数量的方法是采用双阈值法。选择两个阈值,根据高阈值得到一个边缘图像,这样一个图像含有很少的假边缘,但是由于阈值较高,产生的图像边缘可能不闭合,为解决这样一个问题采用了另外一个低阈值。
在高阈值图像中把边缘链接成轮廓,当到达轮廓的端点时,该算法会在断点的8邻域点中寻找满足低阈值的点,再根据此点收集新的边缘,直到整个图像边缘闭合。
三、Canny方法
void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)
- 第一个参数,InputArray类型的image,输入图像,填Mat类对象即可,且需为单通道8位图像
- 第二个参数,OutputArray类型的edges,输出边缘图,和源图像有一样的尺寸和类型
- 第三个参数,double类型的threshold1,第一个滞后性阈值
- 第四个参数,double类型的threshold2,第二个滞后性阈值
- 第五个参数,int类型的apertureSize,表示应用Sobel算子的孔径大小,默认值3
- 第六个参数,bool类型的L2gradient,一个计算图像梯度幅值的标识,默认值false
四、opencv中的实现
1、图像灰度化;
2、高斯滤波(blur)
3、计算梯度
4、用双阈值方法检测和连接边缘
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <math.h>
using namespace cv;
{
Mat img = imread("lena.jpg", CV_LOAD_IMAGE_COLOR);
if(img.empty())
return -1;
Mat src_gray,src_blur,dst;
cvtColor(img,src_gray,CV_BGR2GRAY);
blur(src_gray,src_blur,Size(3,3));
Canny(src_blur,dst,30,200);
namedWindow( "lena", CV_WINDOW_AUTOSIZE );
imshow("lena", img);
imshow("canny",dst);
waitKey(0);
return 0;
}