2017-09-20 9 views
0

ライブラリ関数を使用しないでアフィン変換を実装したいと考えています。 私は "transformed"という名前の画像を持っています。逆変換を適用して "img_org"画像を取得したいと思います。今、私は自分の基本的なGetBilinearPixel関数を使って強度値を設定しています。しかし、イメージは正しく変換されていません。これが私が思いついたものです。 :PythonでwarpAffineのコードを手動で配線する

これは画像( "transformed.png")である:これはイメージ( "img_org.png")である

enter image description here

enter image description here

しかし、私の目標はあります enter image description here

ここで変換行列を確認できます:

pts1 = np.float32([[693,349] , [605,331] , [445,59]]) 
pts2 = np.float32 ([[1379,895] , [1213,970] ,[684,428]]) 
Mat = cv2.getAffineTransform(pts2,pts1) 
B=Mat 

コード:

img_org=np.zeros(shape=(780,1050)) 
img_size=np.zeros(shape=(780,1050)) 

def GetBilinearPixel(imArr, posX, posY): 
return imArr[posX][posY] 

for i in range(1,img.shape[0]-1): 
    for j in range(1,img.shape[1]-1): 
     pos=np.array([[i],[j],[1]],np.float32) 
     #print pos 
     pos=np.matmul(B,pos) 
     r=int(pos[0][0]) 
     c=int(pos[1][0]) 
     #print r,c 
     if(c<=1024 and r<=768 and c>=0 and r>=0): 
      img_size[r][c]=img_size[r][c]+1 
      img_org[r][c] += GetBilinearPixel(img, i, j) 

for i in range(0,img_org.shape[0]): 
    for j in range(0,img_org.shape[1]): 
     if(img_size[i][j]>0): 
      img_org[i][j] = img_org[i][j]/img_size[i][j] 

は私のロジックが間違っていますか?私は非常に非効率なアルゴリズムを適用したことを知っています。 私には洞察力がありますか? 他にもうまく動作するアルゴリズムを教えてください。

(リクエスト)。私はwarpAffine関数を使用したくありません。

+0

warpAffine()を使用したくない特別な理由はありますか? –

+0

はい、私は大学の助手です。私は、学生にwarpAffine関数内の実装を理解させるための課題を与えるための独自のライブラリを作成しています。 –

+0

ポイントはどこから来たのですか? –

答えて

1

私はコードをベクトル化していますが、このメソッドは動作します---私はあなたの実装で正確な問題を見つけることができませんが、これはいくつかの明快さをプラスします。

ベクトル化する設定は、画像内のすべての点を含む線形(均質)配列を作成することです。

x0 x1 ... xN x0 x1 ... xN ..... x0 x1 ... xN 
y0 y0 ... y0 y1 y1 ... y1 ..... yM yM ... yM 
1 1 ... 1 1 1 ... 1 ..... 1 1 ... 1 

すべての点が含まれているように、(xi, yi, 1)が含まれています。変換は、変換行列とこの配列との単なる行列乗算に過ぎません。

画像の名前付け規則が混乱していることがわかりますので、「送信元」またはsrc画像に変換したいので、元の開始画像は「宛先」またはdstです。ただ変換マトリックスを作成し、乗算、ポイントを変換するために、次に

dst = cv2.imread('img.jpg', 0) 
h, w = dst.shape[:2] 
dst_y, dst_x = np.indices((h, w)) # similar to meshgrid/mgrid 
dst_lin_homg_pts = np.stack((dst_x.ravel(), dst_y.ravel(), np.ones(dst_y.size))) 

:心の中で、この線形同種の配列を作成することは、このような何かを見ることができるベアリングという。私は、インデックスとしてそれらを使用すると補間で悩まないよので、変換された画素の位置を丸めます:

src_pts = np.float32([[693, 349], [605, 331], [445, 59]]) 
dst_pts = np.float32([[1379, 895], [1213, 970], [684, 428]]) 
transf = cv2.getAffineTransform(dst_pts, src_pts) 
src_lin_pts = np.round(transf.dot(dst_lin_homg_pts)).astype(int) 

は今、この変換は負のインデックスにいくつかのピクセルを送信する場合、およびそれらと我々インデックス、それはおそらく私たちがやりたいことではないでしょう。もちろんOpenCVの実装では、それらのピクセルを完全に消してしまいます。場所のすべてが正であり、私たちは、いずれも切断しないように、しかし、我々は(あなたはもちろん、あなたがこの点でやりたいことができます)すべての変換ピクセルだけシフトすることができます。

min_x, min_y = np.amin(src_lin_pts, axis=1) 
src_lin_pts -= np.array([[min_x], [min_y]]) 

その後、我々はよトランスフォームがマッピングされるソースイメージsrcを作成する必要があります。私はdstイメージから黒の程度を見ることができるように灰色の背景で作成します。

trans_max_x, trans_max_y = np.amax(src_lin_pts, axis=1) 
src = np.ones((trans_max_y+1, trans_max_x+1), dtype=np.uint8)*127 

ここで行うべきことは、宛先イメージから対応するいくつかのピクセルをソースイメージに配置することだけです。私はピクセルのいずれかを切り捨てていないので、両方の線形ポイントの配列に同じ数のピクセルがあるので、私は変換されたピクセルを元の画像にある色に割り当てます。

src[src_lin_pts[1], src_lin_pts[0]] = dst.ravel() 

もちろん、これは画像上では補間されません。しかし、OpenCVには補間のための組み込み関数はありません(他のメソッドのためのバックエンドC関数がありますが、Python AFAIKでアクセスすることはできません)。しかし、重要な部分---宛先イメージがマップされる場所と元のイメージがあるため、任意の数のライブラリを使用してそのグリッドに補間することができます。あるいは、それほど難しくないので、あなた自身で線形補間を実装してください。これまでにワープされたピクセル位置の丸めを行うことをお勧めします。

cv2.imshow('src', src) 
cv2.waitKey() 

Source image

編集:また、あなたの結果の行列の乗算が3条性(均質)ベクトルを与えるだろうが、これと同じ方法では、あまりにもwarpPerspectiveのために働くだろう、とあなたが最初の二つを分割する必要があります行を3列目にしてそれらをデカルト世界に戻します。それ以外は、それ以外はすべて同じです。

+0

ありがとうございました。私はまた、ポイントが負の座標に変換されるとき、それらが正しくマップされないと考えました。組み込み関数の代わりにループを使いたいと思っていました。気にしないで。 –

+0

ええ; OpenCV実装では、それらのインデックスをそのまま残しています。また、アウトプットシェイプを配置する必要があります。つまり、エクステントも自動的には計算されません。このため、私はOpenCV 'warp'関数を実行する小さなPythonモジュールを作成しましたが、ここで行ったように、送り先のサイズを自動的に計算し、イメージを正の値にシフトします。あなたが行っていることに合っていれば、[ここ](https://github.com/alkasm/padded-transformations)をチェックすることができます。 –

+0

このテクニックに補間を適用したいのですが? –

関連する問題