ponhouse’s blog

写真とMacのブログ

Lightroom Classicで角度の自動補正を一括でかけたい

Lightroom Classic(以下Lr)には写真が傾いていても角度を自動補正する機能があります。

今回はそれを使わず、EXIFを参照して一括で垂直を出したい!という内容。

しかし…

検索で来た人が長々とこのブログを読んでがっかりすることのない様、最初に結論を書きますと、このプロジェクトは失敗というか未完というか、一括して角度自動補正をかけることはできませんでした!

できました!↓

ponhouse.hatenablog.com

 

Lrのこの機能は一見便利に思えますが、どんなアルゴリズムで補正値を出しているのかよくわからず、時々大外れな補正をしてくれちゃいます。

それだけならまだ便利に使えるのですが、このツール、写真一枚一枚に個別にかけるしか出来ません。たくさんある場合でも、選択して一括で自動補正することができないのです。

 

最近のカメラにはEXIFにカメラの傾きを記録している物もあるので、そのEXIF情報を元にLrのxmpファイルを書き換えて反映させたら出来るのではないかとコードを書いていたのですが…角度補正をすると当たり前ですがトリミングされますよね?

そのトリミングが最小限になる比率を測っているのでしょうか?その比率が元画像の比率ではなく、「カスタム」としてよくわからない比率にされちゃいます。

そこで、Lr上で複数選択後、自動同期で角度を自動補正しようとしても、個々に自動補正をかけるわけでもなく、最初に選択した写真の角度がそのまま同期されるだけで…使えない。

上手くできたらプラグインにして簡単に補正できる様にしたいと思っていたのですが、プラグインにしても多分この仕様は同じかと思い、ドキュメントも見ていません。

もったいないので今回分かった事だけまとめようと思います。

xmpに記述されるクロップとその角度は以下の様な関係になっていて、0〜1の範囲でエッジからの距離が記述されます。

ピッタリついている状態(トリミングなし)が1です。

肝心の角度はcrs:CropAngleに記載され、crs:CropAngle="-1.14"の様に小数点以下2桁で記述されます。

Camera Raw namespace

https://developer.adobe.com/xmp/docs/XMPNamespaces/crs/

 

 

EXIFはExifToolを使用し、カメラにセンサーがあれば Roll Angle というタグに左右の傾きが記述されています。

これとは別にPitch Angle(前傾後傾)やYaw Angle(振り?)がありますが、写真の傾きに使うのはRollだけです。そしてYawは基準がよくわからないです…。

これらの他に、カメラの横構図、縦構図を記述する Orientation というタグもあり、鏡像含め0から8までの数値で表されますが、わかりにくいので他の書き方で簡単に書くと、ニコンの場合 Orientation:Horizontal (normal) で横構図。Rotate 90 CW でシャッターボタン上の縦構図。Rotate 270 CW でシャッターボタン下の縦構図となります。

この情報はたくさんありますので、詳しく知りたい方は調べて見てください。

 

EXIFから傾きを取得→Roll Angleに割り当てまではできたのですが、それだけだとLr側でトリミングしてしまうので、各Crop〜にも数値を入れて、元画像のアスペクト比を維持しなければなりません。

これがなかなか複雑で、いまいちよくわからない挙動をするので諦めました。

残念な結果になってしまいましたが、Adobeさんには是非一括自動角度補正を実装してもらいたいです。

できればoption押しなどでEXIFのデータを参照にした自動補正など含めて…。

 

以下は全コードではないですが、PythonでのExifTool→補正数値計算までのコードです。

環境依存の部分もあるので丸々コピペとはいきませんが、参考になりましたら…と言っても作ったのGPTさんですが。

 

 

コードを表示

def get_exif_data(image_path):
"""
ExifToolを使用して画像ファイルのOrientationタグとRoll Angleタグの値を取得します。

Parameters:
image_path (str): 画像ファイルのパス。

Returns:
dict: OrientationとRoll Angleの値を含む辞書。取得できない場合はデフォルト値を使用。
"""
exif_data = {'Orientation': '1', 'RollAngle': 0.0} # デフォルト値

try:
# ExifToolを使用してOrientationとRoll Angleタグを取得
logging.debug(f"ExifToolのパス: {EXIFTOOL_PATH}")
logging.debug(f"実行コマンド: {EXIFTOOL_PATH} -Orientation -RollAngle {image_path}")
result = subprocess.run(
[EXIFTOOL_PATH, '-Orientation', '-RollAngle', image_path],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
if result.returncode != 0:
logging.error(f"ExifToolの実行に失敗しました: {result.stderr.strip()} ({image_path})")
return exif_data # デフォルト値を返す

# ExifToolの出力をログに記録
logging.debug(f"ExifTool出力:\n{result.stdout.strip()}")

# 出力例:
# Orientation : Horizontal (normal)
# Roll Angle : 2.5

output = result.stdout.strip()
if output:
for line in output.split('\n'):
parts = line.split(':', 1)
if len(parts) == 2:
key = parts[0].strip()
value = parts[1].strip()
if key == 'Orientation':
orientation_description = value.split(' ')[0] # "Horizontal"など
orientation_mapping = {
'Horizontal': '1',
'Rotate': '3', # "Rotate 180", "Rotate 90 CW", "Rotate 270 CW"の場合は後述
}
# 詳細なマッピング
if 'Rotate 180' in value:
exif_data['Orientation'] = '3'
elif 'Rotate 90 CW' in value:
exif_data['Orientation'] = '6'
elif 'Rotate 270 CW' in value:
exif_data['Orientation'] = '8'
else:
exif_data['Orientation'] = orientation_mapping.get(orientation_description, '1') # デフォルトは'1'
elif key == 'Roll Angle':
try:
exif_data['RollAngle'] = float(value)
except ValueError:
logging.warning(f"RollAngleの値が数値ではありません: {value} ({image_path})")
except FileNotFoundError:
logging.error(f"ExifToolが見つかりません。ExifToolがインストールされていることを確認してください。 ({image_path})")
except Exception as e:
logging.error(f"EXIFデータの取得中にエラーが発生しました: {e} ({image_path})")
return exif_data

def calculate_crop_angle(orientation, roll_angle):
"""
OrientationおよびRoll Angle値に基づいてCropAngleを計算します。

Parameters:
orientation (str): Orientationの数値値('1', '3', '6', '8')。
roll_angle (float): Roll Angleの値(度単位)。

Returns:
float: 計算されたCropAngleの値。
"""
# Orientationに基づく基本的なCropAngle
orientation_mapping = {
'1': 0.0,
'3': 180.0,
'6': 90.0,
'8': 270.0
}
base_angle = orientation_mapping.get(orientation, 0.0) # デフォルトは0.0

# Roll Angleを追加
crop_angle = base_angle + roll_angle

# 360度を超える場合は調整
crop_angle = crop_angle % 360.0

return crop_angle