【Python】フォルダ内の画像を一度にサイズ変更する

エンジニアのメモ帳
この記事は約10分で読めます。

今回はフォルダ内の画像を一括でリサイズするプログラムの紹介です

 

画像を多く貼るブロガーさんやこれから画像系の機械学習を行う方は画像をリサイズする機会が多いはず…

 

OpenCVを使ったPythonでサクッとできるようにしたので、ぜひご覧ください

 

本記事ではcode prettifyが導入できていないためソースコードがかなり見づらいです。ご了承ください。またプログラムの実行は自己責任でお願いいたします。

 

 

開発環境

OS : Windows 10

Python : 3.6 (Anaconda 4.6.14での仮想環境)

OpenCV : 4.1.0

 

プログラム

ソースコード

#coding: utf-8
import cv2
import os
import sys
import shutil
import unicodedata# パラメーター
WIDTH = 800
HEIGHT = None
SAVE_PATH = “resized_image”

# ファイルの拡張子を調べる関数
def check_img(file_name):
extention = file_name[-3:]

if extention in {“jpg”, “JPG”, “png”, “PNG”}:
result = True
else:
result = False

return result

# リサイズするサイズを算出する関数
# widthかheightのどちらかが指定されていれば、
# アスペクト比を維持したままサイズを変更する。
# 両方指定されていれば、そのサイズにリサイズする
def decide_size(img, width=None, height=None):
if width is not None and height is None:
height = int(img.shape[0] * (width / img.shape[1]))
elif width is None and height is not None:
width = int(img.shape[1] * (height / img.shape[0]))

return (width, height)

# ファイル名に日本語が含まれているかチェックする関数
def is_japanese(file_name):
for ch in file_name:
name = unicodedata.name(ch)
if “CJK UNIFIED” in name \
or “HIRAGANA” in name \
or “KATAKANA” in name:
return True
return False

if __name__ == “__main__”:

# 引数の取得
args = sys.argv

# 引数によって読み込み先を変える
if len(args) >= 2:
load_path = args[1]
else:
load_path = os.getcwd()

# カレントディレクトリを移動する
os.chdir(load_path)
cd = os.getcwd()
print(“Change path here : {}” .format(cd))

# 読み込み先のディレクトリ内のファイルを取得
img_list = os.listdir(cd)

# 保存用のフォルダを作成
save_path = os.path.join(“.”, SAVE_PATH)
if os.path.exists(save_path):
pass
else:
os.mkdir(save_path)
print(“SAVE_PATH was just made now : “, save_path)

# 画像ファイルのみをリサイズして保存
for num, img_name in enumerate(img_list):

# 画像の拡張子をチェックする
if check_img(img_name):
print(“—– {} —–” .format(img_name))

# ファイル名に日本語が含まれているかチェックする
if is_japanese(img_name):
save_name = os.path.join(save_path, “resized_image_” + str(num) + “.jpg”)
shutil.copy(img_name, save_name)
img_name = save_name
print(“Renamed : {}” .format(save_name))
else:
save_name = os.path.join(save_path, img_name)

# 画像を読み込む
img = cv2.imread(img_name)

# サイズを決めてリサイズする
size = decide_size(img, WIDTH, HEIGHT)
new_img = cv2.resize(img, dsize=size)

# リサイズ後の画像を保存する
if cv2.imwrite(save_name, new_img):
print(“Succeced to save.”)
else:
print(“Faild to save.”)

 

工夫と解説

縦横どちらか長さを指定すれば勝手にリサイズできる

#上記ソースコード:10,11行目
WIDTH = 800
HEIGHT = None#上記ソースコード:31~37行目
def decide_size(img, width=None, height=None):
if width is not None and height is None:
height = int(img.shape[0] * (width / img.shape[1]))
elif width is None and height is not None:
width = int(img.shape[1] * (height / img.shape[0]))

return (width, height)

#上記ソースコード:98,99行目
size = decide_size(img, WIDTH, HEIGHT)
new_img = cv2.resize(img, dsize=size)

 

WIDTHとHEIGHTという変数を定義しています

(WIDTHが横幅で、HEIGHTが縦幅の長さで単位はピクセルです)

 

