2017年5月22日月曜日

インデックスカラー

 C#でBitmapをインデックスカラーに変換するのは、Bitmap.Clone(Rectangle, PixelFormat)で、Arg2にFormat8bppIndexedとかを指定すればいい。他にも、4bppや2bpp、1bppも指定できる。ちなみに1bppは白黒の2値画像となる(ネタバレ)。

 Bitmap.CloneでIndexedに変換した場合、以下のカラーパレットが使われる。
たぶん固定値で、変化はしないはず。実際に使われているのは16x14で224色となる。

 Bitmap.Paletteを書き換えればIndexに対するColorを変更することができるが、もちろんIndexとPaletteの関係は変化しないから、単純に色が変わるだけとなる。
 ちらっと試した限りでは自前のPaletteで減色することはできないらしい。そもそも、自分でPaletteを用意できるなら、実際の減色まですべて実装すべき、ということなのだろう。差の評価や拡散によっても再現度はかなり変わるから、自前で作れというのはもっともだと思う。

 それと、帯域が制限されてる回線で画像データを送りたいので、8bppにしてから簡単なランレングスで圧縮してみた。アニメのワンシーンを評価に使うと、画面が一色のときで2%程度、普段が40%前後、状況によって70%くらいの容量となった(ただ簡単にエンコードを書いただけで、デコードも試してないので正しくエンコードされているか不明)。
 減色のアルゴリズムはいくつか有って、グラデーションは閾値を超えるまで同じ色を続けるタイプや、複数の色を並べて見た目上の色をグラデーションさせたりという違いが有る。C#では前者を使っているが、ランレングスでは前者のほうが相性がいい。後者だとグラデーションでは頻繁に値が変化するので、もっと効率のいい圧縮方法を使う必要がある。

 入力画像毎にパレットを生成しなおせば、多少は再現度の高い圧縮が可能なはずだが、1つのパレットを使い続けていれば、最初に1回だけパレットを転送すればいいという利点がある。8bppのパレットを素直に転送すると768バイトになるが、100px四方の8bppは10kB程度で、圧縮して60%くらいまで減らせば6kBになる。そうするとパレットデータが1割くらいを占めるので、かなりデカい。

 そもそもPNGを使えよとか、JPEGを使えよ、という話もあるだろうけど、転送先がワンチップマイコンだったりすると、いちいち展開するのが面倒。今回は相手の処理の都合でインデックスカラーが欲しかったので、JPEGとかで帯域を節約するよりも、減色処理のほうが負荷が高そうということで、8bppBMPを転送することにした。


 そういえば、WinXPとかのペイントで8bppで保存するとかなり劣化したけど、Win7あたりからかなり改善したよなーと思って、Win10のペイントで保存してみたら、かなり劣化した。あれれぇ~~、おかしいなぁ~~。

***

 気がついたらもうこんな時間。ここ2週間くらいはかなり早く寝てたんだけど、急に位相ロックが外れた。夜は涼しくて良いねぇ。静かだし。虫やカエルがうるさいけど。

0 件のコメント:

コメントを投稿