RAWデータが色変換モデルとCoreML推論を通ってJPEGになる流れを抽象的に表した図

Article

RAW現像を自分の色で再現するために、PyTorchからCoreMLまで通した実験

2026年6月9日
6 min read
機械学習
#RAW現像#PyTorch#CoreML#Swift#写真

RAWデータが色変換モデルとCoreML推論を通ってJPEGになる流れを抽象的に表した図

rawlearn という、RAW現像の出力を機械学習で近似するための個人実験を進めていた。

やりたかったのは、RAW現像ソフトそのものを全部作り直すことではない。DNG形式のRAW写真を入力し、普段の参照現像に近い色とトーンへ変換する、小さなローカルMLパイプラインを作れるか試すことだった。

最初はPythonでDNGを読み、線形なRAW処理を組み、そこにニューラルネットワークを足すところから始めた。最終的には、PyTorchで学習したモデルをCoreMLへ変換し、Swift側でDNGを読み込み、タイル分割しながらCoreML推論をかけてJPEGへ書き出すところまで通した。

実写真や個別の撮影データを見せたい話ではなく、「RAW現像の雰囲気を、自分のローカルMLパイプラインでどこまで再現できるか」という技術実験の記録として残しておく。

全部をMLに任せない

最初に決めたのは、RAW現像のすべてをニューラルネットに任せないことだった。

露出補正やホワイトバランスのように、比較的物理的に説明しやすい処理は、通常の線形処理で扱う。一方で、色変換やトーンの調整のように「自分の好み」や「参照現像の雰囲気」が出る部分をMLモデルに任せる。

この分け方にすると、実験の範囲をかなり小さくできる。

RAW入力からいきなり完成画像を生成する巨大なモデルを作るのではなく、前処理で整えた画像に対して、色とトーンの変換を学習する。rawlearn.develop()rawlearn.preview() というPython APIも、この方向で扱いやすくするために用意した。

初期パイプラインでは、RAW読み込みとデモザイクに rawpy / libraw 系の処理を使った。ここでDNGを扱えるようにしておき、その上に学習用データ作成、現像CLI、評価スクリプトを足していった。

ペア画像からパッチを作る

学習データは、RAW入力と参照現像済み画像のペアから作る。

具体的には、ペア画像から 512x512 のパッチを切り出し、RAW側の入力から参照現像に近い出力へ変換できるように学習させる。DNG収集用に collect_dng.py、ペアデータ作成用に prepare_pairs.py、PythonでDNGをJPEGへ現像する develop.py、PSNR/SSIM評価用に evaluate.py を用意した。

このあたりは、かなり地味だが重要だった。

モデルだけを作っても、入力と正解画像の対応が扱いにくいと学習が進まない。RAWと参照現像をペアとして管理し、同じ領域からパッチを切り出し、学習と評価へ渡せる形にする。写真の見た目を扱う実験でも、結局はデータの準備がかなりの割合を占める。

U-Net的な発想からPixelColorNetへ

途中でモデルの方針も見直した。

最初は、空間畳み込みを使うU-Net的な構成も考えていた。画像全体の文脈を見ながら変換する発想としては自然だ。ただ、RAW現像の色変換を主目的にするなら、隣接ピクセルを見すぎることで画像が少しボケるリスクもある。

そこで、現在の方針は PixelColorNet v7 に寄せた。

PixelColorNet v7 は、1x1 Convだけで構成するモデルだ。各ピクセルに同じ色変換MLPを適用するような考え方で、隣接ピクセルは参照しない。つまり、空間的な補正ではなく、ピクセルごとの色変換に集中する。

この制約は、RAW現像の実験としてはむしろ扱いやすかった。

画像の構造を作り替えるのではなく、入力画像のシャープさを保ったまま、色とトーンの変換だけを学習させる。モデルを大きくしすぎず、変換の役割も明確にできる。

PyTorchで学習する

モデルはPyTorchで実装した。

Apple Silicon上で試せるように、MPSを使う学習スクリプトも用意した。損失関数にはL1に加えて、VGGベースのPerceptual Lossを組み合わせる実装がある。

日付でいうと、2026年2月24日に初期実装を作り、2月25日に PixelColorNet v7、DNG収集、現像CLI、学習パイプラインの改修を追加した。3月1日には、CIRAWFilter版モデルの学習で Epoch 87val_loss=0.0590 のベストモデルを残した。

この数字だけで画質を語るつもりはない。写真の見た目は、最終的には人間が見て判断する部分も大きい。ただ、少なくともパイプラインとしては、RAW入力、参照現像、学習、評価までを一つの流れにできた。

CoreMLへ変換する

次にやったのがCoreML変換だ。

2026年2月27日に、CoreML変換スクリプトとSwiftのDNG現像デモを追加した。PyTorchで学習したモデルを .mlpackage 形式へ変換し、Apple環境のアプリ側から使える形にする。

変換時には、入力側にsRGBガンマ変換を内包する with_gamma 版と、ガンマ変換なしの no_gamma 版の2種類を作った。RAW現像パイプラインのどこでガンマを扱うかによって、モデルの入力前提が変わるためだ。

入力形状は、任意解像度に近い柔軟な扱いを想定して変換した。写真は毎回同じサイズとは限らないので、アプリ側に寄せるなら、固定サイズの実験だけで終わらせたくなかった。

SwiftでDNGからJPEGまで通す

Swiftデモでは、Appleの CIRAWFilter でDNGを読み込み、CoreMLモデルをロードし、画像をタイルに分割して推論し、JPEGとして保存する。

この「タイルに分けて推論する」という部分も、実アプリに近づけるうえでは重要だった。写真全体を一度にモデルへ入れるだけなら実験は簡単だが、実際の解像度ではメモリや処理時間の都合が出てくる。タイル分割して扱えるようにすると、モデルをアプリに組み込む現実味が少し増す。

ここまで通すと、単なる研究用コードから一歩進む。

Pythonで学習し、CoreMLへ変換し、SwiftでDNGを読み、推論し、JPEGを書き出す。まだ完成したRAW現像アプリではないが、「このモデルを自作の写真管理・編集アプリ側へ組み込めるか」を考えられる段階にはなった。

やってみてわかったこと

今回の実験で一番大きかったのは、RAW現像を「全部入りの巨大なAI処理」として考えなくてもよい、と確認できたことだ。

露出やホワイトバランスのような処理は従来のパイプラインに任せる。主観的な色やトーンの部分だけを、小さなピクセル単位のモデルに寄せる。そうすると、学習対象も実装もかなり小さくなる。

一方で、実用に近づけるほど、モデル以外の部分が効いてくる。

RAWと参照現像のペアをどう作るか。パッチをどう切るか。ガンマ変換をどこに入れるか。CoreMLへどう変換するか。Swift側でDNGをどう読み、どの単位で推論するか。こういう地味なつなぎ込みが、最終的な使いやすさを決める。

今後のアクションは、写真管理・編集アプリ側へCoreMLモデルを組み込むことだ。

rawlearn単体では、DNGからJPEGまでの実験パイプラインは見えてきた。次はこれを、日常的に写真を扱うアプリの中で、どのタイミングで使うのか、どのくらい手動調整と組み合わせるのかを試したい。

RAW現像を完全に置き換えるというより、自分の色変換モデルをローカルに持ち、必要なところだけアプリから呼び出す。今のところは、そのくらいの距離感がちょうどよさそうだ。