Python PNG画像を自力で出力する その1



PIL(Python Imaging Library)を使用すれば簡単なのは
重々承知しているのですが、あえて自力でPNGを出力
するプログラムを作ってみようと思います。

プログラムを作成する前の準備として、バイナリエディタで
PNGファイルを作ってみます。
※いきなりカラーは難しそうなので、モノクロの画像で。

まず、PNGファイルの構造を読み解いていきます。
作成する画像は
・モノクロ
・8x8ピクセル
とし、以下のように上半分が白、下半分が黒になるようにします。


□□□□□□□□
□□□□□□□□
□□□□□□□□
□□□□□□□□
■■■■■■■■
■■■■■■■■
■■■■■■■■
■■■■■■■■



■PNG識別部
PNGであることのアピール部分です。
これは、どのPNGファイルも固定。
8バイト分、以下のコードを入力します。

0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A



■本体部
複数のチャンクと呼ばれる部分で構成されています。
最低限必要なのは、
・IHDR 画像の大きさ、種類を指定
・PLTE ファイル中で使用するパレット ※モノクロの場合、省略可能
・IDAT 画像データ本体 分割することも可能だが、面倒なので1つで。
・IEND ファイルの末端であることを明示


また、各チャンクの構成は
・チャンクの長さ 4バイト
・チャンク形式 4バイト
・チャンクデータ本体
・CRC32の結果 4バイト
となっています。


IHDRのデータ部分の構成は

イメージの幅 4バイト
イメージの高さ 4バイト
ビットの深さ 1バイト
カラー・タイプ 1バイト
(0:グレイスケール、2:RGB、3:パレット、4:グレイスケール+アルファ、6: RGB+アルファ)
圧縮方式 1バイト
フィルター方式 1バイト
インタレース方式 1バイト

です。

今回の要件に沿ってデータを作ると・・・

【チャンクの長さ】
・長さ ※CRCは数えないことに注意
0x00 0x00 0x00 0x0D


【チャンク形式】
・チャンク形式 IHDRの文字コードを16進数に変換
0x49 0x48 0x44 0x52


【チャンクデータ本体】
・イメージの幅 8ピクセル
0x00 0x00 0x00 0x08
・イメージの高さ 8ピクセル
0x00 0x00 0x00 0x08
・ビットの深さ
0x01
・カラー・タイプ グレースケール
0x00
圧縮方式 現在は0のみ
0x00
フィルター方式 現在は0のみ
0x00
インタレース方式 非インタレース
0x00


最後に、CRC32符号の計算を行うのですが、
チャンク形式コード、チャンクデータ領域を含み、長さ領域を含まない
要するに、形式コードとデータ領域の部分のみで計算します。

この例のIHDRのCRCは
0x49 0x48 0x44 0x52
0x00 0x00 0x00 0x08
0x00 0x00 0x00 0x08
0x01 0x00 0x00 0x00
0x00

このビットのCRCを求めればよいことになります。

こんなコードで求めてみました。
import binascii
import struct

trget = (chr(0x49)+chr(0x48)+chr(0x44)+chr(0x52)+chr(0x00)+
chr(0x00)+chr(0x00)+chr(0x08)+chr(0x00)+chr(0x00)+chr(0x00)+
chr(0x08)+chr(0x01)+chr(0x00)+chr(0x00)+chr(0x00)+chr(0x00))
crc = binascii.crc32(trget)
print binascii.hexlify(struct.pack("!l", crc))


これでCRCが決定します。

【CRC32の結果】
0xEC 0x74 0x83 0x26



次回、IDATを考えてみます。



【参考URL】
PNG (Portable Network Graphics) Specification, Version 1.2
http://www.sutv.zaq.ne.jp/linuz/tks/PngSpec1.2/PNG-Contents.html

PNGについて
http://homepage2.nifty.com/sophia0/png.html


もどる