跳至内容

隔离分割对象

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

孤立物体分割示例

食谱漫步

  1. 从必要的进口开始

    from pathlib import Path
    
    import cv2 as cv
    import numpy as np
    from ultralytics import YOLO
    
    Ultralytics 安装

    有关安装所需程序库的快速指南,请参见Ultralytics 快速入门安装部分。


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

    from ultralytics import YOLO
    
    # Load a model
    model = YOLO('yolov8n-seg.pt')
    
    # Run inference
    result = model.predict()
    
    没有预测论据?

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

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

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

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


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

    # (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 右侧显示的是类对象。

    二进制掩码图像

    # 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
    _ = cv.drawContours(b_mask,
                        [contour],
                        -1,
                        (255, 255, 255),
                        cv.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) 代表白色,这是在二进制掩码中绘制轮廓所需的颜色。

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

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


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

    对象隔离选项

    # Create 3-channel mask
    mask3ch = cv.cvtColor(b_mask, cv.COLOR_GRAY2BGR)
    
    # Isolate object with binary mask
    isolated = cv.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
    _ = cv.imwrite(f'{img_name}_{label}-{ci}.png', iso_crop)
    
    • 在这个例子中, img_name 是源图像文件的基本名称、 label 是检测到的类名,而 ci 是对象检测的索引(在多个实例具有相同类名的情况下)。

完整示例代码

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

from pathlib import Path

import cv2 as cv
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)
        _ = cv.drawContours(b_mask, [contour], -1, (255, 255, 255), cv.FILLED)

        # Choose one:

        # OPTION-1: Isolate object with black background
        mask3ch = cv.cvtColor(b_mask, cv.COLOR_GRAY2BGR)
        isolated = cv.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. 进一步了解分段屏蔽结果


创建于 2023-11-27,更新于 2024-01-14
作者:glenn-jocher(4)、RizwanMunawar(1)、Burhan-Q(1)

评论