Javaで画像処理(その5)

 前回の続き。境界抽出フィルタと細線化フィルタを追加する。

 ■ソースコード
  AbstractImageFilterを継承した、BorderTraceFilterクラスとThinningFilterクラスを実装する。
 ・BorderTraceFilter.java

package test;

import java.awt.Color;

public class BorderTraceFilter extends AbstractImageFilter {
	private int width;
	private int height;
	private int back;
	
	public BorderTraceFilter(Color back){
		this.back=(back.getRGB()<<8);
	}

	public void setDimensions(int w,int h){
		super.setDimensions(w,h);
		width=w;
		height=h;
		consumer.setDimensions(width,height);
	}

	protected void filterImage() {
		int[][] p=createBinary();
		int[][] b=new int[p.length][p[0].length];
		b=border_trace(p,b);
		int[] px=new int[width];
		int backcolor=(back>>8);
		for(int y=0;y<height;y++){
			for(int x=0;x<width;x++){
				if(b[x][y]==0){
					px[x]=(255<<24)+backcolor;
				}else{
					px[x]=getPixelValue(x,y);
				}
			}
			consumer.setPixels(0,y,width,1,colorModel,px,0,width);
		}
	}

	private int[][] border_trace(int[][] p,int[][] b){
		int code=0;
		for(int y=1;y<height-1;y++){
			for(int x=1;x<width-1;x++){
 				if(p[x][y]==1&&b[x][y]==0){
					if(p[x-1][y]==0){
						code=0;
						trace(x,y,code,p,b);
					}else if(p[x+1][y]==0){
						code=4;
						trace(x,y,code,p,b);
					}
				}
			}
		}
		return b;
	}

	private void trace(int x,int y,int code,int[][] p,int[][] b){
		if(!check(x,y,p))return;
		int xs=x;
		int ys=y;
		int x1=x;
		int x2=0;
		int y1=y;
		int y2=0;
		while(x2!=xs||y2!=ys){
			switch(code){
				case 0:
					x2=x1;
					y2=y1+1;
					if(y2<height&&p[x2][y2]==1){
						code=6;
					}else{
						code=2;
					}
					break;
				case 2:
					x2=x1+1;
					y2=y1;
					if(x2<width&&p[x2][y2]==1){
						code=0;
					}else{
						code=4;
					}
					break;
				case 4:
					x2=x1;
					y2=y1-1;
					if(y2>=0&&p[x2][y2]==1){
						code=2;
					}else{
						code=6;
					}
					break;
				case 6:
					x2=x1-1;
					y2=y1;
					if(x2>=0&&p[x2][y2]==1){
						code=4;
					}else{
						code=0;
					}
					break;
			}
			if(x2>=0&&x2<width&&y2>=0&&y2<height){
				if(p[x2][y2]==1){
					b[x2][y2]=1;
					x1=x2;
					y1=y2;
				}
			}
		}
	}

	private int[][] createBinary(){
		int[][] ret=new int[width][height];
		int p;
		for(int y=0;y<height;y++){
			for(int x=0;x<width;x++){
				p=(getPixelValue(x,y)<<8);
				if(p==back){
					ret[x][y]=0;
				}else{
					ret[x][y]=1;
				}
			}
		}
		return ret;
	}

	private boolean check(int x,int y,int[][] p){
		if(p[x-1][y]==0&&p[x+1][y]==0&&p[x][y-1]==0&&p[x][y+1]==0){
			return false;
		}else{
			return true;
		}
	}
}

 ・ThinningFilter.java

package test;

import java.awt.Color;

public class ThinningFilter extends AbstractImageFilter {
	private int width;
	private int height;
	private int back;
	
	public ThinningFilter(Color back){
		this.back=(back.getRGB()<<8);
	}

	public void setDimensions(int w,int h){
		super.setDimensions(w,h);
		width=w;
		height=h;
		consumer.setDimensions(width,height);
	}

	protected void filterImage() {
		thnning();
	}

	private void thnning(){
		int[][] main=createBinary();
		int[][] sub=createBinary();
		int[] aa=new int[9];
		int[] bb=new int[9];
		int rev=1;
		int num=0;
		while(rev!=0){
			rev=0;
			num++;
			for(int y=1;y<height-1;y++){
				for(int x=1;x<width-1;x++){
					if(main[x][y]==0)continue;
					aa[0]=main[x+1][y];		bb[0]=sub[x+1][y];
					aa[1]=main[x+1][y-1];	bb[1]=sub[x+1][y-1];
					aa[2]=main[x][y-1];		bb[2]=sub[x][y-1];
					aa[3]=main[x-1][y-1];	bb[3]=sub[x-1][y-1];
					aa[4]=main[x-1][y];		bb[4]=sub[x-1][y];
					aa[5]=main[x-1][y+1];	bb[5]=sub[x-1][y+1];
					aa[6]=main[x][y+1];		bb[6]=sub[x][y+1];
					aa[7]=main[x+1][y+1];	bb[7]=sub[x+1][y+1];
					int sum=0;
					for(int i=0;i<8;i++) sum +=aa[i];
					if(sum==0)sub[x][y]=0;
					if(sum>=2&&sum<=5){
						if(connect(aa)==1&&connect(bb)==1){
							for(int j=1;j<5;j++){
								if(bb[j]==0){
									int c=aa[j];
									aa[j]=0;
									if(connect(aa)!=1){
										aa[j]=c;
										break;
									}
									aa[j]=c;
								}
								sub[x][y]=0;
							}
						}
					}
					if(sub[x][y]==0)rev++;
				}
			}
			for(int y=0;y<height;y++){
				for(int x=0;x<width;x++){
					main[x][y]=sub[x][y];
				}
			}
		}
		int backcolor=(back>>8);
		for(int y=0;y<height;y++){
			int[] p=new int[width];
			for(int x=0;x<width;x++){
				if(main[x][y]==0){
					p[x]=(255<<24)+backcolor;
				}else{
					p[x]=getPixelValue(x,y);
				}
			}
			consumer.setPixels(0,y,width,1,colorModel,p,0,width);
		}
	}

	private int connect(int[] val){
		val[8]=val[0];
		int num=0;
		for(int i=1;i<val.length;i++){
			if(val[i]==1&&val[i-1]==0)num++;
		}
		return num;
	}

	private int[][] createBinary(){
		int[][] ret=new int[width][height];
		int p;
		for(int y=0;y<height;y++){
			for(int x=0;x<width;x++){
				p=(getPixelValue(x,y)<<8);
				if(p==back){
					ret[x][y]=0;
				}else{
					ret[x][y]=1;
				}
			}
		}
		return ret;
	}

}

次いで、createMenuBarに以下のコードを付け加える。

		JMenuItem tin=new JMenuItem("Thinning");
		tin.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				ThinningFilter fil=new ThinningFilter(Color.WHITE);
				canvas.setBaseImage(applicateFilter(canvas.getImage(),fil,canvas));
				fil=null;
			}
		});
		filter.add(tin);
		
		JMenuItem bor=new JMenuItem("BorderTrace");
		bor.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				 BorderTraceFilter fil=new  BorderTraceFilter(Color.WHITE);
				canvas.setBaseImage(applicateFilter(canvas.getImage(),fil,canvas));
				fil=null;
			}
		});
		filter.add(bor);

 ■実行結果
 実行結果は、以下のとおり(元画像、細線化、境界抽出)。