【ITニュース解説】How I created my own image file format using only Python
2025年09月20日に「Dev.to」が公開したITニュース「How I created my own image file format using only Python」について初心者にもわかりやすく解説しています。
ITニュース概要
Pythonとstructライブラリを使い、独自の画像ファイル形式「.avj」を作成した。画像はヘッダー(メタデータ)とピクセルデータから成り、代替テキストや画像自体のベクトル埋め込みを格納。エンコード・デコードする処理を実装し、機械学習活用を想定している。
ITニュース解説
Pythonだけを使って、自分だけの画像ファイル形式を作り出すという興味深いプロジェクトについて解説する。一般的な画像ファイルは、写真やイラストの情報をコンピューターが理解できるように特定のルールで保存されている。このプロジェクトでは、そのルールをゼロから自分で設計し、実装することで、画像ファイルの仕組みの奥深さを学ぶことができる。特に、機械学習(ML)の分野で画像を扱う際に役立つような、一工夫凝らしたファイル形式になっている。
まず、画像ファイルがどのような要素で構成されているか理解しよう。画像ファイルは大きく分けて二つの主要な部分からできている。一つは「ヘッダー」と呼ばれる部分で、これは画像そのものではなく、画像のメタデータ、つまり「画像に関する情報」を格納する。例えば、画像の幅や高さ、色を表す方法(カラーモード)、ファイルのバージョンなどがここに記録される。もう一つは「データ」と呼ばれる部分で、ここには実際に画像の色や明るさを示すピクセル値が、一つ一つの点としてぎっしり詰まっている。ヘッダーの役割は、コンピューターがデータ部分を正しく読み解き、画面に表示するために必要な「取扱説明書」のようなものだ。
このプロジェクトで作成された独自の画像ファイル形式は「AVJファイル形式」と名付けられており、拡張子は「.avj」だ。このファイル形式の設計では、ヘッダーに格納する情報の種類と並び順を細かく定義している。例えば、ファイルがAVJ形式であることを識別するための「マジックナンバー」(固定の文字列「AVJ1」)、ファイル形式の「バージョン」、画像の「幅」と「高さ」、色の表現方法を示す「カラーモード」(このバージョンではRGBのみをサポート)、そして代替テキストや埋め込みデータと呼ばれるものの「長さ」といった情報が、それぞれ決まったバイト数で並んで格納される。これらの情報は「固定ヘッダー」として、常に同じ位置に、同じ長さで存在する。
さらに、AVJファイル形式には、画像そのもののピクセルデータだけでなく、可変長の情報を複数含んでいる点が特徴だ。具体的には、画像の内容を説明する「代替テキスト」、そしてこの代替テキストと画像データそれぞれから抽出された「埋め込みベクトル」が含まれる。埋め込みベクトルとは、テキストや画像を数値の並び(ベクトル)で表現したもので、機械学習モデルがそれらの意味や類似性を理解するために用いられる非常に重要なデータだ。これらの可変長データは、それぞれがどれくらいの長さを持つかを示す情報が固定ヘッダーに含まれており、その情報をもとにファイルから正確に読み出せるようになっている。AVJファイル形式は、このように画像ピクセルデータと、その画像に関連するテキスト情報、さらにそれらの機械学習向け埋め込みデータをまとめて一つのファイルに格納できる、非圧縮のファイル形式として設計されている。
このようなバイナリデータをファイルに書き込んだり、ファイルから読み出したりする際に、Pythonの標準ライブラリであるstructが非常に重要な役割を果たす。structライブラリは、Pythonのデータ型(整数や文字列など)と、ファイルのバイナリデータ(バイト列)を相互に変換する機能を提供する。たとえば、「4バイトの符号なし整数」や「1バイトの符号なしバイト」といった形式を指定することで、Pythonの整数値をバイナリデータに変換してファイルに書き込んだり(これを「パック」と呼ぶ)、逆にファイルから読み出したバイナリデータをPythonの整数値に戻したり(これを「アンパック」と呼ぶ)できる。この際に、どのバイトから読むか、どのようなデータ型として解釈するかをstructのフォーマット文字列(例:Iは4バイトの符号なし整数、Bは1バイトの符号なしバイト)や、バイトの順序(エンディアン)を指定することで制御する。初心者であれば、とりあえず「リトルエンディアン」を選ぶのが一般的で簡単だとされている。
実際のコード実装では、まず必要なライブラリをインポートするところから始まる。画像処理にはPillow(PIL)ライブラリが、数値計算や配列処理にはNumPyが使われる。また、このプロジェクトではウェブAPIとして機能させるためにFastAPIも使用されている。そして、機械学習モデルによる埋め込み生成のためにPyTorchとtransformersライブラリ、具体的にはCLIPモデルが利用される。
AVJファイルを生成するエンコーダー関数では、まず通常の画像ファイル(例えばJPEGやPNGなど)をPillowで開き、画像データそのものをバイト列に変換する。同時に、画像の幅、高さ、カラーモードといったメタデータも取得する。次に、画像に関連する代替テキストや、CLIPモデルを使って生成された代替テキストと画像の埋め込みベクトルを準備する。埋め込みベクトルはNumPy配列として扱われ、structでパックできるようバイト列に変換される。これらのすべての情報(マジックナンバー、バージョン、幅、高さ、カラーモード、そして代替テキストや埋め込みの長さなど)を、事前に定義したヘッダーのフォーマット文字列に従ってstruct.pack関数を用いてバイナリデータに変換し、ヘッダー部分を作成する。最後に、このヘッダーデータと、エンコードされた代替テキスト、画像モード、二つの埋め込みベクトル、そして元の画像ピクセルデータを全て順番に連結することで、一つの完全なAVJファイルデータが完成する。
一方、作成されたAVJファイルを元の情報に復元するデコーダー関数では、エンコーダーとは逆のプロセスを辿る。まず、AVJファイルデータの先頭から固定ヘッダーのバイト列を読み出し、struct.unpack関数を使って、マジックナンバー、バージョン、幅、高さ、カラーモード、そして代替テキストや埋め込みの長さといった情報をPythonの変数として取り出す。これらの長さの情報は非常に重要で、それを使って次に続く可変長データ(代替テキスト、画像モード、二つの埋め込みベクトル、画像ピクセルデータ)の開始位置と終了位置を正確に特定し、一つずつ抽出していく。代替テキストはUTF-8エンコードされたバイト列として抽出され、その後Pythonの文字列に戻される。埋め込みベクトルはバイト列として抽出され、NumPyのfrombuffer関数を使って元の浮動小数点数の配列に戻される。そして、残りのバイト列が純粋な画像ピクセルデータとなる。最終的に、これらの抽出された全ての情報がPythonの辞書形式で返される。
画像ピクセルデータが抽出されたら、それとヘッダーから得られた画像の幅、高さ、カラーモードを使って、PillowライブラリのImage.frombytes関数を呼び出すことで、元の画像を完璧に再構築し、画面に表示したり、他の画像処理に利用したりできるようになる。
このように、Pythonのstructライブラリと画像処理ライブラリ、さらに機械学習モデルを組み合わせることで、既存の画像ファイル形式の仕組みを深く理解するだけでなく、自分の目的に特化した新しいファイル形式をゼロから設計・実装できることがわかる。これは、システムエンジニアとしてデータ構造やバイナリ操作の基礎を学ぶ上で、非常に貴重な経験となるだろう。独自のファイル形式を作成する過程は、データがどのようにコンピューター上で表現され、保存され、読み出されるのかという根本的な理解を深めることに繋がるのだ。