サイズ変更・回転・切り抜き

画像のサイズ変更(拡大・縮小)

OpenCV で画像サイズを変更するときは resize メソッドを使用する。このメソッドを使用するとき、変更後のサイズを絶対値あるいは倍率で指定する。ただし、JPEG 形式の画像を編集してから再度 JPEG 形式で保存すると画質が劣化するので、なるべく JPEG を介した途中結果の書き出しや再利用を避けたほうがいい。

次のPython サンプルコードでは、1024 × 1052 の画像を読み込んでから、それを 512 × 512 の画像として保存する例を示している。

import cv2

im = cv2.imread('sample.jpg')
im_resized = cv2.resize(im, dsize=(512, 512))

cv2.imwrite('sample_512x512.jpg', im_resized, [int(cv2.IMWRITE_JPEG_QUALITY), 100])

また、あらかじめ倍率を計算しておくことで、横幅を一定の値に変更し、縦幅を横幅と同じ倍率で拡大・縮小することもできる。次のコードは、あるディレクトリ中に含まれているすべての画像に対して、横幅を 512 ピクセルにし、縦幅を横幅の変更倍率に応じて変更させる例を示している。


import cv2
import glob

for image_file in glob.glob('./data/*.jpg'):
    im = cv2.imread(image_file)
    w, h, ch = im.shape

    scale_ratio = 512 / w
    im_resized = cv2.resize(im, dsize=(512, round(h * scale_ratio)))

    output_file = image_file + '.scaled.jpg'
    cv2.imwrite(output_file, im_resized, [int(cv2.IMWRITE_JPEG_QUALITY), 100])

また、倍率をそのまま指定することもできる。次の Python サンプルコードでは、1024 × 1052 の画像を読み込んでから、縦幅と横幅をそれぞれ 30% に縮小する例を示している。

import cv2

im = cv2.imread('sample.jpg')
im_resized = cv2.resize(im, dsize=None, fx=0.3, fy=0.3)

cv2.imwrite('sample_512x512.jpg', im_resized, [int(cv2.IMWRITE_JPEG_QUALITY), 100])

アフィン変換

画像のサイズを変更せずに、内容だけを移動させたり、形を変換させたり、回転させたりすることができる。これらの操作はアフィン変換と呼ばれて、 wrapAffine メソッドで行うことができる。アフィン変換は、画像のデータ(2 次元データ)に変換行列 M をかけて、線形変換を行う。変換行列 M は、3 × 2 の行列であり、左側の 2× 2 の要素は回転を制御し、右側の 1×2 の要素は平行移動を制御している。例えば、次の例は、画像を角度 θ だけ回転させてから、x 方向に tx だけ移動し、y 方向に ty だけ移動する例となっている。

\[ M = \begin{pmatrix} \cos \theta & - \sin \theta & t_{x} \\ \sin \theta & \cos \theta & t_{y} \end{pmatrix} \]

移動

画像を並行移動させる場合は、θ = 0 とすればよく、Python のサンプルコードは次のようになる。画像内容を並行移動すると、もともと内容があった部分が空になる。この場合、0 (黒)で埋められる。

import numpy as np
import cv2

m = np.float32([[1, 0, 400], [0, 1, 200]])
## [[  1.   0. 400.]
##  [  0.   1. 200.]]

im = cv2.imread('sample.jpg')
h, w, ch = im.shape
im_trasformed = cv2.warpAffine(im, m, (w, h))

cv2.imwrite('sample_transformed.jpg', im_trasformed, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
変換前変換後
アフィン変換前の画像 OpenCV によるアフィン変換後(並行移動)の画像

回転

画像を回転させる場合は、θ に角度を代入し、tx = ty = 0 とすればよいだが、回転後、画像の中心がずれるので tx に適当な値を与えて、中心をもとに戻す必要がある。実際の Python のサンプルコードは次のようになる。画像を回転させたとき、もともと画像があった部分が回転によってなくなると、その部分は 0 (黒)で埋められる。とくに、直角以外の回転により画像が斜めになるので、回転後の画像を JPEG 形式で保存すると、ブロックノイズが生じるので注意が必要。

import numpy as np
import math
import cv2

im = cv2.imread('sample.jpg')
h, w, ch = im.shape

r = math.pi
m = np.float32([[math.cos(r), -math.sin(r), h], [math.sin(r), math.cos(r), w]])
## [[-1.0000000e+00 -1.2246469e-16  1.0520000e+03]
##  [ 1.2246469e-16 -1.0000000e+00  1.0240000e+03]]

im_trasformed = cv2.warpAffine(im, m, (h, w))

cv2.imwrite('sample_transformed.jpg', im_trasformed, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
変換前変換後
アフィン変換前の画像 OpenCV によるアフィン変換後(回転)の画像

画像を回転させたりする場合は、画像の中心がずれるため、回転後の中心を画像の中心に戻す計算が必要になる。この計算は getRotationMatrix2D メソッドを使うことで、自動的に計算してくれて、アフィン変換行列に整えてくれる。

import numpy as np
import math
import cv2

im = cv2.imread('sample.jpg')
h, w, ch = im.shape

m = cv2.getRotationMatrix2D((h / 2, w / 2), -180, 1)
## [[-1.0000000e+00 -1.2246469e-16  1.0520000e+03]
##  [ 1.2246469e-16 -1.0000000e+00  1.0240000e+03]]

im_trasformed = cv2.warpAffine(im, m, (h, w))
cv2.imwrite('sample_transformed.jpg', im_trasformed, [int(cv2.IMWRITE_JPEG_QUALITY), 100])

切り抜き

画像の切り抜きは、Python のリストのスライス機能を利用する。例えば、画像を 2 ×l 2 のブロックに分割して、左上のブロックを新たな画像として保存するときのサンプルコードは次のようになる。

import cv2

im = cv2.imread('sample.jpg')
h, w, ch = im.shape

im_cropped = im[0:round(h/2), 0:round(w/2), :]

cv2.imwrite('sample_cropped.jpg', im_cropped, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
変換前変換後
アフィン変換前の画像 OpenCV を使用して画像の一部を切り抜く方法