この記事を読んでもらうと、こんな画像が作れるようになるよ!
【記事の内容】
・Python-OpenCVで射影変換する方法をまずはサクッと紹介(コピペOK)
・どのように座標変換をしているかを図と画像で解説
・他のコードも一行ずつ解説!!
どうもこんにちは!
毎度おなじみ、怪社員です!
今回は、表題の通り、OpenCVを使って実際に画像を引き延ばしたり、縮めたりして、射影変換をやっていこうと思います!
では、細かい解説は後回しにして、さっそくコードと結果を紹介するよ!
コピペして全然OKだけど、別に応用したい時は解説まで読んでもらえると嬉しいよ!
射影変換のコードと結果!
# affine_test_1.py
import numpy as np
import cv2
image = cv2.imread("source_pics/test_1.png", cv2.IMREAD_COLOR)
height, width, channels = image.shape[:3]
source_points = np.array([[0, 0], [0, height], [width, height], [width, 0]], dtype=np.float32)
target_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
mat = cv2.getPerspectiveTransform(source_points, target_points)
perspective_image = cv2.warpPerspective(image, mat, (width, height))
cv2.imwrite("save_pics/perspective_image.png", perspective_image)
解説
1.フォルダ構成
2.座標の考え方
source_points = np.array([[0, 0], [0, height], [width, height], [width, 0]], dtype=np.float32)
target_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
上記のコードは、以下のように翻訳できます。
source_points = np.array([[左上], [左下], [右下], [右上]], dtype=np.float32)
target_points = np.array([[左上], [左下], [右下], [右上]], dtype=np.float32)
よって画像は以下のように座標点を変更されます。それに応じて、画像が伸びたり縮んだりするのです。
※左上が[0, 0]であることに注意しましょう!
source_points = np.array([[0, 0], [0, 600], [600, 600, [600, 0]], dtype=np.float32)
target_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
もう一度、対応する点を上記のコードと見比べてみましょう。※変数の部分に実際の数値をいれてあります。
さて、以上のコードの数値を変更すれば、自由に射影変換を行うことができます。
是非、お手持ちの画像で、お試しください。
以下は今回のコードの解説となります!
その他のコードを一行ずつ解説!
モジュールのインポート
import numpy as np
import cv2
この2行は拡張モジュールのインポートを行います。
1行目が数値計算モジュール【numpy】を【np】としてインポートし、
2行目が画像処理用モジュール【cv2】をそのままの名前でインポートしています。
画像の読み込み(取得)
image = cv2.imread("source_pics/test_1.png", cv2.IMREAD_COLOR)
【cv2.imread】は、実行ファイルと同じ層の”source_pics”フォルダにある”test_1.png”を読み込んで、変数”image”に入れます。
ご自分のファイルを”test_1.png”という名前にするか、その部分を取り込むファイル名に変更しましょう。
画像情報の取得
height, width, channels = image.shape[:3]
【.shape】で画像のタテ・ヨコの画素数(サイズ)を取得します。
先ほど画像データを入れた変数”image”が”shape”という情報を持っているので、それを”height”, “width”, “channels”という3つの変数に入れるということです。
今回”channel”は使いませんが、取り出せる情報の数と、それを入れる変数の数が一致していないとエラーを起こすので、必ず同じ数だけ作成しましょう。
情報を入れる変数の数 = 取り出せる情報の数
変換行列のための座標点の設定
source_points = np.array([[0, 0], [0, height], [width, height], [width, 0]], dtype=np.float32)
target_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
この2行は、【np.array】を使い、それぞれが4点のXY座標を作成しています。
中身はこんな感じです(※ [height, width] は、今回の元画像では [600, 600] です)
source_points = [[ 0. 0.]
[ 0. 600.]
[600. 600.]
[600. 0.]]
target_points = [[200. 0.]
[ 0. 600.]
[600. 600.]
[400. 0.]]
画像で説明したままの座標点がそれぞれの変数の中に入っていますね。
この座標点を基にして画像の変換を行っていきます。
変換行列の計算
mat = cv2.getPerspectiveTransform(source_points, target_points)
【cv2.getPerspectiveTransform】が引数の“source_points”と“target_points”を基に、元画像“image”を変換するための変換行列というものを計算しています。
中身はこんな感じ。
mat = [[ 3.33333333e-01 -3.33333333e-01 2.00000000e+02]
[ 0.00000000e+00 3.33333333e-01 0.00000000e+00]
[ 0.00000000e+00 -1.11111111e-03 1.00000000e+00]]
この記事では数学の話をするつもりはありませんので、学術的な話が知りたい方は(そのうち記事を作りたいですが、今は)【射影変換_変換行列】と検索してみてください m(__)mペコリ。
射影変換
perspective_image = cv2.warpPerspective(image, mat, (width, height))
この行でついに画像の射影変換を行います。
【cv2.warpPerspective】の引数に、『元の画像”image”、変換行列”mat”、そして、出力する画像のサイズ”width”, “height”』を指定します。
例題では、特定の辺を縮小しているだけなので、元画像と同じサイズにしてありますが、実際のサイズは、変換後の座標点を考えながら決定しましょう。
どういうことかと言うと、仮に出力サイズが[600, 600]のまま以下のような座標点をとると、下の画像のようになってしまいます。
source_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
target_points = np.array([[0, 0], [0, 600], [600, 600], [600, 0]], dtype=np.float32)
射影変換後の画像の保存
cv2.imwrite("save_pics/perspective_image.png", perspective_image)
この行で射影変換した画像を指定のフォルダに保存しています。
【cv2.imwrite】を使い、 実行ファイルと同じ層の”save_pics”フォルダに、”perspective_image.png”という名前で”perspective_image”に入っている画像データを保存します。
最後に
いかがでしたでしょうか。
本記事を読んで、『なんとなくだけどOpenCVによる射影変換のやり方が分かった』と、そう思ってくだされば幸いです。
もっとわかりやすい解説・図になるように(本筋が変わらない範囲で)加筆・追加していく予定です!
最後まで読んでくださり、ありがとうございます!
それでは、また次回!