Ultralytics YOLO27: ウェイトリストに登録する。
Get Started
コンピュータービジョンの基礎·タスクを選ぶ·レッスン 4/10
レッスン初級

ボックスとマスク: セグメンテーションが本当に必要な場合

実画像でマスクがボックスよりどれだけ有用かを測定し、出力を利用する後段の処理に基づいて、プロジェクトに形状情報が必要かどうかを判断します。

AIビジョンを思い浮かべたとき、多くの人がイメージするのはマスクです。また、ラベリング予算を最も多く消費するのもマスクです。マスクは物体の正確な形状を示し、ボックスは物体の位置だけを示します。この違いは製品の方向性を大きく左右することもありますが、ほとんどの製品では何も変わりません。

成果

画像上でボックスとマスクの差を測定し、出力を読み取るコードを根拠に、プロジェクトでボックスとマスクのどちらを使うか選びます。

短期集中コース
すでに使い方をご存じの方は、こちらをご覧ください。
  1. ボックスは位置を示し、マスクは形状、面積、正確な輪郭を示します。

  2. 面積、くり抜き、クリアランスが必要ならマスクです。カウント、ゾーン、追跡、切り抜きならボックスです。

  3. 物体ごとに数える場合はインスタンスセグメンテーション、領域の占有率だけを求める場合はセマンティックセグメンテーションです。

  4. 同じ解像度でピクセル数を比較します: retina_masks=True。

  5. ラベリング予算を確定する前に、ボックス20個とポリゴン20個の作業時間を測ります。

実践的

マスクで分かること#

市街地の交差点におけるUltralytics YOLOのインスタンスセグメンテーション。各車、バス、歩行者に個別のマスクが付いています

各物体について、Segmentタスクは検出器が返す情報(クラス、ボックス、信頼度)に加えて、物体に属するピクセルも返します。そのため、上の画像では各車にそれぞれ輪郭が付き、バスや歩行者とは区別されます。マスクはポリゴン(masks.xy)またはバイナリ画像(masks.data)として読み取れます。

形状によって数値はどのくらい変わるでしょうか。各物体について、ボックスの面積とマスクの面積を比較します。

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
result = model("https://ultralytics.com/images/bus.jpg", retina_masks=True, verbose=False)[0]

for box, mask in zip(result.boxes, result.masks.data):
    x1, y1, x2, y2 = box.xyxy[0].tolist()
    box_area = (x2 - x1) * (y2 - y1)
    mask_area = float(mask.sum())  # pixels inside the object's mask
    name, ratio = result.names[int(box.cls)], mask_area / box_area
    print(f"{name:7} box={box_area:7.0f} px  mask={mask_area:7.0f} px  ({ratio:.0%})")
person  box=  97116 px  mask=  49331 px  (51%)
person  box=  68102 px  mask=  21316 px  (31%)
bus     box= 410093 px  mask= 255265 px  (62%)
person  box=  55998 px  mask=  33524 px  (60%)
person  box=  23690 px  mask=  11109 px  (47%)

バスがボックス内で占めるのはわずか62%で、人物の1人は31%です。塗装面積、こぼれた量、棚の空き具合など、ボックスだけから計算した数値には、その割合分の誤差が生じます。製品がこのような数値を報告する場合はマスクが必要です。カウントや位置特定だけなら、追加のピクセル情報があっても結果は変わりません。

同じ解像度でピクセル数を比較する

症状: マスクの面積がボックスの面積より3~4倍小さく見えます。原因: デフォルトではmasks.dataは推論時のサイズ(ここでは640×480)で返される一方、ボックスは元画像のピクセル座標(1080×810)で表されます。対処法: 前述のようにretina_masks=Trueを渡すか、自分で数値をスケーリングします。

デモではなく、出力を利用する側の要件に基づいて判断する#

後段の処理で必要なことボックスで十分か理由
物体を数える、または存在を確認するはい位置が分かれば十分です
物体がゾーンに入ったときにアラートを出すはいボックスがラインを越えたことが分かれば十分です
フレームをまたいで物体を追跡するはいトラッカーはボックスを対応付けます
2つ目の分類器に渡すために切り抜くはい切り抜きにはボックスだけあれば十分です
面積や占有率を測定する(雑草、さび、こぼれ)いいえ不規則な形状では、ボックスの面積に意味がありません
物体を切り抜く(背景除去、AR)いいえ正確な輪郭が必要です
クリアランスや接触を確認する(ロボットの把持、衝突)いいえボックスは物体を実際より大きく見積もります
接触した不規則な物体を分離する多くの場合、いいえ重なったボックスでは、どのピクセルがどの物体に属するかが曖昧になります

