对于图像的处理,基本的步骤是这样的:
step1.取得图像数据
step2.将图像进行平滑处理
step3.进行边缘检测,阈值分析
step4.进行形态学的操作
step5.获取某些特征点
step6.分析数据
利用opencv的Canny函数就可以进行边缘检测。
官网参见https://docs.opencv.org/3.4.1/da/d22/tutorial_py_canny.html
测试一下
# -*- coding: cp936 -*-
import cv2
image = cv2.imread('test.jpg')
cv2.namedWindow("Image")
# Canny函数,三个参数:源图像,低阈值,高阈值
image = cv2.Canny(image, 200, 300)
cv2.imshow("Image", image)
cv2.waitKey(0)
看一下运行效果,左边是原图。
查了Candy函数说明http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.html
Canny 边缘检测算法是 John F. Canny 于1986年开发出来的一个多级边缘检测算法
边缘检测算法优劣的三个主要评价标准是:
- 低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
- 高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
- 最小响应: 图像中的边缘只能标识一次。
Candy算法原理
用代码实现Canny算法过程可以参考https://blog.csdn.net/u012198575/article/details/84846567
主要步骤:
- 降噪(图像平滑处理-低通滤波器)
由于边缘检测对图像中的噪声很敏感,第一步是用5x5高斯滤波器(低通滤波器)去除图像中的噪声。参见https://mp.csdn.net/mdeditor/96101079# - 计算图像梯度(高通滤波器)
图像平滑处理后,使用Sobel算子,x和y方向求导(图像梯度Gx和Gy)。根据梯度Gx和Gy然后获得边界的梯度的方向和大小
Edge_Gradient(G)= G x 2 + G y 2 \sqrt{G_x^2+G_y^2} \quad Gx2+Gy2
Angle(θ)=tan−1 ( G y G x ) \left(\frac{G~y}{G~x} \right) (G xG y)
梯度的方向总是垂直于边界。梯度方向被归为四类:垂直,水平,和两个对角线。 - 非极大值抑制。 这一步排除非边缘像素, 仅仅保留了一些细线条(thin edges,候选边缘)。
在获得梯度的方向和大小之后,应该对整幅图像做一个扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。
- 滞后双阈值化 ,高阈值和低阈值。Canny 推荐的高:低阈值比在 2:1 到3:1之间。
现在要确定那些边界才是真正的边界。这时我们需要设置两个阈值:minVal 和maxVal。当图像的灰度梯度高于maxVal 时被认为是真的边界,那些低于minVal 的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。
上图中,A 高于阈值maxVal 所以是真正的边界点,C 虽然低于maxVal 但高于
minVal 并且与A 相连,所以也被认为是真正的边界点。而B 就会被抛弃,因
为他不仅低于maxVal 而且不与真正的边界点相连。所以选择合适的maxVal
和minVal 对于能否得到好的结果非常重要。
在这一步一些小的噪声点也会被除去,因为我们假设边界都是一些长的线段。
python中的Canny函数说明如下
Canny(image,threshold1,threshold2,edges=None,apertureSize=None,L2gradient=None)
- threshold1:int类型的,低阈值
- threshold2:int类型的,高阈值
- edeges:单通道存储边缘的输出图像
- apertureSize:Sobel算子内核(kSize)大小
- L2gradiend:Bool类型的,真 表示使用更精确的L2范数进行计算(两个方向的倒数的平方再开放),假 表示用L1范数(直接将两个方向导数的绝对值相加)
可以自己写代码来观察阈值变化对图像带来的影响
# -*- coding: cp936 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt
def nothing(x):
pass
cv2.namedWindow('res')
cv2.createTrackbar('max','res',0,300,nothing)
cv2.createTrackbar('min','res',0,200,nothing)
img = cv2.imread('test.jpg')
maxVal=100
minVal=1
while (1):
if cv2.waitKey(20) & 0xFF==27: #20ms中如果按下ESC退出循环。27是ESC的ASCII码
break
maxVal = cv2.getTrackbarPos('max','res')
minVal = cv2.getTrackbarPos('min','res')
if minVal > maxVal:
edge = cv2.Canny(img,200,300)
else:
edge = cv2.Canny(img,minVal,maxVal)
cv2.imshow('res',edge)
cv2.destoryAllWindows()
运行结果
可以发现,当改变阈值后,图像后渐渐过滤掉一些背景,当然,重在阈值的选取上,当阈值选大的时候,也是会将图像的特征完全损坏的。
opencv中还有其他边缘检测方法,参见https://blog.csdn.net/qq_34711208/article/details/81703341