何をしたいか
「いろいろ作りながら学ぶPython入門」という書籍を参考にPythonによる画像処理の自動化やOpenCVの活用方法を学習中です。
Jupyter Notebookにて打ち込んだコードを実行すると、編集する画像が入ったフォルダを選択するよう要求され、これに従って画像が格納されたフォルダと出力先のフォルダを指定すると
・画像のリサイズ
・顔認識した部分をスマイルマークで隠す
・右下にウォーターマーク画像を入れる
・EIXF情報から取得した日付名のフォルダを編集後の写真が格納される
という処理を行うものです。
開発環境
Jupyter notebook
Python 3.7.3
MacBook Proを使用しています
コード
書籍を参考に打ち込んだコードが以下になります。
python
1import os 2import sys 3import shutil 4from datetime import datetime 5from enum import Enum, auto 6 7import tkinter, tkinter.filedialog 8from PIL import Image 9import cv2 10 11FACE_ICON_FILE = 'smile.png' #アイコン画像 12WM_FILE = 'watermark.png' #ウォーターマーク画像 13CASCADE_FILE = 'haarcascade_frontalface_default.xml' #カスケードファイル 14 15class Mode(Enum): 16 BLG = auto() #ブログ用 17 TMB = auto()#サムネイル用 18 19def mask_face(img_cv, cascade, img_pil, mask): 20 """顔をアイコン画像で隠す 21 :param img_cv: 元画像(OpenCV) 22 :param cascade: カスケードファイル 23 :param img_pil: 元画像(PIL) 24 :param mask: 顔を隠す用の画像(PIL) 25 """ 26 #顔認識を実行 27 faces = cascade.detectMultiScale(img_cv, scaleFactor=1.5) 28 29 #認識された顔にアイコンを貼り付け 30 for (x, y, w, h) in faces: 31 mask = mask.resize((w, h)) 32 img_pil.paste(mask, (x, y), mask) 33 34def mkdir_dto(img, output_dir_path): 35 """画像の撮影日のフォルダを作成する 36 :param img: 画像(PIL) 37 :param output_dir_path: 出力先のフォルダーのパス 38 :return: 作成したフォルダーのパス 39 """ 40 EXIF_DTO = 36867 #Exifの撮影日のタグ番号 41 42 # 移動先フォルダー作成。フォルダー名はExifの撮影日からyyyymmdd形式で生成 43 exif = img._getexif() 44 dt = datetime.strptime(exif[EXIF_DTO], '%Y:%m:%d %H:%M:%S') 45 output_sub_dir = dt.strftime('%Y%m%d') 46 output_path = os.path.join(output_dir_path, output_sub_dir) 47 os.makedirs(output_path, exist_ok=True) 48 49 return output_path 50 51def make_img(img, img_name, mode, watermark, output_path): 52 """画像をリサイズし、ウォーターマークを貼り付け、別名で保存する 53 :param img: 画像(PIL) 54 :param img_name: 画像(PIL)ファイル名 55 :param mode: Mode.BLGならブログ用、Mode.TMBならサムネイル用 56 :param watermark: ウォーターマーク画像(PIL) 57 :param output_path: 出力先フォルダーのパス 58 """ 59 BLG_CHAR = '_s' #ブログ画像のファイル名に付加する文字列 60 TMB_CHAR = '_tmb' #サムネイル画像のファイル名に付加する文字列 61 MAX_W_BLG = 600 #ブログ画像の幅の上限 62 MAX_H_BLG = 600 #ブログ画像の高さの上限 63 MAX_W_TMB = 300 #サムネイル画像の幅の上限 64 MAX_H_TMB = 300 #サムネイル画像の高さの上限 65 66 # サイズ、ファイル名の末尾に付加する文字列を設定 67 if (mode == Mode.BLG): #ブログ用 68 w, h = MAX_W_BLG, MAX_H_BLG 69 add_chr = BLG_CHAR 70 elif (mode == Mode.TMB): #サムネイル用 71 w, h = MAX_W_TMB, MAX_H_TMB 72 add_chr = TMB_CHAR 73 else: 74 return None 75 76 #リサイズ 77 img.thumbnail((w, h)) 78 79 #ウォーターマークを付加 80 w_img, h_img = img.size 81 w_wm, h_wm = watermark.size 82 img.paste(watermark, (w_img - w_wm, h_img- h_wm), watermark) 83 84 #ファイル名に文字列を付加して保存 85 fname, ext = os.path.splitext(img_name) 86 img.save(os.path.join(output_path, fname + add_chr + ext)) 87 88# 顔アイコン画像とウォーターマーク画像読み込み 89face_icon = Image.open(FACE_ICON_FILE) 90watermark = Image.open(WM_FILE) 91 92# 認識器生成 93cascade = cv2.CascadeClassifier(CASCADE_FILE) 94 95#元画像フォルダー選択 96root = tkinter.Tk() 97root.withdraw() 98msg = '画像フォルダーを選択してください' 99img_dir_path = tkinter.filedialog.askdirectory(title=msg) 100if (not img_dir_path): #[キャンセル]クリック時の処理 101 print('フォルダーを選んでください。') 102 sys.exit() 103 104#出力先フォルダー選択 105msg = '出力先フォルダーを選択してください。' 106output_dir_path = tkinter.filedialog.askdirectory(title=msg) 107if (not output_dir_path):#[キャンセル]クリック時の処理 108 print('フォルダーを選んでください。') 109 sys.exit() 110 111# 元画像フォルダー内のファイル1つずつ処理 112for img_file in os.listdir(img_dir_path): 113 #元画像読み込み (PIL) 114 img_path = os.path.join(img_dir_path, img_file) 115 img_pil = Image.open(img_path) 116 117 #顔認識用にOpenCVで元画像をグレースケールで別途読み込み 118 img_cv = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) 119 120 #顔を隠す 121 mask_face(img_cv, cascade, img_pil, face_icon) 122 123 #ファイルの移動先フォルダー作成 124 output_path = mkdir_dto(img_pil, output_dir_path) 125 126 #ブログ用画像とサムネイル用画像を作成 127 make_img(img_pil.copy(), img_file, Mode.BLG, watermark, output_path) 128 make_img(img_pil, img_file, Mode.TMB, watermark, output_path) 129 130 #元画像(PIL)を閉じる 131 img_pil.close() 132 133 #元画像を移動 134 shutil.move(img_path, output_path)
発生しているエラー
上記コードを実行し、用意した画像のフォルダを指定しましたが、出力先フォルダを指定するタイミングで以下エラーメッセージが表示され処理が止まってしまいます。
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-1-70972c780404> in <module> 123 124 #ファイルの移動先フォルダー作成 --> 125 output_path = mkdir_dto(img_pil, output_dir_path) 126 127 #ブログ用画像とサムネイル用画像を作成 <ipython-input-1-70972c780404> in mkdir_dto(img, output_dir_path) 43 # 移動先フォルダー作成。フォルダー名はExifの撮影日からyyyymmdd形式で生成 44 exif = img._getexif() ---> 45 dt = datetime.strptime(exif[EXIF_DTO], '%Y:%m:%d %H:%M:%S') 46 output_sub_dir = dt.strftime('%Y%m%d') 47 output_path = os.path.join(output_dir_path, output_sub_dir) TypeError: 'NoneType' object is not subscriptable
打ち込んだコードを何度も読み返しましたが、誤っている箇所もないようで、NoneType' object is not subscriptableエラーの対処法がわからなくなってしまっている状況です。
小生の力不足で恐縮ですが、アドバイスなど頂戴できれば幸いです。
回答1件
あなたの回答
tips
プレビュー