GIS奮闘記

現役GISエンジニアの技術紹介ブログ。主にPythonを使用。

スポンサーリンク

Python と OpenCV で画像の幾何学変換をしてみよう

さて、本日は画像の幾何学変換について書いてみようと思います。以下エントリーと同様に OpenCV を使ってみようと思います。

www.gis-py.com

幾何学変換とは

幾何学変換と聞くだけで頭が痛くなりそうな気がしますがそんなに難しく考える必要はありません。まずは画像を拡大・縮小、回転、スキュー(画像を傾けるような処理)するもの、くらいに捉えておけば大丈夫かと思います(もちろん専門的に勉強するならこれだと足りないと思いますが)。

今回試すこと

  1. 拡大・縮小
  2. 移動
  3. 回転
  4. アフィン変換
  5. 射影変換

使用する画像

大田区公式PRキャラクターの「はねぴょん」の画像を使用します。

f:id:sanvarie:20210111155935p:plain

実行環境

Windows 10 64bit
Python 3.6.6

サンプルコード

拡大・縮小

はねぴょんを横に1.5倍、縦に3倍拡大させてみます。cv2.resize の引数の fx、fyを設定することで拡大縮小の調整ができます。

# -*- coding: utf-8 -*-
import cv2

def main():
    img = cv2.imread(r"D:\blog\data\opencv\hane.jpg")

    res = cv2.resize(img, None, fx=1.5, fy=3,         # 横に1.5倍、縦に3倍にする
                     interpolation = cv2.INTER_CUBIC) # 補間方法(縮小する場合はcv2.INTER_AREA)

    cv2.imshow("res", res)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

このようにはねぴょんが拡大されたことがわかります。

f:id:sanvarie:20210113150749p:plain

移動

はねぴょんを移動させます。

# -*- coding: utf-8 -*-
import cv2
import numpy as np

def main():
    img = cv2.imread(r"D:\blog\data\opencv\hane.jpg")

    rows, cols, _ = img.shape

    M = np.float32([[1,0,100],[0,1,50]]) # X移動100,Y移動50
    dst = cv2.warpAffine(img, M, (cols,rows))

    cv2.imshow("dst", dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

このように画像が移動したことがわかります。

f:id:sanvarie:20210113151952p:plain

回転

はねぴょんを180度回転させます。

# -*- coding: utf-8 -*-
import cv2
import numpy as np

def main():
    img = cv2.imread(r"D:\blog\data\opencv\hane.jpg")
    rows, cols, _ = img.shape

    M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 180, 1)
    dst = cv2.warpAffine(img, M, (cols, rows))

    cv2.imshow("dst", dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

このように画像が180度回転したことがわかります。

f:id:sanvarie:20210116090245p:plain

アフィン変換

アフィン変換は線形変換(拡大や縮小,回転など)と平行移動を組み合わせた変換です。アフィン変換を行うには入力画像と出力画像の対応点の座標が少なくとも3組必要です。今回は以下のように対応点を設定してみました。

f:id:sanvarie:20210116092836p:plain

# -*- coding: utf-8 -*-
import cv2
import numpy as np

def main():
    img = cv2.imread(r"D:\blog\data\opencv\hane.jpg")
    rows, cols, _ = img.shape

    pts1 = np.float32([[16,36],[138,36],[16, 130]])
    pts2 = np.float32([[34,67],[154,84],[63,175]])

    M = cv2.getAffineTransform(pts1,pts2)

    dst = cv2.warpAffine(img, M, (cols, rows))

    cv2.imshow("dst", dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

出力画像の対応点にアフィン変換された結果になりました。

f:id:sanvarie:20210116093405p:plain

射影変換

任意の四角形を任意の四角形に変形することができる変換が射影変換です。はねぴょんの画像では少しわかりにくいので、射影変換に関しては以下の画像を使用します。

f:id:sanvarie:20210116094427p:plain

射影変換を使うことで雑誌を真上から見たように補正することができます。射影変換を行うには入力画像と出力画像の対応点の座標が少なくとも4組必要です。今回は以下のように対応点を設定してみました。

f:id:sanvarie:20210116095309p:plain

# -*- coding: utf-8 -*-
import cv2
import numpy as np

def main():
    img = cv2.imread(r"D:\blog\data\opencv\book.jpg")
    rows, cols, _ = img.shape

    pts1 = np.float32([[765,521],[3135,551],[167, 2023],[4025, 1849]])
    pts2 = np.float32([[0,0],[cols,0],[0,rows],[cols, rows]])

    M = cv2.getPerspectiveTransform(pts1,pts2)

    dst = cv2.warpPerspective(img, M, (cols, rows))
    cv2.imwrite(r"D:\blog\data\opencv\book2.jpg", dst) # 保存

if __name__ == '__main__':
    main()

文字はぼやけてしまっていますが、雑誌を真上から見たように補正することができました。

f:id:sanvarie:20210116103858p:plain

さいごに

実は意外と GIS をつかっていると画像を幾何学変換する機会はあるかと思います。私の場合は地図上に水道の図面を幾何学変換させていました。その図面を下図に水道管などを作図するイメージですかね。まだ基本的なことしか紹介していませんが、OpenCV を使えば簡単に画像の幾何学変換を行うことができます。興味のある方はぜひ試してみてください。