一、直线检测
使用霍夫直线变换做直线检测,前提条件:边缘检测已经完成
# 标准霍夫线变换cv2.HoughLines(image, rho, theta, threshold, lines=None, srn=None, stn=None, min_theta=None, max_theta=None)
- image:经过边缘检测的输出图像,8位,单通道二进制源图像。
- rho:距离步长
- theta:角度步长
- threshold:阈值,只有大于该值的点才有可能被当作极大值,即至少有多少条正弦曲线交于一点才被认为是直线。
# 统计概率霍夫线变换cv2.HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None)
- 经过边缘检测的输出图像,8位,单通道二进制源图像。
- rho:参数极径 r ,以像素值为单位的分辨率,这里一般使用 1 像素。
- theta:参数极角theta, 以弧度为单位的分辨率,这里使用 1 度。
- threshold:检测一条直线所需最少的曲线交点
- minLineLength:线的最短长度,比这个线短的都会被忽略。
- maxLineGap:两条线之间的最大间隔,如果小于此值,这两条线就会被看成一条线。
代码如下:
importcv2importnumpyasnp# 标准霍夫线变换defline_detection_demo(image): # 灰度图像gray=cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 提取边缘edges=cv2.Canny(gray, 50, 150, apertureSize=3) lines=cv2.HoughLines(edges, 1, np.pi/180, 160) forlineinlines: rho, theta=line[0] # line[0]存储的是点到直线的极径和极角,其中极角是弧度表示的a=np.cos(theta) # theta是弧度b=np.sin(theta) x0=a*rhoy0=b*rhox1=int(x0+1000* (-b)) # 直线起点横坐标y1=int(y0+1000*a) # 直线起点纵坐标x2=int(x0-1000* (-b)) # 直线终点横坐标y2=int(y0-1000*a) # 直线终点纵坐标cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imshow("image_lines", image) # 统计概率霍夫线变换defline_detect_possible_demo(image): gray=cv2.cvtColor(image, cv2.COLOR_BGRA2GRAY) edges=cv2.Canny(gray, 50, 150, apertureSize=3) lines=cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength=50, maxLineGap=10) forlineinlines: x1, y1, x2, y2=line[0] cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imshow("line_detect_possible", image) if__name__=="__main__": src=cv2.imread(r"./test/041.png") cv2.imshow("image", src) line_detection_demo(src) line_detect_possible_demo(src) cv2.waitKey(0) cv2.destroyAllWindows()
运行效果如下:
二、圆检测
- 霍夫圆变换的基本原理和霍夫线变换原理类似,只是点对应的二维极径、极角空间被三维的圆心和半径空间取代。在标准霍夫圆变换中,原图像的边缘图像的任意点对应的经过这个点的所有可能圆在三维空间用圆心和半径这三个参数来表示,其对应一条三维空间的曲线。对于多个边缘点,点越多,这些点对应的三维空间曲线交于一点的数量越多,那么他们经过的共同圆上的点就越多,类似的我们也就可以用同样的阈值的方法来判断一个圆是否被检测**到,这就是标准霍夫圆变换的原理, 但也正是在三维空间的计算量大大增加的原因,**标准霍夫圆变化很难被应用到实际中。
- OpenCV实现的是一个比标准霍夫圆变换更为灵活的检测方法——霍夫梯度法,该方法运算量相对于标准霍夫圆变换大大减少。其检测原理是依据圆心一定是在圆上的每个点的模向量上,这些圆上点模向量的交点就是圆心,霍夫梯度法的第一步就是找到这些圆心,这样三维的累加平面就又转化为二维累加平面。第二步是根据所有候选中心的边缘非 0 像素对其的支持程度来确定半径。注:模向量即是圆上点的切线的垂直线。
cv2.HoughCircles(image, method, dp, minDist, circles=None, param1=None, param2=None, minRadius=None, maxRadius=None)
- image:输入图像,8位单通道灰度图像。
- method:圆检测方法
- dp:参数表示累加器与原始图像相比的分辨率的反比参数。例如,如果dp = 1,则累加器具有与输入图像相同的分辨率。如果dp=2,累加器分辨率是元素图像的一半,宽度和高度也缩减为原来的一半。
- minDist:检测到的两个圆心之间的最小距离。如果参数太小,除了真实的一个圆圈之外,可能错误地检测到多个相邻的圆圈。如果太大,可能会遗漏一些圆圈。
- circles:检测到的圆的输出向量,向量内第一个元素是圆的横坐标,第二个是纵坐标,第三个是半径大小。
- param1:Canny边缘检测的高阈值,低阈值会被自动置为高阈值的一半。
- param2:圆心检测的累加阈值,参数值越小,可以检测越多的假圆圈,但返回的是与较大累加器值对应的圆圈。
- minRadius:检测到的圆的最小半径
- maxRadius:检测到的圆的最大半径
代码如下:
importcv2ascvimportnumpyasnp# 霍夫圆检测defdetect_circles_demo(image): # 霍夫圆检测对噪声敏感 边缘保留滤波EPF 消除噪声dst=cv.pyrMeanShiftFiltering(image, 10, 105) # 均值偏移滤波cimage=cv.cvtColor(dst, cv.COLOR_RGB2GRAY) # 输入图像 方法 走步长 最小距离 边缘提取的低值circles=cv.HoughCircles(cimage, cv.HOUGH_GRADIENT, 1, 20, param1=50, param2=30, minRadius=0, maxRadius=0) circles=np.uint16(np.around(circles)) # 把circles包含的圆心和半径的值变成整数foriincircles[0, :]: cv.circle(image, (i[0], i[1]), i[2], (0, 0, 255), 2) # 画圆cv.circle(image, (i[0], i[1]), 2, (255, 0, 0), 2) # 画圆心cv.imshow("circles", image) if__name__=="__main__": src=cv.imread(r"./test/035.png") cv.imshow('input_image', src) detect_circles_demo(src) cv.waitKey(0) cv.destroyAllWindows()
运行效果如下:
三、轮廓发现
""" cv2.findContours(image, mode, method, contours, hierarchy, offset) 参数: 1 要寻找图像的轮廓 只能传入二值图像,不是灰度图像 2 轮廓的检索模式,有四种: cv2.RETR_EXTERNAL 表示只检测外轮廓 cv2.RETR_LIST 检测的轮廓不建立等级关系 cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层 cv2.RETR_TREE 建立一个等级树结构的轮廓 3 轮廓的近似办法 cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1 cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息 返回值: contours:一个列表,每一项都是一个轮廓,不会存储轮廓所有的点,只存储能描述轮廓的点 hierarchy:一个ndarray, 元素数量和轮廓数量一样, 每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0] ~hierarchy[i][3], 分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数 """""" # 函数cv2.drawContours(image, contours, contourIdx, color, thickness, lineType, hierarchy, maxLevel, offset) # 第一个参数是一张图片,可以是原图或者其他。 # 第二个参数是轮廓,也可以说是cv2.findContours()找出来的点集,一个列表。 # 第三个参数是对轮廓(第二个参数)的索引,当需要绘制独立轮廓时很有用,若要全部绘制可设为-1。 # 接下来的参数是轮廓的颜色和线宽"""
代码如下:
# -*- coding: UTF-8 -*-"""@公众号 : AI庭云君@Author : 叶庭云@CSDN : https://yetingyun.blog.csdn.net/"""importcv2defcontours_demo(image): dst=cv2.GaussianBlur(image, (3, 3), 0) gray=cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY) ret, thresh=cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU) # 得到修改后的图像,轮廓点集 各层轮廓的索引contours, hierarchy=cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) fori, contourinenumerate(contours): # 绘制轮廓 第几个 颜色 线宽cv2.drawContours(image, contours, i, (0, 0, 255), -1) print(i) cv2.imshow("detect contours", image) if__name__=="__main__": img=cv2.imread(r"./test/042.png") cv2.namedWindow("input image", cv2.WINDOW_AUTOSIZE) cv2.imshow("input image", img) contours_demo(img) cv2.waitKey(0) cv2.destroyAllWindows()
运行效果如下:
绘制轮廓时,线宽设置为-1,实现填充,效果如下:
通过Canny算法获取二值图像,再进行轮廓发现
# -*- coding: UTF-8 -*-"""@公众号 : AI庭云君@Author : 叶庭云@CSDN : https://yetingyun.blog.csdn.net/"""importcv2defedge_demo(image): # 高斯模糊 降低噪声blurred=cv2.GaussianBlur(image, (3, 3), 0) # 转为灰度图像gray=cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY) # 计算x y 方向梯度grad_x=cv2.Sobel(gray, cv2.CV_16SC1, 1, 0) grad_y=cv2.Sobel(gray, cv2.CV_16SC1, 0, 1) edge_output=cv2.Canny(grad_x, grad_y, 50, 100) returnedge_outputdefcontours_demo(image): binary=edge_demo(image) # 得到修改后的图像,轮廓点集 各层轮廓的索引contours, hierarchy=cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) fori, contourinenumerate(contours): # 绘制轮廓 第几个 颜色 线宽cv2.drawContours(image, contours, i, (0, 0, 255), 2) print(i) cv2.imshow("detect contours", image) if__name__=="__main__": img=cv2.imread(r"./test/036.png") cv2.namedWindow("input image", cv2.WINDOW_AUTOSIZE) cv2.imshow("input image", img) contours_demo(img) cv2.waitKey(0) cv2.destroyAllWindows()
运行效果如下: