跳至内容

隔离分割对象

执行分段任务后,有时需要从推理结果中提取孤立对象。本指南提供了如何使用Ultralytics 预测模式实现这一目的的通用方法。

孤立物体分割示例

食谱漫步

  1. See the Ultralytics Quickstart Installation section for a quick walkthrough on installing the required libraries.


  2. 加载模型并运行 predict() 方法。

    from ultralytics import YOLO
    
    # Load a model
    model = YOLO("yolov8n-seg.pt")
    
    # Run inference
    results = model.predict()
    

    没有预测论据?

    如果未指定来源,则将使用图库中的示例图像:

    'ultralytics/assets/bus.jpg'
    'ultralytics/assets/zidane.jpg'
    

    这有助于使用 predict() 方法。

    有关细分模型的更多信息,请访问 分段任务 页。了解更多 predict() 方法,见 预测模式 部分。


  3. 现在遍历结果和轮廓。对于要将图像保存到文件的工作流程,源图像 base-name 和检测 class-label 检索,以供以后使用(可选)。

    from pathlib import Path
    
    import numpy as np
    
    # (2) Iterate detection results (helpful for multiple images)
    for r in res:
        img = np.copy(r.orig_img)
        img_name = Path(r.path).stem  # source image base-name
    
        # Iterate each object contour (multiple detections)
        for ci, c in enumerate(r):
            # (1) Get detection class name
            label = c.names[c.boxes.cls.tolist().pop()]
    
    1. 要了解有关使用检测结果的更多信息,请参阅 "预测模式方框 "部分。
    2. 了解更多信息 predict() 结果见 处理预测模式的结果
    For-Loop

    单幅图像只会重复第一个循环一次。只有一个检测的单个图像将只重复每个循环一次。


  4. 首先从源图像生成一个二元遮罩,然后在遮罩上绘制填充轮廓。这样就可以将对象与图像的其他部分隔离开来。示例 bus.jpg 为检测到的其中一个 person 右侧显示的是类对象。

    二进制掩码图像

    import cv2
    
    # Create binary mask
    b_mask = np.zeros(img.shape[:2], np.uint8)
    
    # (1) Extract contour result
    contour = c.masks.xy.pop()
    # (2) Changing the type
    contour = contour.astype(np.int32)
    # (3) Reshaping
    contour = contour.reshape(-1, 1, 2)
    
    
    # Draw contour onto mask
    _ = cv2.drawContours(b_mask, [contour], -1, (255, 255, 255), cv2.FILLED)
    
    1. 更多信息 c.masks.xy 看看 预测模式的掩码部分.

    2. 在这里,这些值被转入 np.int32 以兼容 drawContours() 函数。

    3. OpenCV drawContours() 函数希望轮廓的形状为 [N, 1, 2] 更多详情,请参阅下文扩展部分。

    扩展,以了解在界定 contour 变量。

    • c.masks.xy ::提供遮罩轮廓点的坐标,格式为 (x, y).有关详细信息,请参阅 预测模式的掩码部分.

    • .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]在测试过程中,发现它能有效生成所需的轮廓掩码。

    • 价值 -1 指定的 drawContours() 参数指示函数绘制图像中的所有轮廓。

    • "(《世界人权宣言》) tuple (255, 255, 255) 代表白色,这是在二进制掩码中绘制轮廓所需的颜色。

    • 增加 cv2.FILLED 会给轮廓边界所包围的所有像素涂上相同的颜色,在这种情况下,所有被包围的像素都将是白色。

    • 参见 OpenCV 文档 drawContours() 了解更多信息。


  5. 接下来,有两个选项可供选择,分别是如何从此时开始继续处理图像,以及每个选项的后续选项。

    对象隔离选项

    # Create 3-channel mask
    mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
    
    # Isolate object with binary mask
    isolated = cv2.bitwise_and(mask3ch, img)
    
    它是如何工作的?
    • 首先,二进制掩码要从单通道图像转换为三通道图像。这一转换对于随后将遮罩和原始图像合并的步骤是必要的。两幅图像的通道数必须相同,以便与混合操作兼容。

    • 使用 OpenCV 函数合并原始图像和三通道二进制掩码 bitwise_and().该操作保留了 只是 像素值大于零 (> 0) 从两幅图像中提取。由于掩码像素大于零 (> 0) 只是 在轮廓区域内,原始图像中剩余的像素就是与轮廓重叠的像素。

    用黑色像素隔离子选项

    全尺寸图片

    如果保留全尺寸图像,则无需其他步骤。

    全尺寸孤立对象图像示例 黑色背景
    全尺寸输出示例

    经裁剪的对象图像

    裁剪图像使其只包括目标区域所需的其他步骤。

    裁剪黑色背景孤立对象图像示例

    # (1) Bounding box coordinates
    x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32)
    # Crop image to object region
    iso_crop = isolated[y1:y2, x1:x2]
    

    1. 有关边界框结果的更多信息,请参阅 "预测模式 "中的 "框 "部分。
    这段代码有什么作用?
    • "(《世界人权宣言》) c.boxes.xyxy.cpu().numpy() 调用会以 NumPy 数组的形式在 xyxy 格式,其中 xmin, ymin, xmax和 ymax 代表边界框矩形的坐标。参见 预测模式的方框部分 了解更多详情。

    • "(《世界人权宣言》) squeeze() 操作会删除 NumPy 数组中不必要的维数,确保数组具有预期的形状。

    • 使用 .astype(np.int32) 将方框坐标数据类型从 float32 至 int32这样就可以使用索引切片对图像进行裁剪。

    • 最后,使用索引切片从图像中裁剪出边界框区域。边界由 [ymin:ymax, xmin:xmax] 检测边界框的坐标。

    # Isolate object with transparent background (when saved as PNG)
    isolated = np.dstack([img, b_mask])
    
    它是如何工作的?
    • 使用 NumPy dstack() 功能(沿深度轴堆叠数组)与生成的二进制掩膜相结合,将创建一个具有四个通道的图像。这样,在将对象轮廓以外的所有像素保存为 PNG 锉刀

    用透明像素隔离:子选项

    全尺寸图片

    如果保留全尺寸图像,则无需其他步骤。

    示例 全尺寸无背景孤立对象图像
    全尺寸输出 + 透明背景示例

    经裁剪的对象图像

    裁剪图像使其只包括目标区域所需的其他步骤。

    裁剪无背景孤立对象图像示例

    # (1) Bounding box coordinates
    x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32)
    # Crop image to object region
    iso_crop = isolated[y1:y2, x1:x2]
    

    1. 有关边界框结果的更多信息,请参阅 "预测模式 "中的 "框 "部分。
    这段代码有什么作用?
    • 使用时 c.boxes.xyxy.cpu().numpy(),边界框会以 NumPy 数组形式返回,使用 xyxy 方框坐标格式,对应于点 xmin, ymin, xmax, ymax 的边界框(矩形),请参见 预测模式的方框部分 了解更多信息。

    • 添加 squeeze() 确保从 NumPy 数组中删除任何无关的维数。

    • 使用 .astype(np.int32) 将方框坐标数据类型从 float32 至 int32 这在使用索引切片裁剪图像时是兼容的。

    • 最后,使用索引切片法裁剪边界框的图像区域,并使用 [ymin:ymax, xmin:xmax] 检测边界框的坐标。

    如果我想要包括背景在内的裁剪对象怎么办?

    这是Ultralytics 库的内置功能。请参见 save_crop 论点 预测模式推理参数 了解详情。


  6. 下一步该怎么做,完全取决于开发人员。下面是一个可能的下一步的基本示例(将图像保存到文件以备将来使用)。

    • 注意:此步骤为可选步骤,如果您的具体使用情况不需要,可跳过此步骤。
    最后一步示例
    # Save isolated object to file
    _ = cv2.imwrite(f"{img_name}_{label}-{ci}.png", iso_crop)
    
    • 在这个例子中, img_name 是源图像文件的基本名称、 label 是检测到的类名,而 ci 是对象检测的索引(在多个实例具有相同类名的情况下)。

完整示例代码

在这里,上一节中的所有步骤都合并为一个代码块。如果要重复使用,最好定义一个函数来执行以下代码中的部分或全部命令 for-但这是留给读者的一个练习。

from pathlib import Path

import cv2
import numpy as np

from ultralytics import YOLO

m = YOLO("yolov8n-seg.pt")  # (4)!
res = m.predict()  # (3)!

# Iterate detection results (5)
for r in res:
    img = np.copy(r.orig_img)
    img_name = Path(r.path).stem

    # Iterate each object contour (6)
    for ci, c in enumerate(r):
        label = c.names[c.boxes.cls.tolist().pop()]

        b_mask = np.zeros(img.shape[:2], np.uint8)

        # Create contour mask (1)
        contour = c.masks.xy.pop().astype(np.int32).reshape(-1, 1, 2)
        _ = cv2.drawContours(b_mask, [contour], -1, (255, 255, 255), cv2.FILLED)

        # Choose one:

        # OPTION-1: Isolate object with black background
        mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
        isolated = cv2.bitwise_and(mask3ch, img)

        # OPTION-2: Isolate object with transparent background (when saved as PNG)
        isolated = np.dstack([img, b_mask])

        # OPTIONAL: detection crop (from either OPT1 or OPT2)
        x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32)
        iso_crop = isolated[y1:y2, x1:x2]

        # TODO your actions go here (2)
  1. 填充以下内容的行 contour 在这里合并为一行,而在上面是分成多行的。
  2. 这里的内容由你决定!
  3. 有关更多信息,请参阅预测模式。
  4. 更多信息,请参阅 "分段任务"。
  5. 了解更多有关 "与成果合作"的信息
  6. 进一步了解分段屏蔽结果


Created 2023-11-27, Updated 2024-06-18
Authors: glenn-jocher (11), IvorZhu331 (1), RizwanMunawar (1), Burhan-Q (1)

评论