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回かけた後の画像。