こんにちは、技術研究所の山﨑です。
ブログ初投稿!
さて、私は深層学習を活用した画像処理など行っておりますが、検証結果で大量の画像のアウトプットがあります。それらを検証結果のまとめとしてExcelに貼り付けたりするのですが、一つ一つ画像ファイルを挿入するのが・・・メンドイ。
そこで、指定したディレクトリ内の画像ファイルをすべてExcelに貼り付けるプログラムを、Pythonで作ってみました。
貼り付け要件
- 指定したディレクトリ内の画像をすべて貼り付け
- 指定したディレクトリにサブディレクトリがある場合、サブディレクトリ内の画像も再帰的に検索して貼り付け
- Excelに画像を貼り付ける際、ディレクトリ毎に縦に並べて配置
- Excelに画像を貼り付ける際、画像の大きさがまちまちでも、それぞれが重ならないようにセルの大きさを調整
作成したプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
""" Excelに画像貼り付け.py """ import os import glob import imghdr import openpyxl import cv2 # 定数設定 INPUT_IMG_DIR = '.\\test_images\\' # 貼り付ける画像を置いておくルートディレクトリ SHEET_TITLE = '画像貼り付け' # シート名の設定 RESULT_FILE_NAME = '.\\result.xlsx' # 結果を保存するファイル名 # 変数 max_height = [] # 各行の画像の高さの最大値を保持 def get_file_names(set_dir_name): """ ディレクトリ内のファイル名取得(ファイル名のみの一覧を取得) """ file_names = os.listdir(set_dir_name) temp_full_file_names = [os.path.join(set_dir_name, file_name) for file_name in file_names if os.path.isfile(os.path.join(set_dir_name, file_name))] # ファイルかどうかを判定 return temp_full_file_names def attach_img(target_full_file_names, set_column_idx, set_dir_name): """ 画像を呼び出して、Excelに貼り付け """ set_row_idx = 1 column_letter = ws.cell(row=set_row_idx, column=set_column_idx).column # セルの行列番号から、そのセルの列番号の文字列を取得 ws.cell(row=1, column=set_column_idx).value = set_dir_name # 各列の1行目に、貼り付ける画像があるディレクトリ名を入力 max_width = 0 # 画像の幅の最大値を保持するための変数 target_full_file_names.sort() # ファイル名でソート for target_file in target_full_file_names: if imghdr.what(target_file) != None: # 画像ファイルかどうかの判定 img = openpyxl.drawing.image.Image(target_file) print('[' + column_letter + '][' + str(set_row_idx+1) + ']' + target_file + 'を貼り付け') # 画像のサイズを取得して、セルの大きさを合わせる(画像同士が重ならないようにするため) size_img = cv2.imread(target_file) height, width = size_img.shape[:2] if max_width < width: max_width = width if not max_height[set_row_idx-1:set_row_idx]: # 配列「max_height」において、「set_row_idx」番目の要素が存在しなければ、挿入 max_height.insert(set_row_idx-1, height) if max_height[set_row_idx-1] < height: max_height[set_row_idx-1] = height ws.row_dimensions[set_row_idx+1].height = max_height[set_row_idx-1] * 0.75 ws.column_dimensions[column_letter].width = max_width * 0.13 cell_address = ws.cell(row=set_row_idx + 1, column=set_column_idx).coordinate # セルの行列番号から、そのセルの番地を取得 img.anchor = cell_address ws.add_image(img) # シートに画像貼り付け set_row_idx += 1 # ワークブック設定 wb = openpyxl.Workbook() ws = wb.worksheets[0] # 1番目のシートを編集対象にする ws.title = SHEET_TITLE # 1番目のシートに名前を設定 # 貼り付ける画像を置いておくルートディレクトリ内のディレクトリ名を再帰的に取得 dirs = glob.glob(os.path.join(INPUT_IMG_DIR, '**' + os.sep), recursive=True) column_idx = 1 # 各ディレクトリについて操作 for dir_name in dirs: f_names = get_file_names(dir_name) # ファイル名取得 attach_img(f_names, column_idx, dir_name) # 画像貼り付け設定 column_idx += 1 # 次の列へ・・・ # ファイルへの書き込み wb.save(RESULT_FILE_NAME) |
プログラムの補足
4~8行目
必要なライブラリのインポート文です。「openpyxl」がExcelを操作するのに必要なライブラリです(ありがたい!)。また、「imghdr」はファイルが画像ファイルかどうかを判別する際に使用しています。
16~17行目
変数として配列「max_height」を定義しています。ディレクトリ毎に画像をExcelの列に縦に並べていくわけですが、各列の”とある行”の画像ファイルの大きさが異なる場合、一番大きい画像ファイルの高さの値を保持するためのものです。この値を利用して、各行の画像同士の重なりを防ぎます。
35行目
変数「max_width」を定義しています。その列に配置する画像で一番大きい画像ファイルの幅の値を保持するためのものです。この値を利用して、各列の画像同士の重なりを防ぎます。
50~51行目
画像の大きさに合わせて、Excelのセルの大きさを設定している部分になります。セルの単位はポイント、画像の単位はピクセルであることから、各式の最後に変換のために係数が掛けられています。画像が重ならないように、適宜値を変更ください。
細かい話になりますが、Excelでは高さはポイントで設定されているのに対し、幅は表示できる文字数で設定されているそうです。そのため、幅は標準のスタイルのフォントによるようです。ただ、その辺を調査するより、実際係数を変えてみて、画像が重ならないかどうかを見てみる方が早いと思います。
プログラムの実行例
例として、以下のように画像ファイルを配置して、プログラムを動かした後のExcelファイルへの出力結果を記載します。
画像ファイルがあるディレクトリ構造の例

画像ファイルがあるディレクトリ構造の例
出力結果の例

出力結果の例
各列の1行目にはディレクトリ名が表示され、その下にそのディレクトリ内の画像ファイルが貼り付けられています。また、各画像が重ならないように並べることができました。
以上、備忘録を兼ねてちょっとしたプログラムのご紹介でした。何かのお役に立てたなら幸いです。
それでは。。。