边缘检测和轮廓检测
先说两个滤波器
高通滤波器 和 低通滤波器
**高通滤波**主要是将那些比它周围像素亮度强的像素提取出来让它更加亮,一般用作边缘提取
**低通滤波**是在像素与周围像素亮度差值小于一个特定值时,平滑该像素的亮度。主要是用于去噪
然后再说一个核:
核是指一组权重得集合,他会运用在源图像得一个区域,并由此生成目标图像的一个像素。可以把核看作一块可以在图像上可以移动得毛玻璃,玻璃片覆盖区域得光线会按照某种方式进行扩散混合后透过去
话不多说上代码
import numpy as np
import cv2 as cv
from scipy import ndimage
kernal_3x3 = np.array([[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]])
kernal_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]])
src = cv.imread('img3.jpg',0) #加入参数0,将图片转化为灰度图
k3 = ndimage.convolve(src, kernal_3x3)
k5 = ndimage.convolve(src, kernal_5x5)
blurred = cv.GaussianBlur(src, (11, 11), 0) #参数(11, 11)表示的是做卷积的核数
g_hbf = src - blurred
cv.imshow('3x3', k3) # 三核的滤波
cv.imshow('5x5', k5) # 五核的滤波
cv.imshow('g_hbf', g_hbf) #高通滤波器
cv.waitKey(0)
cv.destroyAllWindows()
#在这里我让核得所有值相加为0,当然你也可以根据不同得需求做出一定得更改
在代码中有一个注意得问题就是
运行结果如下
由于要通过核与图像做卷积来完成,但是numpy只接受一维数组,所以在这里使用得是ndimage.convole(),注意核数一定为奇数
很明显采用高斯模糊的效果很好
边缘检测有很多种,但最常用也是检测效果最好的还是canny检测
直接上代码
src = cv.imread('tools.jpg')
canny = cv.Canny(src, 230, 300)
cv.imshow('img3.jpg',canny)
cv.imshow('src', src)
cv.waitKey(0)
cv.destroyAllWindows()
让我们看看结果
就短短的几行代码
但是实际上它进行了五个步骤,即使用高斯滤波器对图片进行去噪,计算梯度,在边缘上使用最大抑制(NMS),在检测到边缘上使用双阈值去除假阳性,最后还会分析所有边缘及其之间的联系,以确保留下真正的边缘并消除不明显的边缘
接下来让我们玩玩轮廓提取
步骤如下:
1)将图像转化为灰度图
2)将灰度图转化为二值图
3)提取轮廓信息
4)在白板画出轮廓
import cv2 as cv
src = cv.imread('img3.jpg')
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
_,binary = cv.threshold(gray, 100, 255, cv.THRESH_BINARY)
contours, h = cv.findContours(binary,cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
h,w,ch = src.shape
blank = np.zeros([h, w, ch], src.dtype)
res = cv.drawContours(blank, contours, -1, (255, 255, 255))
cv.imshow('res',res)
cv.waitKey(0)
cv.destroyAllWindows()
运行结果如下:
有几个函数需要解释以下
cv.threshold(gray, 阈值1, 最大值, 处理方法)
gray为灰度图,阈值1为一个边界值,比如小于阈值1就变成 0, 大于阈值1就变成 255,这就要看最后一个参数是怎样去处理,他会返回一个二值图
cv.findContours(binary, 处理方法, 保存方法) 这个很好理解
cv.drawContours(blank, 坐标,颜色, 线的粗细)
cv.threshold()对图像进行二值化处理
注:res, contours, hierarchy = cv.findContours(image, mode, method)
- res表示处理后的图像,不过在现在cv4的话就可以不用这个值,因为这个函数本身会对图像进行修改
- contours返回的轮廓数组,每个轮廓组由(一系列坐标组成)
- hierarchy:图像的拓扑结构
- image:需要处理的图像,此处必须为二值图像
- mode:轮廓检索模式{
1)cv.RETR_EXTERNAL:只检测外轮廓
2)cv.RETR_LIST:检测的轮廓不建立等级关系
3)cv.RETR_CCOMP:建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边信息
4)cv.RETR_TREE:建立一个等级数结构的轮廓,常用这个方法
} - method:轮廓的保存方法{
1)cv.CHAIN_APPROX_NONE:保存所有的轮廓点
2)cv.CHAIN_APPOX_SIMPLE:压缩水平方向,垂直方向,对角线方向上的元素,只保存某个方向上的两个点(可以简单的理解为只保存一条线上的两个点),这是常用的方法
}
res = cv.drawContours(img, contours, contourIdx, color[,thickness])
res 为处理后的图像
img 需要处理的原图像, 这里会改变原图像,所以采用了img.copy()
contours:轮廓
contourx:需要描绘的轮廓的下标, -1表示绘制所有轮廓
color:需要绘制轮廓的颜色
thickness:轮廓的粗细
‘’’