射影変換サンプル - you1025/my_something_flagments GitHub Wiki

概要

OpenCV を使って画像上で斜めに撮影された矩形を正面から撮影した画像に変換する。
散歩の途中で大宮八幡の良さげな標識を見つけたのでお賽銭を入れた後にゲット。

画像データの取得

github 上に保存した画像ファイルを一時ファイルとして読み込む。

# 画像データの取得
img_url = "https://github.com/you1025/my_something_flagments/raw/images/images/projective_transform/original.png"
data = requests.get(img_url).content

# 画像データを一時ファイルとしてオープンしデータを読み込む
fp = tempfile.NamedTemporaryFile()
fp.write(data)
src = cv2.imread(fp.name)
fp.close()

# BGR -> RGB
src_image = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)

# 元画像の表示
plt.imshow(src_image)
plt.show()

元画像(大宮八幡)

元画像から切り抜く座標を指定

元画像上で斜めに撮影された矩形領域の四隅を指定する。

# 元画像から切り抜く際の四隅を指定(左上-右上-右下-左下)
src_points = np.array([
    [1333, 1142],
    [1660, 1270],
    [1650, 1555],
    [1325, 1453]
], dtype=np.float32)

# 元画像に対象領域の四隅の座標を追加した画像を表示
plt.imshow(src_image)
plt.scatter(src_points[:, 0], src_points[:, 1], marker="x", s=10, c="red")
plt.show()

画像上の赤いバッテンが対象領域。

くり抜き対象

射影変換の実施

上記でくり抜いた矩形領域を正面からの画像に変換する。

# 横幅の倍率を指定(後述)
width_ratio = 1.0

# 出力画像のサイズを設定
width = np.linalg.norm(src_points[1] - src_points[0])  # ベクトル (右上 - 左上) のノルムを算出
width = int(np.floor(width * width_ratio))             # 整数に切り上げ
height = np.linalg.norm(src_points[3] - src_points[0]) # ベクトル (左下 - 左上) のノルムを算出
height = int(np.floor(height))                         # 整数に切り上げ
dst_points = np.array([
    [    0,      0],
    [width,      0],
    [width, height],
    [    0, height]
], dtype=np.float32) # 左上-右上-右下-左下

# 射影変換の実施
M = cv2.getPerspectiveTransform(src_points, dst_points)
output = cv2.warpPerspective(src_image, M, (width, height))

# 射影変換を実施した画像の表示
plt.imshow(output)
plt.show()

矢印(1.0倍)

横幅の倍率(width_ratio)を変更する

上記の変換では横幅の縮尺を変更しない版(width_ratio=1.0)を出力した。
下記では倍率を変更して良さげな出力を検討する。

1.3 倍

見た感じだとこれが良さげかも。

矢印(1.3倍)

1.5 倍

矢印(1.5倍)

他の看板も試す

他にも色々と写したので同様に試してみる。

関数の用意

def get_projective_transform(file_path, src_points, width_ratio=1.0, height_ratio=1.0, show_image=True, figsize=(7, 7)):
    # ファイルのオープン
    src = cv2.imread(file_path)
    src_image = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)

    # 出力画像のサイズを設定
    width = np.linalg.norm(src_points[1] - src_points[0])
    width = int(np.floor(width * width_ratio))
    height = np.linalg.norm(src_points[3] - src_points[0])
    height = int(np.floor(height * height_ratio))
    dst_points = np.array([
        [    0,      0],
        [width,      0],
        [width, height],
        [    0, height]
    ], dtype=np.float32)

    # 射影変換の実施
    M = cv2.getPerspectiveTransform(src_points, dst_points)
    output = cv2.warpPerspective(src_image, M, (width, height))

    # 画像の表示
    if show_image:
        fig, axes = plt.subplots(nrows=1, ncols=2, figsize=figsize)
        axes[0].imshow(src_image)
        axes[0].scatter(src_points[:, 0], src_points[:, 1], marker="x", s=10, c="red")
        axes[1].imshow(output)
        plt.show()

    return output

「一方通行」

元画像上での指定

一方通行

補正した画像

一方通行

「英会話 ピアノ」

元画像上での指定

英会話 ピアノ

補正した画像

英会話 ピアノ

「祈願祭控室」

元画像上での指定

祈願祭控室

補正した画像

祈願祭控室

「清涼殿」

元画像上での指定

清涼殿

補正した画像

清涼殿

「清涼殿2」

元画像上での指定

清涼殿2

補正した画像

清涼殿2

「鳥居」

元画像上での指定

鳥居

補正した画像

鳥居

「迎春」

元画像上での指定

迎春

補正した画像

迎春

参考