はじめに
HEIF(HEIC)形式のファイルについて、読み書きの処理を実装しました。
その際のまとめ記事になります。
-
libheifのビルド - HEIF(HEIC)ファイルの作成
- HEIF(HEIC)ファイルの読込
HEIF(HEIC)とは
HEIF(HEIC)とは、High Efficiency Image File Formatのことで、Moving Picture Experts Group (MPEG) によって開発された画像ファイルフォーマットです。(Wikipediaより)
同程度の画質のJPEG形式と比べて、ファイルサイズを半分ほどに抑えられるとのことです。
画像形式の対応状況(英語)によると、macOS SierraやiOS 11、ブラウザではSafariが対応しているようです。
開発環境
WindowsでHEIFファイルを取り扱うには、HEIF 画像拡張機能(無料)、HEVC ビデオ拡張機能(有料)が必要のようです。
有料の拡張機能を使わずに、D言語でHEIFファイルの読み書き処理を実装しました。
- Windows 10 (バージョン 2004)
- Git (version 2.24.1.windows.2)
- CMake (version 3.15.19101501-MSVC_2)
- Visual C++ ビルドツール 2019 → インストール手順
- DMD v2.096.0
- libheif (release 1.12.0)
- libde265 (v1.0.8)
- libx265 (releasetag 3.4)
libheifのダウンロード
今回Windows10向けにビルドしたライブラリとDLLファイルをGitHubに公開しました。
libheif_v1.12.0.zip
ビルドしたい方のために、以下にビルド方法を書きます。
libheifのビルド
libheifからソースコードを入手できます。
Windows版であれば、Vcpkgから入手できるとのことで、試してみました。
しかし、途中でVisual Studio英語版のインストールが必要とのエラーが出たため、中断して、cmakeから作成しました。
libheifをビルドするには、libde265、libx265が必要なため、先にこれらをビルドします。
VS2019用 x64 Native Toolsコマンドプロンプトを起動します。
libde265のビルド
まずは、libde265です。
以下の順序でコマンドを実行します。
d:\Dev> mkdir heif
d:\Dev> cd heif
d:\Dev\heif> git clone https://github.com/strukturag/libde265
d:\Dev\heif> cd libde265
d:\Dev\heif\libde265> cmake .
d:\Dev\heif\libde265> msbuild ALL_BUILD.vcxproj -p:Configuration=Release
d:\Dev\heif\libde265> cd ..
libx265のビルド
次に、libx265です。
以下の順序でコマンドを実行します。
d:\Dev\heif> git clone https://github.com/videolan/x265
d:\Dev\heif> cd x265\source
d:\Dev\heif\x265\source> cmake .
d:\Dev\heif\x265\source> msbuild ALL_BUILD.vcxproj -p:Configuration=Release
d:\Dev\heif\x265\source> cd ../..
libde265、libx265のビルドが終わったところで、libheifです。
以下の順序でコマンドを実行します。
d:\Dev\heif> git clone https://github.com/strukturag/libheif
d:\Dev\heif> cd libheif
d:\Dev\heif\libheif> cmake -D LIBDE265_INCLUDE_DIR=D:\Dev\heif\libde265 -D LIBDE265_LIBRARY=D:\Dev\heif\libde265\libde265\Release\libde265.lib -D X265_INCLUDE_DIR=D:\Dev\heif\x265\source -D X265_LIBRARY=D:\Dev\heif\x265\source\Release\libx265.lib .
d:\Dev\heif\libheif> msbuild ALL_BUILD.vcxproj -p:Configuration=Release
d:\Dev\heif\libheif> cd ..
lib、DLLファイルの配置
ビルドに成功したら、D:\Dev\heif\libheif\libheif\Releaseにheif.libが作成されます。
heif.libをD:\Dev\heif\libに、各DLLファイルをD:\Dev\heifにコピーします。
d:\Dev\heif> copy D:\Dev\heif\libheif\libheif\Release\heif.lib lib
d:\Dev\heif> copy D:\Dev\heif\libheif\libheif\Release\heif.dll .
d:\Dev\heif> copy D:\Dev\heif\libde265\libde265\Release\libde265.dll .
d:\Dev\heif> copy D:\Dev\heif\x265\source\Release\libx265.dll .
HEIF(HEIC)ファイルの作成
JPEGやPNGファイル等FreeImageで読み込み可能な画像形式ファイルから、HEIFファイルを作成するための実装例です。
ソースコード
/+ dub.sdl:
name "encode_sample"
dependency "bindbc-freeimage" version=">=0.3.0"
versions "FI_318"
dependency "libheif-d" repository="git+https://github.com/devmynote/libheif-d.git" version="92392885c81d9e3f5240acf82a8871b441ec9ab9"
+/
// dub build --build=release --single encode1.d
import std.conv;
import std.stdio;
import std.string;
import std.windows.charset;
import bindbc.freeimage;
import heif.heif;
pragma(lib, "lib/heif.lib");
void main(string[] args)
{
// Argument check
if ( args.length < 3 ){
writeln("Argument required : input output");
return;
}
string ifile = args[1].toMBSz.to!string;
string ofile = args[2].toMBSz.to!string;
// Load FreeImage
FISupport fis = loadFreeImage();
if ( fis != fiSupport ){
if ( fis == FISupport.noLibrary ){
writeln("FISupport.noLibrary");
} else if ( FISupport.badLibrary ){
writeln("FISupport.badLibrary");
}
return;
}
// Load Image
FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(ifile.toStringz);
FIBITMAP *dib = FreeImage_Load(fif, ifile.toStringz);
scope(exit) FreeImage_Unload(dib);
if ( dib == null ){
writeln("FreeImage_Load error");
return;
}
if ( FreeImage_GetBPP(dib) != 32 ){
FIBITMAP *dibold = dib;
dib = FreeImage_ConvertTo32Bits(dibold);
FreeImage_Unload(dibold);
}
int width = FreeImage_GetWidth(dib);
int height = FreeImage_GetHeight(dib);
ubyte* bits = FreeImage_GetBits(dib);
// Encode HEIF Image
heif_image* img;
heif_image_create(width, height, heif_colorspace.heif_colorspace_RGB, heif_chroma.heif_chroma_interleaved_RGBA, &img);
scope(exit) heif_image_release(img);
heif_image_add_plane(img, heif_channel.heif_channel_interleaved, width, height, 8);
int stride;
ubyte* data = heif_image_get_plane(img, heif_channel.heif_channel_interleaved, &stride);
for ( int y = 0; y < height; y++ ){
for ( int x = 0; x < width; x++ ){
int ind1 = x * 4 + y * stride;
int ind2 = x * 4 + (height - 1 - y) * width * 4;
*(data + ind1 ) = *(bits + ind2 + 2); // R
*(data + ind1 + 1) = *(bits + ind2 + 1); // G
*(data + ind1 + 2) = *(bits + ind2 ); // B
*(data + ind1 + 3) = *(bits + ind2 + 3); // A
}
}
// Get the default encoder
heif_context* ctx = heif_context_alloc();
scope(exit) heif_context_free(ctx);
heif_encoder* encoder;
heif_context_get_encoder_for_format(ctx, heif_compression_format.heif_compression_HEVC, &encoder);
scope(exit) heif_encoder_release(encoder);
// Set the encoder parameters
heif_encoder_set_lossy_quality(encoder, 100);
// Encode the image
heif_context_encode_image(ctx, img, encoder, null, null);
heif_context_write_to_file(ctx, ofile.toStringz);
}
コンパイル、実行
実行時にはFreeImage.dllが必要です。
ダウンロードページにあるDownload FreeImage 3.18.0 [WIN32/WIN64]から入手します。
できあがったHEIFファイルは、libheif decoder demoのサイトで内容確認できます。
D:\Dev\heif> dub build --build=release --single encode1.d
Performing "release" build using D:\Dev\dmd2\windows\bin64\dmd.exe for x86_64.
bindbc-loader 0.3.2: target for configuration "noBC" is up to date.
bindbc-freeimage 0.5.0: target for configuration "dynamic" is up to date.
encode_sample ~master: building configuration "application"...
Linking...
To force a rebuild of up-to-date targets, run again with --force.
D:\Dev\heif> encode_sample.exe abc.png abc.heic
HEIF(HEIC)ファイルの読み込み
HEIFファイルから、PNGファイルを作成するための実装例です。
ソースコード
/+ dub.sdl:
name "decode_sample"
dependency "bindbc-freeimage" version=">=0.3.0"
versions "FI_318"
dependency "libheif-d" repository="git+https://github.com/devmynote/libheif-d.git" version="92392885c81d9e3f5240acf82a8871b441ec9ab9"
+/
// dub build --build=release --single decode1.d
import std.conv;
import std.stdio;
import std.string;
import std.windows.charset;
import bindbc.freeimage;
import heif.heif;
pragma(lib, "lib/heif.lib");
void main(string[] args)
{
// Argument check
if ( args.length < 3 ){
writeln("Argument required : input output");
return;
}
string ifile = args[1].toMBSz.to!string;
string ofile = args[2].toMBSz.to!string;
// Load FreeImage
FISupport fis = loadFreeImage();
if ( fis != fiSupport ){
if ( fis == FISupport.noLibrary ){
writeln("FISupport.noLibrary");
} else if ( FISupport.badLibrary ){
writeln("FISupport.badLibrary");
}
return;
}
// Decode HEIF Image
heif_context* ctx = heif_context_alloc();
scope(exit) heif_context_free(ctx);
heif_context_read_from_file(ctx, ifile.toStringz, null);
int num = heif_context_get_number_of_top_level_images(ctx);
heif_item_id[] id = new heif_item_id[num];
heif_context_get_list_of_top_level_image_IDs(ctx, id.ptr, num);
for ( int i = 0; i < num; i++ ){
heif_image_handle* handle;
heif_context_get_image_handle(ctx, id[i], &handle);
scope(exit) heif_image_handle_release(handle);
int width = heif_image_handle_get_width(handle);
int height = heif_image_handle_get_height(handle);
heif_image* img;
heif_decode_image(handle, &img, heif_colorspace.heif_colorspace_RGB, heif_chroma.heif_chroma_interleaved_RGBA, null);
scope(exit) heif_image_release(img);
int stride;
const ubyte* data = heif_image_get_plane_readonly(img, heif_channel.heif_channel_interleaved, &stride);
FIBITMAP* dib = FreeImage_Allocate(width, height, 32);
scope(exit) FreeImage_Unload(dib);
ubyte* bits = FreeImage_GetBits(dib);
for ( int y = 0; y < height; y++ ){
for ( int x = 0; x < width; x++ ){
int ind1 = x * 4 + (height - 1 - y) * width * 4 ;
int ind2 = x * 4 + y * stride;
*(bits + ind1 ) = *(data + ind2 + 2); // B
*(bits + ind1 + 1) = *(data + ind2 + 1); // G
*(bits + ind1 + 2) = *(data + ind2 ); // R
*(bits + ind1 + 3) = *(data + ind2 + 3); // A
}
}
FreeImage_Save(FIF_PNG, dib, (ofile ~ i.to!string ~ ".png").toStringz);
}
}
コンパイル、実行
HEIF(HEIC)ファイルは、複数の画像データを格納可能です。
example.heicにも2つの画像データが含まれます。
その他、nokiatechのgithubにも画像サンプルがあります。
D:\Dev\heif> dub build --build=release --single decode1.d
Performing "release" build using D:\Dev\dmd2\windows\bin64\dmd.exe for x86_64.
bindbc-loader 0.3.2: target for configuration "noBC" is up to date.
bindbc-freeimage 0.5.0: target for configuration "dynamic" is up to date.
decode_sample ~master: building configuration "application"...
Linking...
To force a rebuild of up-to-date targets, run again with --force.
D:\Dev\heif> decode_sample.exe example.heic result
実行の結果、result0.pngとresult1.pngの2ファイルが作成されました。
libheif decoder demoで紹介されている画像と同じ結果になりました。
result0.png

result1.png

参考情報
High Efficiency Image File Format
【iOS】HEIF(HEIC)をさらっとおさらい
HEIF 画像拡張機能(無料)
HEVC ビデオ拡張機能(有料)
libheif
libde265
libx265
libheif decoder demo
nokiatechの画像サンプル
その他、画像処理の記事
これまでに書いた画像処理関連の記事です。相互に関連しあっているので、参考までに紹介します。