あいんずのBLOG

スマホ写真をWebPに変換するときの注意点(EXIF回転とPIL vs ffmpeg)

2026-06-06

ブログに載せるスマホ写真をまとめてWebPに変換した際の知見をメモしておきます。特に「EXIF回転情報の扱い」でハマりやすいポイントがあったので、その対処と、変換ツール(Pillow / ffmpeg / cwebp)の画質差についてまとめます。

なぜWebPに変換するのか

スマホで撮ったJPEG写真は1枚あたり数MBになることも珍しくありません。WebPに変換すると、見た目の劣化をほとんど感じさせずにファイルサイズを大きく削減できます。今回は50枚の写真を quality=85 でWebP化し、合計サイズを大幅に圧縮できました。

ページの表示速度はそのまま体験の質に直結するので、画像の多い記事ほどWebP化の効果は大きくなります。

一番ハマるポイント:EXIF回転情報

スマホの写真には EXIF orientation(回転情報) というメタデータが埋め込まれています。これは「センサーが取り込んだ生のピクセルデータ」と「実際に表示すべき向き」がずれているときに、その差を吸収するための情報です。

たとえば、ある写真は次のような状態でした。

項目
生のピクセル寸法2256 × 4000(縦長)
EXIF orientation8(反時計回りに90°回転して表示)
実際に表示される向き4000 × 2256(横長

つまり「ファイルの中身は縦長だが、表示は横長」という写真が混ざっているわけです。ブラウザはEXIF情報を読んで自動的に回転して表示してくれるので、普段は意識しません。

ところが、単純にピクセルデータだけをWebPに変換してEXIFを捨てると、回転情報が失われて写真が横倒しになってしまうのです。これがWebP変換で最もハマりやすい落とし穴です。

解決策:回転をピクセルに「焼き込む」

対処は明確で、変換前にEXIF回転を実際のピクセルに適用(回転を焼き込み)してしまえば、その後はEXIF情報が無くても正しい向きで表示されます。Pillowなら ImageOps.exif_transpose() 一発で、画像ごとの向きを自動で判定して適用してくれます。

from PIL import Image, ImageOps
import glob, os

for f in glob.glob('*.jpg'):
    im = Image.open(f)
    im = ImageOps.exif_transpose(im)   # EXIF回転をピクセルに焼き込み、フラグを除去
    out = os.path.splitext(f)[0] + '.webp'
    im.save(out, 'webp', quality=85, method=6)
    os.remove(f)

ポイントは以下の通りです。

Pillow vs ffmpeg vs cwebp の画質差

「ffmpegで変換した方が画質が良いのでは?」と思うかもしれませんが、結論から言うと 同じ設定なら画質はほぼ同等 です。

理由はシンプルで、Pillow も ffmpeg も cwebp も、WebPエンコードには内部で同じ libwebp(Google公式ライブラリ)を使っている からです。エンコーダが同じなので、同じ品質設定なら出力に本質的な差は生まれません。

観点Pillowffmpegcwebp(公式CLI)
エンコーダlibwebplibwebplibwebp
同設定での画質同等同等同等
EXIF回転exif_transpose で画像ごとに自動自動補正なし(向きごとに手動でtranspose)同左、要手動
細かい調整quality / method / losslessquality / compression_level最も豊富(-sharp_yuv 等)

実務上のポイントは次の2つです。

# cwebpで画質重視に変換する場合の例
cwebp -q 85 -m 6 -sharp_yuv input.jpg -o output.webp

なお、元がJPEG(すでに非可逆圧縮済み)の場合は、どのツールを使っても「JPEGの劣化を引き継ぐ」点は共通です。元画像以上の画質にはならないので、quality=85 程度で十分というのが実感です。

まとめ