長野エンジニアライフ

東京から長野に移住したエンジニアのブログです。🦒🗻⛰

Python + OpenCV4プログラミングの学習(4)〜アフィン変換〜

本記事はOpenCVによるアフィン変換を行ったときの備忘録である。

アフィン変換とは...
図形を回転させたり引き延ばしたりする変換 ja.wikipedia.org

事前準備

以下の記事で仮想環境opencvEnvを構築していることを前提とする。 kawakeee.hatenablog.com

また、任意の画像を用意しておく。今回はLenna.jpgを用意した。

Lenna

フリップ

以下のコードを写経しflip.pyのファイルを作成。

import cv2

try:
  img = cv2.imread('img/Lenna.jpg')
  if img is None:
    print('ファイルを読み込めません')
    import sys
    sys.exit()
  
  # 画像を上下反転させる
  dst = cv2.flip(img, 0)
  cv2.imwrite('img/flip0.jpg', img)
  cv2.imshow('dst1', dst)

  # 画像を左右反転させる
  dst = cv2.flip(img, 1)
  cv2.imwrite('img/flip1.jpg', img)
  cv2.imshow('dst2', dst)

  # 画像を両軸反転させる
  dst = cv2.flip(img, -1)
  cv2.imwrite('img/flip2.jpg', img)
  cv2.imshow('dst3', dst)

  cv2.waitKey(0)
  cv2.destroyAllWindows()

except:
  import sys
  print("Error:", sys.exc_info()[0])
  print(sys.exc_info()[1])
  import traceback
  print(traceback.format_tb(sys.exc_info()[2]))
  • cv2.flip(...):画像をフリップ(反転)させる
    使用例:cv2.flip(img, 0)
    引数は、順に(対象画像データ, フリップ方向)を表す。 以下のように、フリップ方向の値により反転軸を指定できる。
フリップ方向の値 反転方向
0に等しい 上下反転
左右反転
両軸反転

実行結果

(opencvEnv)$ python filp.py

指定フォルダ配下にフリップされた画像が生成されていることを確認。
OpenCVによるフリップ変換

リサイズ

以下のコードを写経しresize.pyのファイルを作成。

import cv2

try:
  img = cv2.imread('img/Lenna.jpg')
  if img is None:
    print('ファイルを読み込めません')
    import sys
    sys.exit()
  
  SCALE = 0.5
  SCALE2 = 1.62
  # 画像の高さ・幅を取得
  height = img.shape[0]
  width = img.shape[1]

  # 画像を半分(0.5乗じる)のサイズにリサイズ
  dst = cv2.resize(img, (int(width*SCALE), int(height*SCALE)))
  cv2.imwrite('img/resize0.5.jpg', dst)
  cv2.imshow('dst1', dst)

  # 画像を1.62乗じてリサイズ
  dst = cv2.resize(img, (int(width*SCALE2), int(height*SCALE2)))
  cv2.imwrite('img/resize1.62.jpg', dst)
  cv2.imshow('dst2', dst)

  # 画像を縦を圧縮してリサイズ
  dst = cv2.resize(img, (400, 200))
  cv2.imwrite('img/resize400*200.jpg', dst)
  cv2.imshow('dst3', dst)

  cv2.waitKey(0)
  cv2.destroyAllWindows()

except:
  import sys
  print("Error:", sys.exc_info()[0])
  print(sys.exc_info()[1])
  import traceback
  print(traceback.format_tb(sys.exc_info()[2]))

変数SCALEを用意して半分(0.5をかける)のサイズにリサイズしている。

  • cv2.resize(...):画像をリサイズする
    使用例:cv2.resize(img, (400, 200))
    引数は、順に(対象画像,幅, 高さ)を表す。

    実行結果

(opencvEnv)$ python resize.py

指定フォルダ配下にリサイズされた画像が生成されていることを確認。
OpenCVによるリサイズ

回転

以下のコードを写経しrotate.pyのファイルを作成。

import cv2

try:
  img = cv2.imread('img/Lenna.jpg')
  if img is None:
    print('ファイルを読み込めません')
    import sys
    sys.exit()
  
  # 画像の高さ・幅を取得
  height = img.shape[0]
  width = img.shape[1]
  # 回転原点座標を生成
  center = (int(width/2), int(height/2))

  # 回転変換行列の生成 (回転原点座標, 回転角度, スケーリング値)
  affin_trans = cv2.getRotationMatrix2D(center, 33.0, 1.0)
  # 回転変換処理 (対象画像, 回転変換行列, 出力配列(画像サイズの指定))
  dst = cv2.warpAffine(img, affin_trans, (width, height))
  cv2.imwrite('img/rotate033.jpg', dst)
  cv2.imshow('dst1', dst)

  affin_trans = cv2.getRotationMatrix2D(center, 110.0, 1.2)
  dst = cv2.warpAffine(img, affin_trans, (width, height), flags=cv2.INTER_CUBIC)
  cv2.imwrite('img/rotate110.jpg', dst)
  cv2.imshow('dst2', dst)

  cv2.waitKey(0)
  cv2.destroyAllWindows()

except:
  import sys
  print("Error:", sys.exc_info()[0])
  print(sys.exc_info()[1])
  import traceback
  print(traceback.format_tb(sys.exc_info()[2]))