この2つの変数のどちらかに値を入れると、関数decide_sizeの中で自動でアスペクト比を保持したまま片方の値を計算してくれます

 

また、WIDTHとHEIGHTを両方指定した時はその値のサイズにリサイズすることができます

 

リサイズ対象のフォルダを選択しやすい

#上記ソースコード:57~60行目
if len(args) >= 2:
load_path = args[1]
else:
load_path = os.getcwd()

 

そのまま引数無しで起動すると現在の位置(カレントディレクトリ)にあるファイルをリサイズします

 

しかし引数で場所をしていすると、その場所がリサイズ対象になります

 

プチ便利な機能(笑)

 

フォルダ内にある画像以外のファイルは無視できる

#上記ソースコード:16~24行目
def check_img(file_name):
extention = file_name[-3:]if extention in {“jpg”, “JPG”, “png”, “PNG”}:
result = True
else:
result = False

return result

#上記ソースコード:68行目
img_list = os.listdir(cd)

#上記ソースコード:79行目~
for num, img_name in enumerate(img_list):
if check_img(img_name):
(for文内は中略)

 

当たり前ですが、OpenCVのimread関数で画像ファイル以外を突っ込むとエラーが出ます

 

このプログラムではlistdir関数で対象のフォルダ内にある全てのファイルをリスト化して、それをfor文で回して読み込んでます

 

その際にそのファイルの拡張子を関数check_imgで調べます

 

調べ方はシンプルで、ファイル名の後ろ3文字をスライスで取得して、それが画像の拡張子がどうか判定します

 

拡張子は現在JPG(jpg)、PNG(png)のみ対応してます

 

日本語名のファイルも自動で読み取れる

# 上記ソースコード:41~48行目
def is_japanese(file_name):
for ch in file_name:
name = unicodedata.name(ch)
if “CJK UNIFIED” in name \
or “HIRAGANA” in name \
or “KATAKANA” in name:
return True
return False# 上記ソースコード:86~92行目
if is_japanese(img_name):
save_name = os.path.join(save_path, “resized_image_” + str(num) + “.jpg”)
shutil.copy(img_name, save_name)
img_name = save_name
print(“Renamed : {}” .format(save_name))
else:
save_name = os.path.join(save_path, img_name)

 

なぜこの機能を付けたかというとOoenCVが日本語ファイル名の画像を読み込めないことが原因です

 

日本語名のファイルをOpenCVのimread関数で読み込むと戻り値がNoneになります

 

まあプログラマーは普通日本語名のファイルなんて使わないから当然だよな~、と思いながらも読み取れないのは厄介なので自分なりに改善策を考えてみました

 

 

まずは、ファイルを読み取るときに名前に日本語が含まれているかを関数is_japanaseチェックします

 

そして日本語が含まれていたら対象のファイルを保存場所にコピーして、その場で「resized_image_*.jpg」という名前にリネームし、関数imreadに読み取らせます

 

保存場所にコピーすることで最終的にリサイズに保存する際に上書きすることになり、コピーによる容量の増加を防ぐことができます

 

 

ちなみに関数is_japanaseに関してはこちらの方の記事をコピペ参考にさせていただきました、本当にありがとうございます!!

 

 

現状の課題

やはり日本語名での保存が上手くできないのが難点ですね

 

日本語名のファイルはすべて「resized_image_*.jpg」という名前にする仕様になっています

 

おしん
おしん

折角に画像の名前を整理して保存したのに、それを全部てきとーな名前に変えられちゃうのは悲しいよなぁ

 

 

ファイルを読み込む際に日本語名も保存して、リサイズ画像を保存し終わった際にリネームしなおすという荒業があるけど…

 

まあとりあえずは保留で!(笑)

 

 

終わりに

今回は対象フォルダ内にある画像ファイルを任意のサイズにリサイズするプログラムの紹介でした

 

ブログ始めてから画像のサイズ統一を意識しはじめて、自分でプログラム書いてみました

 

以前CNNで簡単な顔認識プログラムを作ったときのコードに日本語対応などを加えて、自分なりに使いやすさを意識してみました

 

画像のリサイズに困っている人の参考になれば幸いです

 

ご閲覧ありがとうございました

 

 

コメント

タイトルとURLをコピーしました