LoginSignup
3
10

More than 1 year has passed since last update.

はじめに

画像認識や物体検出においては、学習データとなる画像を大量に用意する必要があります。
しかし、十分な量のデータが用意できないということはよくあります。
そういった場合には、元データに画像処理を施すことで、データを増やす「データ増幅(data augumentation)」を行うのが一般的です。
データ増幅では、

  • 変形(平行移動、回転、せん断)
  • 色の変換(グレースケール、明るさ調整)
  • ノイズの付与、ぼかし

といった画像処理を行います。

本記事では、画像の変形を行うアフィン変換についてまとめてみました。

参考になったUdemyの講座

  • 講座のタイトル

    【Pythonで学ぶ】OpenCVでの画像処理入門
  • 講座のURL
    https://www.udemy.com/share/1020LuAkIYcVxWTXo=/
  • 受講して良かったポイント
    OpenCVでの画像処理について、基本から丁寧に学ぶことができた。
    また、実際に手を動かしてコードを書くことで、Pythonで画像処理を実装する方法が一通り身についた。
    画像処理は、結果がすぐ目に見えてわかるため、楽しみながら受講できると思います。

補足

  • 本記事および講座では適宜数式を使用していますが、十分に理解できなくても問題なく学習を進めることができます。
  • 本記事では、Google Colaboratoryを使用しています。Udemy講座では、ローカル環境でJupyter Notebookを使用しています。若干異なる部分がありますのでご注意下さい。
  • 本記事作成にあたり、以下も参考にさせて頂きました。

内容

OpenCVとは

OpenCVとは、オープンソースの画像処理ライブラリです。

主な特徴は以下になります。

  • Intel社が開発
  • 商用利用可能※

    ※ただし、SIFT, SURFなど、特許の関係で一部使用できないものもある
  • マルチプラットフォーム

    Linux, Mac, Windowsで使用可能
  • 多言語対応

    Python, C++, Java
  • 画像処理から機械学習まで豊富なコンテンツが用意されている

画像の表示

OpenCVを使用して画像を表示するには、まず以下をインポートします。
※Google Colaboratoryのため、cv2_imshowをインポート。そうでない場合、cv2_imshowのインポートは不要です。

import cv2
from google.colab.patches import cv2_imshow

画像を表示するには、cv2_imshowを使用します。
※Google Colaboratoryのため、cv2_imshowを使用。そうでない場合は、cv2.imshowを使用します。

img = cv2.imread('/path/to/image')
cv2_imshow(img)

image.png

画像の情報を見てみましょう。

img.shape
# (375, 375, 3)

img.shapeの結果は、順番に画像の
height(高さ)、width(幅)、色の種類($RGB$の3色)
を表しています。

画像の任意の場所の画素値は、座標を指定して取得します。
※height, widthの順に指定することに注意

img[100, 200]
# array([176, 207, 200], dtype=uint8)

画素値が$B, G, R$の配列として取得できました。
※OpenCVでは$RGB$ではなく$BGR$の順番で扱うことに注意して下さい。

アフィン変換

アフィン変換とは、回転や平行移動、せん断変形などを表す線形変換のことです。

アフィン変換は、以下の式で表されます。

\begin{pmatrix}
x' \\
y'
\end{pmatrix}
=
\begin{pmatrix}
a & b \\
c & d
\end{pmatrix}
\begin{pmatrix}
x \\
y
\end{pmatrix}
+
\begin{pmatrix}
t_x \\
t_y
\end{pmatrix}

ここで、元の画像の座標を$(x, y)$、アフィン変換後の座標を$(x', y')$としました。
$a, b, c, d$で表された第1項が回転やせん断変形を、$t_x, t_y$で表された第2項が平行移動を表します。

上記の式は、以下のように書くこともできます。
これを、同次座標といいます。

\begin{pmatrix}
x' \\
y' \\
1
\end{pmatrix}
=
\begin{pmatrix}
a & b & t_x\\
c & d & t_y\\
0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
x\\
y\\
1
\end{pmatrix}

以下では、行列計算を行うためにnumpyをインポートしておきます。

import numpy as np

また、画像の高さ$h$と幅$w$を取得しておきます。

h, w = img.shape[:2]

平行移動

平行移動は、以下の式で表されます。

\begin{pmatrix}
x' \\
y' \\
1
\end{pmatrix}
=
\begin{pmatrix}
1 & 0 & t_x\\
0 & 1 & t_y\\
0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
x\\
y\\
1
\end{pmatrix}

平行移動には、warpAffine()関数を使用します。

t_x = 20 
t_y = 60
mat1 = np.float32([[1, 0, t_x], [0, 1, t_y]])
img1 = cv2.warpAffine(img, mat1, (w, h))

imgs = cv2.hconcat([img, img1])
cv2_imshow(imgs)

image.png

回転

回転は以下の式で表されます。

\begin{pmatrix}
x' \\
y' \\
1
\end{pmatrix}
=
\begin{pmatrix}
\cos\theta & -\sin\theta & 0\\
\sin\theta & \cos\theta & 0\\
0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
x\\
y\\
1
\end{pmatrix}

回転にも、warpAffine()関数を使用できます。

theta = np.deg2rad(10)
mat = np.float32([[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0]])
img1 = cv2.warpAffine(img, mat, (w, h))

