コンテンツへスキップ

セグメンテーション・オブジェクトの分離

セグメント・タスクを実行した後、推論結果から分離されたオブジェクトを抽出したい場合があります。このガイドでは、Ultralytics PredictMode を使用して、これを実現する一般的なレシピを提供します。

孤立オブジェクトのセグメンテーション例

レシピ・ウォークスルー

  1. 必要な輸入から始める

    from pathlib import Path
    
    import cv2
    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
    results = 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() 結果参照 予測モードの結果を扱う
    フォア・ループ

    単一の画像は、最初のループを1回だけ繰り返します。1つの画像で1つの検出しかない場合は、各ループを1回だけ繰り返します。


  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
    _ = cv2.drawContours(b_mask,
                        [contour],
                        -1,
                        (255, 255, 255),
                        cv2.FILLED)
    
    1. 詳細はこちら c.masks.xy 見る 予測モードからのマスク・セクション.

    2. ここで値は np.int32 との互換性 drawContours() 関数はOpenCVから提供されている。

    3. オープンCV 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つのエントリで表される。 1で構成される。 2 の値である。その -1 は、この次元に沿った値の数が柔軟であることを示す。

    の説明のために拡大する。 drawContours() コンフィギュレーション

    • をカプセル化する。 contour 変数を角括弧で囲む、 [contour]をテストしたところ、目的の輪郭マスクを効果的に生成できることがわかった。

    • 価値 -1 に指定された。 drawContours() パラメータは,画像に存在するすべての輪郭を描画するように指示します.

    • について tuple (255, 255, 255) は白を表し、この2値マスクの輪郭を描くのに必要な色である。

    • が加わった。 cv2.FILLED この場合、輪郭で囲まれたピクセルはすべて白になる。

    • 参照 OpenCVドキュメント drawContours() をご覧ください。


  5. 次に、この時点から画像をどのように進めるかについて2つの選択肢があり、それぞれに後続の選択肢がある。

    オブジェクト分離オプション

    # Create 3-channel mask
    mask3ch = cv2.cvtColor(b_mask, cv2.COLOR_GRAY2BGR)
    
    # Isolate object with binary mask
    isolated = cv2.bitwise_and(mask3ch, img)
    
    これはどのような仕組みなのか?
    • まず、2値マスクを1チャンネル画像から3チャンネル画像に変換する。この変換は、マスクと元の画像を合成する次のステップで必要となる。両方の画像は、ブレンド操作と互換性があるように、同じチャンネル数を持っている必要があります。

    • 元の画像と3チャンネルのバイナリマスクは、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() 関数(深度軸に沿った配列のスタッキング)と生成されたバイナリマスクの組み合わせにより、4つのチャンネルを持つ画像が作成されます。これにより、オブジェクトの輪郭の外側にあるピクセルをすべて透過させることができます。 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 はオブジェクト検出のインデックスである(同じクラス名のインスタンスが複数ある場合)。

コード例

ここでは、前のセクションのすべてのステップが1つのコードブロックにまとめられている。繰り返し使用する場合は、以下のセクションの一部またはすべてのコマンドを実行する関数を定義するのが最適であろう。 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 は、上では複数に分割されていたが、ここでは1行にまとめられている。
  2. ここに何が入るかはあなた次第だ!
  3. 詳細は「予測モード」を参照。
  4. 詳細はセグメントタスクを参照。
  5. 結果を出す仕事についてもっと知る
  6. セグメンテーション・マスクの結果についてもっと知る


作成日:2023-11-27 更新日:2024-04-27
著者:glenn-jocher(6),RizwanMunawar(1),Burhan-Q(1)

コメント