前回は、転送元座標と転送先座標を内部的に入れ替えて射影変換パラメータを求めて画像に穴が空かないように表示していました。
しかし、下記サイトのCSSのtransformで渡している8個のパラメータを求める際には、特に転送元座標と転送先座標を入れ替えたりしていません。
ホモグラフィ(CSS3 Transform 3D Test)- Shogo Computing Laboratory
これはCSSのtransform内部でパラメータを描画用に逆行列させているからと思われます。射影および回転にしても穴が空かないようにするには、転送先から転送元に逆行列させる必要があるからです。
なので、transform同様に渡されたパラメータの逆行列を求めて、描画処理に渡すようにしたいと思います。
射影変換で求めたパラメータを、3x3の正方行列に格納して逆行列を求めます。
//射影変換パラメータの逆行列を求める var N = []; N.push([ans.e(1), ans.e(2), ans.e(3)]); N.push([ans.e(4), ans.e(5), ans.e(6)]); N.push([ans.e(7), ans.e(8), 1]); var ans_i = $M(N).inv();
そして、描画処理には逆行列したパラメータ(ans_i.e)を使用する。
var i, M = [], V = []; var x, y, X, Y; for(i=0;i<4;i++) { x = origin[i][0]; y = origin[i][1]; X = markers[i].x() - imgx; Y = markers[i].y() - imgy; M.push([x, y, 1, 0, 0, 0, -x*X, -y*X]); M.push([0, 0, 0, x, y, 1, -x*Y, -y*Y]); V.push(X); V.push(Y); } //射影変換を求める var ans = $M(M).inv().x($V(V)); console.log($M(M).inspect()); console.log($V(V).inspect()); console.log(ans.inspect()); //射影変換パラメータの逆行列を求める var N = []; N.push([ans.e(1), ans.e(2), ans.e(3)]); N.push([ans.e(4), ans.e(5), ans.e(6)]); N.push([ans.e(7), ans.e(8), 1]); var ans_i = $M(N).inv(); //描画 draw(); function draw(){ var imgW = imgObj.width; var imgH = imgObj.height; var canvas = document.getElementById('ctx'); var context = canvas.getContext("2d"); var input = context.getImageData(0, 0, imgW, imgH); var output =context.createImageData(imgW, imgH); for(var i = 0; i < imgH; ++i){ for(var j = 0; j < imgW; ++j){ //u = (x*a + y*b + c) / (x*g + y*h + 1) //v = (x*d + y*e + f) / (x*g + y*h + 1) var tmp = j * ans_i.e(3,1) + i * ans_i.e(3,2) + ans_i.e(3,3); var tmpX = (j * ans_i.e(1,1) + i * ans_i.e(1,2) + ans_i.e(1,3)) / tmp; var tmpY = (j * ans_i.e(2,1) + i * ans_i.e(2,2) + ans_i.e(2,3)) / tmp; var floorX = tmpX | 0; var floorY = tmpY | 0; if (floorX >= 0 && floorX < imgW && floorY >= 0 && floorY < imgH) { // 左上 + 右上 + 左下 + 右下 //var pixelData = getPixel(input, j, i); // ピクセル値を取得する var pixelData = getPixel(input, floorX, floorY); // ピクセル値を取得する var R = pixelData.R; var G = pixelData.G; var B = pixelData.B; //setPixel(output, floorX, floorY, R, G, B, 255); setPixel(output, j, i, R, G, B, 255); } } }
今まで代数ライブラリsylvesterを使用して行列計算を求めてきました。これは、8次元連立方程式を求める上では必要だったからですね。
しかし、汎用または自作の3Dライブラリでは、よくて4x4行列クラスまでが用意されているくらいです。これはクォータニオンを使う上で4x4行列が必要なので、ここまでは用意されているわけです。また4×4行列の逆行列の公式があるのも理由の一つでしょう。