投稿者 魔界の仮面弁士  (社会人) 投稿日時 2010/2/15 07:44:50
> 32bitのビットマップってあるんですかぁ。
たとえば、
Using New Bitmap(x, y, Imaging.PixelFormat.Format32bppArgb)
    Using g As Graphics = Graphics.FromImage(bmp)
        '描画処理  
    End Using
    bmp.Save(fileName, Imaging.ImageFormat.Bmp)
End Using
のようにすると、32bit Bitmap が生成されます。

なお、これは非圧縮(BI_RGB)のビットマップです。Bitmap ではランレングス圧縮もサポートされていますが、
4bpp用(BI_RLE4)と 8bpp用(BI_RLE8)しかなく、24bpp や 32bpp には対応していません。
(bpp = Bits Per Pixle = 1ドットごとに必要なビット数)

とはいえ BMP の仕様上は、他の圧縮フォーマットとしてJPEG形式(BI_JPEG)やPNG形式(BI_PNG)が一応存在しています。
もっとも、普通は *.jpg / *.png フォーマットをそのまま使えば済みますから、
よほど特殊な用途でも無い限り、この形式のビットマップの出番は無さそうですけれどね。(^^;)
http://ja.wikipedia.org/wiki/Windows_bitmap
http://msdn.microsoft.com/en-us/library/dd145131.aspx


> GZIP圧縮が効かなかったこともありました
どの圧縮方式を採用するにしても、使い方/使いどころを間違えれば、圧縮の用途を
満たせない事はあると思いますが……そういう話をしているのでは無いのですよね?


> .NETの圧縮ってちゃんと使えるものなんですか?
もちろん使えますが、そもそも画像を PNG 形式で保存するのでは駄目なのでしょうか?
減色機能が弱いのが難点ですが、32bitで保存する場合、無圧縮 BMP よりは遥かに小さくできると思いますよ。

で、GZipStream についていえば、実装されているのは必要最低限の部分のみとなっています。
たとえば Deflate 圧縮器のランタイムパラメーターには、「圧縮効率は最大だが最も遅い」という
圧縮レベルも定義されているのですが、GZipStream では「最速の Deflate アルゴリズム」しか使えないようです。

ただし、System.IO.Compression.GZipStream クラスそのものに関していえば、処理そのものは
きちんと動作しているようですので、この点について不具合があるということは無いと思います。


GZipStream クラスの圧縮効率を調べるため、簡単なテストをしてみてました。
拡張子 gz が、GZipStream で圧縮したバイナリです。
http://www.vb-user.net/junk/replySamples/2010.02.14.21.00/GZipStream.zip

(今回は、画像ヘッダもまとめて一緒に GZipStream で圧縮してあります)
images_logo_lg.gif    10,118 Bytes    サンプルに使った元画像(参考情報)

test.32bit.bmp       121,494 Bytes    32bit ビットマップ
test.32bit.bmp.gz     15,300 Bytes    圧縮率:12.593%

test.24bit.bmp        91,134 Bytes    24bit ビットマップ
test.24bit.bmp.gz     14,375 Bytes    圧縮率:15.773%

test.8bit.bmp         31,438 Bytes    8bit ビットマップ(256色)
test.8bit.bmp.gz      11,885 Bytes    圧縮率:37.805%

test.rle8.bmp         15,140 Bytes    RLE 8bpp 圧縮ビットマップ
test.rle8.bmp.gz      14,609 Bytes    圧縮率:96.493%


元画像は、最小サイズと最大サイズで 14.8KB~118.6KB の開きがありますが、
GZip 圧縮したものはサイズの変化が小さく、11.6KB~14.9KB 程度の開きしかありません。
そのため、圧縮率に大きな開きが出ていることが分かるかと思います。
(元となるデータ次第では、サイズが増加してしまう事もありえます)


もうひとつサンプル。GZipStream の性質を見るために、下記のコードを試してみてください。
http://www.vb-user.net/junk/replySamples/2010.02.14.21.00/Sample.txt

空のフォームに貼ると、テキストボックスが 3 つ現れます。
上の TextBox に、同じ文字を「111111111111111111111111111……」と繰り返し入力してみてください。

GZip ヘッダー等が必要な事もあり、文字数が少ないうちは圧縮後の方が大きくなってしまいますが、
ある程度の文字数になってくると、サイズが逆転する箇所が出てくるかと思います。

1文字では、2バイト→122バイトと大幅な増加となりますが、その後のサイズの変化は緩やかで、
100文字なら、200バイト→126バイトとなりますし、
1000文字なら、2000バイト→144バイトで済みます。

一方、「12345678901234567890123456789012345678901234567890」すなわち
"1234567890" の 5 回繰り返しだと、圧縮後のサイズは 150 バイトになりますが、
繰り返しの無い "12345678909876543210" だと、文字数は先ほどの 4 割しか無いのに、
圧縮後のサイズはむしろ増加することになります(164 バイト)。

ここにいろいろなデータを入力してみると、GZipStream の特徴の一端が見えてくるのではないでしょうか。


ちなみに ZIP や LHA の圧縮では、2段階の圧縮作業が行われているそうです(LZSS + Huffman 等)。

第1段階では、データをモデル化し、繰り返し箇所を見つけてパターン化する作業、
第2段階は、それぞれのパターンの発生頻度に応じて可変長データへ変換する作業、つまり
発生頻度の多いデータを少ないビット数(最低1ビット)で表して圧縮し、その逆に
発生頻度の少ないデータをより多いビット数で表すという符号化処理が行われているのだとか。

独自形式の画像を作るにあたり、もしも余力があるのならば、これら既存の
圧縮技術の仕組みについて学んでおくと、面白いかも知れません。


> できれば.NETのライブラリのみで実現したいです。
System.IO.Compression.GZipStream の圧縮レベルを変更する事はできませんが、知識と時間があれば、
圧縮処理そのものを自分で記述することは不可能では無いと思います。

もし GZipStream 単独で、よりデータ量を小さくしたいのであれば、処理前/処理後の
データ中にある余計な場所を取り除いたり、簡略化してしまうというのはどうでしょう。

たとえば、圧縮後のストリームの先頭には
 1F-8B-08-00-00-00-00-00-04-00
というデータが現れるかと思いますが、これらは
 1F: GZip をあらわす固定値(ID1)
 8B: GZip をあらわす固定値(ID2)
 08: Compression Method。(8 は、Deflate 圧縮であることを表す)
 00: ASCIIか否か、CRCの有無、元ファイル名の有無等(GZipStream ではおそらく 0 固定)
 00,00,00,00: 元ファイルの更新日時(GZipStream では 0 固定で、時刻情報無しを意味する)
 04: 拡張フラグ(4 は、Deflate の最速アルゴリズムが使われたことをあらわす)
 00: OS (00 は FAT の意味: Windows や MS-DOS で採用されているファイル形式)
という意味があります。

しかし現時点では固定値となっていますので、『独自形式の画像』を作るのであれば、
これらを省略してしまっても差し支えないでしょう(これ「だけ」では微々たる差ですが)。