LoginSignup
3
0

More than 3 years have passed since last update.

AVIFファイルの作成

Last updated at Posted at 2021-04-03

はじめに

AVIF形式の画像ファイルについて作成処理を実装しました。
その際のまとめ記事になります。

AVIFとは

AVIFとは、AV1 Image File Formatのことです。AV1というコーデックを使って圧縮を行います。
他の画像形式ファイルと比べて、ファイル圧縮に多少時間がかかりますが、ファイルサイズを大幅に削減できます。

ブラウザでは、ChromeFirefoxOperaが対応しています。
ただし、Firefoxの場合、設定変更(about:config)を行う必要があります。
image.avif.enabledtrue

環境

AVIFファイルを作成するために、libavifを使用します。

  • Windows 10
  • dmd v2.096.0
  • libavif v0.9.0
  • libaom v2.0.2
  • libdav1d v0.8.2

libavifの入手

libavifのReleasesから入手可能です。
この記事で使用したバージョンは、libavif_vs2019_x64_4fedf0bb_Release.zipです。

libaom、libdav1dの入手

以下の説明の通り、コンパイルして作成する必要があります。

今回私がWindows10向けにコンパイルしたライブラリについては、GitHubに公開しています。
aom_v2.0.2.zip
libdav1d_v0.8.2.zip

libaom、libdav1dの作成

libaomlibdav1dをコンパイルして作成する場合、以下の事前インストールが必要です。
参考までに、私の環境でのバージョンをあわせて記載します。

  • Active Perl (v5.28.1 built for MSWin32-x64-multi-thread. Built Oct 28 2020 18:24:51)
  • CMake (version 3.15.19101501-MSVC_2)
  • Git (version 2.24.1.windows.2)
  • meson (0.57.1)
  • nasm (version 2.15.05 compiled on Aug 28 2020)
  • ninja (1.8.2)
  • Visual Stdio (2019)

インストール後にlibavif v0.9.0に対応しているこちらから、aom.cmddav1d.cmdを取得します。
aom.cmdを実行することで、aom\build.libavifフォルダにaom.libが作成されます。
dav1d.cmdを実行することで、dav1d\build\srcフォルダにlibdav1d.aが作成されます。これをlibdav1d.libにリネームして使用しています。

ソースコード

JPEGやPNGなどの画像ファイルからAVIFファイルを作成する処理の実装例です。
libフォルダを作成し、avif_x64_Release.libaom.liblibdav1d.libを格納します。

convAvif.d
/+ dub.sdl:
    name "convAvif"
    dependency "bindbc-freeimage" version=">=0.3.0"
    dependency "libavif-d" version="~>0.9.0"
    versions "FI_318"
    lflags "/NODEFAULTLIB:libcmt.lib"
+/
// dub build --single --build=release convAvif.d
import std.conv;
import std.stdio;
import std.string;
import std.windows.charset;

import avif.avif;
import bindbc.freeimage;

pragma(lib, "lib/avif_x64_Release.lib");
pragma(lib, "lib/aom.lib");
pragma(lib, "lib/libdav1d.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, 0);
    FIBITMAP *dib = FreeImage_Load(fif, ifile.toStringz, 0);
    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);
    }
    FIMEMORY* hmem = FreeImage_OpenMemory();
    FreeImage_SaveToMemory(FIF_BMP, dib, hmem, 0);
    scope(exit) FreeImage_CloseMemory(hmem);
    ubyte* mem;
    uint bufsize;
    FreeImage_AcquireMemory(hmem, &mem, &bufsize);
    ulong offset = (*(mem+13)<<24) + (*(mem+12)<<16) + (*(mem+11)<<8) + *(mem+10);
    mem += offset;

    int orgw = FreeImage_GetWidth(dib);
    int orgh = FreeImage_GetHeight(dib);

    avifEncoder* encoder = avifEncoderCreate();
    encoder.codecChoice = avifCodecChoice.AVIF_CODEC_CHOICE_AOM;
    encoder.minQuantizer = 0;
    encoder.maxQuantizer = 31;
    encoder.minQuantizerAlpha = 0;
    encoder.maxQuantizerAlpha = 31;
    encoder.speed = 5;
    scope(exit) avifEncoderDestroy(encoder);

    avifImage* image = avifImageCreate(orgw, orgh, 8, avifPixelFormat.AVIF_PIXEL_FORMAT_YUV444);
    scope(exit) avifImageDestroy(image);
    avifRGBImage rgb;
    avifRGBImageSetDefaults(&rgb, image);
    scope(exit) avifRGBImageFreePixels(&rgb);
    avifRGBImageAllocatePixels(&rgb);

    for ( int y = 0; y < orgh; y++ ){
        for ( int x = 0; x < orgw; x++ ){
            int ind1 = x * 4 + y * orgw * 4;
            int ind2 = x * 4 + (orgh - 1 - y) * orgw * 4;
            *(rgb.pixels + ind1    ) = *(mem + ind2 + 2);   // R
            *(rgb.pixels + ind1 + 1) = *(mem + ind2 + 1);   // G
            *(rgb.pixels + ind1 + 2) = *(mem + ind2    );   // B
            *(rgb.pixels + ind1 + 3) = *(mem + ind2 + 3);   // A
        }
    }
    avifImageRGBToYUV(image, &rgb);
    avifEncoderAddImage(encoder, image, 0, avifAddImageFlags.AVIF_ADD_IMAGE_FLAG_SINGLE);

    avifRWData avifOutput;
    avifEncoderFinish(encoder, &avifOutput);
    scope(exit) avifRWDataFree(&avifOutput);
    auto f = fopen(ofile.toStringz, "wb");
    scope(exit) fclose(f);
    fwrite(avifOutput.data, 1, avifOutput.size, f);
}

ソースコード補足

  • FreeImageを使って、変換元ファイルを読み込みます。
  • メモリmem上には、ピクセル情報が左下から右方向に格納されています。各ピクセルはBGRA32ビットで構成されます。
  • avifEncoderは、AVIFファイルを作成(エンコード)するときのパラメータを設定できます。
  • avifRGBImageには、ピクセル情報を左上から右方向に格納していきます。各ピクセルはRGBA32ビットで構成されます。 memとは格納順が異なる点に注意してください。

コンパイル、実行例

コマンドプロンプト
D:\Dev\Avif> dub build --single --build=release convAvif.d
Fetching libavif-d 0.9.0 (getting selected version)...
Performing "release" build using D:\Dev\dmd2\windows\bin\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.
convAvif ~master: building configuration "application"...
Linking...
To force a rebuild of up-to-date targets, run again with --force.

コンパイルしたモジュールconvAvif.exeを実行するには、FreeImage.dllが必要です。
ダウンロードページより、入手してください。

エンコードが遅い場合の対処方法

変換元画像が大きい場合、ファイル作成(エンコード)にかなりの時間がかかる可能性があります。
その場合、convAvif.dにある以下のパラメータを調整してください。
maxQuantizermaxQuantizerAlphaは、0から63を設定することができます。

  • 0をセットすると、処理速度は高速、画質は最悪、ファイルサイズは小さめ
  • 63をセットすると、画質は最高、処理速度は遅い、ファイルサイズは大きめ
    encoder.maxQuantizer = 31;
    encoder.maxQuantizerAlpha = 31;

参考

AVIF:新しい次世代の画像圧縮形式の使い方
AV1 Image File Format
AVIF(wikipedia)
AVIF(AV1 Image File Format)についてのメモ

3
0
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
3
0