Newer
Older
SmTIAS-Evaluation / docs / 05_TECH / TECH_01_DNG対応要求仕様.md

SmTIAS-Evaluation DNG 対応要求仕様 (DNG Support Specification for SmTIAS-Evaluation)

本ドキュメントについて(取り込み注記): SmTIAS-LightSim プロジェクトから受領した DNG 対応要求仕様(先方での名称は TECH_05)を,本プロジェクトの命名規則(GUIDE_03)に従い docs/05_TECH/TECH_01_DNG対応要求仕様.md として取り込んだもの.文中の他ドキュメント参照(TECH_01/03/04・TEST_01・SPEC_04 等)は SmTIAS-LightSim 側のドキュメントを指し,本プロジェクト内には存在しない.本プロジェクトでの対応状況は docs/PROGRESS.md を参照.

本ドキュメントは SmTIAS-LightSim プロジェクトから SmTIAS-Evaluation プロジェクトに対する DNG 対応要求仕様 である.SmTIAS 側で実装された定量撮影モード(TECH_01 / TECH_03)の出力フォーマットが PNG から DNG (RAW_SENSOR) に変更されたことに伴い,SmTIAS-Evaluation 側にも DNG 読み込み・解析機能の追加を依頼する.

想定読者 (Intended Reader)

  • SmTIAS-Evaluation 側 Claude セッション: 本ドキュメントを最初に読んで作業着手する
  • 人間レビュー (rinto): 両セッションを橋渡しする

概要 (Overview)

SmTIAS-Evaluation(SmTIAS-Evaluation リポジトリ)に以下の機能追加を要求する:

  1. DNG 読み込み I/F の新規実装(10-bit BGGR Bayer 展開)
  2. メタデータ JSON 読み込み I/F の新規実装(撮影条件・LSC マップ・hot pixel リスト等)
  3. 既存 PNG 経路と並列対応(後方互換)
  4. 既存評価指標との互換(CoV / 勾配 / C/P / max-min 等が DNG 入力でも同じロジックで算出可能)

これらの実装は SmTIAS-LightSim 側にコピーされる前提で,再利用性の高い設計とする.

背景 (Background)

三項プロジェクトの関係

┌─────────────────────────┐      ┌──────────────────────────┐
│  SmTIAS                │      │  SmTIAS-Evaluation      │
│  (Flutter / Android)     │ ──→  │  (Python)                 │
│  撮影アプリ                │      │  実機データ評価ツール       │
│  - PNG 保存(通常モード)   │      │  - 評価指標算出           │
│  - DNG 保存(定量モード)   │      │  - 可視化                 │
└─────────────────────────┘      └──────────────────────────┘
                                          │
                                          │ コピー(uniformity.py 等)
                                          ▼
                              ┌──────────────────────────┐
                              │  SmTIAS-LightSim          │
                              │  (Python / Mitsuba 3)     │
                              │  シミュ vs 実機比較        │
                              └──────────────────────────┘

経緯のまとめ

  • 2026-05-28: SmTIAS-LightSim 側 Step 8 キャリブで「実機 PNG 自体が定量データとして揺らぐ」を発見 (TEST_01)
  • 2026-05-28: SmTIAS 側に TECH_01 定量撮影モード要求仕様 を発行
  • 2026-06-01: SmTIAS 側で実装完了.AQUOS sense3 (SH-02M) が LEVEL_3 で DNG 経路採用 (TECH_03)
  • 2026-06-01: SHADING_MODE = HIGH_QUALITY を本流維持で合意 (TECH_04)
  • 本ドキュメント (2026-06-01): SmTIAS-Evaluation 側にも DNG 対応を依頼

SmTIAS-Evaluation の現状

SmTIAS-Evaluation の I/O は現状 PNG 専用:

# 既存 I/F (src/io/loader.py)
def load_image(path: str) -> numpy.ndarray:
    """PNG 画像を読み込み RGB 配列として返す(H x W x 3, uint8)."""

