Bỏ để qua phần nội dung

Cô lập các đối tượng phân đoạn

Sau khi thực hiện Nhiệm vụ phân đoạn, đôi khi bạn nên trích xuất các đối tượng bị cô lập từ kết quả suy luận. Hướng dẫn này cung cấp một công thức chung về cách thực hiện điều này bằng cách sử dụng Ultralytics Chế độ dự đoán.

Ví dụ về phân đoạn đối tượng bị cô lập

Công thức đi qua

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


  2. Tải mô hình và chạy predict() phương pháp trên một nguồn.

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

    Không có đối số dự đoán?

    Không chỉ định nguồn, các hình ảnh ví dụ từ thư viện sẽ được sử dụng:

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

    Điều này rất hữu ích cho việc xét nghiệm nhanh với predict() phương pháp.

    Để biết thêm thông tin về Mô hình phân đoạn, hãy truy cập nút Nhiệm vụ phân đoạn trang. Để tìm hiểu thêm về predict() Phương pháp, xem Chế độ dự đoán của Tài liệu.


  3. Bây giờ lặp lại kết quả và các đường viền. Đối với dòng công việc muốn lưu hình ảnh vào tệp, ảnh nguồn base-name và phát hiện class-label được truy xuất để sử dụng sau này (tùy chọn).

    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. Để tìm hiểu thêm về cách làm việc với kết quả phát hiện, hãy xem Phần Hộp cho Chế độ Dự đoán.
    2. Để tìm hiểu thêm về predict() Kết quả xem Làm việc với Kết quả cho Chế độ Dự đoán
    Vòng lặp

    Một hình ảnh duy nhất sẽ chỉ lặp lại vòng lặp đầu tiên một lần. Một hình ảnh duy nhất chỉ có một phát hiện duy nhất sẽ lặp lại mỗi vòng lặp chỉ một lần.


  4. Bắt đầu với việc tạo một mặt nạ nhị phân từ hình ảnh nguồn và sau đó vẽ một đường viền đầy lên mặt nạ. Điều này sẽ cho phép đối tượng được cách ly với các phần khác của hình ảnh. Một ví dụ từ bus.jpg cho một trong những phát hiện person Các đối tượng lớp được hiển thị ở bên phải.

    Hình ảnh mặt nạ nhị phân

    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. Để biết thêm thông tin về c.masks.xy xem Phần mặt nạ từ Chế độ dự đoán.

    2. Ở đây các giá trị được đúc vào np.int32 để tương thích với drawContours() hàm từ OpenCV.

    3. The OpenCV drawContours() Chức năng mong đợi các đường viền có hình dạng [N, 1, 2] Mở rộng phần bên dưới để biết thêm chi tiết.

    Mở rộng để hiểu những gì đang xảy ra khi xác định contour biến số.

    • c.masks.xy :: Cung cấp tọa độ của các điểm đường viền mặt nạ ở định dạng (x, y). Để biết thêm chi tiết, hãy tham khảo Phần mặt nạ từ Chế độ dự đoán.

    • .pop() ::Như masks.xy là một danh sách chứa một phần tử duy nhất, phần tử này được trích xuất bằng cách sử dụng pop() phương pháp.

    • .astype(np.int32) ::Sử dụng masks.xy sẽ trả về với kiểu dữ liệu float32, nhưng điều này sẽ không tương thích với OpenCV drawContours() , vì vậy điều này sẽ thay đổi kiểu dữ liệu thành int32 để tương thích.

    • .reshape(-1, 1, 2) :: Định dạng lại dữ liệu thành hình dạng yêu cầu của [N, 1, 2] đâu N là số điểm đường viền, với mỗi điểm được biểu thị bằng một mục nhập duy nhất 1, và mục nhập bao gồm 2 giá trị. Các -1 biểu thị rằng số lượng giá trị dọc theo thứ nguyên này là linh hoạt.

    Mở rộng để giải thích về drawContours() cấu hình.

    • Đóng gói contour biến trong ngoặc vuông, [contour], đã được tìm thấy để tạo ra hiệu quả mặt nạ đường viền mong muốn trong quá trình thử nghiệm.

    • Giá trị -1 được chỉ định cho drawContours() Tham số hướng dẫn hàm vẽ tất cả các đường viền có trong hình ảnh.

    • Các tuple (255, 255, 255) đại diện cho màu trắng, là màu mong muốn để vẽ đường viền trong mặt nạ nhị phân này.

    • Việc bổ sung cv2.FILLED sẽ tô màu tất cả các pixel được bao quanh bởi ranh giới đường viền giống nhau, trong trường hợp này, tất cả các pixel kèm theo sẽ có màu trắng.

    • Xem Tài liệu OpenCV về drawContours() để biết thêm thông tin.


  5. Tiếp theo, có 2 tùy chọn về cách di chuyển về phía trước với hình ảnh từ thời điểm này và tùy chọn tiếp theo cho mỗi tùy chọn.

    Tùy chọn cách ly đối tượng

    # Create 3-channel mask
    mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
    
    # Isolate object with binary mask
    isolated = cv2.bitwise_and(mask3ch, img)
    
    Điều này hoạt động như thế nào?
    • Đầu tiên, mặt nạ nhị phân lần đầu tiên được chuyển đổi từ hình ảnh kênh đơn sang hình ảnh ba kênh. Việc chuyển đổi này là cần thiết cho bước tiếp theo trong đó mặt nạ và hình ảnh gốc được kết hợp. Cả hai hình ảnh phải có cùng số kênh để tương thích với thao tác pha trộn.

    • Hình ảnh gốc và mặt nạ nhị phân ba kênh được hợp nhất bằng hàm OpenCV bitwise_and(). Hoạt động này vẫn được giữ lại chỉ Giá trị pixel lớn hơn 0 (> 0) từ cả hai hình ảnh. Vì các pixel mặt nạ lớn hơn 0 (> 0) chỉ Trong vùng đường viền, các điểm ảnh còn lại từ ảnh gốc là những điểm ảnh chồng lên đường viền.

    Cô lập bằng Black Pixels: Tùy chọn phụ

    Hình ảnh kích thước đầy đủ

    Không có bước bổ sung cần thiết nếu giữ hình ảnh kích thước đầy đủ.

    Ví dụ Kích thước đầy đủ Hình ảnh đối tượng bị cô lập Nền đen
    Ví dụ về đầu ra kích thước đầy đủ

    Hình ảnh đối tượng đã xén

    Các bước bổ sung cần thiết để cắt hình ảnh để chỉ bao gồm vùng đối tượng.

    Ví dụ Cắt hình ảnh đối tượng bị cô lập Nền đen

    # (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. Để biết thêm thông tin về kết quả hộp giới hạn, hãy xem Phần Hộp từ Chế độ Dự đoán
    Mã này làm gì?
    • Các c.boxes.xyxy.cpu().numpy() cuộc gọi truy xuất các hộp giới hạn dưới dạng mảng NumPy trong xyxy định dạng, trong đó xmin, ymin, xmaxymax đại diện cho tọa độ của hình chữ nhật hộp giới hạn. Xem Phần hộp từ Chế độ dự đoán để biết thêm chi tiết.

    • Các squeeze() hoạt động loại bỏ bất kỳ kích thước không cần thiết nào khỏi mảng NumPy, đảm bảo nó có hình dạng mong đợi.

    • Chuyển đổi các giá trị tọa độ bằng cách sử dụng .astype(np.int32) Thay đổi hộp tọa độ kiểu dữ liệu từ float32 đến int32, làm cho chúng tương thích để cắt xén hình ảnh bằng cách sử dụng các lát chỉ mục.

    • Cuối cùng, vùng hộp giới hạn được cắt từ hình ảnh bằng cách sử dụng cắt chỉ mục. Các giới hạn được xác định bởi [ymin:ymax, xmin:xmax] tọa độ của hộp giới hạn phát hiện.

    # Isolate object with transparent background (when saved as PNG)
    isolated = np.dstack([img, b_mask])
    
    Điều này hoạt động như thế nào?
    • Sử dụng NumPy dstack() Chức năng (xếp chồng mảng dọc theo trục sâu) kết hợp với mặt nạ nhị phân được tạo, sẽ tạo ra một hình ảnh với bốn kênh. Điều này cho phép tất cả các pixel bên ngoài đường viền đối tượng trong suốt khi lưu dưới dạng PNG tệp.

    Cô lập với Pixel trong suốt: Tùy chọn phụ

    Hình ảnh kích thước đầy đủ

    Không có bước bổ sung cần thiết nếu giữ hình ảnh kích thước đầy đủ.

    Ví dụ Kích thước đầy đủ Hình ảnh đối tượng bị cô lập Không có nền
    Ví dụ: đầu ra kích thước đầy đủ + nền trong suốt

    Hình ảnh đối tượng đã xén

    Các bước bổ sung cần thiết để cắt hình ảnh để chỉ bao gồm vùng đối tượng.

    Ví dụ: Cắt hình ảnh đối tượng bị cô lập Không có nền

    # (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. Để biết thêm thông tin về kết quả hộp giới hạn, hãy xem Phần Hộp từ Chế độ Dự đoán
    Mã này làm gì?
    • Khi sử dụng c.boxes.xyxy.cpu().numpy(), các hộp giới hạn được trả về dưới dạng mảng NumPy, sử dụng xyxy định dạng tọa độ hộp, tương ứng với các điểm xmin, ymin, xmax, ymax Đối với bài về hộp giới hạn (hình chữ nhật), xem Phần hộp từ Chế độ dự đoán để biết thêm thông tin.

    • Thêm squeeze() đảm bảo rằng mọi kích thước không liên quan đều bị xóa khỏi mảng NumPy.

    • Chuyển đổi các giá trị tọa độ bằng cách sử dụng .astype(np.int32) Thay đổi hộp tọa độ kiểu dữ liệu từ float32 đến int32 sẽ tương thích khi cắt hình ảnh bằng các lát chỉ mục.

    • Cuối cùng, vùng hình ảnh cho hộp giới hạn được cắt bằng cách sử dụng cắt chỉ mục, trong đó các giới hạn được đặt bằng cách sử dụng [ymin:ymax, xmin:xmax] tọa độ của hộp giới hạn phát hiện.

    Điều gì sẽ xảy ra nếu tôi muốn đối tượng đã xén bao gồm nền ?

    Đây là một tính năng tích hợp cho Ultralytics thư viện. Xem save_crop Đối số cho Dự đoán các đối số suy luận chế độ để biết chi tiết.


  6. Phải làm gì tiếp theo hoàn toàn thuộc về bạn với tư cách là nhà phát triển. Một ví dụ cơ bản về một bước tiếp theo có thể (lưu hình ảnh vào tệp để sử dụng trong tương lai) được hiển thị.

    • LƯU Ý: bước này là tùy chọn và có thể bỏ qua nếu không bắt buộc đối với trường hợp sử dụng cụ thể của bạn.
    Ví dụ Bước cuối cùng
    # Save isolated object to file
    _ = cv2.imwrite(f"{img_name}_{label}-{ci}.png", iso_crop)
    
    • Trong ví dụ này, các img_name là tên cơ sở của tệp hình ảnh nguồn, label là tên lớp được phát hiện, và ci là chỉ mục phát hiện đối tượng (trong trường hợp nhiều trường hợp có cùng tên lớp).

Mã ví dụ đầy đủ

Ở đây, tất cả các bước từ phần trước được kết hợp thành một khối mã duy nhất. Để sử dụng lặp đi lặp lại, sẽ là tối ưu để xác định một hàm để thực hiện một số hoặc tất cả các lệnh có trong for-loops, nhưng đó là một bài tập để lại cho người đọc.

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. Dòng dân cư contour được kết hợp thành một dòng duy nhất ở đây, nơi nó được chia thành nhiều dòng ở trên.
  2. Những gì đi đến đây là tùy thuộc vào bạn!
  3. Xem Chế độ Dự đoán để biết thêm thông tin.
  4. Xem Nhiệm vụ Phân đoạn để biết thêm thông tin.
  5. Tìm hiểu thêm về Làm việc với Kết quả
  6. Tìm hiểu thêm về Kết quả mặt nạ phân đoạn


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

Ý kiến