からあげ博士の日常と研究と

博士課程を満期退学した人が好きなことを好きなままに書くところ。

ブログ用画像をリサイズするためにpythonを使う方法

たったひとつの冴えたやりかた。どうもからあげ博士(@phd_karaage)です。

 この文言、ずいぶん前に読んだ神様のメモ帳というラノベで知ったのですが、もともとはこのタイトルのSF小説が元ネタのようですね。

 割と好きなシリーズでしたが、アニメが微妙だったなあという記憶と、作者がいろいろやらかしてほぼ打ち切りのようになった記憶があります。OP曲のカワルミライは割と好き。

 さて、たった1つの冴えたやりかたなんて豪語していますが、別にいくらでもやり方はある気がします。ついでにこれを機にOpenCVのcv2.resizeを使えるようになるとよいのではないかと思って記事を書いている訳です。OpenCVを使うのは普段OpenCVに慣れているから。別にPillowでもいいんじゃないかなあとは思います。さらに言えばこれはブログに限らずリサイズしたいときに使える方法ではある。

端的にコードを示す

import os
import cv2

def resize(path, x, y, out_path):
    img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
    img = cv2.resize(img, dsize = None, fx = x, fy = y)
    cv2.imwrite(out_path, img)


path = "C://Users/***/Desktop/blog/" ## ファイルパスを指定する。アスタリスクはユーザ名
out_path = "C://Users/***/Desktop/blog_resize/" ##出力先パスの指定。

file_list = os.listdir(path)
x = 0.2
y = 0.2

for i in file_list:
    resize_path = path + os.sep + i
    out_resize = out_path + os.sep + i
    resize(resize_path, x, y, out_resize)

使うライブラリは2つだけ

import os
import cv2

 ここで記載している通り、インポートしているのは、oscv2だけ。osはフォルダ内のファイルをリスト化するときに使い、cv2は画像の読み込みとリサイズに使う。1枚しかリサイズしないよ、という場合はosは不要で、cv2.imreadを行うときに画像のフルパスを指定してあげればよい。

関数の中身はこうなっている

def resize(path, x, y, out_path):
    img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
    img = cv2.resize(img, dsize = None, fx = x, fy = y)
    cv2.imwrite(out_path, img)

 まずは、resizeという関数を定義してあげている。ここで要求している引数は、pathと、xyout_pathの4つである。実際の中身はどうなっているかというと、

img = cv2.imread(path, cv2.IMREAD_UNCHANGED)

 でimgに画像を読み込んでRGB値について行列の形にしている。ここの引数にあるpathは画像の入力元となるパスを指定していて、cv2.IMREAD_UNCHANGEDは実はデフォルトの引数なので省略可。明示的になにか色情報について変化させることなく読み込むよと書いているだけである。他には、cv2.IMREAD_GRAYSCALEなどがある。

 続いてリサイズを行っていて、

img = cv2.resize(img, dsize = None, fx = x, fy = y)

 imgという画像に対して、x方向にx倍、y方向にy倍したリサイズ画像を作れと言っている。この時、保存先はimgを指定しているので、この行列はリサイズ後の画像に置き換えられる。倍率を指定するのではなく、画像サイズを指定することも可能。dsize = Noneとなっている部分に、例えばdsize = (x, y)としてあげればよい。

 ここではcv2.resizeの引数を省略していて、画像の補完方法についての引数がある。デフォルト(省略した場合)は、cv2.INTER_LINEARになる。補完方法については別途調べてほしい。

 最後に画像を保存して終わり。

cv2.imwrite(out_path, img)

 imgを、out_pathに保存してねと言っているだけである。

あとは複数に対してぐるぐるするだけ

path = "C://Users/***/Desktop/blog/" ## ファイルパスを指定する。アスタリスクはユーザ名
out_path = "C://Users/***/Desktop/blog_resize/" ##出力先パスの指定。

file_list = os.listdir(path)
x = 0.2
y = 0.2

for i in file_list:
    resize_path = path + os.sep + i
    out_resize = out_path + os.sep + i
    resize(resize_path, x, y, out_resize)

 関数の中身が分かったところで、あとはこの関数をぐるぐるfor文で回すだけ。pathとout_pathを指定してあげて、倍率を指定してあげているだけである。file_list = os.listdir(path)の部分では、フォルダを指定してあげて、そのフォルダ内のファイルをリストにして返してあげている。このリストに基づいてfor文が回る。

 for文の中身では、resize_pathとout_resizeを指定している。これは、先ほど定義したresize関数にpathを入力するとき、画像のファイル名を拡張子を含めた名前にしてあげる必要があるからである。そのため、path + os.sep + iという形で入力するためにもともとのパスに、ファイル名を合わせた文字列を作成してresizeに入力している。out_resizeも同様である。

実はもっと早いやり方がありそう

 実行速度を追い求めるならもっと早く実行できる書き方はありそうな気がしている。とはいえ、ブログ画像を作る程度で数千枚もリサイズするとは到底思えない。それを考えると、楽にすぐ書けて大して問題も起こらなさそうなコードではあると思っている。

 大したことは書いてないけどこんな感じ。