→ DNG 経路の追加が必要.

役割分担 (Role Distribution)

DNG 読み込みの責務は SmTIAS-Evaluation を一次窓口とする.既存パターン(uniformity.py が SmTIAS-Evaluation から SmTIAS-LightSim にコピー)を踏襲する:

プロジェクト責務
SmTIASDNG + meta.json の保存(実装済)
SmTIAS-Evaluation(一次窓口)DNG/meta.json の読み込み I/F を正規実装.既存評価指標との並列対応
SmTIAS-LightSim(コピー側)SmTIAS-Evaluation の DNG 読み込み I/F をコピー(uniformity.py パターン継承)

→ SmTIAS-Evaluation の実装完了後,SmTIAS-LightSim 側 Claude セッションでコピー作業を実施する.

要求仕様 (Requirements)

1. DNG 読み込み I/F

新規モジュール src/io/dng_loader.py を作成:

def load_image_dng(
    path: str,
    demosaic: str = "bilinear",
    black_level: int | None = None,
    output_format: str = "rgb_float32",
) -> numpy.ndarray:
    """SmTIAS 定量モード DNG を読み込み配列として返す.

    Args:
        path: DNG ファイルのパス.
        demosaic: デモザイクアルゴリズム.既定 "bilinear".
            "nearest"(最近傍)/ "bilinear"(双線形)/ "ahd"(適応的)から選択.
        black_level: 黒レベル.None なら meta.json から自動取得 or DNG Exif から取得.
            通常は 64(SH-02M).
        output_format: 出力形式.既定 "rgb_float32".
            "rgb_float32": (H, W, 3) float32, linear, 値域 [0.0, 1.0](飽和は 1.0 でクリップしない)
            "luma_float32": (H, W) float32, linear, 値域 [0.0, 1.0](Rec.709 重み)
            "rgb_uint8": (H, W, 3) uint8, sRGB OETF 適用後(既存 PNG 経路と互換)

    Returns:
        numpy.ndarray. 形状・型は output_format に依存.

    Raises:
        FileNotFoundError: ファイルが存在しない場合.
        ValueError: rawpy が DNG を読み込めなかった場合.
    """

実装方針

  • rawpy.imread() で 10-bit BGGR Bayer を読み込み
  • 黒レベル除去(meta.json sensorCharacteristics.black_level_pattern から取得,未提供時は DNG Exif)
  • デモザイク(postprocess(demosaic_algorithm=...) で実装)
  • カラーマトリクス適用(DNG Exif ColorMatrix1 を参照.無彩色シーンでは影響軽微だが選択肢として用意)
  • 出力形式の変換

output_format の使い分け

  • rgb_float32 / luma_float32: SmTIAS-LightSim のシミュ vs 実機比較に推奨(linear ドメインで突合)
  • rgb_uint8: 既存 SmTIAS-Evaluation パイプラインとの互換用(既存 analysis/uniformity.py 等が uint8 前提の場合に有用)

2. メタデータ JSON 読み込み I/F

新規モジュール src/io/metadata_loader.py を作成:

def load_metadata(path: str) -> dict:
    """SmTIAS 定量モード meta.json を読み込み辞書として返す.

    Args:
        path: meta.json のパス.

    Returns:
        辞書.キー: settings / actual / sensorCharacteristics / lscMap / lscMapRowCount /
        lscMapColumnCount / hardwareLevel 等.
        構造は TECH_03 のスキーマ準拠.

    Raises:
        FileNotFoundError: ファイルが存在しない場合.
        json.JSONDecodeError: JSON 形式が不正な場合.
    """


