小さな受託案件で、クライアントから iPhone の写真をまとめて受け取り、サイトや管理画面に載せる形に整えて返す、という作業はよくあります。届いたファイルが HEIC だった場合、とりあえず JPG に一括変換して納品しがちですが、それだけでは「開けない」問題しか片付きません。サンプルで確認したところ、縦向きの写真が相手の環境で横倒しになる可能性と、JPG にしたらファイルが元より 38% 大きくなるケースがありました。この記事では、納品前に確認している 3 項目を、確認方法と数値つきでまとめます。
検証に使ったサンプル
iPhone の実機で撮った元ファイルではありません。Wikimedia Commons で CC0 として公開されている写真(2015 年に iPhone 6 で撮影された空港の駐機場、3264×2448)を、macOS 標準のエンコーダで HEIC に変換したものです。これとは別に、EXIF の Orientation を 6 にした縦撮り相当のファイルと、4032×3024 に拡大したファイルを用意しました。HDR のゲインマップや Live Photos は実機の元ファイルが手元にないため、今回は扱っていません。数値はすべてこのサンプルでの結果です。
チェック 1:納品先は HEIC を扱えるか
最初に、HEIC のまま渡せるかを確認しました。Playwright 同梱のブラウザエンジン(macOS 上)で img 要素に HEIC を読み込ませると、Chromium 149 と Firefox 151 は onerror になり、createImageBitmap も InvalidStateError でした。WebKit 26.5 は正しい向きで表示できましたが、macOS のシステムデコーダを使っているためで、Safari 全般や Windows、スマートフォンでの挙動は確認していません。Windows で .heic を開くには、Microsoft の案内によると「HEIF 画像拡張機能」と「HEVC ビデオ拡張機能」の両方が必要で、HEVC のほうは有料の場合があります。サーバー側では、pillow-heif を入れていない Pillow 11.3 が UnidentifiedImageError を返し、ffmpeg 7.1 の既定のコマンドでは 512×512 の画像が 1 枚出力されただけでした。これはタイル状に分割された画像の 1 ブロック分です。どちらもプラグインや引数を整えれば HEIC を扱えますが、納品先の環境をこちらで整えることはできません。そのため、相手の環境が一か所でも不明なら HEIC では渡さない、と決めています。
チェック 2:縦撮りの写真が納品先でも縦のままか
縦撮りの写真は、ピクセル自体を回転して保存する方法と、ピクセルは横長のまま EXIF の Orientation に「表示時に 90 度回す」と書く方法の 2 通りで表現されます。macOS の sips は後者で、縦撮りサンプルを sips -s format jpeg x.heic --out out/ で変換すると、出力は 3264×2448 の横長ピクセルで、Orientation は 6 のままでした。Mac のプレビューはこの値を読むので縦に見えます。ところが EXIF を読まないサムネイル生成処理や、幅と高さでトリミング方法を決める処理を通ると横向きになります。Pillow でこのファイルを開くと size は (3264, 2448) で、回転の適用は呼び出し側に任されます。
比較として、ブラウザ上で動く imging(図映)で同じファイルを変換すると、出力は 2448×3264 でピクセルが回転済み、Orientation は 1 でした。正しい向きの sips 版と比べた画素ごとの平均差は 0.7〜1.5 で、二重に回転されてはいません。結果欄に出力サイズが表示されるので、確認の手間はほとんどかかりません。
確認方法としては、縦撮りの写真を 1 枚選び、ピクセルの幅と高さ、Orientation の値を見ます。幅のほうが大きく Orientation が 6 なら、回転がタグ任せになっているファイルです。いちばん確実なのは、納品先が実際に使う管理画面に 1 枚アップロードして表示を見ることです。自分の Mac で開いて確認しても、この問題は見つかりません。
チェック 3:ファイルサイズがどう変わったか
HEIC を JPG にすればサイズはだいたい同じか小さくなる、と思い込んでいましたが、そうではありませんでした。縦撮りサンプル 745.1 KB を imging の既定値(この写真では品質 88 が選ばれました)で JPG にすると 1.01 MB、38% 増です。横撮りでも 744.9 KB から 1019.2 KB と 37% 増でした。4032×3024 のサンプルは 840.1 KB が 1.32 MB(61% 増)になり、品質 80 に下げても 982.9 KB と 17% 大きいままでした。
3264×2448 の縦撮りサンプルでは、JPG 品質 80 が 742.2 KB で元とほぼ同じ、WebP(既定の品質 84)が 423.2 KB で 43% 減、可逆の PNG が 8.64 MB で約 11.9 倍でした。HEIC はもともと JPEG より圧縮効率が高いので、同じ見た目を JPEG で保とうとするとバイト数が増えます。そこで形式は用途で決めています。サイトやミニアプリの表示用で、相手側が WebP を配信できるなら WebP にします。JPG を指定された場合や、印刷・再編集に使う場合は JPG にして、品質は既定値ではなく 80 前後から試します。PNG は可逆を明示的に求められたときだけです。
手順と補足
実際の流れは、枚数が多ければまず sips で一括変換し、縦撮りのものだけ imging で変換し直す形にしています。imging は HEIC を読む前にデコーダを読み込む操作が 1 回必要で、デコードはブラウザ内で行われます。アップロード直後は WebP が選ばれているので、JPG は手動で切り替えます。ファイルは 1 回に 1 枚ずつです。
クライアントには、iPhone の「設定 › カメラ › フォーマット」を「互換性優先」にすると以降の撮影が JPEG になる、と伝えておくと次回以降は楽になります。ただし変わるのは設定後に撮る写真だけで、すでにライブラリにある写真は HEIC のままなので、今回受け取った分は変換が必要です。
使ったツール:https://imging.ai/

