ImageMagick と HEIF 画像

# HEIF / HEIC

HEIF は主に H.265/HEVC のエンコーダを使って画像をファイル化します。
ただ、HEIF 自体は H.264/AVC や JPEG 等も入れられる汎用画像コンテナフォーマット規格です。
そこで H.265/HEVC の画像が含まれる事を示すのに HEIC の呼称があり、拡張子によく使われます。ImageMagick における HEIF のコーダー名は HEIC です。

# ImageMagick の HEIF 対応

2018年5月リリースの ImageMagick-7.0.7-32 (6系は 6.9.9-44) で HEIF 画像に対応しました。
実はその前に、2018年1月の ImageMagick-7.0.7-22 で HEIF 対応したのですが、色々と問題があり、その実装はなかった事にして、libheif の利用を前提にいちから作り直してます。(詳しくは後述)

# HEIF 対応か調べる

ImageMagick で HEIF を扱うには、libheif が入っている環境でビルドする必要があります。手元の ImageMagick が対応しているかは以下のコマンドで分かります。

```shell:
% convert -list format | grep HEIC
     AVIF* HEIC      rw+   AV1 Image File Format (1.12.0)
     HEIC* HEIC      rw+   High Efficiency Image Format (1.12.0)
     HEIF* HEIC      rw+   High Efficiency Image Format (1.12.0)
% convert -list coder | grep HEIC
AVIF            HEIC
HEIF            HEIC
```

ビルドする場合、configure 実行時に以下のように認識されれば HEIF 入出力機能を組み込めます。

```shell:
 ./configure
(略)
checking for libheif... yes
(略)
  HEIC              --with-heic=yes		yes
```

# 使い方

特に他のファイル形式と変わりません。

```shell:
% identify IMG_0001.HEIC  # ← iPhone カメラで撮った写真
IMG_0001.HEIC HEIC 806x605 806x605+0+0 8-bit sRGB 0.000u 0:00.001
% convert IMG_0001.HEIC 0001.jpg
% convert rose: rose.heic
```

## Lossless

ちなみに、ImageMagick は -quality 100 で Lossless (劣化無し) だと HEIF コーデックに指示を出します。

```c:coders/heic.c
    lossless=image_info->quality == 100 ? MagickTrue : MagickFalse;
    chroma=lossless ? heif_chroma_444 : heif_chroma_420;
(略)
    if (lossless != MagickFalse)
      error=heif_encoder_set_lossless(heif_encoder,1);
```

試してみると。。。バグってますね。_(┐「ε:)_  (ImageMagick 7.1.0-2, libheif-1.12.0)

```
% convert lena_std.png  -quality 100  lena_im.heic
% convert lena_im.heic im.png
```

| lena_std.png| im.png|
|---|---|
|![lena_std.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1798/64987a65-f6da-e490-38de-e2b14142dd11.png) | ![im.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1798/8e824c7b-8547-084e-9bb2-75cd6e96b93f.png)|

生成された HEIF コンテナを覗くと YUV420 ですし、libheif の使い方を間違っていそうです。

# 実装

libheif を使って HEIF の入出力をします。libheif はコンテナを扱うライブラリで、実画像データの H.265 エンコード/デコードは x265/libde265 を使います。

なお、libheif の AVIF 対応に便乗して ImageMagick も AVIF に対応しました。HEIF/AVIF どちらの実装も coders/heic.c に含まれます。

- https://github.com/ImageMagick/ImageMagick/blob/main/coders/heic.c

# オプション指定

## heic:preserve-orientation

true で回転方向を維持します。これに false を入れると exif:Orientation が 1 に上書きされます。

## heic:depth-image

これを true にすると Depthデータを複数画像の１つとして取り出せるはずです。(手元にデータがないので試せない)

## heic:chroma

libheif 越しに aom/rav1e, x265 といったエンコーダに渡す(はずの)パラメータです。

libheif/heif_encoder_x265.cc を見ると "420", "422", "444" の３つから選べるはずですが、実際に試すと全部 420 として HEIF 変換されました。これ以外の文字列を渡してもエラーにならないので、おそらく libheif に渡してなさそうです。

なお、AVIF では指定した chroma subsampling で変換されました。libaom にはちゃんと渡ってます。

## heic:speed

speed は AVIF で有効なオプションで、AV1 エンコーダの aom もしくは rav1e に cpu_used として値を渡します。

- aomencのオプション
   - https://ku6.jp/report/92.html

こちらの記事によると --cpu-used で 1〜8 を指定できて、1 が画質優先、8が処理速度優先でトレードオフになるようです。


## heic:chroma-downsampling (2024-02-14 追加機能)

クロマサブサンプリングの方式を選択できます。libheif 1.16 以降が必要です。

- nearest-neighbor
- average
- sharp-yuv

YUV444 を YUV420 に落とすと彩度が落ちがちですが、なるべく残してくれる WebP でお馴染みの sharp-yuv 機能が使えます。

# 備考

## ライセンス

HEIF/HEVC は画像データをエンコードするのに H.265/HEVC エンコーダを利用します。
何かのサービスで H.265/HEVC エンコーダを使いたい場合、そのエンコーダ自体のライセンスもそうですが、それとは別に、H.265/HEVC を使うライセンスも意識する必要があります。

以下のサイトを参考にどうぞ。

- H.265/HEVC特許暗黒時代
  - https://qiita.com/yohhoy/items/c2579097a507b1fbdddb

H.265/HEVC の特許ライセンスを持つ会社が沢山あり、ライセンスプールに全てまとめようとしたが、納得いかない会社が各々ライセンスプールを独自に作って複数存在するので、各々交渉するのが大変そうです。

## iloc offset 問題

iloc の baseOffset, extendOffset の使い方の問題で、ImageMagick (というより libheif) で作った HEIF ファイルが読めないアプリケーションがたまにあります。古いバージョンの LightRoom とか。

- https://github.com/ImageMagick/ImageMagick/issues/1790
- https://feedback-readonly.photoshop.com/conversations/camera-raw-and-dng/camera-rawlightroom-support-additional-flavors-of-heic/5f5f46234b561a3d42758957

トラブルがあった時には思い出すと良いかもしれません。

# 当初のゴタゴタ

ImageMagick は 2017年に立候補する人が現れ、2018年1月に HEIC 実装が入りました。当時は libde265 のみ依存して、コンテナ処理を全部自前で実装していました。あと読み込みonlyです。

- ImageMagick-6.9.9-34差分
  - https://yoya.hatenadiary.jp/entry/20180123/im

コンテナ構造を決めうち処理して iPhone が出力する HEIC のみ動作する、あまり融通の効かない実装のまま改善する気配がなかったのと、libheif が世に出た事により、それまでの実装を全部差し替えて、ImageMagick のコア開発者が libheif を使った実装で1から作り直し 2018年5月にリリースされてます。

- ImageMagick-6.9.9-44差分
  - https://yoya.hatenadiary.jp/entry/20180514/im

また libheif は途中から書き出しにも対応したので、ImageMagick もそれに合わせて HEIF 書き出しが可能になりました。更に、この後、libheif が AVIF にも対応したので、少ない手間での AVIF 対応が可能になっています。
HEIF と AVIF どちらも ISOBMFF コンテナで情報要素が似ているので、もしかしたらという期待はありましたが、大変ラッキーです。

そんな訳で、2018年前半の HEIF 対応と 2018年中旬以降の HEIF 対応は全く別物です。

- https://github.com/ImageMagick/ImageMagick/issues/507
- https://github.com/ImageMagick/ImageMagick/issues/997

# 参考 URL

- https://pwiki.awm.jp/~yoya/?HEIF