def load_lsc_map(path: str) -> numpy.ndarray:
    """meta.json から LSC マップを (4, M, N) 配列として抽出する.

    Args:
        path: meta.json のパス.

    Returns:
        numpy.ndarray of shape (4, M, N), dtype=float32.
        4 = [R, G_even, G_odd, B], M = lscMapRowCount, N = lscMapColumnCount.
        値域: ≥ 1.0(無補正).

    Raises:
        FileNotFoundError / KeyError / ValueError: 仕様外の場合.
    """


def load_hot_pixel_list(path: str) -> list[tuple[int, int]]:
    """meta.json からホット画素リストを (col, row) のタプルリストとして抽出する.

    Args:
        path: meta.json のパス.

    Returns:
        ホット画素座標のリスト.SmTIAS 側で取得実装後に有効化.未提供時は空リスト.
    """

3. Hot pixel マスク機能

メタデータから取得したホット画素を画像上でマスクする機能:

def mask_hot_pixels(
    image: numpy.ndarray,
    hot_pixels: list[tuple[int, int]],
    method: str = "neighbor_mean",
) -> numpy.ndarray:
    """ホット画素位置を周辺平均で置換する.

    Args:
        image: 入力画像 (H, W) or (H, W, 3).
        hot_pixels: ホット画素座標 (col, row) のリスト.
        method: 置換方法."neighbor_mean"(周辺 3x3 平均)/ "zero"(0 で置換).

    Returns:
        マスク済み画像.形状・型は入力と同じ.
    """

4. LSC マップ参照機能(オプション・将来検証用)

本流の処理では使わないが,将来のフォールバック検証用に実装:

def apply_lsc_inverse(
    image: numpy.ndarray,
    lsc_map: numpy.ndarray,
) -> numpy.ndarray:
    """LSC マップを画像に逆適用する(補正前 raw 相当に戻す).

    Args:
        image: 補正済み画像 (H, W) or (H, W, 3).DNG (HIGH_QUALITY) を想定.
        lsc_map: LSC ゲインマップ (4, M, N).

    Returns:
        逆適用後の画像.image_raw = image / interpolate(lsc_map, image.shape).
    """
  • LSC マップ (13×17) を画像解像度 (例 3264×2448) にバイリニア補間
  • 画素ごとに割り算で逆適用
  • SHADING_MODE = OFF 撮影との比較検証に使用(TECH_04 フォールバック条件 C1〜C3)

5. 既存 PNG 経路との並列対応(後方互換)

既存 load_image無修正で温存.新規 DNG 関数を並列追加する形:

# 既存(無修正)
load_image(path) -> RGB uint8

# 新規追加
load_image_dng(path, ...) -> 設定可能形式

ユーザー側で拡張子を見て呼び分ける,もしくは統一インターフェースを別途用意する(後述).

統一インターフェースの選択肢

def load_image_any(path: str, **kwargs) -> numpy.ndarray:
    """拡張子から自動判定して読み込む.

    .png → load_image (uint8 RGB)
    .dng → load_image_dng (kwargs で形式指定)
    """
    ext = Path(path).suffix.lower()
    if ext == ".png":
        return load_image(path)
    elif ext == ".dng":
        return load_image_dng(path, **kwargs)
    else:
        raise ValueError(f"未対応のフォーマット: {ext}")

これは SmTIAS-Evaluation 側の判断で実装するかは任意.推奨は実装.

6. 評価指標との互換

既存 src/analysis/uniformity.py 等の評価関数は 無修正で DNG 入力に対応できるよう,DNG 読み込み I/F の出力を既存関数の入力仕様に合わせる:

既存関数の入力仕様DNG 経路での対応
RGB uint8 (H, W, 3)output_format="rgb_uint8" で uint8 にスケール(10-bit → 8-bit + sRGB OETF 適用)
Luma uint8 (H, W)上記後に Rec.709 重みでスカラー化
Float32 linear (H, W, 3)output_format="rgb_float32" で直接出力

→ 既存解析パイプラインの修正コストを最小化する設計.

データフォーマット詳細 (Data Format Details)

