diff --git a/.gitignore b/.gitignore index 77310e6..765e56e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ .claude/plans/ .claude/commit-context.md +# Local-only release script (contains internal lab server URL) +scripts/release-push.ps1 + # Python __pycache__/ *.py[cod] diff --git a/CLAUDE.md b/CLAUDE.md index 5fb55b3..a50bce1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ ## 開発進捗 -最新: プロジェクト/デバイス名を SmTIAS へ改名し,`load_roi_config` と DNG 輝度化パイプラインを `src/`(`src/config.py`・`src/io/dng_pipeline.py`)へ集約.次はリリースタグ付け +最新: 下・左・右の単灯切り分け評価を実施(TEST_04・`compare_single_leds.py`),output をデバイス別構造(`output/smtias/{figures,results}`・`output/compare/`)へ整理.次は output/results 側の whiteboard 分離 + 3 灯合成 vs 単灯加算の検証 ※ 本欄は**最新ステップ 1 行のみ上書き更新**.詳細な進捗履歴(動機・設計判断・失敗パターン)は [docs/PROGRESS.md](docs/PROGRESS.md) に追記する.運用ルールは GUIDE_05「進捗記録の運用ルール(CLAUDE.md / PROGRESS.md)」を参照. ## 必須ルール(コード実装時) @@ -65,13 +65,17 @@ - アーキテクチャ設計: docs/04_SPEC/SPEC_01_アーキテクチャ設計.md - 照明均一性評価アルゴリズム: docs/04_SPEC/SPEC_02_照明均一性評価アルゴリズム.md +- TIAS 白板撮影プロトコル: docs/04_SPEC/SPEC_03_TIAS白板撮影プロトコル.md +- 白板撮影データ仕様: docs/04_SPEC/SPEC_04_白板撮影データ仕様.md ### 05_TECH(技術設計) - DNG 対応要求仕様: docs/05_TECH/TECH_01_DNG対応要求仕様.md +- TIAS 撮影条件: docs/05_TECH/TECH_02_TIAS撮影条件.md ### 06_TEST(テスト・評価報告) - 照明均一性評価報告書: docs/06_TEST/TEST_01_照明均一性評価報告書.md - PNG/DNG 評価比較報告書: docs/06_TEST/TEST_02_PNG_DNG評価比較報告書.md - 外乱光影響評価報告書: docs/06_TEST/TEST_03_外乱光影響評価報告書.md +- 単灯照明比較評価報告書: docs/06_TEST/TEST_04_単灯照明比較評価報告書.md diff --git a/config/roi_config.json b/config/roi_config.json index 1706232..2d387e4 100644 --- a/config/roi_config.json +++ b/config/roi_config.json @@ -6,5 +6,13 @@ "width": 1400, "height": 1409 } + }, + "tias": { + "whiteboard": { + "x": 163, + "y": 469, + "width": 787, + "height": 734 + } } } \ No newline at end of file diff --git "a/docs/04_SPEC/SPEC_03_TIAS\347\231\275\346\235\277\346\222\256\345\275\261\343\203\227\343\203\255\343\203\210\343\202\263\343\203\253.md" "b/docs/04_SPEC/SPEC_03_TIAS\347\231\275\346\235\277\346\222\256\345\275\261\343\203\227\343\203\255\343\203\210\343\202\263\343\203\253.md" new file mode 100644 index 0000000..33954bb --- /dev/null +++ "b/docs/04_SPEC/SPEC_03_TIAS\347\231\275\346\235\277\346\222\256\345\275\261\343\203\227\343\203\255\343\203\210\343\202\263\343\203\253.md" @@ -0,0 +1,133 @@ +# TIAS 白板撮影プロトコル (TIAS White-Board Capture Protocol) + +本書は,従来型口腔内画像取得装置 **TIAS** の照明均一性を評価するための白板撮影手順とカメラ設定を定める.取得したデータは [SPEC_02 照明均一性評価アルゴリズム](SPEC_02_照明均一性評価アルゴリズム.md) でそのまま解析できることを前提とする. + +> **位置づけ**: 本書は撮影(データ取得)側のプロトコルである.評価指標の定義・算出方法は [SPEC_02](SPEC_02_照明均一性評価アルゴリズム.md),対象範囲は [PLAN_01 要件定義書](../03_PLAN/PLAN_01_要件定義書.md)(優先度 2「照明均一性比較 SmTIAS vs TIAS」)を参照.SmTIAS 側の撮影実績は [TEST_01](../06_TEST/TEST_01_照明均一性評価報告書.md)・[TEST_03](../06_TEST/TEST_03_外乱光影響評価報告書.md) を参照. + +## 目的 (Purpose) + +TIAS を白板で撮影する目的は,**TIAS 単体の照明均一性を定量化し,最終的に SmTIAS と装置間比較する**ことにある.具体的には次の 3 点を満たすデータを取得する. + +- **均一性指標の算出**: 白板(理想的には全面が一様反射率の面光源応答ターゲット)を撮影し,輝度分布から CoV・最大/最小比・中心周辺勾配・動径 min/max 比などを算出する(指標定義は [SPEC_02](SPEC_02_照明均一性評価アルゴリズム.md)).白板を使うのは,**被写体の反射率ムラを排除し,観測される輝度ムラを照明系のムラに帰着させる**ためである. +- **装置間比較の成立**: SmTIAS と同一の評価パイプライン・同一指標で比較できる形式(後述のカメラ設定・データ形式)で取得する. +- **再現性の確認**: 同一条件で複数枚撮影し,セッション内のばらつき(SSIM・指標の分散)を把握する. + +### なぜ条件を SmTIAS と揃えるのか (Rationale: Match SmTIAS Conditions) + +本評価の主目的は **SmTIAS と TIAS の照明系の差**を見ることである.露出・ホワイトバランス・現像処理などの撮影条件が両装置で異なると,観測される輝度ムラに「照明系の差」と「撮影条件の差」が混在し,比較が成立しない.そこで TIAS 側も,可能な範囲で SmTIAS の定量撮影モードと撮影条件を揃え,**純粋に照明系の差だけが指標に現れる**ようにする. + +※ ただし CoV・最大/最小比・中心周辺比・動径 min/max 比は画像内の相対ばらつき指標であり,全画素に等しく作用する係数(露出スケール・線形ゲイン等)には不変である([SPEC_02 キャリブレーション](SPEC_02_照明均一性評価アルゴリズム.md) 参照).したがって**最優先で揃えるべきは「線形性を壊す処理(トーンカーブ・自動露出・ホワイトバランス・ノイズ低減・シェーディング補正)の有無」**であり,露出値・ISO の絶対一致は副次的である. + +## 撮影対象 (Target: White Board) + +- **白板**: TIAS の積分球開口部に合致する**別白板**を用いる(SmTIAS 用白板とは物理形状が異なるため流用しない). + - 開口部全面を均一な反射率で覆える寸法・形状であること. + - 表面は均一拡散反射(マット)で,光沢・色ムラ・汚れ・傷がないこと. + - 評価に使う領域(ROI)が開口内に十分収まること. +- **ROI の扱い**: 白板の白色面のみを ROI とし,開口縁・筐体壁面・フレーム・ラベル等は除外する(ROI 選定手順は [SPEC_02 ROI 設定](SPEC_02_照明均一性評価アルゴリズム.md)).TIAS 用の ROI は SmTIAS とは別座標になるため,`config/roi_config.json` に `tias.whiteboard` として新規登録する. + +> **要確認**: TIAS 開口部の形状・寸法と,それに合致する白板の入手/作製方法は未確定.撮影前に物理的な設置可否を確認する([要確認事項](#要確認事項-open-questions)). + +## カメラ設定 (Camera Settings) + +### 揃える基準値 (SmTIAS 定量撮影モード) + +SmTIAS は定量撮影モードで以下の設定により **RAW_SENSOR (DNG) を線形・無補正で取得**している(実測値: `data/smtias/quantitative/*.meta.json`,機種 SH-02M / AQUOS sense3).TIAS 側はこの設定思想に揃える. + +| 項目 | SmTIAS 定量モードの値 | 揃える狙い | +| --- | --- | --- | +| 出力形式 | RAW_SENSOR (DNG, 10-bit linear) | トーンカーブ・sRGB 符号化を経ない線形信号を得る | +| 自動露出 (AE) | OFF(手動固定) | 露出を固定し,フレーム間で変動させない | +| 露出時間 | 8,333,333 ns(≒ 1/120 s) | 固定露出 | +| ISO 感度 | 40(最低感度) | ノイズ最小・固定 | +| ホワイトバランス (AWB) | OFF,ゲイン [1,1,1,1],色変換 identity | チャンネルゲインを掛けず生の比率を保つ | +| オートフォーカス (AF) | OFF(手動固定) | フォーカス位置を固定 | +| ノイズ低減 (NR) | OFF | 空間的平滑化で輝度分布を歪めない | +| エッジ強調 | OFF | シャープ化による輝度改変を避ける | +| トーンマップ | CONTRAST_CURVE_LINEAR(線形) | 入射光量に比例した値を保つ | +| シェーディング補正 (LSC) | HIGH_QUALITY(補正を適用しビネット除去)※ | レンズ由来の周辺減光を除き,照明系のムラだけを残す | +| ブラックレベルロック | true | 黒レベルを固定 | + +※ **シェーディング補正(LSC)は他の処理と性質が逆で,必ず適用してビネットを除去する**.NR・トーンカーブ・エッジ強調は信号を不可逆に歪めるため OFF にするが,シェーディング補正は**レンズ由来の周辺減光(ビネット)を除く**補正であり,これを掛けないと照明系のムラとカメラのビネットが混在してしまう.SmTIAS では `shading_mode = HIGH_QUALITY` を指定しているが実機 DNG は LSC **未補正**で出力されるため,評価側(`src/io/dng_pipeline.py`)でシェーディングマップを順適用してビネットを除去している(経緯は [TECH_01](../05_TECH/TECH_01_DNG対応要求仕様.md)).**未補正のまま評価すると指標が大きく悪化する**(SmTIAS 実測: CoV 0.070→0.146,中心周辺勾配 8.4%→31%,動径 min/max 0.86→0.53.[TEST_03](../06_TEST/TEST_03_外乱光影響評価報告書.md)).したがって TIAS でも,在カメラ補正を掛けるか,RAW で取得してシェーディングマップを残し評価側で補正できるようにする(どちらでもよいが最終的にビネットは必ず除く).なお実機 TIAS は在カメラ補正もシェーディングマップも持たないため,後述「[TIAS 実機カメラ仕様と差分の扱い](#tias-実機カメラ仕様と差分の扱い-tias-cameras--handling-gaps)」のフラットフィールド補正で対応する. + +### 設定方針 (Capture Policy for TIAS) + +優先度の高い順に次を満たす. + +1. **線形・無加工で取得する(最優先)**: 自動露出・自動ホワイトバランス・ノイズ低減・エッジ強調・トーンカーブをすべて OFF にし,可能なら **RAW** で保存する.これらが ON だと輝度が入射光量に比例しなくなり,照明ムラの評価が成立しない. +2. **シェーディング補正(ビネット除去)は必ず適用する**: 上記の OFF 項目とは逆に,レンズ由来の周辺減光(ビネット)は補正して除く.未補正だと照明系のムラとカメラのビネットが混在し,指標が大きく悪化する(SmTIAS 実測で CoV が約 2 倍・中心周辺勾配が約 4 倍に膨張.[TEST_03](../06_TEST/TEST_03_外乱光影響評価報告書.md)).在カメラ補正を掛けるか,RAW+シェーディングマップで評価側補正できるようにする(SmTIAS と同方式). +3. **すべて手動固定する**: 露出・ゲイン(ISO)・ホワイトバランス・フォーカスを手動で固定し,全撮影で同一値を使う. +4. **飽和・黒つぶれを避ける**: 白板の最明部が飽和(クリップ)せず,かつ十分な信号量になるよう露出を調整する.飽和すると最大/最小比・CoV が過小評価される.SmTIAS では飽和画素率 0%・p99 が白レベル 1023 に対し約 880 と余裕を持たせている.TIAS でも飽和ゼロかつ最明部がレンジの 7〜9 割程度を目安とする. +5. **露出値の一致は副次的**: TIAS のセンサ・光学系・照明強度は SmTIAS と異なるため,露出時間・ISO の絶対値を SmTIAS に一致させる必要はない.**「線形・無加工・飽和なし」を満たした上で TIAS 固有の最適露出**を選び,その値を記録する. + +### TIAS 実機カメラ仕様と差分の扱い (TIAS Cameras & Handling Gaps) + +TIAS の撮影は専用アプリ **TIASshot** が制御し,2 系統のカメラ(Lucam: Lumenera Lw110,ImagingSource: DFK 33UX178 / DFK 23UX249)に対応する.いずれも **RGB24 8-bit(デモザイク済み)出力で RAW ではない**.実機の撮影条件と SPEC_03 基準値との対照は [TECH_02 TIAS 撮影条件](../05_TECH/TECH_02_TIAS撮影条件.md) を参照.SmTIAS 基準(RAW 10-bit linear)と揃わない項目について,本評価での扱いを次に定める. + +#### 8-bit・非 RAW 出力の許容 (8-bit / Non-RAW) + +- TIAS は RAW を取得していないため **RGB24 8-bit を許容する**.ただし条件として,撮影パスが**線形**(γ=1.0・トーンマップ OFF)であり,白板が 8-bit レンジの中〜上を使う適正露出であること.真のリスクはビット深度でなく隠れた非線形なので,リニアリティを担保する. +- 量子化ノイズの CoV への寄与は平均輝度 150〜200 で 0.002 程度と無視できる.デモザイク(HIGH_QUALITY 等)は白板の低空間周波数に対して影響が小さく許容する. +- 8-bit ではノイズに弱い画素単位の最大/最小比よりも,**CoV・中心周辺勾配・動径 min/max(輪帯平均)を主指標**とする. + +#### 色処理(WB・CCM・色強調) (Color Processing) + +- ホワイトバランスは校正なしの**固定値(非 identity)**のまま評価してよい.**全チャンネルが同一の空間パターンを持つ限り,固定の線形結合(WB・CCM)は空間パターンをスカラ倍に保ち,CoV など相対指標は不変**だからである. +- ただし色ビネット等でチャンネルごとに空間パターンが異なると合成輝度の指標がわずかに動く.これを避けるロバストチェックとして,**輝度合成に加えて緑チャンネル単体でも均一性を算出する**(単一チャンネルは一様ゲインに完全不変で,WB/CCM のチャンネル混合を回避できる). +- 非線形・機種間不一致を避けるため,**色強調(DFK23: Color Enhancement)は OFF,色補正行列(Lucam: CCM)は NONE** を推奨する(クリップによる非線形化と 3 系統の色処理差を解消). + +#### ビネット除去(TIAS にはマップがない) (Vignette Removal) + +- TIAS はカメラ・SDK ともレンズシェーディング補正手段を持たず,シェーディングマップも保存していない.SPEC_03 はビネット除去を要求するため,**フラットフィールド補正**で対応する. +- 重要: **TIAS 自身の光で均一面を撮っても照明 L とレンズビネット V は分離できない**(参照画像も `L×V` のため,割っても反射率比≒1 になるだけ).V を特性化するには **TIAS 照明とは独立な既知の一様光源**(均一 LED パネル/ライトボックス/別の積分球出力等)で画面全面を撮影し,正規化して V(x,y) を得て白板画像を除算する. +- どうしても V を特性化できない場合は,結果を**「照明×レンズの系全体の均一性」と明記**し,レンズ補正済みの SmTIAS とは**直接比較不可(TIAS 照明均一性の下限値)**として留保する([TEST](../06_TEST/) 側の報告書にも記載). +- 注: TIAS は積分球で照明が本来きわめて均一なはずのため,未補正画像のムラは**ほぼレンズビネットを測っているだけ**になりうる.ビネット除去は特に重要. + +#### 黒レベル(ダークフレーム減算) (Black Level) + +- 加算オフセット(ImagingSource の Brightness 等)は均一性指標を**良い方に偏らせる**(加算 c で CoV = σ/(μ+c),max/min = (max+c)/(min+c) がともに縮む).機種間でオフセットが異なると(DFK33=240 / DFK23=50)そのまま指標差として混入する. +- 対策として,**機種ごとにダークフレーム(遮光/無照明)を撮影して黒レベルを実測し,指標算出前に減算する**(SmTIAS は黒レベル 64 を減算).Brightness が加算オフセットか乗算ゲインかは SDK で確認し,加算なら減算,乗算なら一様係数として不変扱いとする. + +#### 飽和回避(8-bit) (Saturation Check, 8-bit) + +- ROI 内で値 255(念のため ≥254)の画素数 = 0 を確認し,最明部は 180〜230(レンジの 70〜90%)を目安とする.**飽和画素率・p99・チャンネル別 max をショットごとに記録**する(SmTIAS の `image_statistics` 相当). + +## 撮影手順 (Procedure) + +1. **設置**: TIAS 開口部に白板を装着し,カメラ・白板・装置の相対位置を固定する.撮影中に動かない固定機構を用いる. +2. **設定適用**: 上記 [カメラ設定](#カメラ設定-camera-settings) を適用(AE/AWB/AF/NR/エッジ/トーンカーブを OFF,露出・ISO・WB・フォーカスを手動固定,可能なら RAW).**シェーディング補正は OFF にせず,ビネットを除去できる状態にする**(在カメラ補正 or RAW+シェーディングマップ). +3. **露出調整**: 白板最明部が飽和せず,最明部がレンジの 7〜9 割程度になるよう露出を調整し,値を確定・記録する. +4. **外乱光条件**: 原則として TIAS 自前照明のみで撮影する.室内照明の影響を確認したい場合は,位置・露出を完全固定したまま室内灯を ON/OFF した連続 2 枚を撮影する(手法は [TEST_03 外乱光影響評価](../06_TEST/TEST_03_外乱光影響評価報告書.md)). +5. **複数枚撮影**: 同一条件で複数枚(SmTIAS では 12 枚)撮影し,再現性(SSIM・指標分散)を評価できるようにする. +6. **ダークフレーム取得**: 同一カメラ設定で遮光(無照明)画像を撮影し,黒レベル減算用に保存する(機種ごと). +7. **フラットフィールド取得**: TIAS 照明とは**独立な既知の一様光源**で画面全面を撮影し,レンズビネット V(x,y) 特性化用に保存する(取得できない場合は留保事項として記録). +8. **記録**: 各撮影の条件(露出・ゲイン・WB・色処理状態・Brightness/黒レベル・外乱光の有無・温度等)と,対応するダークフレーム/フラットフィールドのファイル名を記録する. + +## データ管理・命名 (Data Management & Naming) + +- **保存先**: `data/tias/whiteboard/`(現状空)に配置する.SmTIAS は `data/smtias/whiteboard/`(PNG)・`data/smtias/quantitative/`(DNG)に分かれている. +- **メタデータ**: TIAS は現状 `Shot{NO}.png`(RGB24)で保存する.撮影条件を記した **JSON サイドカーをショットごとに併置**する(SmTIAS の `*.meta.json` に準拠できると後段の解析が容易).記録項目: 機種・解像度/ROI 切出,露出時間,ゲイン,WB チャンネルゲイン,CCM/Color Enhancement の状態,γ/トーンマップ状態,Denoise/Sharpness,Brightness(黒レベル),シェーディング補正状態+使用フラットフィールド/ダークフレームのファイル名,外乱光条件,飽和・低露出画素率,チャンネル別 mean/max,撮影日時. +- **参照画像**: ダークフレーム・フラットフィールドは白板本体と紐付くファイル名で同一フォルダに保存する. +- **ファイル名**: 装置・対象・撮影日時が判別できる命名とする(例: `TIAS_<日付>_<連番>` 等.SmTIAS の `SmTIAS_YYYYMMDD_HHMMSS` に倣う). +- **ROI 設定**: `config/roi_config.json` に `tias.whiteboard` キーで登録し,解析時に `--roi tias.whiteboard` で参照する. + +## 要確認事項 (Open Questions) + +撮影に着手する前に確定すべき事項.カメラ仕様は [TECH_02 TIAS 撮影条件](../05_TECH/TECH_02_TIAS撮影条件.md) で判明済み(→ 残る確認点を以下に更新).確定し次第,本書を更新する. + +- [ ] TIAS 開口部に合致する白板の仕様・入手/作製方法と物理的な設置可否 +- [ ] TIAS 固有の最適露出(飽和なし・線形を満たす露出時間/ゲイン)の決定 +- [ ] **独立な一様光源によるフラットフィールド取得手段**の確保(不可なら系全体評価+留保で運用) +- [ ] ダークフレーム取得と Brightness(加算オフセット/乗算ゲイン)の意味確定 +- [ ] 露出の自動/手動フラグの実機確定(ImagingSource の二重フラグ) +- [ ] 色処理を OFF にできるか(Lucam CCM=NONE,DFK23 Color Enhancement OFF) +- [ ] TIAS 用 ROI 座標の決定と `config/roi_config.json` への登録 + +## 関連ドキュメント (Related Documents) + +- [PLAN_01 要件定義書](../03_PLAN/PLAN_01_要件定義書.md) — 評価対象・優先度(優先度 2: SmTIAS vs TIAS 照明均一性比較) +- [SPEC_02 照明均一性評価アルゴリズム](SPEC_02_照明均一性評価アルゴリズム.md) — 評価指標・ROI 設定・解析手順 +- [TECH_01 DNG 対応要求仕様](../05_TECH/TECH_01_DNG対応要求仕様.md) — DNG/メタデータ・LSC の扱い +- [TECH_02 TIAS 撮影条件](../05_TECH/TECH_02_TIAS撮影条件.md) — TIASshot 実機の撮影条件と SPEC_03 基準値との対照 +- [TEST_01 照明均一性評価報告書](../06_TEST/TEST_01_照明均一性評価報告書.md) — SmTIAS 単体の撮影実績(12 枚) +- [TEST_03 外乱光影響評価報告書](../06_TEST/TEST_03_外乱光影響評価報告書.md) — 外乱光条件の撮影手法 diff --git "a/docs/04_SPEC/SPEC_04_\347\231\275\346\235\277\346\222\256\345\275\261\343\203\207\343\203\274\343\202\277\344\273\225\346\247\230.md" "b/docs/04_SPEC/SPEC_04_\347\231\275\346\235\277\346\222\256\345\275\261\343\203\207\343\203\274\343\202\277\344\273\225\346\247\230.md" new file mode 100644 index 0000000..da4edac --- /dev/null +++ "b/docs/04_SPEC/SPEC_04_\347\231\275\346\235\277\346\222\256\345\275\261\343\203\207\343\203\274\343\202\277\344\273\225\346\247\230.md" @@ -0,0 +1,91 @@ +# 白板撮影データ仕様(SmTIAS-Evaluation 引き渡し用) (White-Board Capture Data Specification) + +本書は,TIASshot の白板撮影モードで取得し **SmTIAS-Evaluation 側へ引き渡す白板撮影データ**について,**実際の撮影設定値・ファイル構成・メタデータの意味・評価時の注意**をまとめたものである.撮影プロトコルの目的・基準値・質疑応答(Q&A)は [SPEC_03 TIAS 白板撮影プロトコル](SPEC_03_TIAS白板撮影プロトコル.md) を参照する(本書は実データの説明,SPEC_03 は仕様・根拠という役割分担). + +## 概要 (Overview) + +- TIASshot の **白板撮影モード(照明均一性評価用・DEBUG 限定)** で取得した素の RGB 画像データ. +- カメラ: **ImagingSource DFK 23UX249**(USB3 カラー産業用,Sony IMX249). +- **校正なし**で撮影する(チャート検出・ホワイトバランス自動調整・TCC 色補正を一切行わない).得られるのはカメラ素の RGB. +- 1 撮影セッション = 1 フォルダ(例: `<時刻>-白板/`). + +## 撮影設定(実際の値) (Capture Settings — Actual Values) + +各撮影の正確な値は同梱の `whiteboard_meta.json` に記録される.代表値(DFK 23UX249)は以下のとおり. + +| 項目 | 値 | 備考 | +| --- | --- | --- | +| カメラ | DFK 23UX249(SN 45610026) | | +| 撮影モード | 白板(校正なし・素 RGB) | チャート検出/WB 自動調整/TCC なし | +| 露出 | 16.6 ms(手動固定,≈1/60 s) | 自動露出なし(※ Q6 で実機確定) | +| ゲイン | 0(Auto OFF) | | +| ホワイトバランス | Auto OFF・固定 G 64 / B 140 / R 100 | 校正しないため device-state の固定値のまま | +| ガンマ | 100(= 1.0,線形)・トーンマップ OFF | | +| ノイズ低減 / シャープネス | Denoise 0 / Sharpness 0 | | +| Color Enhancement | **OFF**(撮影前に無効化) | 非線形の色強調を排除 | +| 明るさ (Brightness) | 50 | 黒レベル/オフセット相当か要確認(Q7) | +| 色補正行列 (CCM) | なし | ImagingSource は Color Matrix を持たない | +| シェーディング補正 (LSC) | **なし(ビネット未除去)** | DFK23 は在カメラ LSC を持たず補正データも無い(確認済) | +| 出力形式 | RGB24(8-bit/ch) | RAW ではない | +| 撮影枚数 | 既定 10(「連続撮影枚数」設定値) | SmTIAS と揃えるなら 12 に変更可 | + +## データ構成・ファイル (Files) + +1 セッションフォルダにフレーム数 × 2 枚の画像+メタデータが入る. + +| ファイル | 内容 | 解像度 | +| --- | --- | --- | +| `WhiteBoard_{NNNN}.png` | **ROI 切り出し画像**(評価対象の白板領域) | 1136 × 1528 | +| `WhiteBoard_full_{NNNN}.png` | **クロップなしフルフレーム**(四隅まで含む) | 1200 × 1920 | +| `whiteboard_meta.json` | 撮影条件+飽和統計(後述) | — | + +- ROI: config の `X0 / Y180 / W1136 / H1528` で切り出した白板領域. +- フル: センサ 1920 × 1200 を転置した **1200 × 1920**(クロップなし).ROI で切り捨てた周辺部(上 180px・下 212px・右 64px)を含むため,**レンズビネットの確認・フラットフィールド補正用**に使う. +- 画像はいずれも RGB24(8-bit,OpenCV の BGR 並びで PNG 保存).`{NNNN}` は 1 始まりの連番. + +## メタデータ(whiteboard_meta.json) (Metadata Schema) + +| フィールド | 意味 | +| --- | --- | +| `capture_datetime` | 撮影日時 | +| `device_name` / `serial_number` | カメラ機種名・シリアル | +| `roi_x` / `roi_y` / `roi_width` / `roi_height` | ROI 座標・サイズ | +| `full_frame_saved` / `full_width` / `full_height` | フルフレーム保存有無・サイズ | +| `num_frames` | 撮影枚数 | +| `exposure_ms` | 露出時間(ミリ秒) | +| `gain` | ゲイン(device ネイティブ値) | +| `brightness` | 明るさ(黒レベル/オフセット相当・要確認) | +| `gamma` | ガンマ(100 = 1.0 線形) | +| `wb_blue` / `wb_green` / `wb_red` | ホワイトバランスのチャンネルゲイン(固定値) | +| `color_enhancement_enabled` / `color_enhancement_note` | Color Enhancement の状態(false = 無効化済み) | +| `gamma_setting` / `tone_mapping` / `denoise` / `sharpness` / `color_matrix` | 固定状態の記録(device-state 由来の決め打ち文字列) | +| `shading_correction` / `shading_correction_note` | シェーディング補正状態("未補正"・ビネット混入の注記) | +| `frame_stats[]` | フレームごとの統計(下記) | + +`frame_stats[]` の各要素: + +| フィールド | 意味 | +| --- | --- | +| `frame` | フレーム番号(1 始まり) | +| `saturated_ratio` | 飽和画素率(輝度 ≥ 254 の割合) | +| `p99_luminance` | 輝度の 99 パーセンタイル | +| `mean_B` / `mean_G` / `mean_R` | チャンネル別 平均値 | +| `max_B` / `max_G` / `max_R` | チャンネル別 最大値 | + +※ `frame_stats` は **ROI 画像に対する統計**(白板領域の露出・飽和チェック用).フルフレーム側の統計は含まない.代表的な実測値: 飽和率 0・p99 ≒ 181(レンジの約 71%=飽和なしで十分な信号量). + +## 評価時の注意・留保 (Notes for Evaluation) + +SPEC_03 の Q&A に基づく留保事項. + +- **8-bit RGB(RAW でない)**: Q1 で許容(線形性は確認済み: γ1.0・トーンマップ OFF).主指標は CoV・中心周辺勾配・動径 min/max(輪帯平均でロバスト)を推奨. +- **ビネット未除去(最重要)**: DFK 23UX249 は在カメラのレンズシェーディング補正を持たず,補正データも付加されない(device-state・公式仕様で確認済).したがって観測ムラには**レンズビネットが含まれる**.V の除去には **TIAS 照明とは独立な一様光源でのフラットフィールド撮影**が必要(Q5).**`WhiteBoard_full_*`(四隅を含むフルフレーム)はこの V 特性化・目視確認のために提供**している.補正しないまま評価する場合は「照明×レンズの系全体の均一性」として留保すること. +- **固定・非 identity の WB**: 校正しないためチャンネルゲインは固定([1,1,1] ではない).相対ばらつき指標には不変だが(Q3),ロバストチェックとして**緑チャンネル単体でも均一性を算出**することを推奨. +- **黒レベル(Brightness=50)**: 加算オフセットか別スケールかが未確定(Q7).機種間比較の前に**ダークフレーム減算**で真の黒レベルを実測・減算することを推奨. +- **露出の手動固定**: device-state の自動フラグが二重で,手動固定を実機で確定する必要がある(Q6). + +## 関連 (Related) + +- [SPEC_03 TIAS 白板撮影プロトコル](SPEC_03_TIAS白板撮影プロトコル.md) — プロトコル・基準値・SmTIAS 回答(詳細・根拠) +- [TECH_02 TIAS 撮影条件](../05_TECH/TECH_02_TIAS撮影条件.md) — TIASshot 実機の撮影条件と SPEC_03 基準値との対照 +- 実装: `TIASshot/Cameras/IScam.cs`(`ShotWhiteBoard`),`TIASshot/Utils/ImageStats.cs`(飽和統計・JSON 生成) diff --git "a/docs/05_TECH/TECH_02_TIAS\346\222\256\345\275\261\346\235\241\344\273\266.md" "b/docs/05_TECH/TECH_02_TIAS\346\222\256\345\275\261\346\235\241\344\273\266.md" new file mode 100644 index 0000000..7f69aa2 --- /dev/null +++ "b/docs/05_TECH/TECH_02_TIAS\346\222\256\345\275\261\346\235\241\344\273\266.md" @@ -0,0 +1,190 @@ +# TIAS 撮影条件(照明均一性評価用) (TIAS Capture Conditions for Illumination Uniformity) + +本書は,**TIAS の白板撮影を制御するアプリ「TIASshot」が,照明均一性評価用にどのような条件で撮影しようとしているか**を,コード・設定ファイルから抽出してまとめたものである.[SPEC_03 TIAS 白板撮影プロトコル](../04_SPEC/SPEC_03_TIAS白板撮影プロトコル.md) の要件を満たせるかを **SmTIAS-Evaluation 側に確認してもらう**ことを目的とする. + +> **読む人へ**: 本書は撮影アプリ(TIASshot)の現状実装から起こした事実ベースの記述である.`※` 印・「要確認」は未確定事項である.SPEC_03 との対照表([§4](#4-spec_03-基準値との対照-comparison-with-spec_03))と確認したいこと([§5](#5-確認したいこと-open-questions-for-smtias))を中心に見てほしい.SmTIAS-Evaluation からの回答は [§7](#7-smtias-evaluation-からの回答-answers-from-smtias-evaluation) にある. + +## 1. 前提: 校正なしの撮影 (Premise: Capture Without Calibration) + +通常の TIASshot(舌診用途)は「チャート検出 → ホワイトバランス自動調整 → TCC 色補正 → sRGB 画像出力」という色合わせの校正フローを行う.しかし**照明均一性の評価では色を合わせること自体が目的でない**ため,白板撮影では以下を **行わない**. + +- ホワイトバランスの自動調整(目標輝度へのチャンネルゲイン自動チューニング)を行わない. +- TCC 色補正(チャートからの 17 次元色変換)を行わない.つまり `sRGB` 変換画像は生成しない. + +→ 取得するのは **カメラ素の RGB 画像のみ**.ホワイトバランスのチャンネルゲインは設定ファイル/デバイス状態の**固定値**のまま(フレーム間・セッション間で自動調整しない). + +> **実装メモ**: 現状の TIASshot は校正完了後でないと撮影できないため,照明均一性評価用には**校正を前提としない撮影パスを別途設ける**(DEBUG 機能として追加予定).本書はその撮影が用いる設定をまとめたものである. + +## 2. カメラ構成と出力形式 (Cameras & Output Format) + +- TIASshot は 2 系統のカメラに対応し,**Lucam(Lumenera)を優先し,無ければ ImagingSource** にフォールバックする. +- 撮影出力は **いずれも RGB24(8-bit/ch,デモザイク済みのカラー画像)**.RAW(Bayer/DNG)ではない. + +| カメラ | 機種 | 解像度(撮影) | 出力 | +| --- | --- | --- | --- | +| Lucam | Lumenera Lw110 | 1280 × 1024 | RGB24 8-bit | +| ImagingSource | DFK 33UX178 | 3072 × 2048(ROI 切出あり) | RGB24 8-bit | +| ImagingSource | DFK 23UX249 | 1920 × 1200(ROI 切出あり) | RGB24 8-bit | + +※ ImagingSource は撮影後に ROI(`config.xml` 指定)で切り出す.DFK33UX178: X0/Y330/W1840/H2440,DFK23UX249: X0/Y180/W1136/H1528. + +## 3. カメラ別 撮影条件 (Per-Camera Capture Settings) + +### 3.1 Lucam (Lumenera Lw110) + +設定元: `TIASshot/Cameras/Lucam.cs`(初期化)+ `config.xml`. + +| 項目 | 値 | 備考 | +| --- | --- | --- | +| 出力形式 | RGB24 8-bit | センサ取得 PF_8 → `LucamConvertFrameToRgb24` でデモザイク | +| 露出時間 | 60 ms(手動固定) | `config.xml` `Lucam/Exposure`.自動露出は使わない | +| アナログゲイン | 1.5(固定) | `Lucam/Gain` | +| ホワイトバランス | チャンネルゲイン B 2.62 / G 1.72 / R 1.70(**固定**) | `Lucam/GainB,GainG,GainR`.校正なしのため自動調整しない | +| シャッター | グローバルシャッター | `GlobalShutter` | +| デモザイク | HIGH_QUALITY | `LucamDemosaicMethod.HIGH_QUALITY` | +| 色補正行列 (CCM) | **LED(適用)** ⚠ | `LucamCorrectionMatrix.LED`.SDK 内蔵の光源別 3×3 色補正(チャンネル混合).白板モードでは **NONE 推奨**([§5](#5-確認したいこと-open-questions-for-smtias)) | +| ノイズ低減 / エッジ強調 / トーンカーブ | アプリ側で適用なし | `LucamConversion` 構造体にこれらの項目はない | +| ハードウェアトリガ | OFF | | +| シェーディング補正 (LSC) | **適用なし** ⚠ | アプリ側でビネット補正をしていない.シェーディングマップも保存していない | + +### 3.2 ImagingSource DFK 33UX178 + +設定元: `TIASshot/DFK33UX178.xml`(デバイス状態).接続時にこのファイルを読み込む. + +| 項目 | 値 | 備考 | +| --- | --- | --- | +| 出力形式 | RGB24 8-bit(3072×2048) | | +| 露出 | 手動,0.016667 s(≈1/60 s) | ※ ただし自動フラグが二重に存在し一方が `1`.手動/自動の確定は**要確認** | +| ゲイン | 1.0,Auto=OFF | | +| ホワイトバランス | Auto=OFF,手動 G 64 / B 134 / R 79(**固定**) | 校正なしのため自動調整しない | +| ガンマ | 1.0(線形) | トーンカーブなし | +| トーンマッピング | OFF | | +| ノイズ低減 (Denoise) | 0(OFF) | | +| シャープネス | 0(OFF) | | +| 色補正行列 (Color Matrix) | **OFF** | カメラ内 CCM 無効.Lucam と異なり色補正行列は掛からない | +| ハイライト低減 | OFF | | +| 明るさ (Brightness) | 240 | ※ 黒レベル/オフセット相当か**要確認**(DFK23 とは値が大きく異なる) | +| シェーディング補正 (LSC) | 設定項目に現れず・**適用なし** ⚠ | | + +### 3.3 ImagingSource DFK 23UX249 + +設定元: `TIASshot/DFK23UX249.xml`(デバイス状態). + +| 項目 | 値 | 備考 | +| --- | --- | --- | +| 出力形式 | RGB24 8-bit(1920×1200) | | +| 露出 | 手動,0.016667 s(≈1/60 s) | ※ DFK33 と同じく自動フラグの二重存在.**要確認** | +| ゲイン | 0.0,Auto=OFF | ※ DFK33(1.0)と数値が異なる.スケール/意味は**要確認** | +| ホワイトバランス | Auto=OFF,手動 G 64 / B 140 / R 100(**固定**) | | +| ガンマ | 100(= 1.0,線形)※スケール表記が DFK33 と異なるだけで線形 | | +| トーンマッピング | OFF(Enable=0) | | +| ノイズ低減 (Denoise) | 0(OFF) | | +| シャープネス | 0(OFF) | | +| 色強調 (Color Enhancement) | **ON** ⚠ | DFK33(Color Matrix OFF)と異なり,色強調が有効.機種間で色処理が不一致 | +| ハイライト低減 | OFF | | +| 明るさ (Brightness) | 50 | ※ DFK33(240)と大きく異なる.**要確認** | +| 彩度 (Saturation) | 64 | DFK33 は 100.※色処理の差 | +| シェーディング補正 (LSC) | 設定項目に現れず・**適用なし** ⚠ | | + +## 4. SPEC_03 基準値との対照 (Comparison with SPEC_03) + +SPEC_03「揃える基準値(SmTIAS 定量撮影モード)」と TIASshot の現状を対照する.○=合致,△=条件付き/要確認,✗=相違. + +| SPEC_03 項目 | SmTIAS 基準 | Lucam | DFK33UX178 | DFK23UX249 | +| --- | --- | --- | --- | --- | +| 出力形式 | RAW 10-bit linear | ✗ RGB24 8-bit | ✗ RGB24 8-bit | ✗ RGB24 8-bit | +| 自動露出 AE | OFF 固定 | ○ 固定 60ms | △ 手動だが要確認 | △ 手動だが要確認 | +| ゲイン/ISO | 固定 | ○ 1.5 固定 | ○ 1.0 固定 | ○ 0.0 固定 | +| ホワイトバランス | OFF・gain identity | △ 固定(非 identity) | △ 固定(非 identity) | △ 固定(非 identity) | +| オートフォーカス AF | OFF | △ 要確認 | △ 要確認 | △ 要確認 | +| ノイズ低減 NR | OFF | ○ なし | ○ OFF | ○ OFF | +| エッジ強調 | OFF | ○ なし | ○ Sharpness 0 | ○ Sharpness 0 | +| トーンカーブ | 線形 | ○ なし(≈線形) | ○ γ1.0・ToneMap OFF | ○ γ1.0・ToneMap OFF | +| 色補正/色処理 | identity | ✗ **LED CCM 適用** | ○ Color Matrix OFF | ✗ **Color Enhancement ON** | +| シェーディング補正 LSC | **適用してビネット除去** | ✗ **未除去・マップなし** | ✗ **未除去** | ✗ **未除去** | +| 黒レベルロック | true | △ 要確認 | △ Brightness 240 要確認 | △ Brightness 50 要確認 | + +## 5. 確認したいこと (Open Questions for SmTIAS) + +SmTIAS-Evaluation 側に判断・助言をいただきたい点. + +1. **8-bit RGB(RAW でない)の許容性**: TIAS のカメラは RGB24 8-bit 出力で,RAW 10-bit linear は現状取得していない.量子化・ビット深度が均一性指標(CoV・最大/最小比等)に与える影響は許容範囲か.許容できない場合,Lucam は生 Bayer を取得できる余地がある(要追加実装). +2. **デモザイクの許容性**: いずれも HIGH_QUALITY 等でデモザイク済み.空間補間が高周波の均一性に与える影響は許容か. +3. **ホワイトバランス(固定・非 identity)**: 校正を行わないためチャンネルゲインは固定だが [1,1,1] ではない.SPEC_03 §「なぜ条件を揃えるのか」の「**全画素に等しく作用する一様線形ゲインは相対ばらつき指標に不変**」の理解で,固定 WB のまま評価して問題ないか(=均一性評価に影響しない,で合っているか). +4. **色処理の機種差**: Lucam は LED CCM 適用,DFK23UX249 は Color Enhancement ON,DFK33UX178 は色処理 OFF と,**3 系統で色処理が不一致**.白板モードでは可能な限り無効化(Lucam: CCM=NONE,DFK23: Color Enhancement OFF)すべきか.均一性(輝度)評価なら影響は小さいと見ているが,方針を確認したい. +5. **シェーディング補正 / ビネット(最大の論点)**: TIAS 側はカメラ・SDK ともビネット補正手段を現状もっておらず,シェーディングマップも保存していない.更新後の SPEC_03 は「ビネットは必ず除去」を要求している.対応方針として,(a) 在カメラ補正が使えないか,(b) 使えない場合,評価側でフラットフィールド補正できるよう**均一面の参照画像を別途撮影する**運用で良いか,等を相談したい. +6. **露出の自動/手動の確定**: ImagingSource のデバイス状態で露出の自動フラグが二重に存在する.実機で手動固定になっているかを確認のうえ追記する. +7. **黒レベル / Brightness の扱い**: DFK33=240,DFK23=50 と機種間で差が大きい.黒レベル/オフセットとしての意味と,評価への影響を確認したい. +8. **飽和回避の担保**: 白板最明部がレンジの 7〜9 割・飽和ゼロになるよう露出を決めるが,8-bit でこれをどう検証・記録すればよいか(ヒストグラム確認の手段). +9. **出力形式・メタデータ**: 現状の保存は `Shot{NO}.png`(RGB24).SmTIAS の `*.meta.json` 相当として,どの撮影条件(露出・ゲイン・WB・機種・外乱光有無等)を併記すべきか. + +## 6. 現状の出力・保存 (Current Output & Storage) + +- **保存先**: `config.xml` の `File/SaveFolder`(既定 `C:\TIAS_Data`)配下に `日付\時刻-名称\` を自動生成. +- **画像**: 通常は `Shot{NO}.png`(RGB24)+ `sRGB{CN}_{NO}.jpg`(TCC 変換後)を保存するが,**白板モードでは sRGB は生成せず RGB のみ**とする想定. +- **撮影情報**: `Info.csv` に撮影日時・撮影枚数等を記録(現状は舌診用の項目).白板モードでは SPEC_03 §データ管理・命名に合わせて項目・命名を調整する余地がある. + +## 7. SmTIAS-Evaluation からの回答 (Answers from SmTIAS-Evaluation) + +[§5](#5-確認したいこと-open-questions-for-smtias) の各問への回答.結論として **致命的な対応が必要なのは Q5(ビネット)と Q7(黒レベル)の 2 点**,残りは「概ね許容(条件付き)」である.本回答に基づき SPEC_03 を更新済み([SPEC_03 TIAS 実機カメラ仕様と差分の扱い](../04_SPEC/SPEC_03_TIAS白板撮影プロトコル.md#tias-実機カメラ仕様と差分の扱い-tias-cameras--handling-gaps)). + +### Q1. 8-bit RGB(非 RAW)の許容性 → ○ 許容(条件付き) + +- 許容してよい.量子化ノイズの CoV への寄与は,白板平均が 8-bit レンジの中〜上(mean 150〜200)にあれば σ_quant/μ ≈ 0.3 LSB / 150 ≈ **0.002 程度**で,SmTIAS の CoV 0.04〜0.07 に対し無視できる. +- 条件: 撮影パスが**真に線形**(γ=1.0・トーンマップ OFF,確認済)であること.8-bit の真のリスクはビット深度でなく隠れた非線形なので,リニアリティだけ実機で担保してほしい. +- 8-bit ではノイズに弱い画素単位の最大/最小比より,**CoV・中心周辺勾配・動径 min/max(輪帯平均でロバスト)を主指標**とする運用を推奨.生 Bayer の追加実装は不要. + +### Q2. デモザイクの許容性 → ○ 許容 + +- 白板の照明ムラは低空間周波数であり,デモザイク補間アーティファクト(エッジ・高周波で発生)はほぼ無関係.HIGH_QUALITY で問題なし. + +### Q3. 固定・非 identity の WB → ○ そのまま評価可(理解は概ね正しい) + +- 固定 WB のまま評価してよい.ただし正確な根拠は「一様ゲインだから」ではなく,**「全チャンネルが同一の空間パターン L(x,y) を持つ限り,固定の線形結合(WB でも CCM でも)は空間パターンをスカラ倍に保つので CoV など相対指標は不変」**だから. +- 唯一の注意: 色ビネット等で照明色が空間的に変わるとチャンネルごとに空間パターンが異なり,WB の重み付けで合成 CoV がわずかに動く(Lucam の B=2.62 のように WB ゲインが大きいと色ビネットが増幅されうる). +- ロバストチェックとして,輝度合成に加え **緑チャンネル単体でも均一性を算出**することを推奨(単一チャンネルは一様ゲインに完全不変で,WB/CCM のチャンネル混合を回避できる.G は輝度寄与 0.72 で代表性も高い). + +### Q4. 色処理の機種差 → 可能な限り OFF を推奨 + +- **Color Enhancement(DFK23)は非線形(彩度強調)の可能性が高く害**.必ず OFF に. +- **CCM(Lucam)は固定線形なので輝度均一性への一次影響は小さい**が,①クリップ(負/255 超)による非線形化,②機種間不一致が残る.**NONE 推奨**(両方を解消し 3 系統が揃う). + +### Q5. ★ シェーディング / ビネット(最大の論点)→ フラットフィールド補正で対応(ただし独立光源で) + +- 測定モデル: 画像 I = 照明 L × レンズビネット V ×(白板で一様な反射率).欲しいのは L,邪魔は V. +- **落とし穴**: 提案 (b) を「**TIAS 自身の光で均一面を別撮りして割る**」と解釈すると **L と V を分離できない**(参照画像も `L×V` のため,割っても反射率比 ≒1 になるだけ). +- **正しいフラットフィールド**: V を特性化するには **TIAS 照明とは独立な既知の一様光源**(均一 LED パネル/ライトボックス/別の積分球出力等)で画面全面を撮影し,正規化して V(x,y) を得て白板画像を除算する.まず SDK/カメラベンダ(Lumenera / The Imaging Source)のレンズシェーディングデータが使えないかも確認を. +- どうしても V を特性化できない場合は,結果を**「照明×レンズの系全体の均一性」と明記**し,レンズ補正済みの SmTIAS とは**直接比較不可(TIAS 照明均一性の下限値)**として留保する. +- 特に重要な理由: **TIAS は積分球で照明が本来きわめて均一なはず**.だとすると未補正画像のムラは**ほぼレンズビネットを測っているだけ**になりうる.SmTIAS 実測でも未補正は CoV 約 2 倍・勾配約 4 倍に膨張([TEST_03](../06_TEST/TEST_03_外乱光影響評価報告書.md)).レンズ補正済み SmTIAS と未補正 TIAS を比べると TIAS が不当に悪く見えるため,フェアな比較には V 除去が必須. + +### Q6. 露出の自動/手動 → 手動固定を確定・検証 + +- 手動固定が必須.実機で有効露出がフレーム間一定か(SDK 読み戻し or 連続フレームの平均輝度が一定か)で検証し,二重フラグのどちらが効いているかを確定して追記してほしい. + +### Q7. ★ 黒レベル / Brightness → ダークフレーム減算(要注意) + +- Brightness が**加算オフセット(ペデスタル)**なら,均一性指標を**良い方に偏らせる**(加算 c に対し CoV = σ/(μ+c),max/min = (max+c)/(min+c) がともに縮む).DFK33=240 と DFK23=50 の差が**そのまま指標差として混入**し,機種間比較が無効になる. +- 対策: **機種ごとにダークフレーム(遮光/無照明)を撮影して真の黒レベルを実測し,指標算出前に減算する**(SmTIAS は黒レベル 64 を減算).Brightness が加算か乗算かを SDK で確認し,加算なら減算,乗算なら一様係数で不変扱い. +- ただし要確認: 8-bit で Brightness=240 が純加算ペデスタルなら白板は即 255 飽和でレンジがほぼ無くなる → **240 は別スケールの制御の可能性が高い**.意味(加算/ゲイン/別単位)の確定を依頼したい. + +### Q8. 飽和回避(8-bit)→ ヒストグラムで検証・記録 + +- ROI 内で値 255(念のため ≥254)の画素数 = 0 を確認.目安は最明部 180〜230(レンジの 70〜90%).**飽和画素率・p99・チャンネル別 max をショットごとに記録**(SmTIAS の `image_statistics` 相当).評価側パイプラインに簡易ヒストグラムチェックを追加可能. + +### Q9. 出力形式・メタデータ → JSON サイドカーを推奨 + +- SmTIAS の `*.meta.json` に倣い,**ショットごとに JSON サイドカー**で以下を記録: 機種・解像度/ROI 切出,露出時間,ゲイン,WB チャンネルゲイン,CCM/Color Enhancement の状態,γ/トーンマップ状態,Denoise/Sharpness,Brightness(黒レベル),シェーディング補正状態+使用フラットフィールド/ダークフレームのファイル名,外乱光条件,飽和・低露出画素率,チャンネル別 mean/max,撮影日時. + +### 依頼事項まとめ (Action Items for TIAS Side) + +- [ ] **独立な一様光源**でフラットフィールド(レンズビネット V)を取得する手段の確保(Q5).不可なら系全体評価+留保で運用. +- [ ] 機種ごとの**ダークフレーム取得**と Brightness の意味(加算/乗算)の確定(Q7). +- [ ] **色処理 OFF**(Lucam CCM=NONE,DFK23 Color Enhancement OFF)(Q4). +- [ ] 露出の手動固定の実機確定(Q6). +- [ ] **JSON サイドカー**メタデータの実装(Q9)と飽和チェックの記録(Q8). + +## 関連ドキュメント (Related Documents) + +- [SPEC_03 TIAS 白板撮影プロトコル](../04_SPEC/SPEC_03_TIAS白板撮影プロトコル.md) — 本書が満たそうとする撮影プロトコル +- [TEST_03 外乱光影響評価報告書](../06_TEST/TEST_03_外乱光影響評価報告書.md) — 未補正(LSC なし)で指標が悪化する実測例 +- `TIASshot/Cameras/Lucam.cs`,`TIASshot/DFK33UX178.xml`,`TIASshot/DFK23UX249.xml`,`TIASshot/config.xml` — 本書の設定の出典 diff --git "a/docs/06_TEST/TEST_04_\345\215\230\347\201\257\347\205\247\346\230\216\346\257\224\350\274\203\350\251\225\344\276\241\345\240\261\345\221\212\346\233\270.md" "b/docs/06_TEST/TEST_04_\345\215\230\347\201\257\347\205\247\346\230\216\346\257\224\350\274\203\350\251\225\344\276\241\345\240\261\345\221\212\346\233\270.md" new file mode 100644 index 0000000..0cc4d89 --- /dev/null +++ "b/docs/06_TEST/TEST_04_\345\215\230\347\201\257\347\205\247\346\230\216\346\257\224\350\274\203\350\251\225\344\276\241\345\240\261\345\221\212\346\233\270.md" @@ -0,0 +1,80 @@ +# 単灯照明比較評価報告書 (Single-LED Illumination Comparison) + +## 概要 (Overview) + +SmTIAS の照明は **下・左・右の 3 灯** で構成される.本評価では,各 LED を**単独で点灯**(他 2 灯を黒テープで遮光)して白板を撮影し,**各灯が単体で白板面をどう照らすか(明るさ・照射の偏り・均一性)**を同一指標で比較した.3 灯合成時に互いの暗部を補い合えるか(合成均一性の根拠)を読み取ることが目的である. + +結論を先に述べると,**3 灯はそれぞれ異なる方向を強く照らしており,互いの暗部を補完する配置**になっている.単灯では明るさ・均一性に差があり(明るさ・均一性とも右 > 左 > 下),とくに下灯が最も暗く偏りが大きい. + +## 前提・対象データ (Scope and Samples) + +| 条件 | データ | 撮影時刻(2026-06-15) | +| --- | --- | --- | +| 下のみ点灯(左右を遮光) | `SmTIAS_QM_20260615_141420` | 14:14 | +| 左のみ点灯(下右を遮光) | `SmTIAS_QM_20260615_150300` | 15:03 | +| 右のみ点灯(下左を遮光) | `SmTIAS_QM_20260615_170948` | 17:09 | + +| 項目 | 内容 | +| --- | --- | +| 機種 / モード | SH-02M / 定量モード(RAW_SENSOR,露出・ISO 等は固定) | +| 評価領域 (ROI) | `config/roi_config.json` の `smtias.whiteboard`(白板中央,3 枚共通) | +| 評価条件 | linear(output_color=raw)/ portrait / LSC 補正あり | + +> **前提(本評価の限界)**: 3 枚は遮光作業を挟んだ**別撮影**であり,三脚固定でないため微小な位置ずれの可能性がある.また右のみは初回撮影(`170358`)に黒テープの写り込み(ROI 内に極暗部,max/min≈27.7)があったため**撮り直した**版(`170948`)を採用している(初回分はデータ・結果とも削除済み).プレビュー画像は鏡像のため,**像面の左右は実空間と反転**している点に注意(像面右=実空間左). + +## 結果 (Results) + +### 均一性・空間指標(白板 ROI,輝度は 0〜255 linear スケール) + +| 指標 | 下のみ | 左のみ | 右のみ | +| --- | --- | --- | --- | +| 平均輝度 | 57.75 | 61.46 | **63.55** | +| 標準偏差 | 4.05 | 5.26 | 3.94 | +| CoV | 0.0701 | 0.0856 | **0.0620** | +| 最大/最小比 | 1.787 | 1.860 | **1.659** | +| 最大輝度 / 最小輝度 | 70.34 / 39.37 | 77.88 / 41.86 | 76.35 / 46.01 | +| 中心/周辺比 | 1.115 | 1.107 | **1.088** | +| 勾配 (%) | 10.31 | 9.63 | **8.08** | +| 動径 min/max 比 | 0.832 | 0.841 | **0.865** | + +(**太字**=3 灯中もっとも良好/均一な値) + +### 比較図 + +3 枚の輝度マップ(共通絶対カラースケール)・動径プロファイル重ね描き・指標表を 1 枚にまとめた図を出力した: + +- `output/smtias/figures/compare_single_leds.png` + +## 考察 (Analysis) + +### 1. 明るさ・均一性とも 右 > 左 > 下 + +平均輝度は右 63.55 > 左 61.46 > 下 57.75.均一性(CoV・勾配・max/min・動径 min/max)も総じて右が最良で下が最も劣る.**下灯が 3 灯中もっとも暗く,照射の偏りも大きい**.動径プロファイル(絶対値)でも 3 本が明るさ順に分離し,下灯は中心〜周辺の落ち込みが最も急である. + +### 2. 各灯は異なる方向を照らす — 相補的な配置 + +輝度マップを共通スケールで並べると,明部の位置が灯ごとに異なる(下=像面上側,左=像面右寄り,右=像面中央〜左寄り).プレビューが鏡像のため像面右=実空間左に対応し,**各 LED が自分の側を強く照らしている**.すなわち単灯ごとの暗部を他灯の明部が埋める関係にあり,**3 灯合成で均一化が成立する根拠**になっている. + +### 3. 評価上の含意 + +合成均一性は「3 灯の相補」によって担保されるため,**特定の 1 灯(とくに下灯)の劣化・光量低下は,その灯が主に担う領域の暗部化として合成均一性に直接効く**と考えられる.均一性の弱点になりやすいのは下灯側である. + +## 結論 (Conclusion) + +1. 単灯の明るさ・均一性は **右 > 左 > 下**.下灯が最も暗く偏りが大きい. +2. 3 灯は **異なる方向を照らす相補的な配置**で,互いの暗部を補い合うことで合成均一性が成立する. +3. 合成均一性の弱点になりやすいのは下灯側であり,**個灯の光量・取り付け管理は下灯を重点**にするのが妥当. + +## 限界と今後 (Limitations & Future Work) + +- 3 枚は別撮影(遮光作業を挟む)であり,三脚固定での同一フレーミング撮影に揃えると灯間比較の精度が上がる. +- 本評価は単灯のみ.**3 灯合成の実測**と,単灯マップの**加算合成(下+左+右)**を比較すれば,「相補で均一化される」仮説を定量検証できる(合成実測 ≈ 単灯加算 か). +- 右のみ初回のような ROI 内写り込みは max/min 系を無効化するため,撮影時にフレーム外で遮光する運用を徹底する. + +## 参照 (References) + +- [TEST_03 外乱光影響評価報告書](TEST_03_外乱光影響評価報告書.md) +- [SPEC_03 TIAS 白板撮影プロトコル](../04_SPEC/SPEC_03_TIAS白板撮影プロトコル.md) +- [SPEC_04 白板撮影データ仕様](../04_SPEC/SPEC_04_白板撮影データ仕様.md) +- 評価実装: `scripts/run_uniformity_dng.py`(単灯ごとの指標算出)/`scripts/compare_single_leds.py`(3 単灯比較図) +- 対象データ: `data/smtias/quantitative/`(gitignore 対象) diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 9953d3a..b2e1a85 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -26,3 +26,14 @@ - **リファクタ(共通化)**: 重複していた `load_roi_config` を `src/config.py` に集約.`run_uniformity_dng.py` 内の DNG 輝度化パイプライン(`dng_luminance`/`lsc_gain_rgb`/`_upsample`)を `src/io/dng_pipeline.py` に移設し,compare 系・plot 系スクリプトが「スクリプト間 import」をやめて `src/` から取得するように変更(上記「共通化は将来」を解消).`dng_loader.py` の Rec.709 係数重複は SmTIAS-LightSim への単体コピー前提のため意図的に据え置き. - **ドキュメント**: ルート `README.md` を新設.CLAUDE.md にプロジェクト概要と SPEC_02 索引を追加.TECH_01 のローカル TECH_02 リンク切れを修正. - **検証**: pytest 381 passed,主要スクリプト(run/plot/compare 各種)の smoke test も成功(指標値は改名前と一致). + +- [x] **単灯照明の切り分け評価 + output 構造のデバイス別整理(2026-06-23)**: 下・左・右の 3 灯を 1 灯ずつ単独点灯(他 2 灯を黒テープで遮光)して白板を撮影し,各灯単体の照射偏り・均一性を評価.3 灯合成均一性の根拠(相補配置)を確認した. + - **対象データ**: `data/smtias/quantitative/SmTIAS_QM_20260615_{141420=下,150300=左,170948=右}`.各 DNG を `run_uniformity_dng.py`(linear+LSC,ROI=smtias.whiteboard)で評価. + - **命名判断**: 取り込み時のファイル名は `MiniTIAS_QM_*` だが,既存 QM 規約に統一して `SmTIAS_QM_` へリネーム.LED 条件(下/左/右)は**ファイル名に含めず**報告書側で管理する方針(ユーザー選択). + - **失敗と対処(写り込み)**: 右のみ初回 `170358` は ROI 内に黒テープが写り込み,最小輝度 2.89・max/min≈27.7 と min 依存指標が無効化.撮り直し版 `170948`(min 46.0・max/min 1.66)に差し替え,初回分はデータ・結果とも削除.→ 撮影時はフレーム外で遮光する運用を徹底. + - **結果**: 明るさ・均一性とも **右 > 左 > 下**(mean 63.6/61.5/57.8,CoV 0.062/0.086/0.070).各灯は異なる方向(像面で下=上側/左=右寄り/右=中央〜左寄り;プレビューは鏡像で像面右=実空間左)を強く照らし,互いの暗部を補完する相補配置.合成均一性の弱点は下灯側. + - **新規スクリプト**: `scripts/compare_single_leds.py`(3 単灯を**共通絶対カラースケール**で並置+動径プロファイル重ね描き+指標表,出力 `output/smtias/figures/compare_single_leds.png`).スケールは外れ値に強い p1–p99 で決定. + - **報告書**: `docs/06_TEST/TEST_04_単灯照明比較評価報告書.md` を作成し CLAUDE.md 索引に追記. + - **output 構造の整理(再発防止込み)**: 混在していた `output/figures`・`output/results` をデバイス別へ集約 → `output/smtias/{figures,results}`(SmTIAS 全成果物)/`output/compare/`(smtias↔tias 横断比較)/`output/tias_dfk23*`(既存).さらに figures 内で非 QM(PNG 白板 `SmTIAS_20260408_*` セット)を `output/smtias/figures/whiteboard/` サブフォルダへ分離. + - **スクリプト追従**: 再実行で散らからないよう出力先を更新.compare 系のハードコード `OUTPUT_PATH` を新構造へ(ambient/png_dng→`output/smtias/figures/`,smtias_tias→`output/compare/`)+ `mkdir(parents=True)` を追加.`run_uniformity*.py`・`plot_radial_clean.py` の `--output` 既定を `output/` → `output/smtias/` に変更(TIAS は従来どおり `--output` 明示で分離).`compare_png_dng_slide.py` の mkdir 重複も解消.pytest は 381 passed のまま(テストは tmp_path 依存でパス変更の影響なし). + - **未着手(保留)**: `output/smtias/results/` 側は非 QM/summary を `whiteboard/` 配下へ分離する整理が未対応(figures のみ実施).3 灯合成の実測 vs 単灯加算(下+左+右)の検証も今後の課題(TEST_04 限界節). diff --git a/scripts/compare_ambient_maps.py b/scripts/compare_ambient_maps.py index 6713a1c..2a2d7c4 100644 --- a/scripts/compare_ambient_maps.py +++ b/scripts/compare_ambient_maps.py @@ -34,7 +34,7 @@ INDOOR_PATH = PROJECT_ROOT / "data" / "smtias" / "quantitative" / "SmTIAS_QM_20260601_102753.dng" ROI_CONFIG = PROJECT_ROOT / "config" / "roi_config.json" ROI_KEY = "smtias.whiteboard" -OUTPUT_PATH = PROJECT_ROOT / "output" / "figures" / "compare_ambient_maps.png" +OUTPUT_PATH = PROJECT_ROOT / "output" / "smtias" / "figures" / "compare_ambient_maps.png" def _roi_luminance(path: Path, roi: dict) -> np.ndarray: @@ -80,6 +80,7 @@ ) OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) fig.savefig(str(OUTPUT_PATH), dpi=150, bbox_inches="tight") plt.close(fig) print(f"共通スケール比較スライドを保存しました: {OUTPUT_PATH}") diff --git a/scripts/compare_ambient_radial.py b/scripts/compare_ambient_radial.py index 8b78475..6edf4bb 100644 --- a/scripts/compare_ambient_radial.py +++ b/scripts/compare_ambient_radial.py @@ -40,7 +40,7 @@ INDOOR_PATH = PROJECT_ROOT / "data" / "smtias" / "quantitative" / "SmTIAS_QM_20260601_102753.dng" ROI_CONFIG = PROJECT_ROOT / "config" / "roi_config.json" ROI_KEY = "smtias.whiteboard" -OUTPUT_PATH = PROJECT_ROOT / "output" / "figures" / "compare_ambient_radial.png" +OUTPUT_PATH = PROJECT_ROOT / "output" / "smtias" / "figures" / "compare_ambient_radial.png" def _radial(path: Path, roi: dict) -> tuple[np.ndarray, dict]: @@ -83,6 +83,7 @@ fig.tight_layout() OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) fig.savefig(str(OUTPUT_PATH), dpi=150) plt.close(fig) print(f"重ね描きスライドを保存しました: {OUTPUT_PATH}") diff --git a/scripts/compare_png_dng_slide.py b/scripts/compare_png_dng_slide.py index b28f64d..1a41e7c 100644 --- a/scripts/compare_png_dng_slide.py +++ b/scripts/compare_png_dng_slide.py @@ -47,7 +47,7 @@ DNG_PATH = PROJECT_ROOT / "data" / "smtias" / "quantitative" / "SmTIAS_QM_20260601_124500.dng" ROI_CONFIG = PROJECT_ROOT / "config" / "roi_config.json" ROI_KEY = "smtias.whiteboard" -OUTPUT_PATH = PROJECT_ROOT / "output" / "figures" / "compare_png_dng_slide.png" +OUTPUT_PATH = PROJECT_ROOT / "output" / "smtias" / "figures" / "compare_png_dng_slide.png" # 正規化輝度マップの共通カラースケール(動径ピーク=1.0 を基準とした相対輝度) MAP_VMIN = 0.80 diff --git a/scripts/compare_single_leds.py b/scripts/compare_single_leds.py new file mode 100644 index 0000000..2a93467 --- /dev/null +++ b/scripts/compare_single_leds.py @@ -0,0 +1,164 @@ +"""下・左・右の単灯白板画像 3 枚を 1 枚に並べて比較するスクリプト. + +各 LED を単独点灯(他 2 灯を遮光)して撮影した白板 DNG 3 枚について, +照射の偏り・明るさ・均一性を横並びで比較する.3 灯合成時に互いを補い合えるか +(合成均一性の根拠)を読み取るための図. + +構成: +- 上段: 下・左・右の輝度マップ 3 枚を **共通絶対カラースケール** で並置 + (どの LED が明るいか/どこを照らすかを同時に比較できる) +- 下段左: 動径プロファイル重ね描き(絶対値.明るさ差と減衰の形を同時に表示) +- 下段右: 数値表(平均・CoV・max/min 比・中心/周辺比・勾配・動径 min/max 比) + +すべて linear + LSC 補正・ROI=smtias.whiteboard で揃えて算出する. + +使い方: + python scripts/compare_single_leds.py +""" + +import sys +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np + +PROJECT_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(PROJECT_ROOT)) + +from src.analysis.spatial import ( # noqa: E402 + calc_distance_map, + calc_radial_min_max_ratio, + calc_radial_profile, + calc_spatial_uniformity, +) +from src.analysis.uniformity import calc_uniformity # noqa: E402 +from src.config import load_roi_config # noqa: E402 +from src.io.dng_pipeline import dng_luminance # noqa: E402 +from src.io.loader import extract_roi # noqa: E402 + +# 比較対象(単灯 3 枚.label, ファイル名, 描画色) +QUANT_DIR = PROJECT_ROOT / "data" / "smtias" / "quantitative" +LEDS = [ + ("Bottom LED", QUANT_DIR / "SmTIAS_QM_20260615_141420.dng", "steelblue"), + ("Left LED", QUANT_DIR / "SmTIAS_QM_20260615_150300.dng", "seagreen"), + ("Right LED", QUANT_DIR / "SmTIAS_QM_20260615_170948.dng", "indianred"), +] +ROI_CONFIG = PROJECT_ROOT / "config" / "roi_config.json" +ROI_KEY = "smtias.whiteboard" +OUTPUT_PATH = PROJECT_ROOT / "output" / "smtias" / "figures" / "compare_single_leds.png" + +# 共通絶対カラースケールの設定(外れ値に強いパーセンタイルで vmin/vmax を決める) +SCALE_PERCENTILES = (1.0, 99.0) + + +def _luminance(dng_path: Path, roi: dict) -> np.ndarray: + """DNG を linear + LSC 補正で輝度化し ROI を切り出す(portrait).""" + luma_full = dng_luminance(str(dng_path), apply_lsc=True) + return extract_roi(luma_full, roi["x"], roi["y"], roi["width"], roi["height"]) + + +def _metrics(luminance: np.ndarray) -> dict: + """均一性・空間・動径の指標をまとめて算出する.""" + distance_map = calc_distance_map(*luminance.shape) + profile = calc_radial_profile(luminance, distance_map) + radial = calc_radial_min_max_ratio(profile) + metrics = { + **calc_uniformity(luminance), + **calc_spatial_uniformity(luminance)["zone_stats"], + "radial_min_max_ratio": radial["radial_min_max_ratio"], + "profile": profile, + } + return metrics + + +def main() -> None: + """3 単灯の比較図を生成して保存する.""" + roi = load_roi_config(str(ROI_CONFIG), ROI_KEY) + if roi is None: + raise SystemExit(f"ROI '{ROI_KEY}' が config に見つかりません") + + labels = [label for label, _, _ in LEDS] + colors = [color for _, _, color in LEDS] + lumas = [_luminance(path, roi) for _, path, _ in LEDS] + metrics = [_metrics(luma) for luma in lumas] + + # 共通絶対カラースケール(3 枚をまとめてパーセンタイル) + flat_all = np.concatenate([luma.ravel() for luma in lumas]) + vmin, vmax = np.percentile(flat_all, SCALE_PERCENTILES) + + fig = plt.figure(figsize=(15, 9)) + gs = fig.add_gridspec(2, 3, height_ratios=[1.0, 0.85], hspace=0.28, wspace=0.22) + + # 上段: 輝度マップ 3 枚(共通絶対スケール) + map_axes = [] + im = None + for col, (label, luma) in enumerate(zip(labels, lumas)): + ax = fig.add_subplot(gs[0, col]) + im = ax.imshow(luma, cmap="hot", interpolation="nearest", vmin=vmin, vmax=vmax) + ax.set_title(f"{label} (mean={metrics[col]['mean']:.1f})", fontsize=11) + ax.set_xlabel("X (px)") + if col == 0: + ax.set_ylabel("Y (px)") + map_axes.append(ax) + cbar = fig.colorbar(im, ax=map_axes, fraction=0.020, pad=0.02) + cbar.set_label(f"Luminance (common scale, p{SCALE_PERCENTILES[0]:g}-p{SCALE_PERCENTILES[1]:g})") + + # 下段左: 動径プロファイル重ね描き(絶対値) + ax_prof = fig.add_subplot(gs[1, 0:2]) + markers = ["o", "s", "^"] + for label, color, marker, m in zip(labels, colors, markers, metrics): + profile = m["profile"] + ax_prof.plot( + profile[:, 0], profile[:, 1], marker=marker, ms=4, color=color, + label=f"{label} CoV={m['cov']:.3f}", + ) + ax_prof.set_title("Radial profile (absolute luminance)", fontsize=11) + ax_prof.set_xlabel("Normalized distance from center") + ax_prof.set_ylabel("Mean luminance") + ax_prof.grid(True, linestyle="--", alpha=0.5) + ax_prof.legend(loc="lower left", fontsize=9) + + # 下段右: 数値表 + ax_tbl = fig.add_subplot(gs[1, 2]) + ax_tbl.axis("off") + metric_rows = [ + ("Mean luminance", "mean", "{:.2f}"), + ("CoV", "cov", "{:.3f}"), + ("Max/min ratio", "max_min_ratio", "{:.3f}"), + ("Center/periphery", "center_periphery_ratio", "{:.3f}"), + ("Gradient (%)", "gradient_magnitude", "{:.2f}"), + ("Radial min/max", "radial_min_max_ratio", "{:.3f}"), + ] + rows = [ + [name] + [fmt.format(m[key]) for m in metrics] + for name, key, fmt in metric_rows + ] + table = ax_tbl.table( + cellText=rows, + colLabels=["Metric"] + labels, + cellLoc="center", + loc="center", + ) + table.auto_set_font_size(False) + table.set_fontsize(10) + table.scale(1.0, 1.7) + for j in range(len(labels) + 1): + table[0, j].set_facecolor("#dfe6ee") + table[0, j].set_text_props(weight="bold") + + fig.suptitle( + "Single-LED illumination comparison (bottom / left / right)", + fontsize=14, y=0.98, + ) + + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) + fig.savefig(str(OUTPUT_PATH), dpi=150, bbox_inches="tight") + plt.close(fig) + print(f"3 単灯比較図を保存しました: {OUTPUT_PATH}") + for label, m in zip(labels, metrics): + print(f" {label}: mean={m['mean']:.2f}, CoV={m['cov']:.4f}, " + f"radial min/max={m['radial_min_max_ratio']:.4f}") + + +if __name__ == "__main__": + main() diff --git a/scripts/compare_smtias_tias_radial.py b/scripts/compare_smtias_tias_radial.py new file mode 100644 index 0000000..c911439 --- /dev/null +++ b/scripts/compare_smtias_tias_radial.py @@ -0,0 +1,123 @@ +"""SmTIAS(DNG)と TIAS DFK23(PNG)の動径プロファイルを 1 枚に重ね描きする. + +compare_smtias_tias_relative.png(輝度マップ 2 枚を共通相対スケールで並置した図)の +**動径プロファイル版**.いまは SmTIAS と TIAS で別々の radial_profile.png が 2 枚あるのを, +**各自のピーク=1.0 で正規化** して 1 枚に重ね,中心からの減衰カーブの形を直接比較できるようにする. + +各自ピーク=1.0 で正規化する理由(共通スケールにしない): +- SmTIAS(DNG linear+LSC, 8bit→linear)と TIAS(PNG sRGB, DFK23)は + ビット深度・色空間・センサが異なり,絶対輝度は比較できない. +- 各自の最内ピークを 1.0 に揃えると「中心からどれだけ落ちるか(減衰カーブの形)」だけが残り, + 谷の底がそのまま動径 min/max 比に一致する(compare_png_dng_slide.py 下段と同方式). + +対象(compare_smtias_tias_relative.png と同一入力): +- SmTIAS : DNG (linear + LSC 補正) SmTIAS_QM_20260601_124500 → 動径 min/max 0.860 +- TIAS : PNG (vignette 未補正) WhiteBoard_0001 → 動径 min/max 0.982 + +使い方: + python scripts/compare_smtias_tias_radial.py +""" + +import sys +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np + +PROJECT_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(PROJECT_ROOT)) + +from src.analysis.spatial import ( # noqa: E402 + calc_distance_map, + calc_radial_min_max_ratio, + calc_radial_profile, +) +from src.analysis.uniformity import ( # noqa: E402 + REC709_COEFF_B, + REC709_COEFF_G, + REC709_COEFF_R, +) +from src.config import load_roi_config # noqa: E402 +from src.io.dng_pipeline import dng_luminance # noqa: E402 +from src.io.loader import extract_roi, load_image # noqa: E402 + +# compare_smtias_tias_relative.png と同一の入力 +SMTIAS_PATH = PROJECT_ROOT / "data" / "smtias" / "quantitative" / "SmTIAS_QM_20260601_124500.dng" +TIAS_PATH = PROJECT_ROOT / "data" / "tias" / "whiteboard" / "WhiteBoard_0001.png" +ROI_CONFIG = PROJECT_ROOT / "config" / "roi_config.json" +SMTIAS_ROI_KEY = "smtias.whiteboard" +TIAS_ROI_KEY = "tias.whiteboard" +OUTPUT_PATH = PROJECT_ROOT / "output" / "compare" / "compare_smtias_tias_radial.png" + + +def _smtias_luminance(roi: dict) -> np.ndarray: + """SmTIAS DNG を linear + LSC 補正で輝度化し ROI を切り出す.""" + luma = dng_luminance(str(SMTIAS_PATH), apply_lsc=True) + return extract_roi(luma, roi["x"], roi["y"], roi["width"], roi["height"]) + + +def _tias_luminance(roi: dict) -> np.ndarray: + """TIAS PNG を Rec.709 で輝度化し ROI を切り出す(vignette 未補正).""" + img = load_image(str(TIAS_PATH)) + luma = ( + REC709_COEFF_R * img[:, :, 0] + + REC709_COEFF_G * img[:, :, 1] + + REC709_COEFF_B * img[:, :, 2] + ) + return extract_roi(luma, roi["x"], roi["y"], roi["width"], roi["height"]) + + +def _radial(luminance: np.ndarray) -> tuple[np.ndarray, dict]: + """ROI 輝度から動径プロファイルと min/max 統計を算出する.""" + distance_map = calc_distance_map(*luminance.shape) + profile = calc_radial_profile(luminance, distance_map) + radial = calc_radial_min_max_ratio(profile) + return profile, radial + + +def main() -> None: + """各自ピーク=1.0 で正規化した動径プロファイル重ね描きを生成して保存する.""" + smtias_roi = load_roi_config(str(ROI_CONFIG), SMTIAS_ROI_KEY) + tias_roi = load_roi_config(str(ROI_CONFIG), TIAS_ROI_KEY) + if smtias_roi is None or tias_roi is None: + raise SystemExit("ROI が config に見つかりません") + + smtias_prof, smtias_r = _radial(_smtias_luminance(smtias_roi)) + tias_prof, tias_r = _radial(_tias_luminance(tias_roi)) + + fig, ax = plt.subplots(figsize=(9, 6)) + + # 各自の最内ピーク(radial_max)で正規化して形だけを重ねる + sx, sy = smtias_prof[:, 0], smtias_prof[:, 1] / smtias_r["radial_max"] + tx, ty = tias_prof[:, 0], tias_prof[:, 1] / tias_r["radial_max"] + ax.plot( + sx, sy, marker="o", ms=5, color="#1f3b73", + label=f"SmTIAS min/max = {smtias_r['radial_min_max_ratio']:.3f}", + ) + ax.plot( + tx, ty, marker="s", ms=5, color="#e08214", + label=f"TIAS min/max = {tias_r['radial_min_max_ratio']:.3f}", + ) + + # 谷の底(= 動径 min/max 比)を水平線で強調 + ax.axhline(smtias_r["radial_min_max_ratio"], color="#1f3b73", ls=":", alpha=0.5) + ax.axhline(tias_r["radial_min_max_ratio"], color="#e08214", ls=":", alpha=0.5) + + ax.set_title("Radial profile: SmTIAS vs TIAS DFK23 (normalized, peak = 1.0)", fontsize=12) + ax.set_xlabel("Normalized distance from center") + ax.set_ylabel("Normalized mean luminance (own peak = 1.0)") + ax.grid(True, linestyle="--", alpha=0.5) + ax.legend(loc="lower left", fontsize=10) + fig.tight_layout() + + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) + fig.savefig(str(OUTPUT_PATH), dpi=150) + plt.close(fig) + print(f"動径プロファイル重ね描きを保存しました: {OUTPUT_PATH}") + print(f" SmTIAS (124500) radial min/max = {smtias_r['radial_min_max_ratio']:.4f}") + print(f" TIAS (WhiteBoard_0001) radial min/max = {tias_r['radial_min_max_ratio']:.4f}") + + +if __name__ == "__main__": + main() diff --git a/scripts/plot_radial_clean.py b/scripts/plot_radial_clean.py index 1dc89e6..d47920e 100644 --- a/scripts/plot_radial_clean.py +++ b/scripts/plot_radial_clean.py @@ -85,8 +85,8 @@ ) parser.add_argument( "--output", - default=str(PROJECT_ROOT / "output"), - help="出力ディレクトリのパス(デフォルト: output/)", + default=str(PROJECT_ROOT / "output" / "smtias"), + help="出力ディレクトリのパス(デフォルト: output/smtias/)", ) parser.add_argument( "--no-lsc", diff --git a/scripts/run_uniformity.py b/scripts/run_uniformity.py index 2094d0a..1671960 100644 --- a/scripts/run_uniformity.py +++ b/scripts/run_uniformity.py @@ -149,8 +149,8 @@ ) parser.add_argument( "--output", - default=str(PROJECT_ROOT / "output"), - help="出力ディレクトリのパス(デフォルト: output/)", + default=str(PROJECT_ROOT / "output" / "smtias"), + help="出力ディレクトリのパス(デフォルト: output/smtias/)", ) args = parser.parse_args() diff --git a/scripts/run_uniformity_dng.py b/scripts/run_uniformity_dng.py index cd04bb6..851f708 100644 --- a/scripts/run_uniformity_dng.py +++ b/scripts/run_uniformity_dng.py @@ -110,8 +110,8 @@ ) parser.add_argument( "--output", - default=str(PROJECT_ROOT / "output"), - help="出力ディレクトリのパス(デフォルト: output/)", + default=str(PROJECT_ROOT / "output" / "smtias"), + help="出力ディレクトリのパス(デフォルト: output/smtias/)", ) parser.add_argument( "--no-lsc",