製品で必要な各項目がすべて「はい」なら、検出器を使ってください。形状情報が役立つ場合に限り、YOLO26のセグメンテーションを検討してください。

インスタンスセグメンテーションとセマンティックセグメンテーション#

インスタンスセグメンテーションでは、各物体に個別のマスクを付けるため、重なった車が3台あれば、3台それぞれを区別できます。セマンティックセグメンテーションでは、各ピクセルにクラスだけを割り当てます。すべての車のピクセルが1つの「車」領域になり、台数は分からなくなります。細胞、果物、不良品など、個々の物体を数えたり測定したりする場合はインスタンスセグメンテーションを使います。道路と歩道、作物と土壌など、領域の占有率を測定する場合はセマンティックセグメンテーションを使います。

形状のラベリングにかかるコスト#

ボックスの作成はクリック2回で済みます。適切なポリゴンの作成には数十回のクリックが必要になることもあり、輪郭のすべての部分でアノテーターの判断が分かれる可能性があります。バッグは人物に含めるべきか、影は車の一部か、といった問題です。経験則を鵜呑みにせず、自分でコストを測定してください。アノテーターが20個の物体をボックスでラベリングする時間と、同じ20個をポリゴンでラベリングする時間を計り、その差にデータセットの規模を掛けます。

AI支援ツールを使うと、この差の大部分を縮められます。スマートアノテーションと一括ラベリングでは、クリックまたはボックスからマスクを提案するため、アノテーターは輪郭をなぞるのではなく修正できます。ただし、すべての境界を人が確認する必要があります。

最初のマスクを作る前に境界のルールを決める

アノテーション開始前に、オクルージョン、透明な物体、影、フレーム端で切れている物体、付属物(バッグ、取っ手、荷物)の扱いを決め、アノテーターとレビュアーの両方に同じ資料を渡します。アノテーションのベストプラクティスではルールの徹底方法を説明し、COCO-Segデータセットでは大規模データにおける一貫したマスクを確認できます。

マスクをサンプル確認するときは、はみ出し(マスクが輪郭の外に広がる)、穴(物体内の領域が抜けている)、偽の輪郭(マスクがJPEGのアーティファクトをなぞっている)、曖昧な部分(スーツケースの持ち手はスーツケースのマスクに含めるべきか)を確認します。

試してみる

自分のプロジェクトの画像(またはbus.jpg)で、ボックスとマスクを比較するスクリプトを実行します。主要クラスのマスクとボックスの面積比を記録し、後段の要件に合う行を利用側の表から見つけます。そして「ボックス」または「マスク」のどちらかと、判断の根拠となった利用側の処理を1行で書きます。

完了条件
以下のすべてを満たすと、レッスンは完了です。
  • 主要クラスについて、マスクとボックスの面積比を測定しています。

  • ボックスとマスクのどちらを選ぶかを、判断の根拠となった後段の処理とともに1行で記述しています。

  • マスクを選んだ場合は、ボックス20個とポリゴン20個の作業時間を計り、境界のルールを一覧にしています。

解答を表示

同じバスのカメラ映像を共有する製品が2つあるとします。歩行者カウンターと、車両がシーンのどれだけを占めるかを示す道路占有率レポートです。同じ実行結果から、両方の数値を測定します。

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
result = model("https://ultralytics.com/images/bus.jpg", retina_masks=True, verbose=False)[0]
height, width = result.orig_shape

for name in sorted({result.names[int(c)] for c in result.boxes.cls}):
    rows = [i for i, c in enumerate(result.boxes.cls) if result.names[int(c)] == name]
    union = result.masks.data[rows].amax(dim=0)  # merge masks so overlaps count once
    box_union = sum(float((b[2] - b[0]) * (b[3] - b[1])) for b in result.boxes.xyxy[rows])
    print(f"{name:6} {len(rows)} objects | masks cover {float(union.sum()) / (height * width):.0%} of the image, boxes {box_union / (height * width):.0%}")
bus    1 objects | masks cover 29% of the image, boxes 47%
person 4 objects | masks cover 13% of the image, boxes 28%
  • 歩行者カウンター → ボックス。どちらを使っても人数は4人です。マスクはラベリングコストを増やすだけで、結果は変わりません。
  • 占有率レポート → マスク。ボックスでは、バスがシーンの47%を占めると判定されますが、実際には29%です。ボックスに基づくレポートでは、誤差が半分を超えます。
次のステップ

次は2つの特殊タスクです。出力がスケルトンになる姿勢推定と、ボックスを回転させるOBBです。