深掘り

HEIC とは?ファイルシグネチャ、検出、変換の技術的深掘り

koboshiCo-founder
·4 分で読めます
HEIC とは?ファイルシグネチャ、検出、変換の技術的深掘り
要約

HEIC は単なる小さな JPEG ではない。HEVC エンコード画像を内部に封入した ISO Base Media File Format ベースのコンテナ形式だ。その仕組み、生のバイト読み取りによる検出方法、および互換性が失敗した場合の変換方法を解説する。

HEIC ファイルを十六進エディタで開く。先頭の 12 バイト:

00 00 00 18 66 74 79 70 68 65 69 63

これは ISOBMFF —— MP4 が使うのと同じコンテナ標準だ。0x18(24 バイト)は box サイズ、ftyp はファイルタイプ box、heic はメジャーブランド。HEIC ファイルは画像エンコードではない。特定のブランドマーカーを持つコンテナで、内部に HEVC エンコード画像を封入している。

HEIC とは何か

HEIC = High Efficiency Image Container。Apple がブランド化した HEIF の実装で、MPEG によって 2015 年に ISO/IEC 23008-12 として標準化された。JPEG は圧縮アルゴリズムとファイルフォーマットの両方だが、HEIC はコンテナに過ぎない。内部の圧縮は HEVC(H.265)、4K 動画と同じコーデックだ。

1 つの HEIC ファイルは以下を格納できる:

  • 1 枚のプライマリ画像
  • 複数の代替画像(バースト撮影)
  • 画像シーケンス(Live Photo:静止画 + 3 秒動画)
  • アルファチャンネルと深度マップ
  • チャンネルあたり 16 ビット色深度(JPEG は 8 ビットに制限される)

コンテナは box 構造を使う —— MP4 とまったく同じだ:

Box用途
ftypファイルタイプと互換ブランド
metaアイテムメタデータとプロパティ
mdat生のエンコード画像データ(HEVC ビットストリーム)
ilocmdat 内のアイテム位置

この拡張性こそが、HEIC に JPEG では決して不可能なことをさせている。解析するには box 階層を理解する必要があり、生のビットストリームを読むだけでは足りない。

Apple が切り替えた理由

Apple は HEIC を発明していない。MPEG は 2015 年に HEIF を公開し、Apple は 2017 年の iOS 11 で iPhone のデフォルトカメラフォーマットとして採用した。

推進力はマーケティングではなく、算術だった。iPhone の 1200 万画素写真は JPEG で約 3.5 MB、HEIC で約 1.8 MB だ。50 GB の無料 iCloud ティアでは、この差は約 14,000 枚の追加写真を意味する。ストレージティアを販売する Apple にとって、この計算は自明だ。

もう 1 つの要因はエコシステムの整合性だ。Apple はすでに動画で HEVC を採用していた(iOS 11 の H.265)。静止画に同じコーデックを再利用することは、A シリーズチップ上でハードウェアデコードブロックを共有し、消費電力を低下させ、単一のライセンスパスを維持することを意味した。

トレードオフ

HEIC は互換性という 1 つの点を除いて、ほぼすべての面で JPEG を凌駕している。

側面HEICJPEG
圧縮効率同等画質で約 40–50% 小型ベースライン
色深度最大 16 ビット8 ビット
透明度ありなし
1 ファイル複数画像ありなし
非破壊再編集ありなし
Windows ネイティブサポートHEIF 拡張機能が必要普遍的
Web ブラウザサポートSafari のみ普遍的
Android サポート9 以降でネイティブ普遍的
特許ライセンスHEVC 特許プールJPEG はロイヤリティフリー

HEVC は複数の特許プール(MPEG LA、Access Advance)によってカバーされている。Apple は iOS ユーザーに代わってライセンス料を支払っているが、サードパーティベンダーにはその特典はない。この不確実性が、Apple エコシステム外での採用が停滞した大きな理由だ。

ファイルシグネチャ読み取りによる HEIC 検出

.heic 拡張子は信じるな。先頭 32 バイトを読み、ftyp box をパースする。

有効な HEIC ファイルヘッダーの正確なバイトレイアウト:

