Cách cô lập các đối tượng phân đoạn với Ultralytics YOLO#
Instance segmentation tạo ra một mặt nạ (mask) có độ chính xác đến từng pixel cho mọi đối tượng được phát hiện, có nghĩa là bạn có thể tách riêng từng đối tượng ra khỏi một bức ảnh. Hướng dẫn này chỉ cho bạn cách biến kết quả segmentation của Ultralytics YOLO thành các đối tượng được cô lập bằng cách sử dụng Predict Mode và OpenCV, với nền đen đặc hoặc nền trong suốt để lưu dưới dạng PNG.
Watch: How to Remove Background and Isolate Objects with Ultralytics YOLO Segmentation & OpenCV in Python 🚀
Tại sao cần cô lập đối tượng phân đoạn?#
Việc tách từng đối tượng ra khỏi ảnh mở ra nhiều quy trình xử lý tiếp theo:
- Xóa nền cho ảnh sản phẩm, danh mục hoặc chỉnh sửa sáng tạo.
- Cắt theo đối tượng để xây dựng tập dữ liệu phân loại từ các phát hiện của bạn.
- Xử lý tập trung giúp các bước sau như OCR, phân tích màu sắc hoặc đo lường chỉ nhìn thấy đối tượng, thay vì khung cảnh xung quanh.
- Xuất file PNG trong suốt để ghép các đối tượng lên nền mới.
Quy trình này hoạt động với bất kỳ mô hình segmentation nào của Ultralytics YOLO và tuân theo bốn giai đoạn: chạy inference → trích xuất từng đường viền → cô lập đối tượng → lưu kết quả.
Chạy Segmentation Inference#
Cài đặt các thư viện cần thiết, sau đó tải mô hình segmentation (hậu tố -seg, bắt buộc để tạo mặt nạ) và chạy dự đoán trên ảnh nguồn của bạn:
from ultralytics import YOLO
# Load a segmentation model
model = YOLO("yolo26n-seg.pt")
# Run inference on a source
results = model.predict(source="path/to/image.jpg")Nếu bạn gọi model.predict() mà không có source, Ultralytics sẽ quay về sử dụng các hình ảnh mẫu đi kèm với gói (bus.jpg và zidane.jpg), rất tiện lợi để kiểm tra nhanh quy trình làm việc.
Trích xuất đường bao đối tượng#
Mỗi mục trong results tương ứng với một hình ảnh và việc lặp qua kết quả sẽ trả về từng phát hiện một. Đối với mỗi phát hiện, hãy sao chép hình ảnh gốc, đọc nhãn lớp và vẽ đường viền mặt nạ của đối tượng lên một mặt nạ nhị phân trống. Vùng màu trắng của mặt nạ này đánh dấu chính xác những pixel nào thuộc về đối tượng.
Các đoạn mã trong phần này và phần tiếp theo chạy bên trong vòng lặp phát hiện dưới đây; tập lệnh hoàn chỉnh, có thể sao chép nằm ở phần Full Example.
{ width="240", align="right" }
from pathlib import Path
import cv2
import numpy as np
for r in results:
img = np.copy(r.orig_img)
img_name = Path(r.path).stem # source image base-name
# Iterate each detected object in the image
for ci, c in enumerate(r):
label = c.names[c.boxes.cls.tolist().pop()] # class name
# Build a binary mask and draw the object contour onto it
b_mask = np.zeros(img.shape[:2], np.uint8)
contour = c.masks.xy[0].astype(np.int32).reshape(-1, 1, 2)
cv2.drawContours(b_mask, [contour], -1, (255, 255, 255), cv2.FILLED)c.masks.xy[0]trả về đường viền mask dưới dạng tọa độ điểm(x, y)cho đối tượng trong kết quả phát hiện đơn lẻ này..astype(np.int32)chuyển đổi các điểm từfloat32, thứ màdrawContours()của OpenCV không chấp nhận..reshape(-1, 1, 2)định hình lại các điểm thành bố cục[N, 1, 2]màdrawContours()mong đợi, trong đóNlà số lượng điểm đường viền.
Truyền [contour] với chỉ mục -1 sẽ vẽ tất cả các điểm của đường viền được cung cấp và cv2.FILLED sẽ tô trắng mọi pixel được bao quanh.
Cô lập đối tượng#
Với mask nhị phân đã sẵn sàng, hãy kết hợp nó với ảnh gốc. Có hai kiểu phổ biến, tùy thuộc vào nền bạn muốn:
Chuyển đổi mask thành ba kênh và chỉ giữ lại các pixel trùng với đối tượng. Mọi thứ bên ngoài đường bao sẽ trở thành màu đen:
# Isolate object with a black background
mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
isolated = cv2.bitwise_and(mask3ch, img)Để chỉ giữ lại vùng của đối tượng thay vì toàn bộ ảnh, hãy cắt nó theo BBox của phát hiện:
# Bounding box coordinates
x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32)
# Crop the isolated image to the object region
iso_crop = isolated[y1:y2, x1:x2]Tính năng đó đã có sẵn. Truyền save_crop=True vào predict() và Ultralytics sẽ tự động lưu các phần cắt khung giới hạn (bounding-box crop), không cần tạo mặt nạ.
Lưu kết quả (tùy chọn)#
Việc bạn làm gì với từng đối tượng đã cô lập là tùy thuộc vào bạn. Một bước tiếp theo phổ biến là ghi nó vào ổ đĩa để sử dụng sau:
# Save the isolated object to file
cv2.imwrite(f"{img_name}_{label}-{ci}.png", isolated)Ở đây img_name là phần gốc tên tệp ảnh nguồn, label là tên lớp và ci là chỉ mục phát hiện, do đó nhiều thể hiện của cùng một lớp sẽ có tên tệp độc nhất. Hãy thay thế isolated bằng iso_crop nếu bạn đã áp dụng tùy chọn cắt ảnh ở trên.
Ví dụ đầy đủ#
Tập lệnh dưới đây kết hợp mọi bước thành một khối duy nhất có thể chạy được. Nó sử dụng nền đen theo mặc định; hãy chuyển dòng được đánh dấu đơn thành np.dstack([img, b_mask]) để có tệp PNG trong suốt thay thế:
from pathlib import Path
import cv2
import numpy as np
from ultralytics import YOLO
model = YOLO("yolo26n-seg.pt")
results = model.predict(source="path/to/image.jpg")
for r in results:
img = np.copy(r.orig_img)
img_name = Path(r.path).stem
for ci, c in enumerate(r):
label = c.names[c.boxes.cls.tolist().pop()]
# Build a binary mask from the object contour
b_mask = np.zeros(img.shape[:2], np.uint8)
contour = c.masks.xy[0].astype(np.int32).reshape(-1, 1, 2)
cv2.drawContours(b_mask, [contour], -1, (255, 255, 255), cv2.FILLED)
# Isolate the object (black background)
mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
isolated = cv2.bitwise_and(mask3ch, img) # transparent PNG: isolated = np.dstack([img, b_mask])
# Save or add your custom post-processing here
cv2.imwrite(f"{img_name}_{label}-{ci}.png", isolated)
# Optional: crop to the bounding box before saving
# x1, y1, x2, y2 = c.boxes.xyxy.cpu().numpy().squeeze().astype(np.int32)
# cv2.imwrite(f"{img_name}_{label}-{ci}.png", isolated[y1:y2, x1:x2])Để sử dụng nhiều lần, hãy gói phần thân vòng lặp trong một hàm để bạn có thể gọi nó trên nhiều ảnh.
Kết luận#
Bây giờ bạn đã có một công thức hoàn chỉnh để cô lập các đối tượng được phân đoạn bằng Ultralytics YOLO: chạy inference, xây dựng mặt nạ nhị phân từ mỗi đường viền, sau đó trích xuất đối tượng trên nền đen hoặc nền trong suốt và tùy chọn cắt nó theo khung bao quanh. Khám phá tài liệu đầy đủ về Segment Task và Predict Mode để tùy chỉnh quy trình làm việc cho các lớp của riêng bạn.
Câu hỏi thường gặp#
Tải một model phân đoạn, chạy inference, tạo mask nhị phân từ đường bao của mỗi phát hiện và kết hợp nó với ảnh gốc:
import cv2 import numpy as np from ultralytics import YOLO model = YOLO("yolo26n-seg.pt") results = model.predict(source="path/to/your/image.jpg") 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)Xem phần Full Example để biết vòng lặp theo từng phát hiện hoàn chỉnh.
Có hai phong cách chính. Đối với nền đen, hãy chuyển đổi mặt nạ thành ba kênh và sử dụng
cv2.bitwise_and(). Đối với nền trong suốt (khi lưu dưới dạng PNG), hãy chồng mặt nạ làm kênh alpha thứ tư bằngnp.dstack([img, b_mask]). Cả hai đều được minh họa trong Isolate the Object.Đọc tọa độ BBox từ kết quả phát hiện và cắt ảnh đã cô lập:
x1, y1, x2, y2 = results[0].boxes.xyxy[0].cpu().numpy().astype(np.int32) iso_crop = isolated[y1:y2, x1:x2]Tìm hiểu thêm về kết quả hộp giới hạn (bounding box) trong tài liệu Predict Mode.
Ultralytics YOLO cung cấp khả năng phân đoạn instance thời gian thực, nhanh chóng với việc tạo mask và BBox chính xác, cộng với Python API đơn giản giúp chuyển đổi kết quả inference thành các đối tượng riêng biệt chỉ với vài dòng mã OpenCV.
Có. Sử dụng đối số
save_croptrongpredict()để lưu các phần cắt hộp giới hạn với nền gốc của chúng:results = model.predict(source="path/to/your/image.jpg", save_crop=True)Đọc thêm trong phần Predict Mode Inference Arguments.