カメラで垂直に本等の平面を撮影したかったのに、ちょっとしたずれで遠近感が出てしまうことがあります。しかし射影変換を使えばそのずれを補正することができます。Python/OpenCVであれば誰でも簡単に射影変換を使うことができます。
こんにちは。wat(@watlablog)です。
本日は僕が感動した「射影変換」を習得します!
射影変換で自由自在に画像を変形させよう!
射影変換とは?
射影変換とは、画像の形状を変形させる変換手法の1つです。コンピュータグラフィックスの分野では画像を様々な形状に変形させることで、2Dを3Dに見立てたり、表面にテクスチャを貼ったりしたりすることができます。
画像形状の変換には下図のような変換があります。
(a)合同変換は画像の回転のみを許し、回転角度を合わせれば変換前後の画像は完全一致します。
(b)相似変換は(a)合同変換に加え、拡大と縮小を可能にした変換方法です。
(c)アフィン変換は(a)(b)に加えひし形の変形を可能にした変換方法です。
そして(d)射影変換は(a)(b)(c)の全ての機能に対してさらにどんな四角形にでも変換できる方法です。
射影変換を覚えればカンペキだね。
射影変換でできることを確認しよう!
画像処理は百聞は一見にしかず!まずは射影変換でできることを確認してみましょう。
以下の図は今回PythonとOpenCVで射影変換をしてみた結果です。
碁盤の写真も本の写真も、元々はある角度を持って撮影されたものですが、射影変換をすることで真上から見た図に変換することができました。
このような変換をすることで撮影時のちょっとしたぶれは簡単に補正することができますね。
他にも射影変換ではもっと視覚的な効果を出せますが、僕にはあまりセンスが無いので上のような実用的な扱いを紹介します。
Python/OpenCVで作る射影変換のコード
インポートするパッケージ
import文でインポートするパッケージはOpenCVのcv2とNumPy、表示用のmatplotlibです。
1 2 3 |
import cv2 import numpy as np from matplotlib import pyplot as plt |
画像の読み込み
画像の読み込み部は以下のコードです。今回は碁盤の画像を使います。cv2.imreadの第2引数は1を指定してカラー画像のまま読み込みます(0にするとグレースケール画像になります)。
1 2 |
path = 'igo.jpg' # 画像のパス i = cv2.imread(path, 1) # 画像読み込み |
射影変換のコード
そして以下が射影変換のコードです。
1 2 3 4 5 6 7 |
# 変換前後の対応点を設定 p_original = np.float32([[0,0], [473,55], [14, 514], [467, 449]]) p_trans = np.float32([[0,0], [524,0], [0,478], [524,478]]) # 変換マトリクスと射影変換 M = cv2.getPerspectiveTransform(p_original, p_trans) i_trans = cv2.warpPerspective(i, M, (524, 478)) |
やっていることを以下に図解してみました。
元の画像で射影変換したい4点の座標(\(x, y\))を決めます。この4点が上記コードのp_originalで設定している点です。
この4点を変形させたい点の座標にしているのがp_transで設定している部分です。
Mは変換マトリクスを作成している部分です。難しい式や理論はありますが…ここでは扱いません!
わかってないだけ。
i_transでは作成した変換マトリクスと変換する元画像を使って射影変換をしています。
画像の座標値の取得方法
ちなみに、画像の座標値はmatplotlibのimshowでプロットすることで簡単に取得することができます。マウスカーソルを合わせた位置の座標値(+色情報も)はプロットウィンドウの右下に表示されていますので、ちょっと調べるのに便利です。
プロはエッジ抽出とかを駆使して自動的に座標値を取得したりするのかな?
変換画像の保存と表示
画像の保存とプロットは以下のコードです。これはいつも通りですね。OpenCVは色がBGRですが、matplotlibはRGBなので、BGRをRGBに変換しています。保存はOpenCVの関数で行っているので、表示用の色変換はプロットの直前で実施しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 画像保存 cv2.imwrite("out.jpg", i_trans) # ここからグラフ設定 fig = plt.figure() ax1 = fig.add_subplot(111) # 画像をプロット show = cv2.cvtColor(i_trans, cv2.COLOR_BGR2RGB) ax1.imshow(show) fig.tight_layout() plt.show() plt.close() |
まとめ
本ページでは射影変換の概要を説明し、PythonとOpenCVによるプログラミングをおこないました。
難しい理論は知らなくても(ホントは知っていた方が良いけど)厄介な射影変換を簡単に実施することができました。
自分でやろうとするとひどく時間がかかりそうですが、OpenCVを使うとめちゃくちゃ簡単ですね!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!
全コード
コピペがしやすいように、上で分割記載しているコードを1つにまとめてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import cv2 import numpy as np from matplotlib import pyplot as plt path = 'igo.jpg' # 画像のパス i = cv2.imread(path, 1) # 画像読み込み print(i.shape) # 変換前後の対応点を設定 p_original = np.float32([[0,0], [473,55], [14, 514], [467, 449]]) p_trans = np.float32([[0,0], [524,0], [0,478], [524,478]]) # 変換マトリクスと射影変換 M = cv2.getPerspectiveTransform(p_original, p_trans) i_trans = cv2.warpPerspective(i, M, (524, 478)) cv2.imwrite("out.jpg", i_trans) #ここからグラフ設定 fig = plt.figure() ax1 = fig.add_subplot(111) # 画像をプロット show = cv2.cvtColor(i_trans, cv2.COLOR_BGR2RGB) ax1.imshow(show) fig.tight_layout() plt.show() plt.close() |
コメント