Javaで画像処理(その3)

 前回のサンプルに平滑化フィルタ(中央値、平均値)、特徴抽出フィルタ(Prewitt、Solbel等)、ラプラシアン、鮮鋭化フィルタ等を追加する。

 ■ソースコード
 まず、ImageFilterインターフェイスを実装した抽象クラスAbstractImageFilterを定義し、それを継承したSptialFilterを実装する。

 ・AbstractImageFilter.java

package test;

import java.awt.Dimension;
import java.awt.image.ColorModel;
import java.awt.image.ImageFilter;

abstract class AbstractImageFilter extends ImageFilter {
	protected int pixel[];
	protected Dimension size;
	protected static ColorModel colorModel=ColorModel.getRGBdefault();
	
	public void setDimensions(int w,int h){
		pixel=new int[w*h];
		size=new Dimension(w,h);
	}

	public void setPixels(int x,int y,int w,int h,
			 ColorModel model,byte[] pixels,int offset,int scansize){
		for (int i=0;i<=h-1;i++){
			for (int j=0;j<=w-1;j++){
				pixel[x+j+(y+i)*size.width]
				  =model.getRGB(pixels[offset+j+i*scansize]&0xff);
			}
		}
	}

	public void setPixels(int x,int y,int w,int h,
			  ColorModel model,int[] pixels,int offset,int scansize){
		if(model==colorModel){
			for (int i=0;i<=h-1;i++){
				System.arraycopy(
			  	 pixels,offset+i*scansize,pixel,x+(y+i)*size.width,w);
			}
		}else{
			for(int i=0;i<=h-1;i++){
				for (int j=0;j<=w-1;j++){
					pixel[x+j+(y+i)*size.width]
					 =model.getRGB(pixels[offset+j+i*scansize]);
				}
			}
		}
	}

	public void setHints(int h){
		consumer.setHints(SINGLEPASS|(h&SINGLEFRAME));
	}

	public void setColorModel(ColorModel model){
		consumer.setColorModel(colorModel);
	}

	public void imageComplete(int status){
		if (status==IMAGEERROR||status==IMAGEABORTED){
			consumer.imageComplete(status);
			return;
		}
		filterImage();
		consumer.imageComplete(status);
	}

	protected int getPixelValue(int x,int y){
		return pixel[x+y*size.width];
	}

	protected int[] getPixelRGBValue(int x,int y){
		int p=pixel[x+y*size.width];
		int t=(p>>24&0xff);
		int r=(p>>16&0xff);
		int g=(p>>8&0xff);
		int b=(p&0xff);
		return new int[]{t,r,g,b};
	}

	protected static int[] getPixelRGBValue(int val){
		int t=(val>>24&0xff);
		int r=(val>>16&0xff);
		int g=(val>>8&0xff);
		int b=(val&0xff);
		return new int[]{t,r,g,b};
	}
	
	protected abstract void filterImage();

}

 ・SptialFilter.java

package test;

import java.util.Arrays;


public class SptialFilter extends AbstractImageFilter {
	private int width;
	private int height;
	private Operator op;
	
	public static final Operator OP_MEDIAN=new Median();
	public static final Operator OP_AVERAGE=new General(
				new double[]{0.1,0.1,0.1,0.1,0.2,0.1,0.1,0.1,0.1}	
				);
	public static final Operator OP_PREWITT=new Differential(
			new double[]{1,0,-1,1,0,-1,1,0,-1},
			new double[]{1,1,1,0,0,0,-1,-1,-1}
			);
	public static final Operator OP_SOBEL=new Differential(
			new double[]{1,0,-1,2,0,-2,1,0,-1},
			new double[]{1,2,1,0,0,0,-1,-2,-1}
			);
	public static final Operator OP_LAPLACIAN
			 =new General(new double[]{0,1,0,1,-4,1,0,1,0});
	public static final Operator OP_SHARP
			 =new General(new double[]{0,-1,0,-1,5,-1,0,-1,0});
	public static final Operator OP_VERTICALEDGE
			 =new General(new double[]{-0.5,1,-0.5,-0.5,1,-0.5,-0.5,1,-0.5});
	public static final Operator OP_HORIZONTALEDGE
			 =new General(new double[]{-0.5,-0.5,-0.5,1,1,1,-0.5,-0.5,-0.5});
	
	/**
	 * 
	 */
	public SptialFilter(Operator o) {
		op=o;
	}

	protected void filterImage() {
		int[] p=new int[width];
		for (int y=1;y<height-1;y++){
			p[0]=getPixelValue(0,y);
			for (int x=1;x<width-1;x++){
				int[] px=new int[9];
				px[0]=getPixelValue(x-1,y-1);
				px[1]=getPixelValue(x,y-1);
				px[2]=getPixelValue(x+1,y-1);
				px[3]=getPixelValue(x-1,y);
				px[4]=getPixelValue(x,y);
				px[5]=getPixelValue(x+1,y);
				px[6]=getPixelValue(x-1,y+1);
				px[7]=getPixelValue(x,y+1);
				px[8]=getPixelValue(x+1,y+1);
				p[x]=op.operate(px);
			}
			p[width-1]=getPixelValue(width-1,y);
			consumer.setPixels(0,y,width,1,colorModel,p,0,width);
		}
	}

