画像のサイズ変更(拡大・縮小)
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 だけ移動する例となっている。
移動
画像を並行移動させる場合は、θ = 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])
変換前 | 変換後 |
回転
画像を回転させる場合は、θ に角度を代入し、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])
変換前 | 変換後 |
画像を回転させたりする場合は、画像の中心がずれるため、回転後の中心を画像の中心に戻す計算が必要になる。この計算は 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])
変換前 | 変換後 |