search
LoginSignup
9
Help us understand the problem. What are the problem?

はじめに

画像認識や物体検出においては、学習データとなる画像を大量に用意する必要があります。
しかし、十分な量のデータが用意できないということはよくあります。
そういった場合には、元データに画像処理を施すことで、データを増やす「データ増幅(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エンジニアの方々にとって、少しでもお役に立てれば幸いです。

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
What you can do with signing up
9
Help us understand the problem. What are the problem?