	public static Object createAverageOp(double weight){
		double s=8+weight;
		double[] a=new double[]{
				1/s,1/s,1/s,
				1/s,weight/s,1/s,
				1/s,1/s,1/s};
		return new General(a);
	}
	
	public static Object createOp(double[] arg){
		if(arg.length!=9)throw new RuntimeException();
		return new General(arg);
	}

	public static Object createDiffOp(double[] x,double[] y){
		if(x.length!=9)throw new RuntimeException();
		if(y.length!=9)throw new RuntimeException();
		return new Differential(x,y);
	}
	
	public void setDimensions(int w,int h){
		super.setDimensions(w,h);
		width=w;
		height=h;
		consumer.setDimensions(width,height);
	}

	interface Operator{
		public int operate(int[] p);
	}

	private static class Median implements Operator{
		public int operate(int[] p) {
			Arrays.sort(p);
			return p[4];
		}
	}

	private static class General implements Operator{
		private double[] op;
		General(double[] o){
			op=o;
		}
	
		public int operate(int[] p) {
			int[] rgb=new int[4];
			int[] val=new int[4];
			for(int i=0;i<p.length;i++){
				int[] tmp=getPixelRGBValue(p[i]);
				for(int j=1;j<tmp.length;j++){
					val[j] +=(((double)tmp[j])*op[i]);
				}
			}
			rgb[0]=p[0];
			for(int i=1;i<rgb.length;i++){
				rgb[i]=(int)val[i];
				if(rgb[i]>255)rgb[i]=255;
				if(rgb[i]<0)rgb[i]=0;
			}
			return (rgb[0]<<24)+(rgb[1]<<16)+(rgb[2]<<8)+rgb[3];
		}
	}

	private static class Differential implements Operator{
		private double[] opx;
		private double[] opy;
		Differential(double[] x,double[] y){
			opx=x;
			opy=y;
		}

		public int operate(int[] p) {
			int[] rgb=new int[4];
			double[][] val=new double[2][4];
			for(int i=0;i<p.length;i++){
				int[] tmp=getPixelRGBValue(p[i]);
				for(int j=1;j<tmp.length;j++){
					val[0][j] +=(((double)tmp[j])*opx[i]);
					val[1][j] +=(((double)tmp[j])*opy[i]);
				}
			}
			rgb[0]=p[4];
			for(int i=1;i<rgb.length;i++){
				rgb[i]=(int)Math.sqrt((int)val[0][i]*(int)val[0][i]+
			      (int)val[1][i]*(int)val[1][i]);
				if(rgb[i]>255)rgb[i]=255;
				if(rgb[i]<0)rgb[i]=0;
			}
			return (rgb[0]<<24)+(rgb[1]<<16)+(rgb[2]<<8)+rgb[3];
		}
	}
}

 次いで、前回と同様、createMenuBarに以下のコードを付け加え、フィルタを適用するappSptialメソッドを追加する。

 ・createMenuBarの追加コード

		JMenu sptial=new JMenu("Sptial");
		filter.add(sptial);
		JMenuItem shape=new JMenuItem("Shape");
		shape.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				appSptial(SptialFilter.OP_SHARP);
			}
		});
		sptial.add(shape);
		
		JMenuItem prewitt=new JMenuItem("Prewitt");
		prewitt.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				appSptial(SptialFilter.OP_PREWITT);
			}
		});
		sptial.add(prewitt);
		
		JMenuItem median=new JMenuItem("Median");
		median.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				appSptial(SptialFilter.OP_MEDIAN);
			}
		});
		sptial.add(median);
		
		JMenuItem ave=new JMenuItem("Average");
		ave.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				appSptial(SptialFilter.OP_AVERAGE);
			}
		});
		sptial.add(ave);
		
		
		JMenuItem sobel=new JMenuItem("Spbel");
		sobel.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				appSptial(SptialFilter.OP_SOBEL);
			}
		});
		sptial.add(sobel);
		
		JMenuItem laplacian=new JMenuItem("Laplacian");
		laplacian.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				appSptial(SptialFilter.OP_LAPLACIAN);
			}
		});
		sptial.add(laplacian);
		
		JMenuItem vert=new JMenuItem("Vertical");
		vert.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				appSptial(SptialFilter.OP_VERTICALEDGE);
			}
		});
		sptial.add(vert);
		
		JMenuItem hori=new JMenuItem("Horizontal");
		hori.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				appSptial(SptialFilter.OP_HORIZONTALEDGE);
			}
		});
		sptial.add(hori);

 ・appSptialメソッド

	private void appSptial(SptialFilter.Operator o){
		SptialFilter fil=new SptialFilter(o);
		canvas.setBaseImage(applicateFilter(canvas.getImage(),fil,canvas));
		fil=null;
	}

 ■実行結果
 実行例は以下のとおり。左上が元画像、右上がメディアンフィルタを2回かけた後の画像、左下がPrewittフィルタを2回、右下は横線検出フィルタを2回かけた後の画像。