※ 残念ながら、lightroom現像の再現には失敗しています。
※ もう少し実際の現像状況を見つつ、設定項目を再検証する必要あり(してからここに上げろよって話ではある)
前提条件
RAW画像ファイルはカメラが撮影した未加工のデータを含むため、高品質な画像編集が可能ですが、そのままでは扱いにくく、実際に画像として使用する際には、JPEG形式に変換する必要があります。
私は通常、lightroomにて現像を行っていますが、このプリセットファイルは出力することができ、各設定情報は簡単に読み解くことが可能です。また、簡単にJSONなどのファイルに変換することができます。
今回は、lightroomのプリセットファイルであるXMPファイルを参照し、Pythonを用いてRAWファイルを現像してみます。RAWファイルはオリンパスのDNG形式、lightroomのプリセットファイルは私のオリジナルのものを使用します。
以下は今回使用したファイルのリンクです。
・RAWファイル:https://drive.google.com/file/d/15miXXxP8mv_yEo8fiOLNYS-56k59ua5t/view?usp=drive_link
・プリセットファイル:https://drive.google.com/file/d/16B5S0_Lyb5mf3vZRJg4pMBZ2nClcfcqR/view?usp=drive_link
ライブラリ
今回のプログラムに使用するライブラリの一覧です。
・rawpy
LibRawライブラリのPythonラッパーであり、RAW画像の処理を簡単に行うことができます。
また、ホット/デッドピクセルの修復機能も備えています。
・imageio
imageioは、幅広い画像およびビデオデータを読み書きするための簡単なインターフェースを提供するPythonライブラリです。アニメーション画像や科学データなどもサポートしています。
・lxml
lxmlは、PythonでXMLおよびHTMLを処理するための最も機能豊富で使いやすいライブラリです。libxml2およびlibxsltのCライブラリに基づいており、高速で完全なXML機能を提供します。
プログラムの各パーツについて
1. XMPファイルをJSONに変換
XMPファイルを読み込み、XML形式のデータをパースしてJSON形式に変換します。
def xmp_to_json(xmp_path):
with open(xmp_path, 'rb') as file:
xmp_data = file.read()
root = etree.fromstring(xmp_data)
xmp_dict = {child.tag: child.text.strip() for child in root.iter() if child.text}
return xmp_dict
2. JSONを保存
変換されたJSONデータを指定されたディレクトリに保存します。
def save_json(data, output_dir, xmp_filename):
base_filename = os.path.splitext(os.path.basename(xmp_filename))[0]
json_path = os.path.join(output_dir, f"{base_filename}.json")
with open(json_path, 'w', encoding='utf-8') as json_file:
json.dump(data, json_file, ensure_ascii=False, indent=4)
3. RAWファイルをJPEGに現像
RAWファイルを読み込み、XMPファイルの設定を適用してJPEG形式に変換します。
def convert_raw_to_jpeg(dng_path, output_dir, base_filename, xmp_data):
with rawpy.imread(dng_path) as raw:
params = rawpy.Params()
# XMPデータを適用するための処理
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012' in xmp_data:
params.exposure = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012'] else 0.0
# 他のパラメータも同様に設定
rgb = raw.postprocess(params=params)
output_path = os.path.join(output_dir, f"{base_filename}.jpg")
imageio.imwrite(output_path, rgb, quality=95) # 高画質で保存
このコードでは、XMPファイルから取得したメタデータを使用して、RAW画像の現像パラメータを設定しています。
以下に、具体的な処理について詳しく説明します。
Exposure2012の適用
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012' in xmp_data:
params.exposure = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012'] else 0.0
この部分のコードは、XMPファイル内のExposure2012というタグを探し、その値をparams.exposureに設定しています。Exposure2012は、Adobe Camera RawやLightroomで使用される露出補正の値を示します。この値を適用することで、RAW画像の露出を調整します。
具体的には、以下の手順で処理が行われます
1. XMPデータ内に"{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012"というキーが存在するか確認します。
2. 存在する場合、その値を取得し、浮動小数点数に変換してparams.exposureに設定します。
3. 値が存在しない場合は、デフォルト値として0.0を設定します。
まとめ
いちおう、コード自体の指示通りには現像できました。
が、やはり色味がかなり違っています…うーむ…
シャドウとホワイトバランスは明らかにトチッてますね。
他にもいろいろ再検証する必要があるようです。
参考サイト
今回参考にしたサイト集です。
Adobe XMP Specification
:XMPの仕様について詳細に説明しています。
Adobe Experience Manager
:XMPメタデータの概要とエコシステムについて解説しています。
Accusoft Documentation
:XMPメタデータの構造について説明しています。
Post Partner Blog
:XMPファイルをRAW画像に適用する方法についてのステップバイステップガイド。
Damien Symonds Blog
:XMPデータの基本とその役割について解説しています。
コード
import rawpy
import imageio
import json
import os
from lxml import etree
# ファイルパスの設定
dng_file = r"C:\Users\\Documents\.dng"
xmp_file = r"C:\Users\\Documents\.xmp"
output_dir = r"C:\Users\\Documents"
# 1. XMPファイルをJSONに変換
def xmp_to_json(xmp_path):
with open(xmp_path, 'rb') as file:
xmp_data = file.read()
root = etree.fromstring(xmp_data)
xmp_dict = {child.tag: child.text.strip() for child in root.iter() if child.text}
return xmp_dict
# 2. JSONを保存
def save_json(data, output_dir, xmp_filename):
base_filename = os.path.splitext(os.path.basename(xmp_filename))[0]
json_path = os.path.join(output_dir, f"{base_filename}.json")
with open(json_path, 'w', encoding='utf-8') as json_file:
json.dump(data, json_file, ensure_ascii=False, indent=4)
# 3. RAWファイルをJPEGに現像
def convert_raw_to_jpeg(dng_path, output_dir, base_filename, xmp_data):
with rawpy.imread(dng_path) as raw:
params = rawpy.Params()
# XMPデータを適用するための処理
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012' in xmp_data:
params.exposure = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Exposure2012'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Contrast2012' in xmp_data:
params.contrast = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Contrast2012']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Contrast2012'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Highlights2012' in xmp_data:
params.highlight = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Highlights2012']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Highlights2012'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Shadows2012' in xmp_data:
params.shadow = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Shadows2012']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Shadows2012'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Whites2012' in xmp_data:
params.white = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Whites2012']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Whites2012'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Blacks2012' in xmp_data:
params.black = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Blacks2012']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Blacks2012'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Clarity2012' in xmp_data:
params.clarity = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Clarity2012']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Clarity2012'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Vibrance' in xmp_data:
params.vibrance = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Vibrance']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Vibrance'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Saturation' in xmp_data:
params.saturation = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Saturation']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Saturation'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Temperature' in xmp_data:
params.temperature = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Temperature']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Temperature'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}Tint' in xmp_data:
params.tint = float(xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Tint']) if xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}Tint'] else 0.0
if '{http://ns.adobe.com/camera-raw-settings/1.0/}ToneCurvePV2012' in xmp_data:
params.tone_curve = [float(i) for i in xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}ToneCurvePV2012'].split(',') if i]
if '{http://ns.adobe.com/camera-raw-settings/1.0/}ToneCurvePV2012Red' in xmp_data:
params.tone_curve_red = [float(i) for i in xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}ToneCurvePV2012Red'].split(',') if i]
if '{http://ns.adobe.com/camera-raw-settings/1.0/}ToneCurvePV2012Green' in xmp_data:
params.tone_curve_green = [float(i) for i in xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}ToneCurvePV2012Green'].split(',') if i]
if '{http://ns.adobe.com/camera-raw-settings/1.0/}ToneCurvePV2012Blue' in xmp_data:
params.tone_curve_blue = [float(i) for i in xmp_data['{http://ns.adobe.com/camera-raw-settings/1.0/}ToneCurvePV2012Blue'].split(',') if i]
rgb = raw.postprocess(params=params)
output_path = os.path.join(output_dir, f"{base_filename}.jpg")
imageio.imwrite(output_path, rgb, quality=95) # 高画質で保存
# メイン処理
def main():
base_filename = os.path.splitext(os.path.basename(dng_file))[0]
# XMPをJSONに変換
xmp_data = xmp_to_json(xmp_file)
# JSONを保存
save_json(xmp_data, output_dir, xmp_file)
# RAWファイルをJPEGに変換
convert_raw_to_jpeg(dng_file, output_dir, base_filename, xmp_data)
if __name__ == "__main__":
main()