隔离分割对象
在执行分割任务后,有时需要从推理结果中提取孤立的对象。本指南提供了一个通用方法,介绍如何使用Ultralytics的预测模式来实现这一目标。
逐步指南
-
查看Ultralytics快速入门安装部分,了解快速安装所需库的步骤。
-
加载模型并在源上运行
predict()
方法。没有预测参数?
如果没有指定源,将使用库中的示例图像:
这对于使用
predict()
方法进行快速测试很有帮助。有关分割模型的更多信息,请访问分割任务页面。要了解更多关于
predict()
方法的信息,请参阅文档中的预测模式部分。
-
现在遍历结果和轮廓。对于希望将图像保存到文件的工作流程,检索源图像的
base-name
和检测的class-label
以供后续使用(可选)。from pathlib import Path import numpy as np # (2) 遍历检测结果(有助于处理多张图像) for r in res: img = np.copy(r.orig_img) img_name = Path(r.path).stem # 源图像的base-name # 遍历每个对象轮廓(多个检测) for ci, c in enumerate(r): # (1) 获取检测类名 label = c.names[c.boxes.cls.tolist().pop()]
- 要了解更多关于处理检测结果的信息,请参阅预测模式的Boxes部分。
- 要了解更多关于
predict()
结果的信息,请参阅预测模式的处理结果。
For-Loop
单张图像只会遍历第一个循环一次。单张图像且只有一个检测时,每个循环只会遍历一次。
-
从源图像生成二进制掩码,然后在掩码上绘制填充的轮廓。这将允许从图像的其他部分中隔离对象。右侧显示了
bus.jpg
中检测到的person
类对象之一的示例。import cv2 # 创建二进制掩码 b_mask = np.zeros(img.shape[:2], np.uint8) # (1) 提取轮廓结果 contour = c.masks.xy.pop() # (2) 更改类型 contour = contour.astype(np.int32) # (3) 重塑形状 contour = contour.reshape(-1, 1, 2) # 在掩码上绘制轮廓 _ = cv2.drawContours(b_mask, [contour], -1, (255, 255, 255), cv2.FILLED)
-
有关
c.masks.xy
的更多信息,请参阅预测模式的Masks部分。 -
这里将值转换为
np.int32
,以与OpenCV的drawContours()
函数兼容。 -
OpenCV的
drawContours()
函数期望轮廓具有[N, 1, 2]
的形状,展开下面的部分以获取更多详细信息。
展开以理解定义
contour
变量时发生的情况。-
c.masks.xy
:: 提供掩码轮廓点的坐标,格式为(x, y)
。更多详情请参阅预测模式的Masks部分。 -
.pop()
:: 由于masks.xy
是一个包含单个元素的列表,使用pop()
方法提取该元素。 -
.astype(np.int32)
:: 使用masks.xy
将返回float32
数据类型,但这与OpenCV的drawContours()
函数不兼容,因此将其数据类型更改为int32
以实现兼容性。 .reshape(-1, 1, 2)
:: 将数据重新格式化为所需的形状[N, 1, 2]
,其中N
是轮廓点的数量,每个点由一个条目1
表示,该条目由2
个值组成。-1
表示该维度上的值的数量是灵活的。
展开以解释
drawContours()
配置。-
在测试中发现,将
contour
变量封装在方括号[contour]
中,可以有效地生成所需的轮廓掩码。 -
为
drawContours()
参数指定的值-1
指示函数绘制图像中存在的所有轮廓。 -
tuple
(255, 255, 255)
表示白色,这是在二值掩码中绘制轮廓所需的颜色。 -
添加
cv2.FILLED
将使轮廓边界内的所有像素着色为相同颜色,在这种情况下,所有封闭的像素都将为白色。 -
有关更多信息,请参阅 OpenCV 文档中的
drawContours()
。
-
-
接下来,从这一点开始处理图像有两种选择,每种选择都有后续的子选项。
对象隔离选项
Example
# 创建三通道掩码 mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR) # 使用二值掩码隔离对象 isolated = cv2.bitwise_and(mask3ch, img)
这是如何工作的?
-
首先,二值掩码从单通道图像转换为三通道图像。这种转换对于后续步骤是必要的,因为在该步骤中掩码和原始图像将被合并。两张图像必须具有相同数量的通道才能兼容混合操作。
-
使用 OpenCV 函数
bitwise_and()
将原始图像和三通道二值掩码合并。此操作仅保留两张图像中大于零(> 0)
的像素值。由于掩码像素仅在轮廓区域内大于零(> 0)
,因此从原始图像中保留的像素是与轮廓重叠的像素。
使用黑色像素隔离:子选项
全尺寸图像
如果保持全尺寸图像,则不需要额外的步骤。
裁剪对象图像
需要额外的步骤来裁剪图像,仅包括对象区域。
# (1) 边界框坐标 x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32) # 裁剪图像到对象区域 iso_crop = isolated[y1:y2, x1:x2]
- 有关 边界框 结果的更多信息,请参阅 预测模式中的 Boxes 部分
这段代码做了什么?
-
c.boxes.xyxy.cpu().numpy()
调用以xyxy
格式检索边界框作为 NumPy 数组,其中xmin
、ymin
、xmax
和ymax
表示边界框矩形的坐标。有关更多详细信息,请参阅 预测模式中的 Boxes 部分。 -
squeeze()
操作从 NumPy 数组中移除任何不必要的维度,确保其具有预期的形状。 -
使用
.astype(np.int32)
转换坐标值将框坐标数据类型从float32
更改为int32
,使其兼容使用索引切片进行图像裁剪。 -
最后,使用索引切片从图像中裁剪边界框区域。边界由检测边界框的
[ymin:ymax, xmin:xmax]
坐标定义。
- 使用NumPy的
dstack()
函数(沿深度轴进行数组堆叠)与生成的二进制掩码结合,将创建一个具有四个通道的图像。这使得在保存为PNG
文件时,对象轮廓外的所有像素都可以透明。### 带有透明像素的隔离:子选项 ??? info "全尺寸图像" 如果保持全尺寸图像,则不需要额外的步骤。 <figure markdown> ![无背景的全尺寸隔离对象示例图像](https://github.com/ultralytics/docs/releases/download/0/example-full-size-isolated-object-image-no-background.avif){ width=240 } <figcaption>示例全尺寸输出 + 透明背景</figcaption> </figure> ??? info "裁剪对象图像" 需要额外的步骤来裁剪图像,仅包含对象区域。 ![无背景的裁剪隔离对象示例图像](https://github.com/ultralytics/docs/releases/download/0/example-crop-isolated-object-image-no-background.avif){ align="right" } ```{ .py .annotate } # (1) 边界框坐标 x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32) # 裁剪图像至对象区域 iso_crop = isolated[y1:y2, x1:x2] ``` 1. 有关边界框结果的更多信息,请参见[预测模式中的边界框部分](../modes/predict.md/#boxes) ??? question "这段代码是做什么的?" - 使用`c.boxes.xyxy.cpu().numpy()`时,边界框以NumPy数组的形式返回,使用`xyxy`边界框坐标格式,对应于边界框(矩形)的点`xmin, ymin, xmax, ymax`,更多信息请参见[预测模式中的边界框部分](../modes/predict.md/#boxes)。 - 添加`squeeze()`确保从NumPy数组中移除任何多余的维度。 - 使用`.astype(np.int32)`转换坐标值将边界框坐标数据类型从`float32`更改为`int32`,这在使用索引切片裁剪图像时将兼容。 - 最后,使用索引切片裁剪边界框的图像区域,其中边界使用检测边界框的`[ymin:ymax, xmin:xmax]`坐标设置。
如果我想裁剪的对象包括背景怎么办?
这是Ultralytics库的内置功能。有关详细信息,请参见预测模式推理参数中的
save_crop
参数。
-
-
接下来做什么完全取决于你作为开发者。 这里展示了一个可能的下一步的基本示例(将图像保存到文件以供将来使用)。
- 注意: 此步骤是可选的,如果不需要,可以跳过。
示例最终步骤
- 在此示例中,
img_name
是源图像文件的基本名称,label
是检测到的类别名称,ci
是对象检测的索引(以防同一类别名称有多个实例)。
完整示例代码
在这里,将前一节中的所有步骤组合成一个代码块。为了重复使用,最好定义一个函数来执行for
循环中包含的部分或全部命令,但这留给读者作为练习。
from pathlib import Path
import cv2
import numpy as np
from ultralytics import YOLO
m = YOLO("yolo11n-seg.pt") # (4)!
res = m.predict() # (3)!
# 迭代检测结果 (5)
for r in res:
img = np.copy(r.orig_img)
img_name = Path(r.path).stem
# 迭代每个对象轮廓 (6)
for ci, c in enumerate(r):
label = c.names[c.boxes.cls.tolist().pop()]
b_mask = np.zeros(img.shape[:2], np.uint8)
# 创建轮廓掩码 (1)
contour = c.masks.xy.pop().astype(np.int32).reshape(-1, 1, 2)
_ = cv2.drawContours(b_mask, [contour], -1, (255, 255, 255), cv2.FILLED)
# 选择其一:
# 选项-1: 隔离对象,黑色背景
mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
isolated = cv2.bitwise_and(mask3ch, img)
# 选项-2: 隔离对象,透明背景(保存为PNG时)
isolated = np.dstack([img, b_mask])
# 可选: 检测裁剪(从选项1或选项2)
x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32)
iso_crop = isolated[y1:y2, x1:x2]
# TODO 你的操作在这里 (2)
contour
的代码行合并为一行,而在上面它被拆分为多行。
2. 这里的内容由你决定!
3. 有关更多信息,请参阅 预测模式。
4. 有关更多信息,请参阅 分割任务。
5. 了解更多关于 处理结果
6. 了解更多关于 分割掩码结果
常见问题
如何使用 Ultralytics YOLO11 进行分割任务中的对象隔离?
要使用 Ultralytics YOLO11 隔离对象,请按照以下步骤操作:
-
加载模型并运行推理:
-
生成二进制掩码并绘制轮廓:
-
使用二进制掩码隔离对象:
分割后保存隔离对象的选项有哪些?
Ultralytics YOLO11 提供了两种主要选项来保存隔离对象:
-
带黑色背景:
-
带透明背景:
有关更多详细信息,请访问 预测模式 部分。
如何使用 Ultralytics YOLO11 将隔离对象裁剪到其边界框?
要将隔离对象裁剪到其边界框:
-
获取边界框坐标:
-
裁剪隔离图像:
有关边界框结果的更多信息,请参阅 预测模式 文档。
为什么我应该使用 Ultralytics YOLO11 进行分割任务中的对象隔离?
Ultralytics YOLO11 提供:
- 高速 实时对象检测和分割。
- 准确的边界框和掩码生成 以实现精确的对象隔离。
- 全面的文档 和易于使用的 API 以提高开发效率。
探索使用 YOLO 的好处,请参阅 分割任务文档。
我可以使用 Ultralytics YOLO11 保存包括背景的隔离对象吗?
是的,这是 Ultralytics YOLO11 的内置功能。在 predict()
方法中使用 save_crop
参数。例如:
有关 save_crop
参数的更多信息,请参阅 预测模式推理参数 部分。