千語ではなく1ピクセル





数か月前、q_autoやg_autoなどの新機能の実装を休みながら、チームチャットで、さまざまな画像ストレージ形式が単一ピクセル画像を圧縮する方法について冗談を言っていました。 これに対して、ブログ編集者のOrlyから、これについての投稿を依頼されました。 私は言った:「もちろん、そうではない。 しかし、これは非常に短い投稿になります。 結局のところ、1つのピクセルについて何を伝えることができますか。」



私は非常に間違っていたように見えます。



1つのピクセルで何ができますか?



Webの初期の頃、単一ピクセルの画像は、現在CSSで行われていることの松葉杖としてよく使用されていました。 インデント、線、長方形、半透明の背景の作成-ピクセルを目的のサイズに拡大縮小するだけで、多くのことができます。 今日まで残っているピクセルの別の用途は、ビーコン、追跡、分析ツールです。



レスポンシブWebデザインでは、ページの読み込みを保留する一時的なスタブとしてシングルピクセル画像を使用します。 ほとんどのブラウザーはHTTPクライアントヒントをサポートしていないため、レスポンシブ画像を含む一部のオプションは、ページが完全に読み込まれて画像の実際のサイズを計算し、JavaScriptを使用して単一ピクセル画像を必要な画像に置き換えるのを待っています。





壊れた画像



シングルピクセル画像には別の用途があります。「デフォルト」画像として使用できます。 何らかの理由で目的の画像が見つからない場合は、ブラウザで「壊れた画像」として表示される「404-Not Found」を表示するよりも、1つの透明ピクセルを表示した方がよい場合があります。 いずれの場合でも、目的の画像は表示されませんが、これに焦点を合わせない方がよりプロフェッショナルになり、「壊れた画像」のアイコンが表示されます。



それでは、1ピクセルの画像が便利です。 そして、1x1画像をエンコードする最良の方法は何ですか?



明らかに、画像圧縮形式の場合、これは境界線の場合です。 画像が1つのピクセルで構成される場合、特に圧縮するものはありません。 非圧縮データには1ビットから4バイトが含まれます-解釈に応じて:白黒(1ビット)、グレースケール(1バイト)、アルファ付きグレースケール(2バイト)、RGB(3バイト)、RGBA(4バイト)。



ただし、データのみをエンコードすることはできません。どの画像形式でも、データの解釈を指定する必要があります。 少なくとも、画像の高さと幅、ピクセルあたりのビット数を知る必要があります。



見出し



通常、高さと幅のエンコードには4バイトが使用されます。数字ごとに2バイトです(1バイトの場合、ピクチャの最大サイズは255x255になります)。 カラーレンダリングのタイプ(グレースケール、RGB、またはRGBA)を指定するために別のバイトが必要だとします。 このような最小限の形式では、単一ピクセルの画像は少なくとも6バイト(白いピクセルの場合)、最大9バイト(任意の色の半透明のピクセルの場合)を占有します。



ただし、実際の形式のヘッダーには通常、より多くの情報が含まれています。 すべての形式の最初の数バイトには、「Hey! 私はこの特定の形式のファイルです!」 このバイトシーケンスは、マジックナンバーとも呼ばれます。 たとえば、仕様バージョンに応じて、GIFは常にGIF87aまたはGIF89aで始まります。PNG-PNGを含む8バイトシーケンスでは、JPEGにはJFIFまたはExif文字列などを含むヘッダーがあります。



ヘッダーにはメタ情報が含まれる場合があります。 デコードに必要なこの形式固有のデータ。形式のどの亜種を使用するかを決定します。 メタデータの一部はデコードに必要ではありませんが、それでも、カラープロファイル、方向、ガンマ、ピクセルあたりのドット数など、画面上での表示方法を決定するために使用されます。 また、コメント、タイムスタンプ、著作権表示、GPS座標などの生産データでもかまいません。 これは、仕様に応じて、オプションまたは必須のデータになる場合があります。 もちろん、このデータはファイルサイズを増やします。 したがって、オプションの情報がすべて削除される最小限のファイルについて説明しましょう。さもないと、貴重なバイトをナンセンスに費やすことになります。



ヘッダーに加えて、マーカー、チェックサム(転送の正確性や、ファイルを台無しにする可能性のある他のプロセスの結果を確認するために使用される)などの追加情報がファイル内にあります。 すべてのデータを揃えるために、ファイルにインデントを含める必要があることがあります。



単一ピクセルの最小限の画像は、ファイル形式に含まれる「余分な」情報の量を示します。 見ます。



これは、1つの白いピクセルを持つ67バイトのPNGファイルの16進ダンプです。