imgs = cv2.hconcat([img, img1])
cv2_imshow(imgs)

image.png

回転移動は、角度を決めラジアンに直し、$\cos\theta, \sin\theta$という形にするのが少し面倒です。また、回転の中心を任意の位置に変えたいということもあります。
回転移動に関しては、便利な関数getRotationMatrix2Dが用意されています。
こちらを使用してみます。

mat1 = cv2.getRotationMatrix2D((w/2, h/2), 10, 1)
img1 = cv2.warpAffine(img, mat1, (w, h))
imgs = cv2.hconcat([img, img1])
cv2_imshow(imgs)

image.png

さらに、90°回転と180°回転については、もっと簡単に書くことができます。
rotate関数を使用します。

時計回りに90°回転させる場合、引数をcv2.ROTATE_90_CLOCKWISEとします。

img1 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
img2 = cv2.rotate(img1, cv2.ROTATE_90_CLOCKWISE)
img3 = cv2.rotate(img2, cv2.ROTATE_90_CLOCKWISE)
imgs = cv2.hconcat([img, img1, img2, img3])
cv2_imshow(imgs)

image.png

反時計回りに90°回転させる場合、引数をcv2.ROTATE_90_COUNTERCLOCKWISEとします。

img1 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
img2 = cv2.rotate(img1, cv2.ROTATE_90_COUNTERCLOCKWISE)
img3 = cv2.rotate(img2, cv2.ROTATE_90_COUNTERCLOCKWISE)
imgs = cv2.hconcat([img, img1, img2, img3])
cv2_imshow(imgs)

image.png

180°回転させる場合、引数をcv2.ROTATE_180とします。

img1 = cv2.rotate(img, cv2.ROTATE_180)
imgs = cv2.hconcat([img, img1])
cv2_imshow(imgs)

image.png

せん断

せん断は以下の式で表されます。

\begin{pmatrix}
x' \\
y' \\
1
\end{pmatrix}
=
\begin{pmatrix}
1 & \tan\theta_1 & 0\\
\tan\theta_2 & 1 & 0\\
0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
x\\
y\\
1
\end{pmatrix}

ここで$\theta_1, \theta_2$は、それぞれ
$y$軸方向から$x$軸方向への傾き、$x$軸方向から$y$軸方向への傾き
を表す角度です。

まずは、$x$軸方向へのせん断。

theta2 = np.deg2rad(0)

theta1 = np.deg2rad(10)
mat = np.float32([[1, np.tan(theta1), 0], [np.tan(theta2), 1, 0]])
img1 = cv2.warpAffine(img, mat, (w, h))

theta1 = np.deg2rad(20)
mat = np.float32([[1, np.tan(theta1), 0], [np.tan(theta2), 1, 0]])
img2 = cv2.warpAffine(img, mat, (w, h))

theta1 = np.deg2rad(30)
mat = np.float32([[1, np.tan(theta1), 0], [np.tan(theta2), 1, 0]])
img3 = cv2.warpAffine(img, mat, (w, h))

imgs = cv2.hconcat([img, img1, img2, img3])
cv2_imshow(imgs)

image.png

次に、$y$軸方向へのせん断。

theta1 = np.deg2rad(0)

theta2 = np.deg2rad(10)
mat = np.float32([[1, np.tan(theta1), 0], [np.tan(theta2), 1, 0]])
img1 = cv2.warpAffine(img, mat, (w, h))

theta2 = np.deg2rad(20)
mat = np.float32([[1, np.tan(theta1), 0], [np.tan(theta2), 1, 0]])
img2 = cv2.warpAffine(img, mat, (w, h))

theta2 = np.deg2rad(30)
mat = np.float32([[1, np.tan(theta1), 0], [np.tan(theta2), 1, 0]])
img3 = cv2.warpAffine(img, mat, (w, h))

imgs = cv2.hconcat([img, img1, img2, img3])
cv2_imshow(imgs)

image.png

$x$軸、$y$軸方向のせん断を組み合わせることももちろん可能。

theta1 = np.deg2rad(5)
theta2 = np.deg2rad(10)
mat = np.float32([[1, np.tan(theta1), 0], [np.tan(theta2), 1, 0]])
img1 = cv2.warpAffine(img, mat, (w, h))

cv2_imshow(img1)

image.png

まとめ

OpenCVを使用したアフィン変換について説明しました。
図形の幾何学的な変形により、少ないデータを水増しすることができます。
また、今回紹介したアフィン変換に加え、その他図形の変形、色の変換、ぼかしなどといった画像処理を使用することで、さらに多くの量やバリエーションに富んだデータを用意することができます。

これらの方法についても、冒頭でご紹介したUdemyの講座
【Pythonで学ぶ】OpenCVでの画像処理入門
では説明されていますので、興味のある方は是非受講してみて下さい!

画像認識や物体検出において、学習データとして画像を用意することは地味で大変な作業です。
機械学習・ディープラーニングというと、最先端だったりスマートな技術のほうにどうしても目が向きがちです。
しかし、このデータを準備する作業を真面目にしっかりとやらないと、精度が思うように出なかったり、予想外の推論をしてしまったりということが起こります。
ですので、地味で大変な作業ですが、しっかりと取り組む必要があると考えています。
そういった部分で、AIエンジニアの方々にとって、少しでもお役に立てれば幸いです。

3
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
10