Python OpenCV 画像の射影変換 warpPerspective 台形補正

OpenCV

この記事では、PythonのOpenCVで画像を射影変換する方法を紹介する。

ここではcv2.warpPerspective()を使い、画像を射影変換する。

この関数は、引数として画像(NumPy配列ndarray)と変換行列、出力サイズを渡して使用する。

関数と主な引数

cv2.wrapPerspective(src, M, dsize)

src画像(NumPy配列ndarray)
M画像変換に使用する変換行列
cv2.getPerspectiveTransform()
によって簡単に変換行列を作成できる。
dsize出力画像のサイズ
(width, height)

cv2.getPerspectiveTransform(src, dst)

src元画像上の4点の座標(4×2のndarray、float32形式にする)
array([[ x1, y1],
[ x2, y2],
[ x3, y3],
[ x4, y4]], dtype=float32)
dst上記の4点がアフィン変換で移動する座標(4×2のndarray、float32形式にする)
array([[ x1′, y1′],
[ x2′, y2′],
[ x3′, y3′],
[ x4′, y4′]], dtype=float32)

画像の射影変換について

画像の射影変換も幾何変換の一つであり、行列を用いて表すことができる。まずはその概要を紹介する。

射影変換

画像上の点\((x, y)\)が、変換により点\((x’, y’)\)に移動する場合を考える。このとき射影変換は、アフィン変換の式を拡張した以下の形で表される。

アフィン変換\(\left( \begin{matrix} x’ \\ y’ \\ 1 \end{matrix} \right) = \left( \begin{matrix} a & b & t_x \\ c & d & t_y \\ 0 & 0 & 1 \end{matrix} \right) \left( \begin{matrix} x \\ y \\ 1 \end{matrix} \right)\)
射影変換\(\left( \begin{matrix} x’ \\ y’ \\ 1 \end{matrix} \right) = \left( \begin{matrix} h_{11} & h_{12} & h_{13} \\ h_{21} & h_{22} & h_{23} \\ h_{31} & h_{32} & h_{33} \end{matrix} \right) \left( \begin{matrix} x \\ y \\ 1 \end{matrix} \right)\)

アフィン変換では変換行列の上側2×3の部分のみ使っていたが、3×3すべて使うことでより一般的な変換を表現できる。

このような射影変換は任意の四角形から別の任意の四角形への変換となり、台形補正などで利用する。

射影変換(projective transformation)は、透視変換(perspective transformation)やホモグラフィ変換(homography transformation: 平面射影変換)とも呼ばれる。

使用例

以下のキーボードの写真を例に、使用例を示す。斜めから撮った写真を台形補正する。

先にライブラリのインポートと、写真の読み込みを行う。

Code:

import cv2
import matplotlib.pyplot as plt
import numpy as np

# 画像の読み込み
img = cv2.imread('./pic/keyboard.jpg')

画像をcv2.wrapPerspective()で射影変換(台形補正)

アフィン変換する場合は、変換前、変換後の4点の座標を決める必要がある。ここでは変数src_pts, dst_ptsに4×2のndarrayとして設定する。変換前の画像に、src_ptsの4点をプロットして確認する。

Code:

# 変換前、変換後の4点の座標を設定する
src_pts = np.array([[100, 100], [680, 110], [35, 310], [740, 310]], dtype=np.float32)
dst_pts = np.array([[0, 0], [580, 0], [0, 200], [580, 200]], dtype=np.float32)

img_marked = img.copy()

# 画像上にsrc_ptsの4点をプロット
for pt in src_pts:
    cv2.drawMarker(img_marked, tuple(pt.astype(int)), (0, 0, 255), thickness=4, markerType=cv2.MARKER_SQUARE)

# 画像の表示
plt.imshow(cv2.cvtColor(img_marked, cv2.COLOR_BGR2RGB))
plt.show()

Output:

ここで設定した4点が、長方形の角になるように射影変換を行い、台形補正をする。

射影変換の変換行列は、cv2.getPerspectiveTransform()を用いて簡単に作成することができる。引数にsrc_pts, dst_ptsを指定して変換行列を作成し、cv2.warpPerspective()の第2引数に指定する。また、cv2.warpPerspective()の第3引数には出力画像のサイズをタプル型で指定する必要がある。

Code:

# 変換行列を作成、適用
M = cv2.getPerspectiveTransform(src_pts,dst_pts)
img_perspect = cv2.warpPerspective(img_marked,M,(580,200))

# 変換画像の表示
plt.imshow(cv2.cvtColor(img_perspect, cv2.COLOR_BGR2RGB))
plt.show()

# 変換行列の表示
print('変換行列:')
print(M)

Output:

変換行列:
[[ 1.04396525e+00  3.23132101e-01 -1.36709735e+02]
 [-2.19923355e-02  1.27555546e+00 -1.25356312e+02]
 [-1.09961678e-04  1.13009484e-03  1.00000000e+00]]

射影変換(台形補正)によって、キーボードを真上から撮ったように変換できた。

コメント

タイトルとURLをコピーしました