QOI(Quite OK Image Format)は、ドミニク・シャブレフスキ()によって開発され2021年11月24日に初めて発表された24ビットまたは32ビットカラービットマップ(ラスター)画像の可逆画像圧縮の仕様である。

概要 計画された目標は、PNGよりも実装が簡単で高速に処理ができるオープンソースの可逆圧縮方式を作成することであった。第三者によるYouTubeの動画では、PNGとQOIの圧縮がどのように行われるかについて説明している。作者による最初の発表とこの動画では、同様のファイルサイズのPNGと比較した場合、エンコードは20倍から50倍速く、デコードは3倍から4倍速いと主張している。作者はQOIの仕様をパブリックドメイン(CC0)であると宣言した。

ソフトウェアとプログラミング言語の対応 QOIはImageMagick、IrfanView(バージョン4.60以降)、FFmpeg(バージョン5.1以降)及びGraphicconverter(バージョン11.8以降)がネイティブに対応している。

コミュニティが作成したプラグインをGIMP、Paint.NET及びXnViewで利用することができる。

Rust、Python、Java、C++、C#などのプログラミング言語向けの実装も存在する。

ファイルフォーマット ヘッダ QOIファイルは14バイトのヘッダと、それに続く任意の数のデータチャンクと8バイトの終端マーカーで構成される。 qoi_header { char magic; // マジックナンバー(qoif) uint32_t width; // 画像の幅(ビッグエンディアン) uint32_t height; // 画像の高さ(ビッグエンディアン) uint8_t channels; // 3 = RGB、4 = RGBA uint8_t colorspace; // 0 = 線形アルファチャンネル付きsRGB // 1 = 全てのチャンネルが線形 }; 色空間とチャンネルのフィールドは純粋に情報を提供するだけである。これらによってデータチャンクの符号化方法が変更されることはない。

符号化 画像は行ごとに左から右、上から下に符号化が行われる。デコーダとエンコーダは最初のピクセルの前のピクセル値がであるとして処理を開始する。で指定された全てのピクセルが処理されると画像が完成する。ピクセルは次のように符号化される:

直前のピクセル値に基づく連長圧縮() 以前のピクセル値に基づく() 直前のピクセルのRGB値に基づく差分符号化(または) 完全なRGB値またはRGBA値(または)

カラーチャンネルはアルファチャンネルと乗算済みでないと想定される。エンコーダとデコーダは過去に出現したピクセル値をゼロ値で初期化されたにバッファとして保持する。このバッファの添え字は、ピクセルのRGBA値にハッシュ関数を適用して得られた値を使用する。

エンコーダでは、インデックスのピクセル値が現在のピクセルと一致する場合、このインデックスの位置はとしてストリームに書き込まれる。添え字を計算するハッシュ関数は次の通り: index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64

各チャンクは2ビットまたは8ビットのタグで始まり、それにデータビットが続く。全てのチャンクはバイトアライメントされている。これらのデータビットに符号化された全ての値は左端に最上位ビットがある。8ビットのタグは2ビットのタグよりも優先される。デコーダは最初に8ビットのタグの存在を確認する必要がある。バイトストリームの終端は7個のとそれに続く1個のでマークされる。

チャンクの種類は次の通り:

Byte Byte Byte Byte 7 6 5 4 3 2 1 0 7 .. 0 7 .. 0 7 .. 0 1 1 1 1 1 1 1 0 赤 緑 青

8ビットタグ (254) 8ビット赤チャンネル値 8ビット緑チャンネル値 8ビット青チャンネル値

アルファ値は前のピクセルから変更されない。

Byte Byte Byte Byte Byte 7 6 5 4 3 2 1 0 7 .. 0 7 .. 0 7 .. 0 7 .. 0 1 1 1 1 1 1 1 1 赤 緑 青 アルファ

8ビットタグ(、255) 8ビット赤チャンネル値 8ビット緑チャンネル値 8ビット青チャンネル値 8ビットアルファチャンネル値

Byte(範囲: 0 .. 63) 7 6 5 4 3 2 1 0 0 0 インデックス

2ビットタグ() 6ビット添え字()

有効なエンコーダは同じインデックスに対して2個以上の連続したチャンクを出してはならない。代わりにを使用する必要がある。

Byte(範囲: 64 .. 127) 7 6 5 4 3 2 1 0 0 1 赤チャンネル値の差分 緑チャンネル値の差分 青チャンネル値の差分

2ビットタグ() 直前のピクセルの赤チャンネルとの差分値(2ビット、) 直前のピクセルの緑チャンネルとの差分値(2ビット、) 直前のピクセルの青チャンネルとの差分値(2ビット、)

現在のチャンネルとの差分の計算にはが使用されているので、の結果は255となり、の結果は0となる。

値はエクセス2で表現される符号無し整数として保存される。例えば、−2は0()として保存され、1は3()として保存される。アルファ値は前のピクセルから変更されない。

Byte(範囲: 128 .. 191) Byte 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 1 0 緑チャンネル値の差分 赤チャンネル値 - 緑チャンネル値 青チャンネル値 - 緑チャンネル値

2ビットタグ() 直前のピクセルの緑チャンネルとの差分値(6ビット、) 赤チャンネルと緑チャンネルとの差分値(4ビット、) 青チャンネルと緑チャンネルとの差分値(4ビット、)

緑チャンネルは一般的な変化の方向を示すために使用され、6ビットで符号化される。赤チャンネルと青チャンネルはこれらと緑チャンネルとの差分に基づいて符号化される。つまり: dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g) db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g) 現在のチャンネルとの差分の計算にはラップアラウンド演算が使用されているので、の結果は253となり、の結果は1となる。

値は緑チャンネルはエクセス32、赤チャンネルと青チャンネルがエクセス8で表現される符号無し整数として保存される。アルファ値は前のピクセルから変更されない。

Byte(範囲: 192 .. 253) 7 6 5 4 3 2 1 0 1 1 ランレングス

2ビットタグ() 直前のピクセルを繰り返すランレングス(6ビット)

ランレングスはエクセス−1で保存される。ランレングス63と64(と)はタグとタグによって占有されているので、不正であることに注意 。

脚注

注釈

出典

外部リンク

仕様書