OpenCV系列(七)边缘提取
边缘提取,指数字图像处理中,对于图片轮廓的一个处理。对于边界处,灰度值变化比较剧烈的地方,就定义为边缘。也就是拐点,拐点是指函数发生凹凸性变化的点。二阶导数为零的地方。并不是一阶导数,因为一阶导数为零,表示是极值点。
边缘提取
边缘检测的基本思想首先是利用边缘增强算子,突出图像中的局部边缘,然后定义像素的“边缘强度”,通过设置阈值的方法提取边缘点集。由于噪声和模糊的存在,监测到的边界可能会变宽或在某点处发生间断。因此,边界检测包括两个基本内容:(1)用边缘算子提取出反映灰度变化的边缘点集。(2)在边缘点集合中剔除某些边界点或填补边界间断点,并将这些边缘连接成完整的线。
边缘定义
图像灰度变化率最大的地方(图像灰度值变化最剧烈的地方)。图像灰度在表面变化的不连续造成的边缘。一般认为边缘提取是要保留图像的灰度变化剧烈的区域,这从数学上看,最直观的方法就是微分(对于数字图像来说就是差分),在信号处理的角度来看,也可以说是用高通滤波器,即保留高频信号。边缘信息包含两个方面:1.像素的坐标 2.边缘的方向
Part 1
Canny 算法
边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。这些包括
深度上的不连续
表面方向不连续
物质属性变化
场景照明变化。
边缘检测是图像处理和计算机视觉中,尤其是特征提取中的一个研究领域。Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:
好的检测 - 算法能够尽可能多地标识出图像中的实际边缘。
好的定位 - 标识出的边缘要尽可能与实际图像中的实际边缘尽可能接近。
最小响应 - 图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。
Canny算法在分为如下几个步骤:
输入彩色图像,通过高斯模糊去除噪声 GaussianBlur
灰度转换 cvtColor
计算梯度 Sobel/Scharr
非最大信号抑制
高低阙值 输出二值图像
接下来让我们来敲代码在OpenCV中实现Canny算法吧:
import cv2
import numpy as np
def Canny_demo(img):
blur=cv2.GaussianBlur(img,(5,5),0) #除去噪声
gray=cv2.cvtColor(blur,cv2.COLOR_BGR2GRAY) #图像灰度
xgrad=cv2.Scharr(gray,cv2.CV_16SC1,1,0) #求梯度Scharr算子
ygrad=cv2.Scharr(gray,cv2.CV_16SC1,0,1)
edge=cv2.Canny(xgrad,ygrad,0,255)
dst=cv2.bitwise_and(img,img,mask=edge)
return edge,dst
首先我们定义了一个Canny_demo函数,函数的主要功能是: 使用高斯模糊除去图片的噪声,接着使用cv2.cvtColor 把图像灰度化,然后求梯度Schaar算子,然后使用Canny算法进行边缘检测,最后返回掩模和图片,函数写完了,我们来进行调用:
if __name__ == '__main__':
img=cv2.imread('opencv_image/morph02.png')
cv2.imshow('img',img)
_,image=Canny_demo(img)
cv2.imshow('demo',image)
cv2.waitKey(0)
运行结果如下:
Tips:像上面的运行结果,会发现噪点挺多的,我们可以通过加大高斯模糊的算子来除去更多的噪声。如图,这是把GaussianBlur中(5,5)加到(9,9)的结果。可见噪声变得少了。
Part 2
直线检测
直线检测的前提条件是:边缘检测已经完成!所以这时候我们可以再写一个函数来调用我们刚刚写好的Canny函数,这样子我们就可以不需要重复地去造轮子。所有代码如下:
import cv2
import numpy as np
def Canny_demo(img):
blur=cv2.GaussianBlur(img,(9,9),0) #除去噪声
gray=cv2.cvtColor(blur,cv2.COLOR_BGR2GRAY) #图像灰度
xgrad=cv2.Scharr(gray,cv2.CV_16SC1,1,0) #求梯度Scharr算子
ygrad=cv2.Scharr(gray,cv2.CV_16SC1,0,1)
edge=cv2.Canny(xgrad,ygrad,0,255)
dst=cv2.bitwise_and(img,img,mask=edge)
return edge,dst
def find_lines(img):
edge,_=Canny_demo(img)
lines=cv2.HoughLinesP(edge,1,np.pi/180,100,
minLineLength=50,
maxLineGap=10)
for line in lines:
x1,y1,x2,y2=line[0]
cv2.line(img,(x1,y1),(x2,y2),(255,0,0),1)
return img
if __name__ == '__main__':
img=cv2.imread('opencv_image/morph02.png')
cv2.imshow('img',img)
image=find_lines(img)
cv2.imshow('demo',image)
cv2.waitKey(0)
程序运行结果:
函数解析:
HoughLinesP(image,rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None
作用:获得霍夫直线
image | 必须是二值图像,推荐使用canny边缘检测的结果图像 |
rho | 线段以像素为单位的距离精度,double类型的,推荐用1.0 |
theta | 线段以弧度为单位的角度精度,推荐用numpy.pi/180 |
threshold | 累加平面的阈值参数,int类型,超过设定阈值才被检测出线段,值越大,基本上意味着检出的线段越长,检出的线段个数越少。根据情况推荐先用100试试 |
lines | 这个参数的意义未知,发现不同的lines对结果没影响,但是不要忽略了它的存在 |
minLineLength | 线段以像素为单位的最小长度,根据应用场景设置 |
maxLineGap | 同一方向上两条线段判定为一条线段的最大允许间隔(断裂),超过了设定值,则把两条线段当成一条线段,值越大,允许线段上的断裂越大,越有可能检出潜在的直线段 |