コンピュータを楽しもう!!

今、自分が面白くていろいろやってみたことを書き綴りたいと思います。連絡先はtarosa.yでgmail.comです。

縮小画像をきれいに表示したい

動物将棋を作っていて「駒が小さい」という意見がありました。私の端末はQVGAなので現在の駒サイズでちょうど良い感じなのですが、世の中は画面の大きな端末だらけなので、QVGA用にサイズを合わせた駒サイズ(約50×50pixel)では小さくて、見えないというのはわかる気がします。

そこで、正月休みに大きな画面サイズにも対応できるように、動物将棋のプログラムを改造していました。考え方は簡単で、もともと大きな画像を用意しておき、表示時に画面サイズに合わせて小さくして表示しようというものです。

大きな画像の用意

実際に、250×250ピクセルの駒を用意しました。駒は下記のような感じです。ちなみに、駒がメロンなのは、動物将棋は卒業して、くだものしょうぶというゲームに代えて公開しようかなという考えからです。

駒は「すいか」「メロン」「バナナ」「みかん」「ぶどう」の5種類です。このサイズの駒を用意して、縮小して使おうと考えました。

駒を縮小する

現在の駒のサイズは、52×52ピクセルです。Luaridaの命令だと画像読み込み時に、下記のようにするだけです。

 canvas.loadBmp( "/sdcard/luarida/melon.png", 0, 0, 51, 51 )

すると、下のような残念な画像になってしまいました。

この原因は、Luaridaのjavaプログラム内で縮小時にアンチエイリアスなどの処理を行っていないことが原因かなと思って、プログラムの見直しを始めました。

javaプログラム

javaのプログラムでは、画像をワークに取り込むときに、単純に下記のような感じで処理をしているだけです。

workcanvas.drawBitmap(bmp, new Rect(0, 0, w, h), new Rect(gx0, gy0, gx1+1, gy1+1), null);

そこで、テストプログラムを作り、縮小部分を下記のように改造してみました。

Matrix matrix = new Matrix();
float sc = 52f/250f;
matrix.postScale( sc, sc );
Bitmap scaledBitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix,true);

これで、scaledBitmapは52×52ピクセルの補正が効いた画像になっているはずです。ところが、結果は上とまったく同じでした。drawBitmap()は補正が掛かっているのですね。たぶん。

段階的に縮小する

もっと、元画像に近い画像に縮小できるように、いろいろと試してみましたが、きれいな画像を得ることができませんでした。仕方が無いので、自力でLanczos法やBi-Cubic法、Bi-Linear法のプログラムを実装し、パラメータを操作して、何とかきれいな画像を出せないかと試みましたが、結果的にはだめで、諦めました。

この試行錯誤で得た結果は、「一気に縮小してきれいな画像を得ようとするには限界がある」でした。

そこで、段階的に縮小する方向に考え方を代えました。例えば、250×250ピクセルの画像をn回縮小処理を繰り返して52×52ピクセルの画像を生成するという考え方です。この場合、n回で250ピクセルから52ピクセルに縮小するには、縮尺率scをいくつにすればいいかという話ですが、250*sc*sc*sc・・・n回 = 52ということなので、250*sc^n=52という式になります。
この式を解くと、sc = exp(ln(52/250)/n)となります。早速これをjavaでプログラムしてみました。

int wx = bmp.getWidth();
int newwx = 52;
Matrix matrix = new Matrix();
int n = 3; //縮小回数
float sc = (float)Math.exp(Math.log((float)newwx/(float)wx)/(float)n);
matrix.postScale( sc, sc );
Bitmap scaledBitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix,true);
for( int i=0; i<n-1; i++ ){
  scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix,true);
}
workcanvas.drawBitmap(scaledBitmap, new Rect(0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight()), new Rect(0, 0, newwx, newwx), null);

プログラム自体は、matrix.postScale()によって設定した縮尺率の画像変換をループで繰り返すという単純なものです。結果は良好でした。
n=1の場合、今までと同じ結果です。

n=2の場合、少し変わりました。

n=3の場合、メロンの線が繋がってきました。

n=4の場合、元絵と同じイメージになりました。

n=5の場合、nが大きくなると、エッジがボケてきますね。

n=10の場合、n=10にしてみました。イメージは元絵に一番近いですが、全体的にボケていますね。

n=4くらいがいい感じでしょうか。

拡大にも有効か?

scが1.0以上の場合も、上のプログラムが有効に働くかどうかを試してみました。今度は逆に44×44ピクセルの画像を250×250ピクセルの画像に拡大してみたいと思います。
元絵は下です。

n=1の場合、一発変換の場合です。

n=2の場合、少しボケてきています。

n=3の場合、ボケが進みます。

n=10の場合、すごくボケています。

拡大に関しては、一発で拡大しても問題無いことがわかりました。nが増えると逆に画像がボケていくので、逆効果であり、拡大には有効な手段ではないことがわかりました。

Luaridaの画像表示命令については、nを引数に持てるように変更したいと思います。