LoginSignup
1
2

More than 1 year has passed since last update.

HEIF(HEIC)ファイルの読み書き実装

Last updated at Posted at 2021-05-22

はじめに

HEIF(HEIC)形式のファイルについて、読み書きの処理を実装しました。
その際のまとめ記事になります。

  • libheifのビルド
  • HEIF(HEIC)ファイルの作成
  • HEIF(HEIC)ファイルの読込

HEIF(HEIC)とは

HEIF(HEIC)とは、High Efficiency Image File Formatのことで、Moving Picture Experts Group (MPEG)によって開発された画像ファイルフォーマットです。(Wikipediaより)
同程度の画質のJPEG形式と比べて、ファイルサイズを半分ほどに抑えられるとのことです。
画像形式の対応状況(英語)によると、macOS SierraiOS 11、ブラウザではSafariが対応しているようです。

開発環境

WindowsHEIFファイルを取り扱うには、HEIF 画像拡張機能(無料)HEVC ビデオ拡張機能(有料)が必要のようです。
有料の拡張機能を使わずに、D言語でHEIFファイルの読み書き処理を実装しました。

libheifのダウンロード

今回Windows10向けにビルドしたライブラリとDLLファイルをGitHubに公開しました。
libheif_v1.12.0.zip
ビルドしたい方のために、以下にビルド方法を書きます。

libheifのビルド

libheifからソースコードを入手できます。
Windows版であれば、Vcpkgから入手できるとのことで、試してみました。
しかし、途中でVisual Studio英語版のインストールが必要とのエラーが出たため、中断して、cmakeから作成しました。

libheifをビルドするには、libde265libx265が必要なため、先にこれらをビルドします。
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 ../..

libde265libx265のビルドが終わったところで、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\Releaseheif.libが作成されます。
heif.libD:\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)ファイルの作成

JPEGPNGファイル等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.pngresult1.pngの2ファイルが作成されました。
libheif decoder demoで紹介されている画像と同じ結果になりました。
result0.png
result0.png
result1.png
result1.png

参考情報

High Efficiency Image File Format
【iOS】HEIF(HEIC)をさらっとおさらい
HEIF 画像拡張機能(無料)
HEVC ビデオ拡張機能(有料)
libheif
libde265
libx265
libheif decoder demo
nokiatechの画像サンプル

その他、画像処理の記事

これまでに書いた画像処理関連の記事です。相互に関連しあっているので、参考までに紹介します。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2