DNG (RAW_SENSOR) の値域

  • 10-bit raw: 整数値 0〜1023
  • 黒レベル: 64(SENSOR_BLACK_LEVEL_PATTERN から取得)
  • 黒レベル除去後: 0〜959
  • 飽和判定: 値 > 900 を要警告(飽和の影響)
  • DNG Exif にカラーマトリクス ColorMatrix1/2 等が埋め込み済み(rawpy が自動で参照)

sRGB OETF の扱い

  • 定量モード DNG は linear ドメイン(TONEMAP_MODE = CONTRAST_CURVE 線形のため sRGB OETF 未適用)
  • 既存通常モード PNG は sRGB OETF 適用済(端末の TONEMAP デフォルト)
  • 評価時の OETF の扱いは 解析対象次第
    • 形状指標(CoV / 勾配等): linear ドメインで算出が物理的に整合
    • 既存実機との互換比較: sRGB ドメインで算出(既存 SmTIAS-Evaluation の挙動と一致)

output_format パラメータでユーザーが選択可能にする.既定は rgb_float32(linear)を推奨.

デモザイクアルゴリズム

rawpy.imread().postprocess(demosaic_algorithm=...) のパラメータ:

速度品質用途
rawpy.DemosaicAlgorithm.LINEARバイリニア.既定推奨
rawpy.DemosaicAlgorithm.AHD適応的.カラー画像で有利だが照明均一性評価には過剰
rawpy.DemosaicAlgorithm.DHT高品質.処理重め

→ 既定は LINEAR で運用.demosaic 引数でユーザーが切り替え可能.

ディレクトリ構造(提案)

SmTIAS-Evaluation 既存構造に従い:

src/io/
├── __init__.py
├── loader.py             # 既存 PNG 読み込み(無修正)
├── dng_loader.py         # 新規: DNG 読み込み
├── metadata_loader.py    # 新規: meta.json 読み込み
└── unified_loader.py     # 新規(任意): 統一 I/F load_image_any

追加依存パッケージ

pyproject.toml の dependencies に:

[project]
dependencies = [
    "rawpy>=0.21.0",  # DNG 読み込み
    # 既存依存はそのまま
]

または requirements.txt に:

rawpy>=0.21.0

段階的実装(Phase 分割)

Phase内容推定工数
Phase 1DNG 読み込み I/F (dng_loader.py) と単体テスト1 日
Phase 2メタデータ JSON 読み込み I/F (metadata_loader.py) と単体テスト半日
Phase 3LSC マップ参照・hot pixel マスク機能の実装半日
Phase 4既存解析パイプラインとの統合・動作確認半日
Phase 5既存 PNG / 新規 DNG の並列対応検証(V1〜V3)半日
Phase 6ドキュメント更新(SPEC_01/SPEC_02 等)半日

合計: 約 3.5 日./implement パイプラインで Phase ごとに進める.

SmTIAS-LightSim 側へのコピー手順 (Copy to SmTIAS-LightSim)

SmTIAS-Evaluation での実装完了後,SmTIAS-LightSim 側 Claude セッションで以下を実施:

  1. コピー対象ファイル:
    • SmTIAS-Evaluation/src/io/dng_loader.pySmTIAS-LightSim/src/sm_tias_lightsim/evaluation/dng_loader.py
    • SmTIAS-Evaluation/src/io/metadata_loader.pySmTIAS-LightSim/src/sm_tias_lightsim/evaluation/metadata.py
  2. 既存パターンに従う (uniformity.py と同じ流れ)
  3. ファイル先頭にコピー元を明記:
    """DNG 読み込みモジュール.
    
    SmTIAS-Evaluation/src/io/dng_loader.py を SmTIAS-LightSim にコピーしたもの.
    オリジナルは SmTIAS-Evaluation 側で管理する.バグ修正・機能追加はそちらで行い,
    本プロジェクトに反映する形を取る.
    """
    
  4. 既存 sm-evaluate との統合: 既存 cli/evaluate.py に DNG 入力経路を追加
  5. テストを SmTIAS-LightSim 側でも実装(SmTIAS-Evaluation 側のテストをコピー+プロジェクト固有部分を追加)