00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR| 00000010 00 00 00 01 00 00 00 01 01 00 00 00 00 37 6e f9 |.............7n.| 00000020 24 00 00 00 0a 49 44 41 54 78 01 63 68 00 00 00 |$....IDATx.ch...| 00000030 82 00 81 4c 17 d7 df 00 00 00 00 49 45 4e 44 ae |...L.......IEND.| 00000040 42 60 82 |B`.|
      
      







このファイルは、8バイトの「マジックナンバー」PNGで構成され、その後に13バイトのIHDRヘッダーセグメント、10バイトの「圧縮」データを含むIDATイメージセグメント、およびIENDエンドマークが続きます。 各データセグメントは、長さと4バイトのセグメント識別子を持つ4バイトのセグメントで始まり、4バイトのチェックサムで終わります。 これら3つのデータは必須であるため、いずれにしても、67バイトのファイルから36バイトを消費します。



黒いピクセルも67バイト、透明なピクセルは68バイトを占め、任意のRGBAカラーは67〜70バイトを占めます。



JPEGヘッダーはより長いです。 最小の単一ピクセルJPEGは141バイトであり、透明ではありません。 JPEGはアルファチャネルをサポートしていません。



GIFヘッダーに関しては、3つのユニバーサルフォーマットの中で最もコンパクトです。 白いピクセルは、35バイトのGIFでエンコードできます。



 00000000 47 49 46 38 37 61 01 00 01 00 80 01 00 00 00 00 |GIF87a..........| 00000010 ff ff ff 2c 00 00 00 00 01 00 01 00 00 02 02 4c |...,...........L| 00000020 01 00 3b |..;|
      
      







および透明-43:



 00000000 47 49 46 38 39 61 01 00 01 00 80 01 00 00 00 00 |GIF89a..........| 00000010 ff ff ff 21 f9 04 01 0a 00 01 00 2c 00 00 00 00 |...!.......,....| 00000020 01 00 01 00 00 02 02 4c 01 00 3b |.......L..;|
      
      







リストされているすべての形式について、ほとんどのブラウザで表示される小さなファイルを作成できますが、仕様に違反して作成されるため、イメージデコーダーはいつでもファイルが壊れている(そして正しい)ことを訴え、「壊れた画像」-それを回避しようとしています。



それでは、ウェブに最適なシングルピクセル画像フォーマットは何ですか? オプションがあります。 ピクセルが不透明の場合、GIF。 透明な場合-GIFも。 GIFが半透明の場合、PNGはGIFの透明度が「はい」または「いいえ」であるためです。



これはほとんど意味がありません。 これらのファイルはいずれも1つのネットワークパケットに収まるため、速度に違いはなく、ストレージの違いは一般に無視できます。 それでも、それを理解するのは楽しいです-少なくともフォーマット愛好家にとっては。



もっとエキゾチックなフォーマットはどうですか?



WebP形式を使用して、品質を損なうことなくバージョンを選択します。 WebP形式で品質を損なうことのない単一ピクセルの画像は、34〜38バイトかかります。 損失あり-アルファチャネルの存在に応じて、44〜104バイト。 たとえば、品質を損なうことなく34バイトのWebPで完全に透明なピクセルを次に示します。



 00000000 52 49 46 46 1a 00 00 00 57 45 42 50 56 50 38 4c |RIFF....WEBPVP8L| 00000010 0d 00 00 00 2f 00 00 00 10 07 10 11 11 88 88 fe |..../...........| 00000020 07 00 |..|
      
      







しかし、品質が低下した同じピクセル(デフォルト)のWebPは、82バイトを占有します。



 00000000 52 49 46 46 4a 00 00 00 57 45 42 50 56 50 38 58 |RIFFJ...WEBPVP8X| 00000010 0a 00 00 00 10 00 00 00 00 00 00 00 00 00 41 4c |..............AL| 00000020 50 48 0b 00 00 00 01 07 10 11 11 88 88 fe 07 00 |PH..............| 00000030 00 00 56 50 38 20 18 00 00 00 30 01 00 9d 01 2a |..VP8 ....0....*| 00000040 01 00 01 00 02 00 34 25 a4 00 03 70 00 fe fb fd |......4%...p....| 00000050 50 00 |P.|
      
      







違いは、品質と透明度が失われたWebPは、1つのコンテナファイルに2つの画像として保存されることです。1つの画像はRGBのデータを保存する品質が低下し、もう1つはアルファチャネルデータが失われません。



BPG



BPG形式には、品質を失うことなく損失するモードもあり、逆のパターンが適用されます。 損失のあるBPGは31バイトで1ピクセルを保存します-すべての最小の指標:



 00000000 42 50 47 fb 00 00 01 01 00 03 92 47 40 44 01 c1 |BPG........G@D..| 00000010 71 81 12 00 00 01 26 01 af c0 b6 20 bc b6 fc |q.....&.... ...|
      
      







品質を損なうことのないBPGは59バイトかかります。 透明ピクセルはBPGで57バイトかかります

Losslessおよび113バイトのロスレスBPG。 興味深いことに、単一の白いピクセルの場合、BPGはWebPよりも優れています(31バイト対38)。1つの透明ピクセルでは、WebPはBPGよりも優れています(34バイト対57)。



フリフ





そして、FLIFがあります。 もちろん、品質を損なうことなく無料の画像形式(Free Lossless Image Format)の主な著者であることを忘れることはできません。 以下は、単一の白いピクセルの15バイトFLIFです。



 00000000 46 4c 49 46 31 31 00 01 00 01 18 44 c6 19 c3 |FLIF11.....D...|
      
      







そして、ここに黒のための14バイトがあります:



 00000000 46 4c 49 46 31 31 00 01 00 01 1e 18 b7 ff |FLIF11........|
      
      







ゼロは255よりも圧縮率が高いため、黒いピクセルは小さくなります。タイトルは単純です。最初の4バイトは常に「FLIF」で、次は人間が読める色とインターレースの指定です。 私たちの場合、これは「1」です。これは、色(灰色の陰影)の1つのチャネルを意味します。 次のバイトは色深度です。 「1」は、チャネルごとに1バイトを意味します。 次の4バイトは、イメージの次元、0x0001〜0x0001です。 次の4または5は圧縮データです。



完全に透明なピクセルも、FLIFで14バイトを占有します。



 00000000 46 4c 49 46 34 31 00 01 00 01 4f fd 72 80 |FLIF41....Or|
      
      







この場合、1つではなく4つのカラーチャネル(RGBA)があります。 データセクションが長くなると予想されますが(結局、4倍のチャネルがあります)、そうではありません:アルファ値がゼロ(ピクセルが透明)であるため、RGB値は重要ではないと見なされ、ファイルに含まれません。



任意のRGBAカラーの場合、FLIFファイルには最大20バイトを使用できます。



それは、FLIFが画像符号化コンテストの1ピクセルカテゴリのリーダーであることを意味します。 それがまだいくつかの重要な競争だった場合:)



それでも、FLIFはリーダーにはなりません。 私が言及した最小フォーマットを覚えていますか? 6〜9バイトのサイズで1ピクセルをエンコードするものですか? そのような形式はないため、カウントされません。 しかし、これに十分近い既存のフォーマットがあります。



ポータブルビットマップ形式 (PBM)と呼ばれ、1980年代の非圧縮の画像形式です。 PBMで単一の白いピクセルを8バイトだけでコーディングする方法は次のとおりです。



 00000000 50 31 0a 31 20 31 0a 30 |P1.1 1.0|
      
      







はい、16進ダンプは不要です。この形式は人間が読むことができます。 テキストエディタで開くことができます。



 P1 1 1 0
      
      







最初の行(P1)は、画像が2色であることを示しています。 グレーの濃淡ではなく、2色のみ-黒(番号1)と白(0)。 2行目は画像の寸法です。 次に、スペースで区切られた数字のリストがあります。これはピクセルごとに1つの数字です。 私たちの場合、0。



白黒以外のものが必要な場合は、PGM形式を使用して、任意の色の単一ピクセルをわずか12バイト、またはPPMを14バイトのサイズで表すことができます。 対応するFLIF(またはその他の圧縮形式)よりも常に小さくなります。



従来の形式のPNMファミリ(PBM、PGM、およびPPM)は透明性をサポートしていません。 Portable Arbitrary Map (PAM)と呼ばれるPNMアドオンがあり、透過性があります。 しかし、私たちにとっては、冗長性のために適切ではありません。 透明ピクセルを表す最小のPAMファイルは次のとおりです。



 P7 WIDTH 1 HEIGHT 1 DEPTH 4 MAXVAL 1 TUPLTYPE RGB_ALPHA ENDHDR \0\0\0\0
      
      







最後の行には4つのゼロバイトが含まれています。 合計67バイト。 RGBAの代わりにアルファチャネルでグレースケールを使用できます。これにより、データセクションの2バイトを節約できます。 ただし、TUPLTYPEをRGB_ALPHAからGRAYSCALE_ALPHAに変更する必要があるため、71バイトのファイルを取得します。 さらに、処理プログラムはMAXVAL 1を好まない場合があり、MAXVAL 255(さらに2バイト)に変更する必要があります。



一般に、透明度のない単一ピクセル画像の場合、最小はPNM(PNMの場合8から14バイト対FLIFの14から18)であり、透明度の場合、最小はFLIF(FLIFの場合14から20バイト対67から69)です。 PAMのバイト)。



さまざまな単一ピクセル画像に最適なファイルサイズの比較プレートを次に示します。







非圧縮形式が圧縮形式よりも優れているのは奇妙に思えるかもしれません。 しかし、考えてみると、シングルピクセル画像は画像圧縮の最悪の選択肢です。 ファイル全体はヘッダーと追加情報で構成され、その中にはデータがほとんどありません。 また、圧縮は予測可能性に基づいているため、圧縮できるデータはほとんどありません。また、単一ピクセルをどのように予測できますか?



All Articles