iPhoneで撮った写真をPCに送ったら拡張子が .heic で、「開けない」「相手に送っても表示されない」——地味にあるあるです。HEIC(HEIF)はJPEGより高効率で容量も小さいのですが、ChromeやFirefox、少し前のWindowsではそのままではデコードできないことが多く、結局JPEGに変換したくなります。
この記事では、HEICをJPEGに変換する方法を、ブラウザ(JavaScript)とサーバー/スクリプト(Python)の両方でまとめます。
ゴール:HEIC画像を、環境を選ばず開けるJPEGに変換できるようになること。
なぜ変換にライブラリが要るのか
HEICの中身は HEVC(H.265)で圧縮された画像です。JPEG/PNGと違ってブラウザの <img> や canvas が標準では解釈できない(Safari以外)ため、libheif というデコーダを通す必要があります。JavaScriptなら heic2any(libheifのwasmを内包)、Pythonなら pillow-heif が定番です。
JavaScript:ブラウザだけで変換する(heic2any)
heic2any は libheif を内包しているので、これ1つでデコード〜JPEG出力までできます。
<script src="https://cdn.jsdelivr.net/npm/heic2any@0.0.4/dist/heic2any.min.js"></script>
// file: <input type="file"> で選ばれた HEIC ファイル
async function heicToJpeg(file, quality = 0.9) {
const result = await heic2any({
blob: file,
toType: "image/jpeg",
quality, // 0〜1
});
// 1枚なら Blob、複数画像を含むHEICなら Blob[] が返る
return Array.isArray(result) ? result : [result];
}
あとは受け取ったBlobをダウンロードさせるだけです。
function download(blob, name) {
const url = URL.createObjectURL(blob);
const a = Object.assign(document.createElement("a"), { href: url, download: name });
a.click();
setTimeout(() => URL.revokeObjectURL(url), 1000);
}
document.querySelector("#file").addEventListener("change", async (e) => {
for (const file of e.target.files) {
const base = file.name.replace(/\.hei[cf]$/i, "");
const blobs = await heicToJpeg(file, 0.9);
blobs.forEach((b, i) => download(b, blobs.length > 1 ? `${base}_${i + 1}.jpg` : `${base}.jpg`));
}
});
PNGにしたいときは toType: "image/png"(quality は無視されます)。
Python:スクリプト/サーバーで変換する(pillow-heif)
バッチ処理やサーバー側なら、Pillow に HEIF サポートを足す pillow-heif が手軽です。
pip install pillow pillow-heif
from PIL import Image
import pillow_heif
# これで Pillow が .heic / .heif を開けるようになる
pillow_heif.register_heif_opener()
img = Image.open("photo.heic")
img = img.convert("RGB") # JPEGはアルファ非対応なのでRGBに
img.save("photo.jpg", quality=90)
フォルダ内を一括変換するならこう。
from pathlib import Path
from PIL import Image
import pillow_heif
pillow_heif.register_heif_opener()
for p in Path("photos").glob("*.heic"):
Image.open(p).convert("RGB").save(p.with_suffix(".jpg"), quality=90)
つまずきやすいところ
-
変換後にファイルが大きくなることがある。 HEICはJPEGより高効率なので、同じ画質だとJPEGの方が容量が増えるのはよくあることです(異常ではありません)。サイズを抑えたいなら
qualityを下げます。 - ChromeやFirefoxはHEICをネイティブに開けない。 だからこそ libheif(heic2any / pillow-heif)でデコードします。Safariは開ける場合がありますが、依存すると環境差でハマります。
-
向き(EXIF Orientation)。 変換で回転情報が落ちて画像が横倒しになることがあります。Pythonなら
ImageOps.exif_transpose(img)を通してから保存すると安全です。 - Live Photos などで複数画像を含むHEIC。 heic2any は配列を返すことがあるので、上のコードのように「配列も単体も受ける」形にしておくと安全です。
-
透過・カラープロファイル。 JPEGはアルファを持てないため、Pythonでは
convert("RGB")を忘れずに。
まとめ
- HEICは中身がHEVC圧縮のため、変換には libheif 系のデコーダが要る
- ブラウザだけなら heic2any(
toType: "image/jpeg"+quality) - サーバー/スクリプトなら pillow-heif(
register_heif_opener()→ Pillowでそのまま開く) - サイズ増加・向き・複数画像・アルファの4点だけ気をつければ実用十分
「HEICが開けない・送れない」は変換ひとつで解決できます。参考になれば嬉しいです。