こんにちは、フリーランスエンジニアの太田雅昭です。
問題の概要
Node.jsの画像処理ライブラリ「Sharp」でAVIF画像を読み込んで処理しようとしたところ、エラーが発生しました。メタデータの取得は成功するものの、実際の変換処理でエラーとなる現象です。
エラーの再現
以下のコードで問題を再現できます。
import sharp from "sharp";
const TEST_FILE = "test.avif";
async function test() {
try {
const metadata = await sharp(TEST_FILE).metadata();
console.log("Metadata:", metadata);
// リサイズして別ファイルに出力
await sharp(TEST_FILE).resize(100, 100).toFile("converted.avif");
console.log("変換完了");
} catch (error) {
console.error(error);
}
}
test();
出力結果
メタデータは正常に取得できます。
Metadata: {
format: "heif",
width: 416,
height: 740,
space: "rgb16",
channels: 3,
depth: "ushort",
isProgressive: false,
isPalette: false,
bitsPerSample: 10, // ← 10-bitであることに注目
pages: 1,
pagePrimary: 0,
compression: "av1",
hasProfile: false,
hasAlpha: false,
autoOrient: {
width: 416,
height: 740,
},
}
しかし、変換処理でエラーが発生します。
error: test.avif: bad seek to 13948
test.avif: bad seek to 13921
test.avif: bad seek to 13917
heif: Invalid input: Unspecified: Bitstream not supported by this decoder (2.0)
原因の特定
10-bit AVIFの確認
heif-infoコマンドで画像の詳細を確認します。
% heif-info -d test.avif | grep bit
| | | bits_per_channel: 10,10,10
| | | high_bitdepth: 1
| | | twelve_bit: 0
この結果からも、10-bit depth(ビット深度)のAVIF画像であることが判明しました。
Sharpの制限
Sharpの公式ドキュメントによると、プリビルドバイナリでは8-bit depthのAVIFのみサポートされています。
Prebuilt binaries limit AVIF support to the most common 8-bit depth.
関連issue: https://github.com/lovell/sharp/issues/2688
つまり、プリビルドSharpは10-bit以上のAVIF画像の入力(デコード)に対応していないことが原因でした。
Sharp v0.33.3での10-bit出力対応
2024年3月リリースのv0.33.3から、10-bit/12-bit AVIFの出力には対応しています(PR #4036)。
// v0.33.3以降で使用可能
await sharp(input)
.avif({ bitdepth: 10 }) // または 12
.toFile(output);
ただし、これはあくまで出力のみの対応のようです。またプリビルドバイナリでは8ビットのみサポートされているため、10ビットや12ビットを使用するには—build-from-sourceでソースからビルドする必要があるようです。
解決策
FFmpegやImageMagickは10-bitに対応しているため、それらで変換するか、Sharpから乗り換える必要があります。
まとめ
- Sharp v0.33.3で10-bit AVIF出力には対応したが、入力は未対応
- プリビルドバイナリは8-bitのみ(入力・出力とも)
- 10-bit AVIF画像を読み込むにはFFmpegやImageMagickを使用する
iPhoneが標準で10-bit撮影する時代に、Sharpの入力対応が待たれます。