diff --git a/CLAUDE.md b/CLAUDE.md index 2313597..58c5331 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,6 +20,7 @@ - `docs/03_TECH/TECH_01_操舵量計算仕様.txt` — PD 制御、2領域偏差、速度制御 - `docs/03_TECH/TECH_02_システム構成仕様.txt` — Pi/PC の役割分担、通信フロー、設計方針 - `docs/03_TECH/TECH_03_デバッグオーバーレイ仕様.txt` — オーバーレイ表示項目、描画色、GUI 操作 +- `docs/03_TECH/TECH_04_線検出精度向上方針.txt` — 線検出が最重要ファクターである理由、照明・影の課題、改善の方向性 ### 環境(セットアップ時に参照) - `docs/04_ENV/ENV_01_技術スタック選定.txt` — ZMQ, PySide6, OpenCV, Picamera2, RPi.GPIO, python-dotenv diff --git "a/docs/03_TECH/TECH_04_\347\267\232\346\244\234\345\207\272\347\262\276\345\272\246\345\220\221\344\270\212\346\226\271\351\207\235.txt" "b/docs/03_TECH/TECH_04_\347\267\232\346\244\234\345\207\272\347\262\276\345\272\246\345\220\221\344\270\212\346\226\271\351\207\235.txt" new file mode 100644 index 0000000..80f03ce --- /dev/null +++ "b/docs/03_TECH/TECH_04_\347\267\232\346\244\234\345\207\272\347\262\276\345\272\246\345\220\221\344\270\212\346\226\271\351\207\235.txt" @@ -0,0 +1,695 @@ +======================================================================== +線検出精度向上方針 (Line Detection Accuracy Improvement) +======================================================================== + + +1. 概要 (Overview) +------------------------------------------------------------------------ + + 1-0. 目的 + + 黒線の検出精度がコースアウトの有無を左右する最重要ファクターで + あることを示し,現在の手法の課題と改善手法の選択肢を定義する. + + 1-1. 線検出がコースアウトの最重要ファクターである理由 + + 自律走行の制御フローは以下の通りである. + + カメラ画像 → 線検出 → position_error/heading/curvature + → PD 制御 → 操舵量 + + PD 制御以降はいかにパラメータを調整しても,入力である + position_error が誤っていれば正しい操舵は得られない. + すなわち,線検出の精度がシステム全体の制御品質の上限を決める. + + ・線が正しく検出できる → 正確な偏差 → 正確な操舵 + ・線の検出が崩れる → 誤った偏差 → 反対方向への操舵 → コースアウト + + 速度を上げるほど 1 フレームあたりの移動距離が増えるため, + 検出の崩れが即コースアウトにつながる.高速化と精度向上は + 不可分の関係にある. + + 1-2. パイプライン構成 + + 線検出パイプラインは 4 段階で構成される. + 各段階ごとに手法を選択・組み合わせることで精度を向上させる. + + [Stage 1] 前処理(照明正規化) + [Stage 2] 二値化(線と背景の分離) + [Stage 3] 後処理(穴埋め・ノイズ除去・幅正規化) + [Stage 4] 特徴抽出(中心線 → 多項式フィッティング) + + +2. 検出対象の特性 (Target Characteristics) +------------------------------------------------------------------------ + + 2-1. 黒線の物理的特性 + + 本システムの走行コースは,道路の白線に近い性質を持つ黒線である. + + ・形状: 幅一定の単一直線(カーブも含むが幾何学的に連続) + ・色: 高輝度の床面に対して明確に暗い(理想条件下) + ・連続性: 途切れがなく,2次多項式で十分近似できる + ・幅: 画像上で数ピクセル〜数十ピクセル程度 + + 2-2. 理想条件と実環境の乖離 + + 理想条件(均一な拡散光,均質な床面)では固定閾値の2値化でも + 明確に検出できる.しかし実環境では以下の要因が存在する. + + ・光源の影響 + - 蛍光灯・LED 照明による局所的な輝度ムラ + - 窓からの外光による時間変動 + - カメラの自動露出による全体輝度の変動 + + ・影の影響 + - ロボット車体自体が床に影を落とす + - 床の継ぎ目・段差による輝度の乱れ + + ・床面の影響 + - 床材の光沢による反射スポット + - 黒線上の光沢(テープの反射) + + これらにより,黒線と背景の輝度差が局所的に縮小・逆転し, + 固定閾値の2値化では正しい線形状を得られないことがある. + + 2-3. 実環境で発生する2つの典型的な劣化 + + ■ 穴(光による欠損) + + 黒線テープの表面が光を反射し,反射部分の輝度が背景と同程度に + なるため,二値化後に線の中央が白抜け(穴)になる現象. + 多項式フィッティングの点が欠損し,検出が不安定になる. + + ■ 広がり(陰による膨張) + + 車体の影や照明ムラにより黒線の周囲の床が暗くなり, + 二値化後に線の幅が実際より広がる現象. + 広がった行はピクセル数が多いためフィッティングを支配し, + 線の位置がずれる原因になる. + + +3. 現在の手法と限界 (Current Approach and Limitations) +------------------------------------------------------------------------ + + 3-1. 現在の処理パイプライン + + ・Stage 1(前処理): グレースケール → CLAHE → ガウシアンブラー + ・Stage 2(二値化): 固定閾値(BINARY_INV) + ・Stage 3(後処理): オープニング → 横方向クロージング + ・Stage 4(特徴抽出): 全白ピクセルに2次多項式フィッティング + + 詳細は `TECH_01_操舵量計算仕様.txt` の + 「2. 画像処理パイプライン」を参照する. + + 3-2. 現在の対策とその効果・限界 + + ■ CLAHE(局所コントラスト強調) + + ・効果: 局所的な輝度ムラを補正し,黒線と背景の差を拡大する + ・限界: 輝度差が完全になくなった領域では効果がない + + ■ 固定閾値二値化 + + ・効果: 実装がシンプルで処理が高速 + ・限界: 1つの閾値で画像全体を判定するため, + 照明ムラが大きい場合に不均一な分離が生じる + - 閾値を低くする → 反射スポットが黒線として誤検出 + - 閾値を高くする → 影の下の黒線が検出されない + + ■ モルフォロジー処理 + + ・効果: 孤立ノイズの除去(オープニング), + 線の途切れの補間(横方向クロージング) + ・限界: 線の位置が大きくずれたノイズには対処できない. + 誤検出された大きな塊はフィッティングを大幅に歪める + + ■ 全白ピクセルフィッティング + + ・効果: 実装が単純 + ・限界: 陰で幅が広がった行はピクセル数が多く, + フィッティングへの寄与が大きいため線の位置がずれる + + 3-3. 残存する課題 + + ・強い照明ムラ環境では固定閾値が不安定になりやすい + ・カメラの自動露出でシーンが変わると適切な閾値が変動する + ・黒線上に光沢がある場合,線の中央が白抜けして線幅が細くなる + ・陰による幅の広がりが全白ピクセルフィッティングを歪める + + +4. Stage 1: 前処理の手法比較 (Pre-processing) +------------------------------------------------------------------------ + + 4-0. 目的 + + 照明ムラや輝度変動の影響を低減し,後段の二値化を安定させる. + + 4-1. CLAHE(現在の手法) + + 局所領域ごとにヒストグラム均等化を行い,コントラストを強調する. + + ・計算量: 低 + ・穴への効果: △(反射が強い場合は輝度差を復元できない) + ・陰への効果: ○(局所コントラスト向上で境界が明確になる) + ・実装: `cv2.createCLAHE(clipLimit, tileGridSize)` + ・備考: 現在の手法.照明ムラには有効だが根本的な正規化ではない + + 4-2. 背景除算正規化 + + 画像を大きなカーネルでぼかした画像で割り, + 照明の勾配(低周波成分)を除去する. + + ・計算量: 低(ガウシアンブラー1回 + 除算) + ・穴への効果: ○(局所的な高輝度を正規化できる) + ・陰への効果: ◎(照明勾配が除去されるため暗い領域も正規化) + ・実装: + + blur_bg = cv2.GaussianBlur(gray, (ksize, ksize), 0) + normalized = (gray.astype(np.float32) * 255.0 + / (blur_bg.astype(np.float32) + 1.0)) + normalized = np.clip(normalized, 0, 255).astype(np.uint8) + + ・パラメータ: ksize は線幅の 10 倍程度(大きいほど広い照明ムラに対応) + ・備考: 照明の勾配が緩やかな場合に最も効果的. + 急激な明暗境界(影の縁)には効きにくい + + 4-3. Black-hat 変換 + + モルフォロジーのクロージング結果から原画像を引くことで, + 「背景より暗い構造」だけを直接抽出する. + + ・計算量: 低(モルフォロジー演算1回) + ・穴への効果: ○(暗い構造として線全体を検出できる) + ・陰への効果: ◎(背景の輝度変動が除去される) + ・実装: + + kernel = cv2.getStructuringElement( + cv2.MORPH_ELLIPSE, (ksize, ksize)) + blackhat = cv2.morphologyEx( + gray, cv2.MORPH_BLACKHAT, kernel) + + ・パラメータ: ksize は線幅の 2〜3 倍(線より大きく, + 影より小さいサイズ) + ・備考: 黒線検出に原理的に最もフィットした手法. + 「背景に対してどれだけ暗いか」を直接出力するため, + 背景の絶対輝度に依存しない + + 4-4. ホモモルフィックフィルタ + + 画像の対数を取り,FFT で低周波(照明成分)と + 高周波(反射率成分)を分離し,照明成分を抑制する. + + ・計算量: 中(FFT + 逆 FFT) + ・穴への効果: ○(照明成分を分離するため反射の影響が減る) + ・陰への効果: ◎(照明成分の除去が原理的に正確) + ・実装: `np.fft.fft2` + ハイパスフィルタ + `np.fft.ifft2` + ・備考: 理論的には最も正確だが,FFT の計算量が + リアルタイム処理に影響する可能性がある. + 320x240 であれば実用範囲内 + + 4-5. LAB 色空間 L チャネル + + BGR → LAB 変換し,知覚均等な輝度チャネル L を使用する. + + ・計算量: 極低(色変換のみ) + ・穴への効果: △(グレースケールと大差ない) + ・陰への効果: △(輝度の表現が若干改善される程度) + ・実装: `cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)[:,:,0]` + ・備考: 単体での効果は限定的.他の手法と組み合わせて使う + + +5. Stage 2: 二値化の手法比較 (Binarization) +------------------------------------------------------------------------ + + 5-0. 目的 + + 前処理後の画像から黒線と背景を分離する. + + 5-1. 固定閾値(現在の手法) + + 1つの閾値で画像全体を二値化する. + + ・計算量: 極低 + ・穴への効果: ✕(反射で輝度が上がった部分は検出できない) + ・陰への効果: ✕(影で暗くなった床を誤検出する) + ・実装: `cv2.threshold(src, thresh, 255, THRESH_BINARY_INV)` + ・備考: Stage 1 で照明が正規化されていれば十分機能する. + Stage 1 が弱い場合は照明ムラに脆弱 + + 5-2. 大津の方法(Otsu) + + ヒストグラムからクラス間分散を最大化する閾値を自動決定する. + + ・計算量: 極低(ヒストグラム計算のみ) + ・穴への効果: ✕(グローバル閾値のため局所問題に弱い) + ・陰への効果: △(全体の明暗変化には追従する) + ・実装: `cv2.threshold(src, 0, 255, + THRESH_BINARY_INV + THRESH_OTSU)` + ・備考: 閾値の手動調整が不要になる利点がある. + ただし局所的な照明ムラには固定閾値と同様に弱い + + 5-3. 適応的閾値(ガウシアン加重平均) + + 各ピクセルの周囲 blockSize x blockSize 領域の + ガウシアン加重平均から閾値を算出する. + + ・計算量: 低(積分画像ベース) + ・穴への効果: ○(局所的に閾値が変わるため反射領域にも対応) + ・陰への効果: ○(影のある領域で閾値が下がるため追従できる) + ・実装: + + cv2.adaptiveThreshold( + src, 255, + cv2.ADAPTIVE_THRESH_GAUSSIAN_C, + cv2.THRESH_BINARY_INV, + blockSize, C) + + ・パラメータ: blockSize(局所領域サイズ,奇数), + C(閾値からの減算定数) + ・備考: 局所照明ムラへの耐性が固定閾値より大幅に向上する. + Stage 1 が弱い場合の有力な選択肢 + + 5-4. 適応的閾値(平均値) + + ガウシアン加重ではなく単純平均で閾値を算出する. + + ・計算量: 低 + ・穴への効果: ○ + ・陰への効果: △(ガウシアンより若干ノイズに弱い) + ・実装: `cv2.ADAPTIVE_THRESH_MEAN_C` を指定 + ・備考: ガウシアン版より高速だが精度はやや劣る + + 5-5. Sauvola 閾値 + + 局所の平均値と標準偏差から閾値を計算する. + コントラストが低い領域(= 線がない領域)を自動的に + 背景として扱える特性がある. + + ・計算量: 低〜中(積分画像 + 二乗積分画像) + ・穴への効果: ○ + ・陰への効果: ◎(低コントラスト領域を無視できる) + ・実装: OpenCV 標準にないため自前実装が必要 + + mean = cv2.blur(src, (k, k)) + sq_mean = cv2.blur( + (src.astype(np.float32)) ** 2, (k, k)) + std = np.sqrt(sq_mean - mean.astype(np.float32) ** 2) + thresh = mean * (1.0 + coeff * (std / 128.0 - 1.0)) + + ・備考: 文書画像の文字検出で実績が高い. + 黒線検出にも適している + + 5-6. 行ごと Otsu + + 画像を行(または行のブロック)単位で分割し, + 行ごとに Otsu の方法で閾値を決定する. + + ・計算量: 低(行数分の Otsu 計算) + ・穴への効果: ○(行単位で閾値が変わる) + ・陰への効果: ○(行ごとの照明変化に追従できる) + ・実装: for ループで行ブロックごとに `cv2.threshold` + Otsu + ・備考: 縦方向の照明勾配に強い.横方向のムラには弱い + + +6. Stage 3: 後処理の手法比較 (Post-processing) +------------------------------------------------------------------------ + + 6-0. 目的 + + 二値化後の画像から穴を埋め,ノイズを除去し, + 線の幅を正規化して特徴抽出の精度を向上させる. + + 6-1. オープニング(現在の手法) + + 収縮 → 膨張で孤立した小さなノイズピクセルを除去する. + + ・計算量: 極低 + ・穴への効果: -(穴の除去には寄与しない) + ・陰への効果: △(小さな誤検出は除去できる) + ・備考: 現在の手法.孤立ノイズ除去として引き続き有効 + + 6-2. 横方向クロージング(現在の手法) + + 膨張 → 収縮で線の横方向の途切れを補間する. + + ・計算量: 極低 + ・穴への効果: ○(横方向の途切れを埋める) + ・陰への効果: - + ・備考: 現在の手法.横長の穴には有効だが丸い穴には効きにくい + + 6-3. 等方クロージング + + 円形カーネルによるクロージングで, + 方向を問わず穴(光スポット等)を埋める. + + ・計算量: 極低 + ・穴への効果: ◎(丸い穴にも対応できる) + ・陰への効果: - + ・実装: + + hole_kernel = cv2.getStructuringElement( + cv2.MORPH_ELLIPSE, (hole_size, hole_size)) + binary = cv2.morphologyEx( + binary, cv2.MORPH_CLOSE, hole_kernel) + + ・パラメータ: hole_size は想定される穴の最大径 + ・備考: 横方向クロージングの代替または補完として使用する + + 6-4. 距離変換 + 閾値マスク + + 二値画像の各白ピクセルについて,最も近い黒ピクセルまでの + 距離を算出し,閾値以上の距離(= 中心部)だけを残す. + + ・計算量: 低 + ・穴への効果: -(穴の除去には寄与しない.先にクロージングが必要) + ・陰への効果: ◎(陰で広がった外縁は距離が短いため除去される) + ・実装: + + dist = cv2.distanceTransform( + binary, cv2.DIST_L2, 5) + _, center_mask = cv2.threshold( + dist, half_width_px, 255, cv2.THRESH_BINARY) + + ・パラメータ: half_width_px は黒線の幅の半分(ピクセル単位) + ・備考: 陰による幅の広がりを削り取る最も直接的な手法. + クロージングで穴を埋めた後に適用すると効果的 + + 6-5. スケルトン化(細線化) + + 二値画像の塊を反復的に収縮し, + 1ピクセル幅の中心線(骨格)を抽出する. + + ・計算量: 中(反復処理のため塊の幅に比例) + ・穴への効果: -(穴があると骨格が分断される.先にクロージングが必要) + ・陰への効果: ◎(幅に関わらず中心線が得られる) + ・実装: `cv2.ximgproc.thinning(binary)` + ・依存: `opencv-contrib-python` の `ximgproc` モジュールが必要 + ・備考: 幅の正規化としては最も確実だが, + 追加依存と計算量を考慮する必要がある + + 6-6. 連結成分フィルタ + + 連結成分分析で各塊の面積・アスペクト比を算出し, + 線として不適切な塊(小さすぎる,横に広すぎる等)を除去する. + + ・計算量: 低 + ・穴への効果: - + ・陰への効果: ○(大きな誤検出塊を面積で除去できる) + ・実装: + + n, labels, stats, _ = cv2.connectedComponentsWithStats( + binary) + # stats で面積・幅・高さを確認し, + # 異常な塊のラベルを 0(背景)に置換 + + ・備考: 線から離れた場所の大きな誤検出に有効. + 線に隣接した陰は連結成分が線と結合するため効かない + + 6-7. 幅フィルタ(行ごと) + + 各行の白ピクセルの幅(右端 - 左端)を計算し, + 期待される線幅の範囲外の行を除外する. + + ・計算量: 極低 + ・穴への効果: - + ・陰への効果: ◎(幅が広すぎる行を直接除外できる) + ・実装: + + for y in range(height): + xs = np.where(binary[y] > 0)[0] + if len(xs) > 0: + width = xs[-1] - xs[0] + 1 + if width > max_line_width: + binary[y] = 0 # 幅が異常な行を除去 + + ・パラメータ: max_line_width は線幅の最大許容値(ピクセル) + ・備考: 実装が最も簡単で陰への効果が高い. + ただし陰が線の片側だけに広がった場合は中心がずれる + + +7. Stage 4: 特徴抽出の手法比較 (Feature Extraction) +------------------------------------------------------------------------ + + 7-0. 目的 + + 二値化・後処理後の画像から線の位置・傾き・曲率を算出する. + + 7-1. 全白ピクセルフィッティング(現在の手法) + + 検出された全白ピクセルの (y, x) 座標に + 2次多項式 x = f(y) をフィッティングする. + + ・計算量: 低 + ・穴への効果: △(点が欠損した行の影響は小さいが精度は下がる) + ・陰への効果: ✕(幅が広い行のピクセルがフィッティングを支配する) + ・実装: `np.polyfit(ys, xs, 2)` + ・備考: 現在の手法.Stage 3 で幅が正規化されていれば有効だが, + 陰の影響を受けやすい根本的な弱点がある + + 7-2. 行ごと中心抽出 + フィッティング + + 各行の白ピクセル群の中心(平均 x)を1点ずつ抽出し, + 中心点列に対してフィッティングする. + + ・計算量: 極低 + ・穴への効果: ○(穴のある行は中心が計算できず除外される) + ・陰への効果: ◎(幅が広がっても中心位置はほぼ変わらない. + 各行が等しく1票なので幅による支配が発生しない) + ・実装: + + centers_y, centers_x = [], [] + for y in range(height): + xs = np.where(binary[y] > 0)[0] + if len(xs) >= min_line_width: + centers_y.append(y) + centers_x.append(float(np.mean(xs))) + coeffs = np.polyfit(centers_y, centers_x, 2) + + ・備考: コード変更が最小で効果が最大の改善. + 幅による重み付けの偏りを根本的に解消する + + 7-3. 行ごと中央値抽出 + フィッティング + + 7-2 の亜種.平均値の代わりに中央値を使用する. + + ・計算量: 極低 + ・穴への効果: ○ + ・陰への効果: ◎(中央値は外れ値に更に強い) + ・実装: `np.mean(xs)` を `np.median(xs)` に変更 + ・備考: 陰が線の片側だけに広がった場合に平均値より頑健 + + 7-4. RANSAC フィッティング + + ランダムにサンプルした点から仮モデルを作り, + 外れ値を除去しながらフィッティングする. + + ・計算量: 中(反復回数 x サンプル数に比例) + ・穴への効果: ◎(欠損があっても外れ値として除外) + ・陰への効果: ◎(外れ値(= 陰のピクセル)を直接除外) + ・実装: `sklearn.linear_model.RANSACRegressor` または自前実装 + ・依存: scikit-learn を使う場合は追加依存が発生 + ・備考: 最もロバストだが計算量が最大. + 行ごと中心抽出で十分な場合はオーバースペック + + 7-5. 重み付きフィッティング + + 距離変換値(= 線の中心からの距離)を重みとして + フィッティングに使用する.中心に近いピクセルほど重みが大きい. + + ・計算量: 低 + ・穴への効果: ○ + ・陰への効果: ○(外縁のピクセルの重みが自動的に小さくなる) + ・実装: `np.polyfit(ys, xs, 2, w=weights)` + ・備考: 全ピクセルを使いつつ陰の影響を低減できる折衷案 + + 7-6. スプライン補間 + + 2次多項式の代わりにスプライン曲線を使用する. + + ・計算量: 低 + ・穴への効果: ○ + ・陰への効果: ○ + ・実装: `scipy.interpolate.UnivariateSpline` + ・依存: scipy(多くの場合すでにインストール済み) + ・備考: S 字カーブ等,2次多項式では表現できない + 複雑な形状に対応できる.ただし過学習のリスクがある + + +8. Stage 0: 撮影条件の最適化 (Camera Settings) +------------------------------------------------------------------------ + + 8-0. 目的 + + ソフトウェア処理の前段階として,撮影条件を制御することで + 入力画像の品質を安定させる. + + 8-1. カメラ露出の固定 + + Picamera2 の自動露出(AE)を無効化し,固定値で撮影する. + + ・コスト: なし(ソフトウェア設定のみ) + ・効果: ◎(フレーム間の輝度変動を排除し,閾値パラメータを安定させる) + ・注意: 環境ごとに適切な露出値を設定する必要がある + ・参照: `src/pi/camera/capture.py` の撮影パラメータ設定 + + 8-2. ホワイトバランスの固定 + + 自動ホワイトバランス(AWB)を無効化する. + + ・コスト: なし + ・効果: ○(色味変動によるグレースケール値の揺れを防止) + + 8-3. 赤外 LED + IR カメラ + + 赤外光源と IR パスフィルタを使用して撮影する. + + ・コスト: 高(ハードウェア追加が必要) + ・効果: ◎(黒は赤外線を吸収するため,可視光の影響を完全に排除) + ・備考: コスト面で優先度は低いが,原理的には最も頑健な手法 + + +9. 推奨する組み合わせ案 (Recommended Combinations) +------------------------------------------------------------------------ + + 9-0. 選定の考え方 + + コースアウトを防ぐ上で「安定性」は「精度」より優先する. + 誤検出が 1 フレームでも入るとコースアウトしうるため, + 頑健性(ロバスト性)の高い手法を選択すること. + + 以下の3案はいずれも穴・陰の両方に対して高い耐性を持つ. + 処理時間は 320x240 画像での概算値であり, + 30fps(~33ms/フレーム)に対していずれも十分な余裕がある. + + 9-1. 案A: Black-hat 中心型(推奨) + + Black-hat が「背景より暗い構造」を直接抽出するため, + 照明正規化後の画像は非常にクリーンになり, + 固定閾値でも安定する.計算量が最も少なくパラメータも少ない. + まず試すべき案である. + + ・パイプライン: + + グレースケール → Black-hat → ブラー → 固定閾値 + → 等方クロージング → 距離変換マスク + → 行ごと中心抽出 → polyfit + + ・各段階の処理時間(概算): + - Stage 1: Black-hat 変換 ~0.2ms + - Stage 1: ガウシアンブラー ~0.2ms + - Stage 2: 固定閾値 ~0.05ms + - Stage 3: 等方クロージング ~0.1ms + - Stage 3: 距離変換 + 閾値マスク ~0.2ms + - Stage 4: 行ごと中心抽出 + polyfit ~0.2ms + - 合計 ~1.0ms + + ・穴: ◎(クロージングで穴を埋め,距離変換で中心を抽出) + ・陰: ◎(Black-hat が背景輝度を除去 + 距離変換が外縁を削る + + 中心抽出が幅変動を無視) + ・変更量: 中 + ・追加パラメータ: 3個(Black-hat カーネルサイズ, + 等方クロージングサイズ,距離変換閾値) + ・追加依存: なし + + 9-2. 案B: 二重正規化型 + + 背景除算で大域的な照明勾配を除去した上で, + 適応的閾値が局所的なムラも処理する. + 原理の異なる2手法が補完し合う「二重防壁」構成であり, + どちらか一方では対処できないケースにも対応できる. + + ・パイプライン: + + グレースケール → 背景除算正規化 + → 適応的閾値(ガウシアン) + → 等方クロージング → 距離変換マスク + → 行ごと中心抽出 → polyfit + + ・各段階の処理時間(概算): + - Stage 1: 背景除算(大カーネルブラー + 除算) ~0.5ms + - Stage 2: 適応的閾値(ガウシアン) ~0.3ms + - Stage 3: 等方クロージング ~0.1ms + - Stage 3: 距離変換 + 閾値マスク ~0.2ms + - Stage 4: 行ごと中心抽出 + polyfit ~0.2ms + - 合計 ~1.3ms + + ・穴: ◎(背景除算で反射の影響を低減 + + 適応的閾値が局所的に追従) + ・陰: ◎(背景除算が影の勾配を除去 + + 適応的閾値が局所閾値を調整) + ・変更量: 中 + ・追加パラメータ: 4個(背景除算カーネルサイズ, + blockSize,C,距離変換閾値) + ・追加依存: なし + + 9-3. 案C: 最高ロバスト型 + + 全段階で最もロバストな手法を選択した構成. + Black-hat + 適応的閾値の二重正規化に加え, + RANSAC で前段を突破した外れ値も排除する. + 全段階に防壁があるため,極端な照明環境でも破綻しにくい. + + ・パイプライン: + + グレースケール → Black-hat → 適応的閾値(ガウシアン) + → 等方クロージング → 距離変換マスク + → 行ごと中央値抽出 → RANSAC polyfit + + ・各段階の処理時間(概算): + - Stage 1: Black-hat 変換 ~0.2ms + - Stage 2: 適応的閾値(ガウシアン) ~0.3ms + - Stage 3: 等方クロージング ~0.1ms + - Stage 3: 距離変換 + 閾値マスク ~0.2ms + - Stage 4: 行ごと中央値抽出 ~0.2ms + - Stage 4: RANSAC polyfit ~1.5ms + - 合計 ~2.5ms + + ・穴: ◎ + ・陰: ◎ + ・変更量: 大 + ・追加パラメータ: 5個(Black-hat カーネルサイズ, + blockSize,C,距離変換閾値,RANSAC 閾値) + ・追加依存: scikit-learn(RANSAC) + ・備考: RANSAC が処理時間の大部分を占める. + 案A・B で十分な精度が得られる場合はオーバースペック + + 9-4. 3案の総合比較 + + ・処理時間: + - 案A: ~1.0ms(対現在比 ~1.0x) + - 案B: ~1.3ms(対現在比 ~1.3x) + - 案C: ~2.5ms(対現在比 ~2.5x) + + ・穴耐性: 案A ◎,案B ◎,案C ◎ + ・陰耐性: 案A ◎,案B ◎,案C ◎ + ・追加パラメータ数: 案A 3個,案B 4個,案C 5個 + ・追加依存: 案A なし,案B なし,案C scikit-learn + ・実装変更量: 案A 中,案B 中,案C 大 + + いずれも 30fps に対して十分な余裕がある. + まず案A を実装して効果を確認し,不足があれば + 案B・案C に段階的に進めることを推奨する + + +10. 評価方法 (Evaluation) +------------------------------------------------------------------------ + + 10-1. 定性評価 + + デバッグオーバーレイ(`TECH_03_デバッグオーバーレイ仕様.txt` 参照) + を使い,二値化画像と多項式フィッティング結果を目視確認する. + + ・確認ポイント + - 二値化画像で黒線が一本の連続した塊として描かれているか + - 床面(背景)の誤検出塊がないか + - 多項式フィッティング線が実際の黒線に重なっているか + - 光の穴や陰の広がりが後処理で正しく補正されているか + + 10-2. 定量評価 + + 実走テストでコースアウトに至るまでの周回数・走行距離を記録し, + 手法変更前後で比較する. + + ・評価環境: 通常照明,強照明,照明ムラありの3条件を推奨 + ・計測項目: 周回数,コースアウト回数,position_error の分散