本文主要是介绍分水岭算法分割和霍夫变换识别图像中的硬币,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
首先解释一下第一种分水岭算法:
一、分水岭算法
分水岭算法是一种基于拓扑学的图像分割技术,广泛应用于图像处理和计算机视觉领域。它将图像视为一个拓扑表面,其中亮度值代表高度。算法的目标是通过模拟雨水从山顶流到山谷的过程,将图像分割成若干独立的区域。
分水岭算法的步骤和原理:
-
距离变换:
- 首先对图像进行预处理,将图像转化为灰度图,并进行二值化处理(如Otsu算法)。
- 对二值图像应用距离变换,计算每个前景像素到最近的背景像素的距离,生成距离图。距离变换后的图像可以看作是一幅"地形图",前景像素的距离值越大,代表的高度越高。
-
寻找局部极大值:
- 在距离图中找到局部极大值点。这些点通常位于目标物体的中心位置,将作为初始标记。局部极大值点是那些比其邻域像素值更大的点。
-
创建标记图:
- 创建一个与原始图像大小相同的标记图,将局部极大值点的位置赋值为不同的标签(从1开始编号),其余区域标记为0。
-
应用分水岭算法:
- 将距离图的负值作为输入图像,标记图作为初始标记,应用分水岭算法。分水岭算法通过模拟水从局部极大值点流向低谷的过程,不断合并像素,形成分割区域。
- 在这个过程中,水从局部极大值点流向低谷,如果两个不同的标签的水流在某处相遇,该处将被标记为边界。
-
生成分割结果:
- 分水岭算法最终会将图像分割成多个区域,每个区域对应一个标签。边界区域通常被标记为0。
分水岭算法的优点和缺点:
优点:
- 分水岭算法可以生成闭合的区域边界,适用于目标物体具有明确边界的图像。
- 算法可以自动确定分割区域的数量,无需事先设定。
缺点:
- 对噪声和边缘模糊敏感,容易产生过分割,即将一个目标物体分割成多个区域。
- 需要进行预处理以减少噪声和增强边缘(如均值漂移滤波)。
示例代码解释:
# 计算每个二值像素到最近零像素的精确欧几里得距离, 然后找到此距离图中的局部峰值
D = ndimage.distance_transform_edt(thresh)
localMax = peak_local_max(D, footprint=np.ones((3, 3)), min_distance=40, labels=thresh)# 根据找到的局部峰值创建标记数组, 标记数组的值对应于每个硬币的序号
markers = np.zeros_like(thresh, dtype=np.int32)
markers[tuple(localMax.T)] = np.arange(1, len(localMax) + 1)# 应用分水岭算法, 将图像分割为不同的区域
labels = watershed(-D, markers, mask=thresh)
- 计算距离变换:
ndimage.distance_transform_edt(thresh)
计算每个前景像素到最近背景像素的欧几里得距离,生成距离图D
。 - 寻找局部极大值:
peak_local_max(D, footprint=np.ones((3, 3)), min_distance=40, labels=thresh)
在距离图中寻找局部极大值,这些点将作为初始标记。 - 创建标记图:
markers
初始化为全零矩阵,将局部极大值点的位置赋值为不同的标签。 - 应用分水岭算法:
labels = watershed(-D, markers, mask=thresh)
使用分水岭算法对距离图的负值进行分割,生成标签图labels
。
通过以上步骤,分水岭算法将输入图像分割成若干独立区域,每个区域代表一个目标物体。
以检测这张图为例子:
使用分水岭算法流程如下:
-
读取图像并应用均值漂移滤波:
- 使用
cv2.imread
读取输入图像。 - 使用
cv2.pyrMeanShiftFiltering
对图像进行均值漂移滤波,平滑图像并减少噪点。
- 使用
-
转换为灰度图并二值化:
- 使用
cv2.cvtColor
将平滑后的图像转换为灰度图。 - 使用
cv2.threshold
结合 Otsu 算法进行自动阈值二值化,将图像转换为二值图像。
- 使用
-
计算欧几里得距离并找到局部峰值:
- 使用
ndimage.distance_transform_edt
计算每个二值像素到最近零像素的欧几里得距离,生成距离变换图。 - 使用
peak_local_max
找到距离图中的局部峰值,这些峰值将作为分水岭算法的初始标记。
- 使用
-
创建标记数组并应用分水岭算法:
- 创建一个与二值图像大小相同的标记数组
markers
,将局部峰值的位置赋值为不同的标签。 - 使用
watershed
函数进行分水岭算法,将图像分割成不同区域,每个区域对应一个硬币。
- 创建一个与二值图像大小相同的标记数组
-
遍历分割出的不同区域,绘制轮廓和标签:
- 遍历分割后的标签,跳过背景标签(标签为0)。
- 为每个硬币创建一个掩码图像,设置对应标签区域为白色,其余区域为黑色。
- 使用
cv2.findContours
查找掩码图像中的轮廓,并找到最大的轮廓(即硬币区域)。 - 使用
cv2.minEnclosingCircle
计算最小外接圆的圆心坐标和半径。 - 在原始图像上绘制圆形轮廓和标签。
-
显示最终结果图像:
- 使用
cv2.imshow
显示处理后的图像。 - 使用
cv2.waitKey
和cv2.destroyAllWindows
控制显示窗口。
- 使用
上述流程通过图像平滑、二值化、距离变换、局部峰值检测和分水岭算法,实现了对硬币图像的分割,并在分割后的图像上绘制了硬币的轮廓和编号标签。
完整代码如下:
import numpy as np
import cv2
from skimage.feature import peak_local_max
from skimage.segmentation import watershed
from scipy import ndimage
import imutils# 读取图像并应用均值漂移滤波来平滑图像,减少噪点
image = cv2.imread('/coins/1.jpg')
shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)# 将图像转换为灰度图,然后使用Otsu算法自动确定阈值进行二值化
gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]# 计算每个二值像素到最近零像素的精确欧几里得距离,然后找到此距离图中的局部峰值
# 这些峰值将作为分水岭算法的初始标记
D = ndimage.distance_transform_edt(thresh)
localMax = peak_local_max(D, footprint=np.ones((3, 3)), min_distance=40, labels=thresh)# 根据找到的局部峰值创建标记数组,标记数组的值对应于每个硬币的序号
markers = np.zeros_like(thresh, dtype=np.int32)
markers[tuple(localMax.T)] = np.arange(1, len(localMax) + 1)# 应用分水岭算法,将图像分割为不同的区域
labels = watershed(-D, markers, mask=thresh)# 遍历分割出的不同区域,绘制出每个硬币的轮廓和标签
for label in np.unique(labels):if label == 0:continue# 创建一个掩码图像,将当前标签对应的区域设置为白色,其他区域设置为黑色mask = np.zeros(gray.shape, dtype="uint8")mask[labels == label] = 255# 查找掩码图像中的轮廓,并找到最大的轮廓(即硬币区域)cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)cnts = imutils.grab_contours(cnts)c = max(cnts, key=cv2.contourArea)# 计算最小外接圆的圆心坐标和半径((x, y), r) = cv2.minEnclosingCircle(c)# 在原始图像上绘制圆形轮廓和标签cv2.circle(image, (int(x), int(y)), int(r), (0, 255, 0), 2)cv2.putText(image, "{}".format(label), (int(x) - 10, int(y)),cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)# 显示最终的结果图像
cv2.imshow("Output", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
分割结果如下:
二、 霍夫变换
霍夫变换(Hough Transform)是图像处理中的一种重要技术,用于检测图像中的几何形状。霍夫圆检测(Hough Circle Transform)是霍夫变换的一个具体应用,用于检测图像中的圆形物体。
霍夫圆检测的原理和步骤:
-
边缘检测:
- 首先对图像进行边缘检测,常用的方法是Canny边缘检测。边缘检测可以提取出图像中的显著边缘,减少数据量并突出目标物体的轮廓。
-
参数空间定义:
- 在检测圆的过程中,需要定义圆的参数空间。一个圆由三个参数定义:圆心坐标 (x, y) 和半径 r。霍夫圆检测将在参数空间中搜索圆的可能位置和大小。
-
投票累加:
-
在边缘检测后的二值图像中,每个边缘点 (x, y) 都会在参数空间中投票支持可能的圆心和半径组合。具体而言,对于每个边缘点 (x, y) 和每个可能的半径 r,可以根据圆的方程计算圆心坐标 (a, b):
-
在参数空间中累加 (a, b) 的投票次数。
-
-
检测局部最大值:
- 在参数空间中,投票次数最多的位置即为最可能的圆心和半径组合。通过检测参数空间中的局部最大值,确定圆的存在和位置。
-
绘制检测到的圆:
- 根据检测到的圆心坐标和半径,在原始图像上绘制圆形轮廓。
示例代码:
以下是一个使用OpenCV进行霍夫圆检测的示例代码:
import cv2
import numpy as np# 读取图像
image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 应用高斯模糊,减少噪声
blurred = cv2.GaussianBlur(gray, (9, 9), 2)# 使用霍夫圆检测
circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, dp=1.2, minDist=20,param1=50, param2=30, minRadius=15, maxRadius=30)# 如果检测到圆
if circles is not None:circles = np.round(circles[0, :]).astype("int")for (x, y, r) in circles:# 绘制圆的轮廓cv2.circle(image, (x, y), r, (0, 255, 0), 4)# 绘制圆心cv2.rectangle(image, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)# 显示结果图像
cv2.imshow("output", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码详解:
-
读取图像并转换为灰度图:
image = cv2.imread('image.jpg') gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
-
应用高斯模糊:
- 使用高斯模糊(Gaussian Blur)来平滑图像,减少噪声。
blurred = cv2.GaussianBlur(gray, (9, 9), 2)
-
使用霍夫圆检测:
- 调用
cv2.HoughCircles
函数进行霍夫圆检测。参数解释如下:blurred
:输入的灰度图像。cv2.HOUGH_GRADIENT
:检测方法,使用梯度信息。dp=1.2
:累加器分辨率与图像分辨率的反比关系。minDist=20
:检测到的圆之间的最小距离。param1=50
:Canny边缘检测的高阈值。param2=30
:累加器阈值,用于检测圆的阈值,越小越容易检测到不明显的圆。minRadius=15
和maxRadius=30
:检测圆的半径范围。
circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, dp=1.2, minDist=20,param1=50, param2=30, minRadius=15, maxRadius=30)
- 调用
-
绘制检测到的圆:
- 如果检测到圆,将其绘制在原始图像上。
if circles is not None:circles = np.round(circles[0, :]).astype("int")for (x, y, r) in circles:cv2.circle(image, (x, y), r, (0, 255, 0), 4)cv2.rectangle(image, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
-
显示结果图像:
- 显示绘制了圆的结果图像。
cv2.imshow("output", image) cv2.waitKey(0) cv2.destroyAllWindows()
通过上述步骤和代码,霍夫圆检测可以在图像中自动识别和绘制圆形目标。
识别图中硬币的完整代码如下:
import cv2
import numpy as np# 读取图像并转换为灰度图像
image = cv2.imread('/coins/1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 应用高斯模糊
gray = cv2.GaussianBlur(gray, (15, 15), 0)# 使用霍夫圆变换检测圆
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1.2, minDist=50, param1=50, param2=30, minRadius=20,maxRadius=60)# 确保至少检测到一个圆
if circles is not None:circles = np.round(circles[0, :]).astype("int")for (i, (x, y, r)) in enumerate(circles):# 绘制圆圈和中心点cv2.circle(image, (x, y), r, (0, 255, 0), 2)cv2.putText(image, str(i + 1), (x - 10, y), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)# 显示最终结果图像
cv2.imshow("Detected Coins", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
检测结果如下:
这篇关于分水岭算法分割和霍夫变换识别图像中的硬币的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!