轮廓检测 (Contour Detection)

1.基本轮廓检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import cv2
import numpy as np

# 读取图像并转为灰度图
img = cv2.imread('image.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化处理
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 查找轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 绘制轮廓
result = img.copy()
cv2.drawContours(result, contours, -1, (0, 255, 0), 2)

2.轮廓检索模式

  • cv2.RETR_EXTERNAL: 只检索外部轮廓
  • cv2.RETR_LIST: 检索所有轮廓,不建立层级关系
  • cv2.RETR_CCOMP: 检索所有轮廓,建立两级层级关系
  • cv2.RETR_TREE: 检索所有轮廓,建立完整层级关系

3.轮廓近似方法

  • cv2.CHAIN_APPROX_NONE: 存储所有轮廓点

  • cv2.CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只保留端点

4.轮廓属性计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for contour in contours:
# 轮廓面积
area = cv2.contourArea(contour)

# 轮廓周长
perimeter = cv2.arcLength(contour, True)

# 边界矩形
x, y, w, h = cv2.boundingRect(contour)

# 最小外接圆
(cx, cy), radius = cv2.minEnclosingCircle(contour)

# 轮廓重心
M = cv2.moments(contour)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])

连通域计算 (Connected Components)

1.基本连通域分析

1
2
3
4
5
6
# 使用cv2.connectedComponents
num_labels, labels = cv2.connectedComponents(binary)

# 创建彩色标签图像
label_img = np.uint8(255 * labels / np.max(labels))
colored_labels = cv2.applyColorMap(label_img, cv2.COLORMAP_JET)

2.带统计信息的连通域分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 使用cv2.connectedComponentsWithStats
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary)

# stats包含每个连通域的统计信息:
# [x, y, width, height, area]
for i in range(1, num_labels): # 跳过背景(label=0)
x, y, w, h, area = stats[i]
cx, cy = centroids[i]

print(f"连通域 {i}: 面积={area}, 中心=({cx:.1f}, {cy:.1f})")

# 在图像上标记
cv2.rectangle(result, (x, y), (x+w, y+h), (255, 0, 0), 2)
cv2.circle(result, (int(cx), int(cy)), 3, (0, 0, 255), -1)

3.连通域过滤

1
2
3
4
5
6
7
8
9
10
# 根据面积过滤小连通域
min_area = 100
filtered_labels = labels.copy()

for i in range(1, num_labels):
if stats[i, cv2.CC_STAT_AREA] < min_area:
filtered_labels[filtered_labels == i] = 0

# 重新标记过滤后的连通域
filtered_binary = (filtered_labels > 0).astype(np.uint8) * 255

4.保存所有连通域

1
2
3
4
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(g_cam, connectivity=8)
output_image = np.zeros_like(g_cam)
for i in range(1, num_labels):
output_image[labels == i] = 255

5.单独保存连通域

1
2
3
4
5
6
7
for label in range(1, num_labels):
#################
# 单个连通域
#################
component_image = np.zeros_like(labels)
component_image[labels == label] = 255
component_image = np.uint8(component_image)