哈喽,大家好,我们今天了解一下OpenCV中的边缘检测功能实现。在一些案例中,我们需要对物体进行边缘检测,而且是越精准越好。那么,OpenCV提供了哪些边缘检测的方法呢?
我们先看一个例子,看看到底什么是边缘检测,效果又是如何?
代码语言:javascript复制import cv2
import numpy as np
from scipy import ndimage # 提供了基础的图像处理功能
# 3x3的卷积核,卷积核里的参数或权重加起来等于0
kernel_3x3 = np.array([[-1,-1,-1],[-1,6,-1],[-1,-1,-1]])
# 5x5的卷积核,卷积核里的参数或权重加起来等于0
kernel_5x5 = np.array( [[-1,-1,-1,-1,-1],
[-1,1,2,1,-1],
[-1,2,4,2,-1],
[-1,1,2,1,-1],
[-1,-1,-1,-1,-1]])
img = cv2.imread('02.jpg', cv2.IMREAD_GRAYSCALE) # 读取图片,灰度
k3 = ndimage.convolve(img, kernel_3x3) # 卷积运算
# 关于 ndimage.convolve(),参考: https://docs.scipy.org/doc/scipy-0.18.1/reference/generated/scipy.ndimage.convolve.html
k5 = ndimage.convolve(img, kernel_5x5)
blurred = cv2.GaussianBlur(img, (17,17), 0) # (17,17)高斯内核大小,标准差为0
hpf = img - blurred # HPF是一种算法,不需要深究
cv2.imshow('gray_img', img) # 原始灰度图
cv2.imshow('3x3', k3) # 经过3x3卷积后的图
cv2.imshow('5x5', k5) # 经过5x5卷积后的图
cv2.imshow('blurred', blurred) # 经过高斯运算后的图
cv2.imshow('hpf',hpf) # 经过HPS后的图
cv2.waitKey()
cv2.destroyAllWindows()
运算结果如下:
① 原始灰度图
② 经过3x3卷积后的图
③ 经过5x5卷积后的图
④ 经过高斯运算后的图
⑤ 经过HPS后的图
或许同学们对代码中涉及到的一些算法有些疑惑,这里简述一下"高斯滤波"算法。
高斯滤波是一种线性平滑滤波,对于除去高斯噪声有很好的效果。高斯算法在官方文档给出的解释是高斯滤波是通过对输入数组的每个点与输入的高斯滤波模板执行卷积计算然后将这些结果一块组成了滤波后的输出数组,通俗的讲就是高斯滤波是对整幅图像进行加权平均的过程,每一个像素点的值都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
Canny 边缘检测算法(使用比较频繁)
1. 该算法比较复杂,不过,实现起来共5步,我们看一下:
① 首先用Gaussian滤波对图片进行降噪;
② 计算梯度;
③ 在边缘使用NMS(非极大值抑制)【关于该算法的讲解,后续我会分享】,筛选出最优的边缘检测;
④ 对所有检测到的边缘应用双阈值(比如下面案例中写的200和300);
⑤ 分析所有边缘以及彼此之间的连接,保留真正的边缘,丢弃弱边缘。
2. Canny算法案例:
代码语言:javascript复制# Canny 算法
import cv2
import numpy as np
img = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE) # 读取图片,转换为灰度
cv2.imwrite('test_canny.jpg', cv2.Canny(img, 200, 300)) # Canny边缘检测,然后保存新图片
cv2.imshow('Canny', cv2.imread('test_canny.jpg')) # 显示新图片
cv2.waitKey(0)
cv2.destroyAllWindows()
输入图像:
输出图像:
轮廓检测案例:
代码语言:javascript复制import cv2
import numpy as np
img = np.zeros((200, 200), dtype=np.uint8) # 黑色
img[50:150, 50:150] = 255 # 这个区域内的像素值更新为255,白色
ret, thresh = cv2.threshold(img, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
"""方法解析:
cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
参数:
第一个参数是寻找轮廓的图像;
第二个参数表示轮廓的检索模式,有四种(本文介绍的都是新的cv2接口):
cv2.RETR_EXTERNAL 表示只检测外轮廓
cv2.RETR_LIST 检测的轮廓不建立等级关系
cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
cv2.RETR_TREE 建立一个等级树结构的轮廓。
第三个参数method为轮廓的近似办法
cv2.CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法
返回值
cv2.findContours()函数返回两个值,一个是轮廓本身,还有一个是每条轮廓对应的属性。
"""
color = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
img = cv2.drawContours(color, contours, -1, (0, 255, 0), 2)
"""方法解析:
cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
参数:
第一个参数是指明在哪幅图像上绘制轮廓;image为三通道才能显示轮廓
第二个参数是轮廓本身,在Python中是一个list;
第三个参数指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓。
第四个参数指定轮廓颜色;
第五个参数表明轮廓线的宽度,如果是-1(cv2.FILLED),则为填充模式。
"""
cv2.imshow('contours', color)
cv2.waitKey()
cv2.destroyAllWindows()
输出结果: