橘智橘智
FakeOrange
预计阅读时间:4分钟10秒

OpenCV的Watershed算法进行数量检测

分水岭算法数量检测(对复杂任务更通用)

0
0

1. 概述

  • 目标:通过Watershed算法检测图像中的物体数量,适用于处理重叠或紧密接触的物体。
  • 使用技术

2. 环境准备

与前面相同,确保安装以下库:

pip install opencv-python numpy

3. 图像读取与预处理

与之前的步骤相同,我们先读取图像并进行灰度化、模糊和二值化。

import cv2
import numpy as np

# 读取图像
img = cv2.imread('objects.jpg')

# 转为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 应用高斯模糊
blurred = cv2.GaussianBlur(gray, (11, 11), 0)

# 二值化处理
_, thresh = cv2.threshold(blurred, 150, 255, cv2.THRESH_BINARY_INV)

# 显示预处理结果
cv2.imshow('Threshold Image', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

4. 应用形态学操作

我们使用形态学操作对物体进行膨胀操作,以帮助分离连在一起的物体。

# 定义核
kernel = np.ones((3, 3), np.uint8)

# 应用开运算(先腐蚀后膨胀)去除噪声
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)

# 膨胀操作
sure_bg = cv2.dilate(opening, kernel, iterations=3)

# 显示膨胀后的背景
cv2.imshow('Sure Background', sure_bg)
cv2.waitKey(0)
cv2.destroyAllWindows()

代码说明

  • 开运算:通过腐蚀再膨胀去除小噪声点。
  • 膨胀:进一步扩展物体边界,确保可以识别出物体的背景。

5. 应用距离变换

使用距离变换来帮助区分相邻的物体。

# 距离变换
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)

# 对结果进行阈值化,确保只有物体的中心区域被保留
_, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)

# 转换为uint8类型
sure_fg = np.uint8(sure_fg)

# 找到未知区域(背景减去前景)
unknown = cv2.subtract(sure_bg, sure_fg)

# 显示距离变换后的前景
cv2.imshow('Sure Foreground', sure_fg)
cv2.waitKey(0)
cv2.destroyAllWindows()

代码说明

  • 距离变换:根据物体中心的距离将其标记为前景区域。
  • 阈值化:通过设置阈值,仅保留物体的核心区域。

6. 应用 Watershed 算法

我们使用 Watershed 来区分相邻或重叠的物体。

# 标记连通区域
_, markers = cv2.connectedComponents(sure_fg)

# 对marker加1,确保背景为0
markers = markers + 1

# 将未知区域标记为0
markers[unknown == 255] = 0

# 应用Watershed算法
markers = cv2.watershed(img, markers)

# 边界处的标记为-1
img[markers == -1] = [0, 0, 255]

# 显示分割后的图像
cv2.imshow('Watershed Result', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

代码说明

  • 连通区域标记:我们为每个物体分配一个唯一的标签。
  • Watershed算法:通过 cv2.watershed 对图像进行分割,重叠物体的边界会被正确识别。

7. 检测物体数量

通过检测分割后的标记区域,统计物体数量。

# 统计物体数量(去除背景和边界标记)
num_objects = np.max(markers) - 1  # 减去背景和边界的标记
print(f"检测到的物体数量: {num_objects}")

代码说明

  • np.max(markers):标记值的最大值代表了物体的数量。
  • 减去1是为了排除背景的标记。

8. 完整代码

下面是完整的代码,使用 Watershed 算法进行物体数量检测。

import cv2
import numpy as np

# 读取图像
img = cv2.imread('objects.jpg')

# 转为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 应用高斯模糊
blurred = cv2.GaussianBlur(gray, (11, 11), 0)

# 二值化处理
_, thresh = cv2.threshold(blurred, 150, 255, cv2.THRESH_BINARY_INV)

# 定义核
kernel = np.ones((3, 3), np.uint8)

# 应用开运算去除噪声
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)

# 膨胀操作
sure_bg = cv2.dilate(opening, kernel, iterations=3)

# 距离变换
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)

# 对结果进行阈值化,确保只有物体的中心区域被保留
_, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)

# 转换为uint8类型
sure_fg = np.uint8(sure_fg)

# 找到未知区域(背景减去前景)
unknown = cv2.subtract(sure_bg, sure_fg)

# 标记连通区域
_, markers = cv2.connectedComponents(sure_fg)

# 对marker加1,确保背景为0
markers = markers + 1

# 将未知区域标记为0
markers[unknown == 255] = 0

# 应用Watershed算法
markers = cv2.watershed(img, markers)

# 边界处的标记为-1
img[markers == -1] = [0, 0, 255]

# 显示分割后的图像
cv2.imshow('Watershed Result', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 统计物体数量(去除背景和边界标记)
num_objects = np.max(markers) - 1
print(f"检测到的物体数量: {num_objects}")

9. 总结

通过结合 Watershed 算法,我们能够有效地分离重叠或紧密接触的物体,并进行数量检测。这种方法特别适用于需要分割物体边界的复杂场景,例如硬币、细胞等。


评论