元派遣プログラマの自称技術系ブログです。雑記とか自作のオープンソースプロジェクトの話とか。
Javaとか組込とかできます。お仕事ください。

逆行列を使わないパースペクティブ補正付きのパターン取得クラス

逆行列を使わずに馬鹿正直に方程式を解いて、パースペクティブ補正をしながらパターンを取得するクラスができました。

まだ高速化していないので速度的にはそんなに早くないですが、読みやすく書いてあります。

get_cparaのところにちょっと細工があって、8x8方程式を4x5方程式2個に分割して解いてます。定数項があるので、計算量はもっと減るはずですけどね。

/**
 * 遠近法を使ったパースペクティブ補正付きのINyARColorPatt
 *
 */
public class NyARColorPatt_Perspective implements INyARColorPatt
{
	private int[] _patdata;
	private NyARBufferReader _buf_reader;
	private NyARRgbPixelReader_INT1D_X8R8G8B8_32 _pixelreader;
	private NyARIntSize _size;
	
	private static final int LOCAL_LT=100;
	private NyARIntPoint2d _pickup_lt=new NyARIntPoint2d();
	private NyARIntSize _pickup_size=new NyARIntSize();

	/**
	 * 例えば、64
	 * @param i_width
	 * 取得画像の解像度幅
	 * @param i_height
	 * 取得画像の解像度高さ
	 */
	public NyARColorPatt_Perspective(int i_width, int i_height)
	{
		//入力制限
		assert i_width>2 && i_height>2;
		
		this._size=new NyARIntSize(i_width,i_height);
		this._patdata = new int[i_height*i_width];
		this._buf_reader=new NyARBufferReader(this._patdata,NyARBufferReader.BUFFERFORMAT_INT1D_X8R8G8B8_32);
		this._pixelreader=new NyARRgbPixelReader_INT1D_X8R8G8B8_32(this._patdata,this._size);
		setEdgeSize(0,0);
		return;
	}
	/**
	 * 矩形領域のエッジサイズを指定します。
	 * エッジの計算方法は以下の通りです。
	 * 1.マーカ全体を(i_x_edge*2+width)x(i_y_edge*2+height)の解像度でパラメタを計算します。
	 * 2.ピクセルの取得開始位置を(i_x_edge/2,i_y_edge/2)へ移動します。
	 * 3.開始位置から、width x height個のピクセルを取得します。
	 * 
	 * ARToolKit標準マーカの場合は、width/2,height/2を指定してください。
	 * @param i_x_edge
	 * @param i_y_edge
	 */
	public void setEdgeSize(int i_x_edge,int i_y_edge)
	{
		assert(i_x_edge>=0);
		assert(i_y_edge>=0);
		//ピックアップサイズの決定
		this._pickup_lt.x=i_x_edge+LOCAL_LT;
		this._pickup_lt.y=i_y_edge+LOCAL_LT;
		//ピックアップ開始位置を決定
		this._pickup_size.w=i_x_edge*2+this._size.w;
		this._pickup_size.h=i_y_edge*2+this._size.h;
		return;
	}
	public void setEdgeSizeByPercent(int i_x_percent,int i_y_percent)
	{
		assert(i_x_percent>=0);
		assert(i_y_percent>=0);
		setEdgeSize(this._size.w*i_x_percent/50,this._size.h*i_y_percent/50);
		return;
	}

	
	public final int getWidth()
	{
		return this._size.w;
	}
	public final int getHeight()
	{
		return this._size.h;
	}
	public final NyARIntSize getSize()
	{
		return 	this._size;
	}
	public final INyARBufferReader getBufferReader()
	{
		return this._buf_reader;
	}
	public final INyARRgbPixelReader getRgbPixelReader()
	{
		return this._pixelreader;
	}
	final protected boolean get_cpara(final NyARIntPoint2d[] i_vertex,double[] o_param)throws NyARException
	{
		double[][] la1,la2;
		double[] ra1,ra2;
		double ltx=LOCAL_LT;
		double lty=LOCAL_LT;
		double rbx=LOCAL_LT+this._pickup_size.w;
		double rby=LOCAL_LT+this._pickup_size.h;
		la1=new double[4][5];
		la2=new double[4][5];
		ra1=new double[4];
		ra2=new double[4];
		//A,B,C,(GH)の方程式
		la1[0][0]=ltx;	la1[0][1]=lty;	la1[0][2]=1;	la1[0][3]=-ltx*i_vertex[0].x;	la1[0][4]=-lty*i_vertex[0].x;
		la1[1][0]=rbx;	la1[1][1]=lty;	la1[1][2]=1;	la1[1][3]=-rbx*i_vertex[1].x;	la1[1][4]=-lty*i_vertex[1].x;
		la1[2][0]=rbx;	la1[2][1]=rby;	la1[2][2]=1;	la1[2][3]=-rbx*i_vertex[2].x;	la1[2][4]=-rby*i_vertex[2].x;
		la1[3][0]=ltx;	la1[3][1]=rby;	la1[3][2]=1;	la1[3][3]=-ltx*i_vertex[3].x;	la1[3][4]=-rby*i_vertex[3].x;
		ra1[0]=i_vertex[0].x;ra1[1]=i_vertex[1].x;ra1[2]=i_vertex[2].x;ra1[3]=i_vertex[3].x;
		NyARSystemOfLinearEquationsProcessor.doGaussianElimination(la1,ra1,5,4);

		//D,E,F,(GH)の方程式
		la2[0][0]=ltx;	la2[0][1]=lty;	la2[0][2]=1;	la2[0][3]=-ltx*i_vertex[0].y;	la2[0][4]=-lty*i_vertex[0].y;
		la2[1][0]=rbx;	la2[1][1]=lty;	la2[1][2]=1;	la2[1][3]=-rbx*i_vertex[1].y;	la2[1][4]=-lty*i_vertex[1].y;
		la2[2][0]=rbx;	la2[2][1]=rby;	la2[2][2]=1;	la2[2][3]=-rbx*i_vertex[2].y;	la2[2][4]=-rby*i_vertex[2].y;
		la2[3][0]=ltx;	la2[3][1]=rby;	la2[3][2]=1;	la2[3][3]=-ltx*i_vertex[3].y;	la2[3][4]=-rby*i_vertex[3].y;
		ra2[0]=i_vertex[0].y;ra2[1]=i_vertex[1].y;ra2[2]=i_vertex[2].y;ra2[3]=i_vertex[3].y;
		NyARSystemOfLinearEquationsProcessor.doGaussianElimination(la2,ra2,5,4);
		//GHを計算
		double A,B,C,D,E,F,G,H;
		H=(ra2[3]-ra1[3])/(la2[3][4]-la1[3][4]);
		G=ra2[3]-la2[3][4]*H;
		//残りを計算
		F=ra2[2]-H*la2[2][4]-G*la2[2][3];
		E=ra2[1]-H*la2[1][4]-G*la2[1][3]-F*la2[1][2];
		D=ra2[0]-H*la2[0][4]-G*la2[0][3]-F*la2[0][2]-E*la2[0][1];
		C=ra1[2]-H*la1[2][4]-G*la1[2][3];
		B=ra1[1]-H*la1[1][4]-G*la1[1][3]-C*la1[1][2];
		A=ra1[0]-H*la1[0][4]-G*la1[0][3]-C*la1[0][2]-B*la1[0][1];
		o_param[0]=A;
		o_param[1]=B;
		o_param[2]=C;
		o_param[3]=D;
		o_param[4]=E;
		o_param[5]=F;
		o_param[6]=G;
		o_param[7]=H;
		return true;
	}	
	private final int[] __pickFromRaster_rgb_tmp = new int[3];
	/**
	 * 
	 * @param image
	 * @param i_marker
	 * @return 切り出しに失敗した
	 * @throws Exception
	 */
	public boolean pickFromRaster(INyARRgbRaster image, NyARSquare i_square)throws NyARException
	{
		//遠近法のパラメータを計算
		double[] cpara = new double[8];
		if (!get_cpara(i_square.imvertex, cpara)) {
			return false;
		}
		
		int img_x = image.getWidth();
		int img_y = image.getHeight();

		int[] rgb_tmp = __pickFromRaster_rgb_tmp;

		//ピクセルリーダーを取得
		INyARRgbPixelReader reader=image.getRgbPixelReader();

		for(int iy=0;iy<this._size.h;iy++){
			for(int ix=0;ix<this._size.w;ix++){
				//1ピクセルを作成
				int cx=this._pickup_lt.x+ix;
				int cy=this._pickup_lt.y+iy;
				final double d=cpara[6]*cx+cpara[7]*cy+1.0;
				final int x=(int)((cpara[0]*cx+cpara[1]*cy+cpara[2])/d);
				final int y=(int)((cpara[3]*cx+cpara[4]*cy+cpara[5])/d);
				if (x >= 0 && x < img_x && y >= 0 && y < img_y) {
					reader.getPixel(x, y, rgb_tmp);
					this._patdata[iy*this._size.w+ix]=(((rgb_tmp[0])&0xff)<<16)|(((rgb_tmp[1])&0xff)<<8)|(((rgb_tmp[2])&0xff));
				}
			}
		}
		return true;
	}	
}