Bytes 0–3:   Box サイズ(ビッグエンディアン uint32)
Bytes 4–7:   Box タイプ: "ftyp"(0x66 0x74 0x79 0x70)
Bytes 8–11:  メジャーブランド: "heic" または "heif" または "mif1"
Bytes 12–15: マイナーバージョン(通常 0x00000000)
Bytes 16+:   互換ブランドリスト(例: "mif1"、"heic"、"MiHE")

ブラウザ側 TypeScript:

async function isHeic(file: File): Promise<boolean> {
  const buffer = await file.slice(0, 32).arrayBuffer()
  const bytes = new Uint8Array(buffer)

  if (String.fromCharCode(...bytes.slice(4, 8)) !== "ftyp") return false

  const brand = String.fromCharCode(...bytes.slice(8, 12))
  return ["heic", "heif", "mif1", "msf1"].includes(brand)
}

私たちの変換器はデコードを試みる前にこのチェックを実行する。ブランドが違えば即座に拒否される。CPU は無駄にならない。

Python

def is_heic(path: str) -> bool:
    with open(path, "rb") as f:
        header = f.read(32)

    if len(header) < 12:
        return False

    if header[4:8].decode("ascii", errors="ignore") != "ftyp":
        return False

    return header[8:12].decode("ascii", errors="ignore") in {"heic", "heif", "mif1", "msf1"}

高レベルのチェックには filetype

import filetype

kind = filetype.guess("photo.heic")
if kind and kind.extension in ("heic", "heif"):
    print(kind.mime)  # image/heic

または pillow-heif

from pillow_heif import is_heif

if is_heif("photo.heic"):
    # 有効なコンテナ
    pass

Go

func isHeic(path string) bool {
	f, err := os.Open(path)
	if err != nil {
		return false
	}
	defer f.Close()

	buf := make([]byte, 32)
	if _, err := f.Read(buf); err != nil {
		return false
	}

	if string(buf[4:8]) != "ftyp" {
		return false
	}

	brand := string(buf[8:12])
	return brand == "heic" || brand == "heif" || brand == "mif1" || brand == "msf1"
}

PHP

function isHeic(string $path): bool {
    $header = file_get_contents($path, false, null, 0, 32);
    if (strlen($header) < 12) return false;
    if (substr($header, 4, 4) !== 'ftyp') return false;

    return in_array(substr($header, 8, 4), ['heic', 'heif', 'mif1', 'msf1'], true);
}

fileinfo を使用:

$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file('photo.heic');
// image/heic または image/heif

ImageMagick CLI

ImageMagick 7+ は libheif でコンパイルされていれば HEIC をサポートする:

magick identify -verbose photo.heic | grep "Format:"
# Format: HEIC (High Efficiency Image Container)

libheif でコンパイルされていない場合、no decode delegate for this image format で失敗する。ほとんどの Linux ディストリビューションでは、libheif-examples をインストールすると代替として heif-convert が提供される。

変換パス

ファイルが HEIC だからといって開けるとは限らない。Windows には Microsoft Store の HEIF Image Extensions が必要だ。ほとんどのブラウザは <img> タグでの HEIC レンダリングを拒否する。Android 9+ はネイティブに処理するが、古いデバイスはそうではない。

解決策:変換する。私たちの HEIC to JPG コンバーター はブラウザ内でパイプライン全体を処理する:

  1. ファイルをドロップ — 単一写真またはフォルダ全体、バッチ処理は自動的だ。
  2. シグネチャ検証 — 上記のバイトチェックが各ファイルに対して実行される。HEIC でないファイルは明確な不一致メッセージと共に直ちに拒否される。
  3. クライアントサイドデコード — libheif の WebAssembly ビルドがローカルで実行される。アップロードなし。サーバーに触れない。
  4. 品質保持出力 — 90% 品質の JPG は元の解像度と色精度を維持する。
  5. バッチダウンロード — 個別ファイルまたは単一の ZIP アーカイブ。

透明度付きのロスレス出力が必要な場合:HEIC to PNG。Web 最適化サイズが必要な場合:HEIC to WebP

あなたのファイルはデバイスから離れない。それが重要な点だ。

その他のおすすめ記事