検証方法 (Verification)

実装完了後,以下を実施して受け入れ判定とする:

V1. PNG vs DNG 整合性

  • 同一シーン(白板)を SmTIAS で 通常モード PNG + 定量モード DNG で連続撮影
  • 両方を SmTIAS-Evaluation で評価
  • 評価指標(CoV / 勾配 / C/P 等)の差を確認
    • 完全一致は期待しない(定量モードは ISP 介入最小化のため差が出るのが正常)
    • DNG 経路でも妥当な値が出ることを確認(CoV > 0, 勾配 > 0 等の基本検証)

V2. 既存 PNG 経路の回帰

  • 既存通常モード PNG での評価指標が 変化しないこと
  • 既存テストが全合格すること

V3. メタデータ JSON 読み込み

  • load_metadatasettings.exposure_time_ns 等が正しく取得できる
  • load_lsc_map で (4, 13, 17) の配列が返る
  • load_hot_pixel_list で空リスト or リスト(SmTIAS 側実装次第)が返る

V4. SmTIAS-LightSim 側へのコピー後検証

  • コピー後の SmTIAS-LightSim 側で同じ DNG を読み込んで 同じ評価指標が出ること
  • SmTIAS-LightSim の既存テストが全合格

開放質問 (Open Questions)

実装着手時に確認が必要な事項:

ID質問確認方法
Q1rawpy のバージョン互換性は問題ないか環境で pip install rawpy → 実 DNG で動作確認
Q2デモザイク手法の既定値は LINEAR で OK か数枚の DNG で AHD と比較して照明均一性指標がほぼ同じか確認
Q3カラーマトリクス適用は無彩色シーンで影響軽微か既定 OFF(rawpy デフォルト)か ON(DNG Exif 参照)かを実機で比較
Q4既存 analysis/uniformity.py は uint8 / float32 のどちらを受けるかコードを確認.既存仕様に合わせて output_format の既定を決める
Q5統一 I/F load_image_any を実装するかSmTIAS-Evaluation 側判断.SmTIAS-LightSim 側では便利だが任意
Q6LSC 逆適用機能は Phase 3 で実装するか後回しかフォールバック条件 C1〜C3 未発動なので Phase 3 では雛形のみで OK
Q7既存ドキュメント (SPEC_01/SPEC_02) の更新範囲DNG 経路追加に伴う仕様変更を反映.Phase 6 で対応

引き継ぎ手順 (Hand-off Procedure)

SmTIAS-Evaluation 側 Claude セッションでの作業着手手順.TECH_02 SmTIAS-LightSim への引き継ぎ と同様の構造.

Step A: 環境準備

  • rawpy パッケージを追加(pip install rawpy または pyproject.toml 更新 + uv sync 等)
  • サンプル DNG を取得(SmTIAS 側で撮影済の reference_measurement/quantitative/ から 1 枚)

Step B: Phase 1(DNG 読み込み)から順次実装

  • /implement TECH_05 Phase 1: DNG 読み込み I/F で起動
  • coder → tester → refactorer → doc 更新の標準パイプライン
  • Phase 1 完了後に動作確認 → Phase 2 へ

Step C: 完了の伝達方法

  • SmTIAS-Evaluation 側で /implement 完了後,SmTIAS-LightSim 側で確認できる形にする:
    • SmTIAS-Evaluation src/io/dng_loader.py の最終形をリポジトリ master にマージ
    • rinto が SmTIAS-LightSim 側 Claude セッションを開いて「コピーしてほしい」と指示
    • SmTIAS-LightSim 側 Claude が SmTIAS-Evaluation のローカルリポジトリから読み込んでコピー

既存ドキュメントとの整合 (Cross-References)