Изоляция объектов сегментации
После выполнения задачи сегментации иногда требуется извлечь изолированные объекты из результатов инференса. В этом руководстве представлен общий рецепт того, как это сделать с помощью режима прогнозирования Ultralytics.
Watch: How to Remove Background and Isolate Objects with Ultralytics YOLO Segmentation & OpenCV in Python 🚀
Пошаговое руководство
-
Ознакомьтесь с разделом быстрой установки Ultralytics для получения краткой инструкции по установке необходимых библиотек.
-
Загрузи модель и выполни метод
predict()на источнике.from ultralytics import YOLO # Load a model model = YOLO("yolo26n-seg.pt") # Run inference results = model.predict()
Если не указать источник, будут использованы примеры изображений из библиотеки:
'ultralytics/assets/bus.jpg'
'ultralytics/assets/zidane.jpg'Это полезно для быстрого тестирования с помощью метода predict().
Дополнительную информацию о моделях сегментации можно найти на странице задачи сегментации. Чтобы узнать больше о методе predict(), см. раздел Режим прогнозирования в документации.
***
Теперь перебери результаты и контуры. Для рабочих процессов, где требуется сохранение изображения в файл, извлекаются base-name (базовое имя) исходного изображения и class-label (метка класса) детекции для дальнейшего использования (необязательно).
```{ .py .annotate }
from pathlib import Path
import numpy as np
# (2) Iterate detection results (helpful for multiple images)
for r in results:
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. To learn more about working with detection results, see [Boxes Section for Predict Mode](../modes/predict.md#boxes).
2. To learn more about `predict()` results see [Working with Results for Predict Mode](../modes/predict.md#working-with-results)Цикл For
Для одного изображения первый цикл сработает только один раз. Одно изображение с единственной детекцией приведет к выполнению каждого цикла только один раз.
-
Начни с создания бинарной маски из исходного изображения, а затем нарисуй заполненный контур на этой маске. Это позволит изолировать объект от остальных частей изображения. Пример с
bus.jpgдля одного из обнаруженных объектов классаpersonпоказан справа.{ width="240", align="right" }
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)-
Для получения дополнительной информации о
c.masks.xyсм. раздел Маски в режиме прогнозирования. -
Здесь значения приводятся к типу
np.int32для совместимости с функциейdrawContours()из OpenCV. -
Функция OpenCV
drawContours()ожидает, что контуры будут иметь форму[N, 1, 2]. Разверни раздел ниже для получения подробной информации.
Expand to understand what is happening when defining the
contourvariable.- `c.masks.xy` :: Provides the coordinates of the mask contour points in the format `(x, y)`. For more details, refer to the [Masks Section from Predict Mode](../modes/predict.md#masks). - `.pop()` :: As `masks.xy` is a list containing a single element, this element is extracted using the `pop()` method. - `.astype(np.int32)` :: Using `masks.xy` will return with a data type of `float32`, but this won't be compatible with the OpenCV `drawContours()` function, so this will change the data type to `int32` for compatibility. - `.reshape(-1, 1, 2)` :: Reformats the data into the required shape of `[N, 1, 2]` where `N` is the number of contour points, with each point represented by a single entry `1`, and the entry is composed of `2` values. The `-1` denotes that the number of values along this dimension is flexible.
Expand for an explanation of the
drawContours()configuration.- Encapsulating the `contour` variable within square brackets, `[contour]`, was found to effectively generate the desired contour mask during testing. - The value `-1` specified for the `drawContours()` parameter instructs the function to draw all contours present in the image. - The `tuple` `(255, 255, 255)` represents the color white, which is the desired color for drawing the contour in this binary mask. - The addition of `cv2.FILLED` will color all pixels enclosed by the contour boundary the same, in this case, all enclosed pixels will be white. - See [OpenCV Documentation on `drawContours()`](https://docs.opencv.org/4.8.0/d6/d6e/group__imgproc__draw.html#ga746c0625f1781f1ffc9056259103edbc) for more information.
-
-
Далее есть 2 варианта того, как продолжить работу с изображением, и последующая опция для каждого из них.
Варианты изоляции объекта
# 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)только в пределах области контура, пиксели, оставшиеся от исходного изображения, являются теми, которые перекрываются с контуром.
Изоляция с черными пикселями: подварианты
Изображение в полном размере
Дополнительные шаги не требуются, если нужно сохранить изображение в полном размере.
Изображение с обрезанным объектом
Требуются дополнительные шаги, чтобы обрезать изображение так, чтобы оно включало только область объекта.
{ align="right" }
# (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]- Для получения дополнительной информации о результатах bounding box см. раздел Боксы в режиме прогнозирования
Что делает этот код?
-
Вызов
c.boxes.xyxy.cpu().numpy()извлекает ограничивающие рамки (bounding boxes) в виде массива NumPy в форматеxyxy, гдеxmin,ymin,xmaxиymaxпредставляют координаты прямоугольника ограничивающей рамки. Подробнее см. в разделе Боксы в режиме прогнозирования. -
Операция
squeeze()удаляет любые ненужные размерности из массива NumPy, обеспечивая ожидаемую форму. -
Преобразование значений координат с помощью
.astype(np.int32)меняет тип данных координат рамки сfloat32наint32, делая их совместимыми для обрезки изображения с использованием срезов индексов. -
Наконец, область ограничивающей рамки обрезается из изображения с помощью срезов индексов. Границы определяются координатами
[ymin:ymax, xmin:xmax]ограничивающей рамки детекции.
Что, если я хочу получить обрезанный объект **вместе** с фоном?
Это встроенная функция библиотеки Ultralytics. Подробности см. в аргументе save_crop для Аргументов инференса в режиме прогнозирования.
- Что делать дальше — полностью зависит от тебя как от разработчика. Ниже показан базовый пример одного из возможных следующих шагов (сохранение изображения в файл для будущего использования).
- ПРИМЕЧАНИЕ: этот шаг является необязательным и его можно пропустить, если он не требуется для твоего конкретного сценария использования.
Пример финального шага
# 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("yolo26n-seg.pt") # (4)!
res = m.predict(source="path/to/image.jpg") # (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]
# Add your custom post-processing here (2)- Строка, заполняющая
contour, здесь объединена в одну, тогда как выше она была разделена на несколько. - {==Что здесь написать — решать тебе!==}
- Дополнительную информацию см. в Режиме прогнозирования.
- Дополнительную информацию см. в Задаче сегментации.
- Узнай больше о работе с результатами
- Узнай больше о результатах масок сегментации
Часто задаваемые вопросы (FAQ)
Как мне изолировать объекты с помощью Ultralytics YOLO26 для задач сегментации?
Чтобы изолировать объекты с помощью Ultralytics YOLO26, выполни следующие шаги:
-
Загрузи модель и запусти инференс:
from ultralytics import YOLO model = YOLO("yolo26n-seg.pt") results = model.predict(source="path/to/your/image.jpg") -
Создай бинарную маску и нарисуй контуры:
import cv2 import numpy as np img = np.copy(results[0].orig_img) b_mask = np.zeros(img.shape[:2], np.uint8) contour = results[0].masks.xy[0].astype(np.int32).reshape(-1, 1, 2) cv2.drawContours(b_mask, [contour], -1, (255, 255, 255), cv2.FILLED) -
Изолируй объект с помощью бинарной маски:
mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR) isolated = cv2.bitwise_and(mask3ch, img)
Обратись к руководству по Режиму прогнозирования и Задаче сегментации для получения дополнительной информации.
Какие варианты доступны для сохранения изолированных объектов после сегментации?
Ultralytics YOLO26 предлагает два основных варианта сохранения изолированных объектов:
-
С черным фоном:
mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR) isolated = cv2.bitwise_and(mask3ch, img) -
С прозрачным фоном:
isolated = np.dstack([img, b_mask])
Более подробную информацию можно найти в разделе Режим прогнозирования.
Как я могу обрезать изолированные объекты до их ограничивающих рамок с помощью Ultralytics YOLO26?
Чтобы обрезать изолированные объекты до их ограничивающих рамок:
-
Извлеки координаты ограничивающей рамки:
x1, y1, x2, y2 = results[0].boxes.xyxy[0].cpu().numpy().astype(np.int32) -
Обрежь изолированное изображение:
iso_crop = isolated[y1:y2, x1:x2]
Узнай больше о результатах ограничивающих рамок в документации Режим прогнозирования.
Почему мне стоит использовать Ultralytics YOLO26 для изоляции объектов в задачах сегментации?
Ultralytics YOLO26 предоставляет:
- Высокоскоростное обнаружение объектов и сегментацию в реальном времени.
- Точное создание ограничивающих рамок и масок для точной изоляции объектов.
- Исчерпывающую документацию и простой в использовании API для эффективной разработки.
Исследуй преимущества использования YOLO в документации по задаче сегментации.
Могу ли я сохранять изолированные объекты вместе с фоном с помощью Ultralytics YOLO26?
Да, это встроенная функция в Ultralytics YOLO26. Используй аргумент save_crop в методе predict(). Например:
results = model.predict(source="path/to/your/image.jpg", save_crop=True)Читай подробнее об аргументе save_crop в разделе Аргументы инференса в режиме прогнозирования.