java.awt.image.PixelGrabber

目的
イメージにモザイクフィルタをかける

関連クラス

今回のソース
//////////////////// PixelGrabberDemo.java ////////////////////

class PixelGrabberDemo
{
	public static void main(String args[])
	{
		// パラメータの取得
		String image_name = args[0];
		int rate = Integer.parseInt(args[1]);

		new MainWindow(image_name,rate);
	}
}

////////////////////////////////////////////////////////////

//////////////////// MainWindow.java ////////////////////

import java.awt.*;
import java.awt.event.*;

// メインウインドウ
class MainWindow extends Frame
{
	public MainWindow(String image_name,int rate)
	{
		super("Mosaic Filter");

		// ウインドウリスナの追加
		WindowListener listener = new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		};
		addWindowListener(listener);

		// イメージの取得
		Toolkit toolkit = getToolkit();
		Image image = toolkit.getImage(image_name);

		// イメージの読み込み待ち
		MediaTracker tracker = new MediaTracker(this);
		tracker.addImage(image,1);

		try
		{
			tracker.waitForID(1);
		}
		catch(InterruptedException e)
		{
			System.err.println(e.toString());
		}

		// キャンバスの生成
		ImageCanvas canvas = new ImageCanvas(image,rate);
		canvas.setSize(image.getWidth(canvas),image.getHeight(canvas));
		add(canvas,BorderLayout.CENTER);

		// ウインドウの表示
		pack();
		setVisible(true);
	}
}

////////////////////////////////////////////////////////////

//////////////////// ImageCanvas.java ////////////////////

import java.awt.*;
import java.awt.image.PixelGrabber;
import java.awt.image.MemoryImageSource;

// イメージ表示キャンバス
class ImageCanvas extends Canvas
{
	Image image;
	Image mosaic_image;

	// コンストラクタ
	public ImageCanvas(Image image,int rate)
	{
		MosaicFilter filter = new MosaicFilter(image,rate);
		mosaic_image = filter.pixelize(this);
	}

	// イメージの描画
	public void paint(Graphics g)
	{
		g.drawImage(mosaic_image,0,0,this);
	}
}

////////////////////////////////////////////////////////////

//////////////////// MosaicFilter.java ////////////////////

import java.awt.*;
import java.awt.image.PixelGrabber;
import java.awt.image.MemoryImageSource;
import java.awt.image.ImageObserver;

class MosaicFilter
{
	ImageObserver observer;
	Image image;
	int rate;
	int pixels[];

	public MosaicFilter(Image image,int rate)
	{
		this.image = image;
		this.rate = rate;
	}

	// イメージのモザイク化
	public Image pixelize(ImageObserver observer)
	{
		this.observer = observer;
		int width = image.getWidth(observer);
		int height = image.getHeight(observer);

		// イメージの幅、高さがレートで割り切れるかどうか
		// 割り切れないときは、補正ismodをかける
		int ismod = 0;
		if((width % rate + height % rate) > 0)
		{
			ismod = 1;
		}

		// PixelGrabberの構築
		pixels = new int[width * height];
		PixelGrabber pg = new PixelGrabber(image,0,0,width,height,pixels,0,width);

		try
		{
			// 配列にイメージが格納されるのを待つ
			pg.grabPixels();
		}
		catch(InterruptedException e)
		{
			System.err.println(e.toString());
		}

		// 各基点を探し出す
		for(int i=0;i<pixels.length - (rate * width) * ismod;i+=width * rate)
		{
			for(int j=0;j<width - ((rate - 1) * ismod);j+=rate)
			{
				HandleBasicPixel(i+j);
			}
		}

		MemoryImageSource memory_image = new MemoryImageSource(width,height,pixels,0,width);
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		return(toolkit.createImage(memory_image));
	}

	// 基点を元にした矩形の取得とピクセル値の合成
	protected void HandleBasicPixel(int point)
	{
		int width = image.getWidth(observer);
		int red,green,blue;
		red = green = blue = 0;

		// 矩形の色の取得と合成
		for(int i=0;i<rate;i++)
		{
			for(int j=0;j<rate;j++)
			{
				int current_point = point + i*width + j;

				red += ((pixels[current_point] & 0x00FF0000) >> 16);
				green += ((pixels[current_point] & 0x000FF00) >> 8);
				blue += pixels[current_point] & 0x000000FF;
			}
		}

		// 矩形内の色の平均値を計算
		red = (red / (rate*rate)) << 16;
		green = (green / (rate*rate)) << 8;
		blue = blue / (rate*rate);

		// 各ピクセルに合成値をセット
		for(int i=0;i<rate;i++)
		{
			for(int j=0;j<rate;j++)
			{
				int current_point = point + i*width + j;
				pixels[current_point] = 0xFF000000 | red | green | blue;
			}
		}
	}
}

////////////////////////////////////////////////////////////
Source is here. (ZIP Format,2422Byte,Shift-JIS)

コンパイル&実行
javac PixelGrabberDemo.java
java PixelGrabberDemo <ファイル名(イメージ)> <レート>

説明
(概略)

まずは、実行イメージを見てください。

モザイク前モザイク後

左がモザイクをかける前、右がモザイクをかけた後です。
モザイクがかかっている方は、5Pixelの矩形(正方形)が並ぶ形になっています。
つまり、5x5の矩形の中の色を混ぜて、
5x5の矩形の全てのピクセルに、混ざった色を設定しています。

以下の図を見てください。

モザイクの説明

これは、8Pixelx8Pixelのイメージをモデル化したものです。
青い枠で囲まれたのが、各ピクセルに相当し、
赤い枠で囲まれたのが、矩形に相当します。(レート2)
上で説明したのは、この赤枠で囲まれた4Pixel全ての色を混ぜ合わせ、
その4Pixelに、出来上がった混色を入れてやるということです。

黄緑色で塗られたピクセルは、基点と定義しました。
この基点は、各正方形の一番左上の点で、
矩形を探し出すのに利用しています。

(サンプルプログラムの説明)

イメージの読み込み待ち
以前、長いファイルを扱った時に、全てが読み込まれない
という症状に困ったということがありましたが、
イメージでも、きちんと読み込まれていなくて、
Widthが取得出来なくて悩んでしまいました。
やはり、イメージもサイズが大きいファイルの部類に入るので、
読み込み待ちの処理はしておいた方が無難でしょう。
canvas.setSize(image.getWidth(canvas),image.getHeight(canvas));
キャンバスのサイズを合わせています。
間違って、フレームのサイズを合わせてしまうと、
タイトルバーが考慮されず、イメージが切れてしまうので注意しましょう。

また、このプログラムにおける、ImageObserverは、
ImageCanvasクラスにあたります。
ImageObserverとは、Imageの情報を受け取るためのインターフェースです。
つまり、イメージの幅や高さなどは、ImageObserverを介して取得します。
各基点を探し出す
ここでいう基点とは、モザイクの各矩形の左上のポイントです。
この基点を元に、矩形の各点の色情報を得ます。(概略参照)
矩形の色の取得と合成
矩形内の色を取得し、全て足しあわせ、
矩形内のピクセルの数で割って平均し、
各ピクセルの色と置き換えます。(色の平均化)
int current_point = point + i*width + j;
該当ピクセルの計算なのですが、
2次元配列を扱うわけではないので、相当面倒ですね。
PixelGrabberを使うときは、プログラミングだけでなく、
考え方も柔軟で無いとダメ!?