画像の拡大・縮小(resize)


pythonで画像をリサイズ(拡大・縮小)する方法を紹介します。

今回は
  • cv2.resize()によるリサイズ
  • アフィン変換によるリサイズ
  • PILresizeによるリサイズ
の3手法を紹介しています。

開発環境はjupyter notebook、言語はpythonです。

今回使用した画像・コード一式はgoogle driveにも保存しておりますので、必要に応じてお使いください。

作成したコード

#モジュールのインポート
import cv2
import glob
import numpy as np
import matplotlib.pyplot as plt
import os
from PIL import Image
%matplotlib inline

#affine変換によるリサイズ用def文
def resize_affine(path,ratio):
    img = cv2.imread(path)
    h, w = img.shape[:2]
    src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
    dest = src * ratio
    affine = cv2.getAffineTransform(src, dest)
    img3 = cv2.warpAffine(img, affine, (int(float(w)*ratio), int(float(h)*ratio)), cv2.INTER_LANCZOS4)
    cv2.imwrite(os.path.split(path)[1][:-4]+"_resize_affine_"+str(ratio)+".bmp",img3)
    return

#PILによるリサイズ用def文
def resize_PIL(path,ratio):
    img = Image.open(path)
    w,h = img.size    
    img_resize = img.resize((int(float(w)*ratio),int(float(h)*ratio)),Image.LANCZOS)      
    img_resize.save(os.path.split(path)[1][:-4]+"_PIL_"+str(ratio)+".bmp")
    return

#画像の読み込み
filename = glob.glob("*.jpg")
img = cv2.imread(filename[0])
plt.imshow(img)

#拡大倍率の指定
ratio = 0.5

for n in range(len(filename)):
    img = cv2.imread(filename[n])
    #パターン1:cv2.resize()によるリサイズ
    img_cv2resize = cv2.resize(img, dsize=None, fx=ratio, fy=ratio,interpolation=cv2.INTER_LANCZOS4)
    cv2.imwrite(os.path.split(filename[n])[1][:-4]+"_cv2resize_"+str(ratio)+".bmp",img_cv2resize)
    
    #パターン2:アフィン変換によるリサイズ
    resize_affine(filename[n],ratio)
    
    #パターン3:PILによるリサイズ
    resize_PIL(filename[n],ratio)
一応、プログラムと同ディレクトリに入れたjpgファイルに対して、3種類の処理が一気に実行されるよう記述しております。
また、今回は元の画像の大きさを縦横ともに0.5倍になるよう設定していますが、アス比も自由に設定できるのでカスタマイズしてみてください。

処理の解説

1. cv2.resize()によるリサイズ

用いたプログラムはこちらです。
ratio = 0.5
cv2.resize(img, dsize=None, fx=ratio, fy=ratio,interpolation=cv2.INTER_LANCZOS4)
今回はfxとfyに拡大率(ratio)である0.5を入力することで、画像の大きさを半分にしています。

さらに、interpolationによってデータの不足部分を補完する方法を指定しています。
使用できる補間法は以下の表にまとめています。
具体的な解説は省略しますが、表の下の補間法ほど、補間精度が高くなる(画質が悪くなりにくい)と考えてください。

補間法概要
cv2.INTER_NEAREST最近傍補間
cv2.INTER_LINEARバイリニア補間
cv2.INTER_AREA平均画素法
cv2.INTER_CUBICバイキュービック補間
cv2.INTER_LANCZOS4ランツォシュ

また、下記のようdsizeを書き換えて、指定の高さhと幅wを持つ画像にリサイズすることができます。
w,h = img.shape[:2]
cv2.resize(img, dsize=(w,h))
この方法は紹介した3つの手法の中では最も使用しやすいコマンドかなと思います。

2. affine変換によるリサイズ

使用したプログラムは下記の通りです。
def resize_affine(path,ratio):
    img = cv2.imread(path)
    h, w = img.shape[:2]
    src = np.array([[0.0, 0.0],[0.0, 1.0],[1.0, 0.0]], np.float32)
    dest = src * ratio
    affine = cv2.getAffineTransform(src, dest)
    img3 = cv2.warpAffine(img, affine, (int(float(w)*ratio), int(float(h)*ratio)), cv2.INTER_LANCZOS4)
    cv2.imwrite(os.path.split(path)[1][:-4]+"_resize_affine_"+str(ratio)+".bmp",img3)
    return
本プログラムは変数srcから変数destへと、cv2.getAffine()とcv2.warpAffine()を使って変換する処理になります。
下図を見ていただけると処理のイメージが湧きやすいと思います。
画像上の3点の座標を、変換前(src)と変換後(dest)でどのように移動させるか指定するという感じですね。
 affine変換によるリサイズの模式図

なお、cv2.warpAffine()内の(int(float(w)*ratio), int(float(h)*ratio))は変換後の画像サイズとなります。
ここの設定を誤ると黒い余白(黒いのに余白というのも変な感じですが・・・)ができたり、画像が切り出されてしまうので注意です。

見た感じ、3手法の中で最も画質が悪かったのですが、affine変換だとリサイズ以外にも並進なども同時にできるので、利用する頻度は多いかもしれません。

3. PILによるリサイズ

使用したプログラムは以下の通りです。
#PILによるリサイズ用def文
def resize_PIL(path,ratio):
    img = Image.open(path)
    w,h = img.size    
    img_resize = img.resize((int(float(w)*ratio),int(float(h)*ratio)),Image.LANCZOS)      
    img_resize.save(os.path.split(path)[1][:-4]+"_PIL_"+str(ratio)+".bmp")
    return
上2つの手法と流れは大して変わらないので説明はいらないと思います。
ただ、interpolationの指定方法が少し違いますね。
PILでは下記の6つ手法がinterpolationとして使用できるそうです。
  • NEAREST
  • BOX
  • BILINEAR
  • HAMMING
  • BICUBIC
  • LANCZOS
3手法全てにおいて、ランツォシュでリサイズしてみましたが、なぜかこのPILによるリサイズが最も画質が良かったです。あまりPILは便利だと思わないですが、使ってみる価値はありそうです。

今回使用した画像:鴨らぁめん/らーめん 鴨 to 葱

基本パワポで図を作っているので、リサイズの処理に説得力を出せず、今回はトップ画のみの登場となりました。
鴨と葱の旨味が詰まった最高のラーメンでした。ここはリピートしたいと思っています。

コメント