QOI

QOI（Quite OK Image Format）は、ドミニク・シャブレフスキ（ポーランド語: Dominic Szablewski）によって開発され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[4]; // マジックナンバー（qoif） uint32_t width; // 画像の幅（ビッグエンディアン） uint32_t height; // 画像の高さ（ビッグエンディアン） uint8_t channels; // 3 = RGB、4 = RGBA uint8_t colorspace; // 0 = 線形アルファチャンネル付きsRGB // 1 = 全てのチャンネルが線形 }; 色空間とチャンネルのフィールドは純粋に情報を提供するだけである。これらによってデータチャンクの符号化方法が変更されることはない。 画像は行ごとに左から右、上から下に符号化が行われる。デコーダとエンコーダは最初のピクセルの前のピクセル値が{r: 0, g: 0, b: 0, a: 255}であるとして処理を開始する。width * heightで指定された全てのピクセルが処理されると画像が完成する。ピクセルは次のように符号化される: 直前のピクセル値に基づく連長圧縮（QOI_OP_RUN） 以前のピクセル値に基づく辞書式圧縮法（英語版）（QOI_OP_INDEX） 直前のピクセルのRGB値に基づく差分符号化（QOI_OP_DIFFまたはQOI_OP_LUMA） 完全なRGB値またはRGBA値（QOI_OP_RGBまたはQOI_OP_RGBA） カラーチャンネルはアルファチャンネルと乗算済みでないと想定される。エンコーダとデコーダは過去に出現したピクセル値をゼロ値で初期化されたarray[64]にバッファとして保持する。このバッファの添え字は、ピクセルのRGBA値にハッシュ関数を適用して得られた値を使用する。 エンコーダでは、インデックスのピクセル値が現在のピクセルと一致する場合、このインデックスの位置はQOI_OP_INDEXとしてストリームに書き込まれる。添え字を計算するハッシュ関数は次の通り: index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 各チャンクは2ビットまたは8ビットのタグで始まり、それにデータビットが続く。全てのチャンクはバイトアライメントされている。これらのデータビットに符号化された全ての値は左端に最上位ビットがある。8ビットのタグは2ビットのタグよりも優先される。デコーダは最初に8ビットのタグの存在を確認する必要がある。バイトストリームの終端は7個の0x00とそれに続く1個の0x01でマークされる。 チャンクの種類は次の通り: 8ビットタグ b11111110（254） 8ビット赤チャンネル値 8ビット緑チャンネル値 8ビット青チャンネル値 アルファ値は前のピクセルから変更されない。 8ビットタグ（b11111111、255） 8ビット赤チャンネル値 8ビット緑チャンネル値 8ビット青チャンネル値 8ビットアルファチャンネル値 2ビットタグ（b00） 6ビット添え字（0..63） 有効なエンコーダは同じインデックスに対して2個以上の連続したQOI_OP_INDEXチャンクを出してはならない。代わりにQOI_OP_RUNを使用する必要がある。 2ビットタグ（b01） 直前のピクセルの赤チャンネルとの差分値（2ビット、-2..1） 直前のピクセルの緑チャンネルとの差分値（2ビット、-2..1） 直前のピクセルの青チャンネルとの差分値（2ビット、-2..1） 現在のチャンネルとの差分の計算にはラップアラウンド演算（英語版）が使用されているので、1 - 2の結果は255となり、255 + 1の結果は0となる。 値はエクセス2で表現される符号無し整数として保存される。例えば、−2は0（b00）として保存され、1は3（b11）として保存される。アルファ値は前のピクセルから変更されない。 2ビットタグ（b10） 直前のピクセルの緑チャンネルとの差分値（6ビット、-32..31） 赤チャンネルと緑チャンネルとの差分値（4ビット、-8..7） 青チャンネルと緑チャンネルとの差分値（4ビット、-8..7） 緑チャンネルは一般的な変化の方向を示すために使用され、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) 現在のチャンネルとの差分の計算にはラップアラウンド演算が使用されているので、10 - 13の結果は253となり、250 + 7の結果は1となる。 値は緑チャンネルはエクセス32、赤チャンネルと青チャンネルがエクセス8で表現される符号無し整数として保存される。アルファ値は前のピクセルから変更されない。 2ビットタグ（b11） 直前のピクセルを繰り返すランレングス（6ビット） ランレングスはエクセス−1で保存される。ランレングス63と64（b111110とb111111）はQOI_OP_RGBタグとQOI_OP_RGBAタグによって占有されているので、不正であることに注意。 [脚注の使い方] ^ 各色がそれぞれ8ビットの色深度を持つRGB。 ^ 8ビットのアルファチャンネルと各色がそれぞれ8ビットの色深度を持つRGBA。 ^ 完全な一覧はプロジェクトのリポジトリのREADMEを参照。 ^ a b “Lossless Image Compression in O(n) Time”. Phoboslab.org (2021年11月24日). May 1, 2022閲覧。 ^ Reducible (March 3, 2022). “How PNG Works: Compromising Speed for Quality”. YouTube.com. May 1, 2022閲覧。 ^ “ImageMagick - Image Formats”. May 4, 2022閲覧。 ^ “History of IrfanView Changes/Versions”. www.irfanview.com. 2022年5月10日閲覧。 ^ “FFmpeg Changelog - Gitweb”. ffmpeg.org. 2022年7月13日閲覧。 ^ “GraphicConverter Release Notes version 11.8 (build 5762)”. Lemke Software. 21 February 2023閲覧。 ^ James Hein. “Moving images to the next level”. Bangkok Post. https://www.bangkokpost.com/tech/2279999/moving-images-to-the-next-level April 1, 2022閲覧。  ^ Simon Sharwood. “Developer creates ‘Quite OK Image Format’ – but it performs better than just OK”. The Register. https://www.theregister.com/2021/12/21/quite_ok_image_format/  ^ Szablewski, Dominic (2022年1月5日). “The Quite OK Image Format Specification”. 2022年6月5日閲覧。 この記事には現在パブリックドメインとなった次の出版物からの記述が含まれています。 公式ウェブサイト 仕様書 qoi - GitHub