順に、対象画像を反時計周りの方向に33°、110°回転させる。

  • cv2.getRotationMatrix2D(...):回転変換行列の生成
    使用例:cv2.getRotationMatrix2D(center, 33.0, 1.0)
    引数は、順に(回転原点座標,回転角度,スケーリング値)を表す。
  • cv2.warpAffine(...):画像のアフィン変換を実行
    使用例:cv2.warpAffine(img, affin_trans, (width, height))
    引数は、順に(対象画像,変換行列,出力配列(画像サイズ))を表す。

実行結果

(opencvEnv)$ python rotate.py

指定フォルダ配下に回転された画像が生成されていることを確認。
OpenCVによる回転処理

透視投影

以下のコードを写経しperspective.pyのファイルを作成。

import cv2
import numpy as np

try:
  # (225 * 225)の画像
  img = cv2.imread('img/Lenna.jpg')
  if img is None:
    print('ファイルを読み込めません')
    import sys
    sys.exit()
  
  # 画像の列・行の大きさを取得
  rows,cols = img.shape[:2]
  x0 = cols/4
  x1 = (cols*3)/4
  y0 = rows/4
  y1 = (rows*3)/4

  # 入力の座標を指定
  list_srcs = np.float32([
    [x0, y0],
    [x0, y1],
    [x1, y1],
    [x1, y0]
  ])

  # pattern-0
  x_margin = cols/10
  y_margin = rows/10

  # 出力の座標を指定(台形)
  list_dsts = np.float32([
    [x0+x_margin, y0+y_margin],
    list_srcs[1],
    list_srcs[2],
    [x1-x_margin, y0+y_margin]
  ])
  # 透視変換行列の生成 (入力座標, 出力座標)
  perspective_matrix = cv2.getPerspectiveTransform(list_srcs, list_dsts)
  # 画像の透視投影処理 (対象画像, 透視変換行列, 出力配列(画像サイズの指定))
  dst = cv2.warpPerspective(img, perspective_matrix, (cols, rows))
  cv2.imwrite('img/dst0.jpg', dst)
  cv2.imshow('dst0', dst)

  #pattern-1
  x_margin = cols/8
  y_margin = rows/8
  list_dsts = np.float32([list_srcs[0],list_srcs[1],[x1-x_margin, y1-y_margin],[x1-x_margin, y0+y_margin]])
  perspective_matrix = cv2.getPerspectiveTransform(list_srcs,list_dsts)
  dst = cv2.warpPerspective(img, perspective_matrix, (cols, rows))
  cv2.imwrite('img/dst1.jpg', dst)
  cv2.imshow('dst1', dst)

  #pattern-2
  x_margin = cols/6
  y_margin = rows/6
  list_dsts = np.float32([[x0+x_margin, y0+y_margin],list_srcs[1],[x1-x_margin, y1-y_margin],list_srcs[3]])
  perspective_matrix = cv2.getPerspectiveTransform(list_srcs,list_dsts)
  dst = cv2.warpPerspective(img, perspective_matrix, (cols, rows))
  cv2.imwrite('img/dst2.jpg', dst)
  cv2.imshow('dst2', dst)

  cv2.waitKey(0)
  cv2.destroyAllWindows()

except:
  import sys
  print("Error:", sys.exc_info()[0])
  print(sys.exc_info()[1])
  import traceback
  print(traceback.format_tb(sys.exc_info()[2]))

list_srcsに入力座標、list_dstsに出力座標を指定し、3パターンの透視投影を実行。

  • cv2.getPerspectiveTransform(...):透視変換行列の生成
    使用例:cv2.getPerspectiveTransform(list_srcs, list_dsts)
    引数は、順に(入力座標,出力座標)を表す。返戻値は引数に対する透視変換行列。
  • cv2.warpPerspective(...):画像の透視投影を実行
    使用例:cv2.warpPerspective(img, perspective_matrix, (cols, rows))
    引数は、順に(対象画像,透視変換行列,出力配列(画像サイズ))を表す。

実行結果

(opencvEnv)$ python perspective.py

指定フォルダ配下に透視投影された画像が生成されていることを確認。
OpenCVによる透視投影

トリミング

以下のコードを写経しtrimming.pyのファイルを作成。

import cv2

try:
  img = cv2.imread('img/Lenna.jpg')
  if img is None:
    print('ファイルを読み込めません')
    import sys
    sys.exit()
  
  # 画像の高さ・幅を取得
  height = img.shape[0]
  width = img.shape[1]

  # imgをスライス処理してトリミング
  dst = img[40:height, 40:width]
  cv2.imwrite('img/Trimming.jpg', dst)
  cv2.imshow('dst0', dst)
  
  cv2.waitKey(0)
  cv2.destroyAllWindows()

except:
  import sys
  print("Error:", sys.exc_info()[0])
  print(sys.exc_info()[1])
  import traceback
  print(traceback.format_tb(sys.exc_info()[2]))

トリミングはimgに対してスライスする事で可能。元画像の上部、左側の40pxを除去させる。

実行結果

(opencvEnv)$ python trimming.py

指定フォルダ配下にトリミングされた画像が生成されていることを確認。
OpenCVによるトリミング処理

まとめ

  • cv2.flip(...):画像をフリップ(反転)させる
  • cv2.getRotationMatrix2D(...):回転変換行列の生成
  • cv2.warpAffine(...):画像のアフィン変換を実行
  • cv2.getPerspectiveTransform(...):透視変換行列の生成
  • cv2.warpPerspective(...):画像の透視投影を実行

ソースコード

github.com