最近在做一个跟测量有关的项目,进行椭圆检测用不到深度学习,仅仅是简单的图像处理,但是确实把我难到了。这里分享两个椭圆检测的思路。一个是通过openCV的椭圆检测器实现的,另一个是通过霍夫变化实现的。
有一说一,看到里面各种没见过的处理方法,我就好像挨了一巴掌,然后告诉我:你的视觉还没入门呢。
一、 完整的椭圆检测流程
拿到一张图片,我们首先需要进行预处理
1. 图像预处理
- 灰度化:将彩色图像转换为灰度图像,简化处理过程。
- 滤波:使用高斯滤波、中值滤波等方法去除图像噪声。
- 边缘检测:应用Canny、Sobel等边缘检测算法来提取图像中的边缘信息。边缘是图像中物体轮廓的基础。
2. 轮廓检测
- 轮廓查找 :使用OpenCV等库中的
findContours
函数来检测图像中的轮廓。 - 轮廓筛选:根据轮廓的大小、形状等特征筛选出可能的椭圆轮廓。
3. 椭圆拟合
- 最小二乘法拟合:对筛选出的轮廓,使用最小二乘法拟合椭圆。常用的方法有基于代数距离的最小二乘法(直接拟合椭圆方程)和基于几何距离的最小二乘法(如椭圆拟合的RANSAC算法)。
- 椭圆检测器 :使用专门的椭圆检测器,如OpenCV中的
fitEllipse
函数,它可以直接在轮廓上拟合一个椭圆。
4. 验证和优化
- 验证拟合结果:通过计算拟合椭圆的参数(如长短轴、中心点、旋转角度等)与图像中实际轮廓的匹配程度来验证拟合结果。
- 优化参数:根据实际需求调整滤波、边缘检测、轮廓筛选等步骤的参数,以获得更好的识别效果。
二、 openCV的椭圆检测器操作示例
1. 图像预处理
js
## 获取图像
imageSrc = './images/droppy.png'
image = cv2.imread(imageSrc)
## 1. 灰度处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 2. 边缘检测
edges = cv2.Canny(blurred, 50, 150)
## 侵蚀尺度设置
length = max(gray.shape)
# 膨胀和腐蚀可以帮助减少噪声和小轮廓
kernel = np.ones((int(length/20), int(length/20)), np.uint8)
edges = cv2.dilate(edges, kernel, iterations=1)
edges = cv2.erode(edges, kernel, iterations=1)
# 3. 轮廓追踪
_, contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
这里为了更好的排除一些小的轮廓,又加了一步侵蚀,效果确实不错。
这是原图、灰度和边缘检测后的对比图。
这是轮廓追踪后的效果图
2. 筛选检测椭圆
对于轮廓需要进行椭圆拟合,然后筛选不合规的椭圆。我这里进行了两步筛选。
- 检查椭圆中心是否在图像内部
- 对椭圆按照面积进行排序,找到面积最大椭圆
检测长宽比,因为范围不好界定我给注释掉了,需要的可以加上。
js
ellipses = []
for contour in contours:
# 1. 检测椭圆
ellipse = cv2.fitEllipse(contour)
# 椭圆中心坐标;椭圆长轴和短轴的长度(直径);椭圆旋转角度
center, axes, orientation = ellipse
# 2. 检查椭圆中心是否在图像内部
if 0 <= center[0] <= image.shape[0] and 0 <= center[1] <= image.shape[1]:
# min_aspect_ratio = 0.7
# max_aspect_ratio = 1.3
# # 检测长宽比
# if min_aspect_ratio <= aspect_ratio <= max_aspect_ratio:
ellipses.append(ellipse)
# cv2.ellipse(image, ellipse, (0, 255, 255), 2) # 绘制黄色
# 3.筛选椭圆
# 对椭圆按照面积进行排序,找到面积最大的圆
sorted_ellipses = sorted(ellipses, key=lambda item: item[1][0] * item[1][1], reverse=True)
selectedEllipse = sorted_ellipses[0]
cv2.ellipse(image, selectedEllipse, (0, 255, 0), 2)
cv2.imshow('Ellipse', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 结果图
绿色就是我们筛选出的椭圆,可以说是非常完美
三、霍夫变换检测操作示例
基本操作方式与上面基本相同
1. 加载图片,转换成灰度图并检测边缘
js
from skimage import data,draw,color,transform,feature
import cv2
#1. 加载图片,转换成灰度图并检测边缘
image_rgb = data.coffee()[0:220, 160:420] #裁剪原图像,不然速度非常慢
image_gray = color.rgb2gray(image_rgb)
edges = feature.canny(image_gray, sigma=2.0, low_threshold=0.55, high_threshold=0.8)
2. 执行椭圆变换
js
#2. 执行椭圆变换
result =transform.hough_ellipse(edges, accuracy=20, threshold=250,min_size=100, max_size=120)
3. 估计椭圆参数
js
#3. 估计椭圆参数
result.sort(order='accumulator') #根据累加器排序
best = list(result[-1]) #排完序后取最后一个
yc, xc, a, b = [int(round(x)) for x in best[1:5]]
orientation = best[5]
selectedEllipse = ((xc,yc),(2*a,2*b),orientation)
4. 在原图上画出椭圆
js
#4. 在原图上画出椭圆
cv2.ellipse(image_rgb, selectedEllipse, (0, 255, 0), 2)
cv2.imshow('Ellipse', image_rgb)
cv2.waitKey(0)
cv2.destroyAllWindows()
四、椭圆检测器和霍夫变换的优劣分析
-
椭圆检测器精度一般,速度较快。 他核心的原理是获得能够包围轮廓的最小椭圆,所以如果轮廓识别不精准,椭圆识别效果就会很差。
-
霍夫变换的精度很好,但是速度太慢,尤其是像素比较大的图片,会让你怀疑是不是程序卡死了。 霍夫变换更贴近我们想要识别的椭圆,但是需要注意对图片进行压缩。