WWDC17で新しく発表された画像フォーマットHEIFを使ってみた

# HEIFとは

HEIFとはHigh Efficiency Image File Formatの略でMPEGよって開発された画像フォーマットの標準規格です。ちなみにヒーフと呼ぶ場合が多いようです。iOS11から公式にサポートされ、**JPEG以上の圧縮率**、**深度情報**、**透過処理**、**タイル化**、**アニメーション**などJPEGにはなかった機能や改善点がある新しい画像フォーマットです。JPEGなどとの詳しい差分は[wiki](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format)に載っています。

# H.265/HEVCとは

まずHEIFを語る上で避けて通れない事として、H.265/HEVC(High Efficiency Video Coding)があります。ISO/IECのMPEGとITU-TのVCEGによる研究開発チームJCT-VC によって提案され、ITU-Tは2013年に承認されました。

HEIFはHEVCのコーデックが選択可能（というかこれ以外ありえない？）でHEIFとHEVCの違いはなんとなくでも理解しておきましょう。


[Working with HEIF and HEVC](https://developer.apple.com/videos/play/wwdc2017/511/)によると
HEVCのエンコードは以下の環境でエンコード可能で

||iOS|
|---|---|
|Hardware Encode|A10 Fusion chip（**iPhone7**、**iPhone7 Plus**）|
|Software Encode|無理|


逆にデコードに関しては

||iOS|
|---|---|
|Hardware Decode|A9 chip（**iPhone6s**、**iPhone6s Plus**）以上|
|Software Decode|All iOS Devices|

の端末で読み込む事が可能です。
なんか動かねーぞという場合には以上の内容を参考にしてください。
あっあともちろん**iOS11以上でAPIは使用可能です**。


# 実際にHEIFを作成するには

HEIFを作成するには大まかに２つの方法があって、

1. iPhone7, iPhone7 Plus, macOSのAPIを用いてエンコードする
2. [FFmpeg](https://ffmpeg.org/)と[NokiaのHEIFの実装](https://github.com/nokiatech/heif)を利用する

正直1.は研究段階ならともかく現実的ではないので2.を選択するのが無難だと思います。

FFmpegはHomebrewからインストールできます。

1\. H.265/HEVCのエンコーダーが必要なのでまず以下のコマンドを入力します

```sh
brew install x265
```


2\. FFmpegをオプションをつけてインストールします

```sh
brew uninstall ffmpeg
brew install ffmpeg --with-x265
```



3\. NokiaのHEIFの実装をビルドします

```sh
git clone https://github.com/nokiatech/heif.git
cd heif
cmake .
make
```


4\. FFmpegを用いてJPEGやPNGをHVECのbytestreamに変換します

~~`crf`は画像のQualityなのでスケールの範囲は0から51で、0ではLossless、23がデフォルト、51は最低です。 より小さい値でよりよい画質となります。~~
（追記）どうやらH.264ドキュメントを見ていたようです、@yohhoy さんの通りなので、間違えのないように

```sh
ffmpeg -i ./your-image.png -crf 12 -preset slower -pix_fmt yuv420p -f hevc bitstream.265
```

5\. configファイルを作成します

詳しい内容は[wikiのリンク](https://github.com/nokiatech/heif/wiki/II.-HEIF-Writer-Library-and-Configuration-Examples)で説明してありますので、こちらをどうぞ。HEIFは画像の中にサムネイルの情報を入れることができるのですが、今回は必要ないので除外してます（あってるのかな？）。

```json
{
	"general": {
		"output": {
			"file_path": "output.heic"
		},
		"brands": {
			"major": "mif1",
			"other": ["mif1", "heic", "hevc"]
		}
	},
	"content": [{
		"master": {
			"file_path": "./path-to/bitstream.265",
			"hdlr_type": "pict",
			"code_type": "hvc1",
			"encp_type": "meta"
		}
	}]
}
```

6\. 3でビルドした実行ファイルに5で作成したconfig.jsonを指定して実行します。

```sh
./Bins/writerapp ./path/to/config.json
```


作成したHEIFファイルの中身は[HEIFのブランチ gh-pages](https://github.com/nokiatech/heif/tree/gh-pages)で確認することができます。


# iOSでHEIFを読み込むには

iOSでローカルのHEIFファイルを読み込むには以下のコードを書きます。

```swift
// Read a jpeg image from file

let inputURL = URL(fileURLWithPath: "/tmp/image.heic")

if let source = CGImageSourceCreateWithURL(inputURL as CFURL, nil) {
    let image = CGImageSourceCreateImageAtIndex(source, 0, nil)
    // set image
}
```

ちなみにHEIFファイルがWEB上にある場合には

```swift
let inputURL = URL(string: "https://../image.heic")

// 何かのダウンローダー
downloader.download(url: inputURL) { (data: Data?, error: Error) in
    if let data = data, let source = CGImageSourceCreateWithData(data as CFData, nil) {
        let image = CGImageSourceCreateImageAtIndex(source, 0, nil)
        // set image
    }
}
```


# でっ実際どれくらい圧縮されんのよ

記事の一覧を表示するための192x192のサムネイル画像を30個ほど圧縮してみましたが、JPEGと比べると**15~20%**近く合計で圧縮されていました。
画像サイズはffmpegのQualityのオプションによって上下すると思うので、一概には言えませんが[WebP](https://developers.google.com/speed/webp/)とも遜色ない圧縮率です。

HEIF最高や！！WebPなんて最初っからいらんかったんや！！

実験はiPhone 7Plusで行いましたが、デコード時間もハードウェアアクセラレーションを使っているおかげもあるのか、違和感なく表示されていました。

# 最後に

今回調べる上で技術的なドキュメントやWebの資料が少ないので、多少苦労しました。とくにHEVCなどのコーデック周りは専門家がいたほうがやりやすいと思います。何か情報クレ...クレメンス...。

自分たちが作っているアプリの要件やサポートOSや運用なども含めて、今後導入するかどうかを検討するといいと思います。

(追記)HEVC及びHEIFのエンコーダには特許の問題がありffmpegなどを使用した実用化は難しいです。現状iPhoneでしかHEIFのエンコードが不可能なので、サーバサイドでの作成は将来に期待しましょう。