Posts tagged ‘ImageIO’

gif画像のリサイズ

透過色のあるgif画像の出力
で、透過色付きgif画像の出力は出来たが、出力したgifファイルをリサイズする必要が出てきました。

これを実現するには、アフィン変換(アフィン写像参照)という考え方を使って行うらしい。
アフィーンというかギャフンという感じだ。

実装はAffineTransformOpを使って元イメージをフィルタさせるらしい。

このクラスは、アフィン変換を使用して、ソースのイメージまたは Raster の 2 次元座標からデスティネーションのイメージまたは Raster の 2 次元座標への線形マッピングを実行します。使用される補間のタイプは、コンストラクタを介して、RenderingHints オブジェクトまたはこのクラスで定義されている整数型補間タイプのうちの 1 つによって指定されます。

それにしてもいつもすごく分かりにくい説明ですね。

ちなみに、java.awt.imageパッケージのxxxxOpというクラスは、操作対象があって、その対象に対しての操作が出来るモノらしいです。
AffineTransformOpは、文字通りアフィン変換を対象に対して行うものです。

アフィン変換にはいくつか種類があって、リサイズということは拡大縮小(スケーリング)なので、その内容を指定して
AffineTransformを引数として
AffineTransformOpに渡します。

AffineTransformは、スケーリング変換の場合AffineTransform#getScaleInstance
取得できますが、この引数はリサイズ後のサイズではなく、元画像とリサイズ後画像の比率(リサイズ後画像/元画像)を指定します。

	//比率を出す
	double resizeWidthRatio = (double)resizeWidth / (double)src.getWidth();
	double resizeHeightRatio = (double)resizeHeight / (double)src.getHeight();
 
	AffineTransform at = AffineTransform.getScaleInstance(resizeWidthRatio, resizeHeightRatio);
	AffineTransformOp atOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);

このAffineTransformOpの第二引数には以下の3つのどれかを指定します。

バイキュービック法によると、TYPE_BICUBICを選択するのが画質が良さそう。

しかし、これでgifファイルを書き込みしても透過されてくれないので、自力でやる必要がある。
記事が埋もれそうなので次回に。


gifイメージの出力

javaのImageIOで画像の出力が出来るが、今のところ、pngだと特に問題なく出力出来る。

//サーブレットで使う場合
	private void writeImage(BufferedImage image){
 
		HttpServletResponse response = ResponseUtil.getResponse();
		ServletOutputStream sos = null;
		ImageOutputStream ios = null;
		try{
		sos = response.getOutputStream();
		response.setContentType("image/png");
		ios = ImageIO.createImageOutputStream(sos);
 
		ImageWriter iw = ImageIO.getImageWritersByMIMEType("image/png").next();
 
		iw.setOutput(ios);
		iw.write(image);
		iw.dispose();
 
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try {
				ios.close();
				sos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
 
	}

上記の場合、BufferedImageをTYPE_INT_ARGBにして、生成したBufferedImageのgraphicsに
以下のように指定すると背景がとりあえず透過色になるようです。

BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
Graphics2D gdImage = image.createGraphics();
gdImage.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));

しかし、gifで出力した際に、上記指定をして出力すると背景が真っ黒になってしまう。
なぜだろう?と思ったが、gifは256色までしか扱えないが、pngはフルカラーをサポートし、
さらにアルファチャンネルというのを持ち、半透明などの色も扱える。
gifは透過色を1色しか指定出来ない。

なので、pngで微妙に透過された表示できていた色が滅色されて真っ黒になってしまったのだろうと思いました。
じゃあgifで透過色を指定するのはどうやるの?

色々調べたところ、この

BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);

のところで第三引数にTYPE_INT_ARGBを指定した場合
JavaDocによると

整数型ピクセルにパックされた 8 ビット RGBA 色成分によるイメージを表します。このイメージは、DirectColorModel (アルファあり) を持ちます。このイメージのカラーデータには、アルファがあらかじめ乗算されていません。このタイプが BufferedImage コンストラクタへの imageType 引数として使用される場合、作成されるイメージは JDK 1.1 以前のリリースで作成されるイメージと一貫性があります。

と記載があり、DirectColorModelとかいうものを使っているらしいということがわかりました。
さらにその説明を見る
なにやらよく分かりませんが、

このカラーモデルは X11 の TrueColor に類似しています。

とあるので、約1670万色を扱っているのだとわかります。
gifでサポートしているのは256色が限度なので、ImageIOで書き込みする際には微妙な色は滅色されることが予想されます。
じゃあどうするのか。

BufferedImageには

public BufferedImage(int width,
int height,
int imageType,
IndexColorModel cm)

なコンストラクタがあります。
先ほど出てきたDirectColorModelの仲間っぽい「IndexColorModel」というのが出てきます。
これはなんだ?と思ってこちらに分かりやすく説明してくれているページを見つけたのですが、
どうしても理解できず、それでまずはビット演算や、シフト演算
を最初から勉強しなおしたのでした。