fullseye

学習データセット規約の知識 — COCO / YOLO / VOC と外観検査での落とし穴

fullseye は inspection_dataset / optscene_instances / optscene_defect_mask画素完全な真値を吐ける。だが真値は、学習フレームワークが読める形に変換して初めて 使える。この文書は op の説明ではなく、その受け皿側 —— 座標規約・面積の定義・ 評価指標の癖・分割の作り方 —— を書く教材である。 optics/guides/mv_*.md(2-D 撮像系)、3d/guides/depth_sensors.md(3-D 撮像系)と 同じ位置づけで、こちらは出口にあたる。


1. bbox の表現は 3 つあり、fullseye はさらに別

ここが事故の 8 割。同じ矩形が 4 通りの数字で書ける。

形式 並び 原点 単位
COCO [x, y, width, height] 左上、0 始まり 絶対 px
YOLO(Ultralytics) class x_center y_center width height 左上、0 始まり 画像幅・高さで正規化(0–1)
Pascal VOC(XML) xmin ymin xmax ymax 左上 絶対 px、1 始まりとして扱うのが通例(MATLAB 由来)。変換コードによって −1 する / しないが割れているので、他人のスクリプトを信じない
fullseye optscene_instances (x0, y0, x1, y1) 左上、0 始まり 絶対 px、右下を含む(inclusive)

optscene_instances の bbox は右下を含むので、幅は x1 − x0 ではなく x1 − x0 + 1 である。変換式:

w = x1 - x0 + 1
h = y1 - y0 + 1
coco = [x0, y0, w, h]                                   # 絶対 px
yolo = [(x0 + w / 2) / W, (y0 + h / 2) / H, w / W, h / H]   # 0-1 正規化

1 画素の取り違えは小さな欠陥ほど致命的になる(§4)。


2. COCO の中身(一次情報で押さえるべき点)


3. 評価指標の中身(mAP を鵜呑みにしない)

pycocotools の既定値(cocoeval.Params):

パラメータ
IoU 閾値 0.50, 0.55, ..., 0.95(10 段)
Recall 閾値 0.00 … 1.00(101 段)
面積区分 all = [0, 1e10] / small = area < 32² = 1024 px²medium = 32²–96² / large > 96²
maxDets 1, 10, 100

外観検査でここが効く:


4. 外観検査に固有の落とし穴

  1. 良品にはアノテーションが無い。 COCO は「アノテーションが 0 件の画像」を許すし、 YOLO は「対象が無い画像には .txt を置かなくてよい」と明記している。ところが 多くの学習スクリプトは空アノテーションの画像を黙って捨てる。捨てられると 良品が 1 枚も学習に入らず、「何かしら欠陥を出す」モデルになる。 良品の枚数が学習ログの枚数と一致しているか、最初に数える。
  2. 極端なクラス不均衡。 欠陥画素は全画素の 10⁻⁴ 〜 10⁻⁶ のオーダー。 画素単位の accuracy は 99.99% でも中身が空、という結果が普通に出る。 1 つの数字は「完璧」と「何も出していない」を区別しないので、 欠陥画素に対する再現率と誤検出数を必ず別に出す。
  3. 分割の漏れ(leakage)。 合成データでは、同じ母材・同じ乱数系統から出た複数枚が train と val の両側に入りやすい。ドメインランダム化で見た目が違っても、 同じ欠陥配置なら実質同じサンプルである。inspection_datasetseed と 欠陥ラベルでグループを作り、グループ単位で分割する(画像単位で分けない)。
  4. 合成データはラベルが完璧すぎる。 実データのアノテーションは人が引くので、 境界が数 px 揺れ、微小欠陥は見落とされる。完璧な真値だけで学習したモデルは、 実データで評価するとアノテーション側の揺れを誤検出として数えられる。 sim2real の差は画像の見た目だけでなく、ラベルの質にもある
  5. meta を捨てない。 inspection_dataset は照明種別・露光・ゆらぎ量・欠陥ラベルを meta に返す。これは学習には要らないが、失敗の切り分けには要る (「暗視野のときだけ落ちる」は meta が無いと分からない)。

5. fullseye からの書き出し(対応表)

欲しいもの fullseye の出どころ 変換
画像 inspection_dataset(...)["image"] そのまま(量子化済み)
bbox optscene_instances(...)["bbox"](inclusive) §1 の式で COCO / YOLO へ
クラス ["kind"] category_id に写す。0 始まりに揃える
インスタンスマスク ["mask"](H×W bool) RLE 化は列優先で(§2)
面積 ["area_px"] COCO の area に入れる(bbox 面積ではない)
意味的マスク optscene_defect_mask セグメンテーション学習用
深度真値 inspection_dataset(...)["depth_mm"] 単位 mm。スケールを一緒に運ぶ
再現情報 ["meta"] データセットの info / 独自フィールドに残す

6. 現状の穴(正直に)


7. 診断表 —— 症状から原因へ

症状 まず疑う 確かめ方
良品を一枚も学習していない気配 空アノテーションの画像がローダで落ちている 学習ログの枚数と生成枚数を突き合わせる
画素 accuracy 99.99% なのに何も出ない クラス不均衡 欠陥画素の再現率と誤検出数を別々に出す
val の成績が良すぎる 分割の漏れ(同じ seed・同じ欠陥配置が両側にいる) seed と欠陥ラベルでグループを作り、グループ単位で分け直して再学習
小さい欠陥だけ AP が低い IoU 0.5 が小箱に厳しい(§3 の表) 中心距離、または画素 IoU(segm)で測り直す
箱が全部 1 px ずれている/全滅する inclusive と exclusive の取り違え、VOC の 1 始まり 変換後の bbox を画像に描いて目で確認する(数字だけ見ない)
マスクが意味不明な形に崩れる RLE を行優先で作った order="F" で作り直す(§2)
1 枚に大量の欠陥があると再現率が頭打ち maxDets=100 maxDets を上げて再評価
合成では良いのに実データで誤検出だらけ ラベルの質の差(真値が完璧すぎる) 実データの GT を数枚引き直し、境界の揺れ幅を測って合成側に反映する
クラス番号が 1 つずれる COCO 素データの category_id は 1 始まり・欠番あり カテゴリ一覧を出力し、連番に写す辞書を明示的に作る

出典(一次情報)