圧縮が効かない

タグの編集
投稿者 トマト  (小学生) 投稿日時 2010/2/9 01:53:32
こんにちは。もうすぐ卒業のトマトです。また変な質問に来ました。
C#で新しい画像ファイル(独自の)を作っているのですが、ファイルサイズが普通のビットマップと同じぐらいに大きくなってしまうのでデフレートやGZIPで圧縮してみたのですが全然効果がありません。
それどころか12MB→20MBぐらいになりました。(写真)
保存方法は、FileStreamでARGBを順番に1バイトずつ保存しています。

できれば.NETのライブラリのみで実現したいです。
投稿者 (削除されました)  () 投稿日時 2010/2/9 04:31:25
(削除されました)
投稿者 brv  (中学生) 投稿日時 2010/2/10 08:09:01
>保存方法は、FileStreamでARGBを順番に1バイトずつ保存しています。
おそらく、この方法だと普通のビットマップ並み(ビットマップ以上?)に重くなります。
ZIPしても軽くならないということは、圧縮する画像が写真なのでしょう。

写真をJPEG画像くらいに圧縮するには、かなり高い技術が必要です。
JPEG形式で保存するときは、人の目に認識されにくい色の要素を削除したり、色の変化を波で表したりして画像を軽くしています。これを直接行う.NETのライブラリはないと思います。

普通にJPEGで保存すればよいと思いますが、
なぜ独自形式の画像を作ろうと考えているのでしょうか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2010/2/11 01:14:52
2色(1bit)ビットマップなら、1バイトで8ドット分の情報を表せますし、
16色(4bit)ビットマップなら、1バイトで2ドット分の情報を表せます。
256色(8bit)ビットマップなら、1バイトで1ドット分の情報を表せます。


> ARGBを順番に1バイトずつ保存しています。
ということは、1ドットあたり 4バイト必要ということですよね。(32bitカラー)

その場合、たとえば 縦横2000ドットの正方形写真があり、それを非圧縮で格納する場合、
単純換算でも、2000×2000×4 のバイト数、つまり約15MBの領域が必要となります。
(実際には、これに加えて画像サイズや色数などの情報が必要になります)


> デフレートやGZIPで圧縮してみたのですが
データの並びによっては、圧縮処理によって、かえって肥大化してしまう事もあります。
(既に JPEG/PNG 等で圧縮済みのデータを、さらに圧縮しようとした場合など)


> 全然効果がありません
独自形式にこだわらないなら、Png 形式で保存してしまうとか。
http://dobon.net/vb/dotnet/graphics/saveimage.html

この場合、作成した画像を AzConvPNG 等で再保存すれば、さらに小さくなる可能性もあります。
http://hp.vector.co.jp/authors/VA033749/index.html


> できれば.NETのライブラリのみで実現したいです。
圧縮アルゴリズムの話を知りたいのであれば、このあたりは如何でしょう。

考え方自体の話なので、System.IO.Compression などのように、手軽に使うというわけには
いきませんが、実装の手間さえ惜しまなければ、.NET Framework のみでも実現可能かと思います。

http://www.ruche-home.net/?%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%2FBMP%A5%D5%A5%A1%A5%A4%A5%EB%BB%C5%CD%CD%2FRLE%A4%CB%A4%E8%A4%EB%B0%B5%BD%CC
http://www.01-tec.com/document/basic_compression.html
http://oku.edu.mie-u.ac.jp/~okumura/compression/
http://www.amazon.co.jp/o/ASIN/4797324287
http://www.amazon.co.jp/o/ASIN/478983672X
投稿者 トマト  (小学生) 投稿日時 2010/2/14 21:58:56
おでかけしていて返信遅くなりました。

>おそらく、この方法だと普通のビットマップ並み(ビットマップ以上?)に重くなります。
アルファをなしにするとビットマップより数バイト小さくなります。

>なぜ独自形式の画像を作ろうと考えているのでしょうか? 
透明に対応したビットマップを作ろうとしまして・・・。
でもあまりにファイルが大きくなるので圧縮しようと思いました。

非圧縮でのファイルの構造は
・UTF-8で「SBM」
・アルファが含まれているか(0か1)
・UTF-8で横のサイズ
・Byte(255)
・UTF-8で縦のサイズ
・Byte(255)
・ARGBの繰り返し
となっています。

デフレートでいろいろな画像で試してみます。
投稿者 トマト  (小学生) 投稿日時 2010/2/14 22:11:50
ペイントで描いた簡単な絵でもダメでした。ファイルが大きくなりました。
非圧縮で行くか難しいのをゴリゴリ書くか・・・

とりあえずありがとうございました。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2010/2/14 23:49:54
> 透明に対応した
“透明”には、大きく分けて 2 種類のフォーマットがあります。
GIF のように、パレットの特定色を「透明色」とするフォーマットと、
PNG のように、アルファ値をもって「透明度」を持たせるフォーマットです。

> ビットマップを作ろうとしまして・・・。
アルファ値に対応した画像フォーマットには、
 ・32bit カラーの BMP ファイル/DIB ファイル
 ・32bit カラーの PNG ファイル
 ・32bit カラーの ICO ファイル
 ・32bit カラーの EMF ファイル
などがありますので、独自形式にこだわる必要は無いかも。
投稿者 トマト  (小学生) 投稿日時 2010/2/15 00:53:06
32bitのビットマップってあるんですかぁ。

いきなりですが、ちょっと前にもGZIP圧縮が効かなかったこともありました。.NETの圧縮ってちゃんと使えるものなんですか?
投稿者 (削除されました)  () 投稿日時 2010/2/15 01:22:41
(削除されました)
投稿者 (削除されました)  () 投稿日時 2010/2/15 05:50:26
(削除されました)
投稿者 (削除されました)  () 投稿日時 2010/2/15 06:11:11
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 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 で採用されているファイル形式)
という意味があります。

しかし現時点では固定値となっていますので、『独自形式の画像』を作るのであれば、
これらを省略してしまっても差し支えないでしょう(これ「だけ」では微々たる差ですが)。
投稿者 トマト  (小学生) 投稿日時 2010/2/17 23:13:18
圧縮ってむずかしいなぁ。
サンプルまで用意してくださってありがとうございます。