本ドキュメントについて(取り込み注記): 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 プロジェクトから MiniTias-Evaluation プロジェクトに対する DNG 対応要求仕様 である.MiniTias 側で実装された定量撮影モード(TECH_01 / TECH_03)の出力フォーマットが PNG から DNG (RAW_SENSOR) に変更されたことに伴い,MiniTias-Evaluation 側にも DNG 読み込み・解析機能の追加を依頼する.
MiniTias-Evaluation(MiniTias-Evaluation リポジトリ)に以下の機能追加を要求する:
これらの実装は SmTIAS-LightSim 側にコピーされる前提で,再利用性の高い設計とする.
┌─────────────────────────┐ ┌──────────────────────────┐
│ MiniTias │ │ MiniTias-Evaluation │
│ (Flutter / Android) │ ──→ │ (Python) │
│ 撮影アプリ │ │ 実機データ評価ツール │
│ - PNG 保存(通常モード) │ │ - 評価指標算出 │
│ - DNG 保存(定量モード) │ │ - 可視化 │
└─────────────────────────┘ └──────────────────────────┘
│
│ コピー(uniformity.py 等)
▼
┌──────────────────────────┐
│ SmTIAS-LightSim │
│ (Python / Mitsuba 3) │
│ シミュ vs 実機比較 │
└──────────────────────────┘
MiniTias-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 経路の追加が必要.
DNG 読み込みの責務は MiniTias-Evaluation を一次窓口とする.既存パターン(uniformity.py が MiniTias-Evaluation から SmTIAS-LightSim にコピー)を踏襲する:
| プロジェクト | 責務 |
|---|---|
| MiniTias | DNG + meta.json の保存(実装済) |
| MiniTias-Evaluation(一次窓口) | DNG/meta.json の読み込み I/F を正規実装.既存評価指標との並列対応 |
| SmTIAS-LightSim(コピー側) | MiniTias-Evaluation の DNG 読み込み I/F をコピー(uniformity.py パターン継承) |
→ MiniTias-Evaluation の実装完了後,SmTIAS-LightSim 側 Claude セッションでコピー作業を実施する.
新規モジュール 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:
"""MiniTIAS 定量モード 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 を読み込みsensorCharacteristics.black_level_pattern から取得,未提供時は DNG Exif)postprocess(demosaic_algorithm=...) で実装)ColorMatrix1 を参照.無彩色シーンでは影響軽微だが選択肢として用意)output_format の使い分けrgb_float32 / luma_float32: SmTIAS-LightSim のシミュ vs 実機比較に推奨(linear ドメインで突合)rgb_uint8: 既存 MiniTias-Evaluation パイプラインとの互換用(既存 analysis/uniformity.py 等が uint8 前提の場合に有用)新規モジュール src/io/metadata_loader.py を作成:
def load_metadata(path: str) -> dict:
"""MiniTIAS 定量モード 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:
ホット画素座標のリスト.MiniTias 側で取得実装後に有効化.未提供時は空リスト.
"""
メタデータから取得したホット画素を画像上でマスクする機能:
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:
マスク済み画像.形状・型は入力と同じ.
"""
本流の処理では使わないが,将来のフォールバック検証用に実装:
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).
"""
既存 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}")
これは MiniTias-Evaluation 側の判断で実装するかは任意.推奨は実装.
既存 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" で直接出力 |
→ 既存解析パイプラインの修正コストを最小化する設計.
SENSOR_BLACK_LEVEL_PATTERN から取得)ColorMatrix1/2 等が埋め込み済み(rawpy が自動で参照)→ output_format パラメータでユーザーが選択可能にする.既定は rgb_float32(linear)を推奨.
rawpy.imread().postprocess(demosaic_algorithm=...) のパラメータ:
| 値 | 速度 | 品質 | 用途 |
|---|---|---|---|
rawpy.DemosaicAlgorithm.LINEAR | 速 | 中 | バイリニア.既定推奨 |
rawpy.DemosaicAlgorithm.AHD | 中 | 高 | 適応的.カラー画像で有利だが照明均一性評価には過剰 |
rawpy.DemosaicAlgorithm.DHT | 中 | 高 | 高品質.処理重め |
→ 既定は LINEAR で運用.demosaic 引数でユーザーが切り替え可能.
MiniTias-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 1 | DNG 読み込み I/F (dng_loader.py) と単体テスト | 1 日 |
| Phase 2 | メタデータ JSON 読み込み I/F (metadata_loader.py) と単体テスト | 半日 |
| Phase 3 | LSC マップ参照・hot pixel マスク機能の実装 | 半日 |
| Phase 4 | 既存解析パイプラインとの統合・動作確認 | 半日 |
| Phase 5 | 既存 PNG / 新規 DNG の並列対応検証(V1〜V3) | 半日 |
| Phase 6 | ドキュメント更新(SPEC_01/SPEC_02 等) | 半日 |
合計: 約 3.5 日./implement パイプラインで Phase ごとに進める.
MiniTias-Evaluation での実装完了後,SmTIAS-LightSim 側 Claude セッションで以下を実施:
MiniTias-Evaluation/src/io/dng_loader.py → SmTIAS-LightSim/src/sm_tias_lightsim/evaluation/dng_loader.pyMiniTias-Evaluation/src/io/metadata_loader.py → SmTIAS-LightSim/src/sm_tias_lightsim/evaluation/metadata.pyuniformity.py と同じ流れ)"""DNG 読み込みモジュール. MiniTias-Evaluation/src/io/dng_loader.py を SmTIAS-LightSim にコピーしたもの. オリジナルは MiniTias-Evaluation 側で管理する.バグ修正・機能追加はそちらで行い, 本プロジェクトに反映する形を取る. """
cli/evaluate.py に DNG 入力経路を追加実装完了後,以下を実施して受け入れ判定とする:
load_metadata で settings.exposure_time_ns 等が正しく取得できるload_lsc_map で (4, 13, 17) の配列が返るload_hot_pixel_list で空リスト or リスト(MiniTias 側実装次第)が返る実装着手時に確認が必要な事項:
| ID | 質問 | 確認方法 |
|---|---|---|
| Q1 | rawpy のバージョン互換性は問題ないか | 環境で 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 を実装するか | MiniTias-Evaluation 側判断.SmTIAS-LightSim 側では便利だが任意 |
| Q6 | LSC 逆適用機能は Phase 3 で実装するか後回しか | フォールバック条件 C1〜C3 未発動なので Phase 3 では雛形のみで OK |
| Q7 | 既存ドキュメント (SPEC_01/SPEC_02) の更新範囲 | DNG 経路追加に伴う仕様変更を反映.Phase 6 で対応 |
MiniTias-Evaluation 側 Claude セッションでの作業着手手順.TECH_02 MiniTIAS 引き継ぎ手順 と同様の構造.
pip install rawpy または pyproject.toml 更新 + uv sync 等)reference_measurement/quantitative/ から 1 枚)/implement TECH_05 Phase 1: DNG 読み込み I/F で起動/implement 完了後,SmTIAS-LightSim 側で確認できる形にする:
src/io/dng_loader.py の最終形をリポジトリ master にマージ