質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

openpyxl

openpyxlは、Excel2007以降のファイル(xlsx/xlsm/xltx/xltm)を読み書きするためのPythonライブラリです。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

4130閲覧

画像付きExcelの出し方(numpyを用いて)

unser

総合スコア58

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

openpyxl

openpyxlは、Excel2007以降のファイル(xlsx/xlsm/xltx/xltm)を読み書きするためのPythonライブラリです。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2021/08/26 14:05

dataframeに
numpyの画像データが格納されている場合の処理で,
画像付きのexcel_fileを出力したいと考えております。

具体的には
事前設定として,何かしらのexcel_fileを用意し,以下のコードを実装し,
そのエクセルファイルに画像を入れる作業をしたいと考えています。

作りたいエクセルファイルの想像

idimg
1kの中身
2kの中身

具体的には,以下のような実装です。

python

1import pandas as pd 2import numpy as np 3import matplotlib.pyplot as plt 4from PIL import Image as Image_pil 5import openpyxl 6from openpyxl.drawing.image import Image as Image_xl 7 8 9k=[[[0,0,0],[200,200,200],[0,0,0],[200,200,200]], 10 [[200,200,200],[0,0,0],[200,200,200],[0,0,0]], 11 [[0,0,0],[200,200,200],[0,0,0],[200,200,200]], 12 [[200,200,200],[0,0,0],[200,200,200],[0,0,0]]] 13k=np.array(k) 14 15# kの中身確認 16# Image_pil.fromarray((k).astype(np.uint8)) 17# plt.imshow(k) 18 19# df 作成 20lst=[] 21for i in range(10): 22 lst.append([k]) 23 24df=pd.DataFrame(lst,columns=['img']) 25 26#画像挿入するエクセルファイルを指定 27t=!pwd 28out_excel_path=t[0]+"???.xlsx" 29wb = openpyxl.load_workbook(out_excel_path) 30ws = wb["Sheet1"] 31 32#挿入する画像を指定 33for index, row in df.iterrows(): 34 img_pil = Image_pil.fromarray((row[0]).astype(np.uint8)) 35 img_xl = Image_xl(img_pil) 36 #画像挿入 37 ws.add_image(img_xl, 'E'+str(index+1)) 38 #保存 39 wb.save(out_excel_path) 40

kの中身

しかし,上記を実装すると

terminal

1--------------------------------------------------------------------------- 2AttributeError Traceback (most recent call last) 3<ipython-input-21-0914d2352001> in <module> 4 38 ws.add_image(img_xl, 'E'+str(index+1)) 5 39 #保存 6---> 40 wb.save(out_excel_path) 7 8/usr/local/lib/python3.9/site-packages/openpyxl/workbook/workbook.py in save(self, filename) 9 405 if self.write_only and not self.worksheets: 10 406 self.create_sheet() 11--> 407 save_workbook(self, filename) 12 408 13 409 14 15/usr/local/lib/python3.9/site-packages/openpyxl/writer/excel.py in save_workbook(workbook, filename) 16 291 archive = ZipFile(filename, 'w', ZIP_DEFLATED, allowZip64=True) 17 292 writer = ExcelWriter(workbook, archive) 18--> 293 writer.save() 19 294 return True 20 295 21 22/usr/local/lib/python3.9/site-packages/openpyxl/writer/excel.py in save(self) 23 273 def save(self): 24 274 """Write data into the archive.""" 25--> 275 self.write_data() 26 276 self._archive.close() 27 277 28 29/usr/local/lib/python3.9/site-packages/openpyxl/writer/excel.py in write_data(self) 30 75 self._write_worksheets() 31 76 self._write_chartsheets() 32---> 77 self._write_images() 33 78 self._write_charts() 34 79 35 36/usr/local/lib/python3.9/site-packages/openpyxl/writer/excel.py in _write_images(self) 37 114 # delegate to object 38 115 for img in self._images: 39--> 116 self._archive.writestr(img.path[1:], img._data()) 40 117 41 118 42 43/usr/local/lib/python3.9/site-packages/openpyxl/drawing/image.py in _data(self) 44 49 # don't convert these file formats 45 50 if self.format in ['gif', 'jpeg', 'png']: 46---> 51 img.fp.seek(0) 47 52 fp = img.fp 48 53 else: 49 50/usr/local/lib/python3.9/site-packages/PIL/Image.py in __getattr__(self, name) 51 544 ) 52 545 return self._category 53--> 546 raise AttributeError(name) 54 547 55 548 @property 56 57AttributeError: fp

というバグが出たり,excelファイルが壊れたりしてしてしまいます。

どのようにこのバグを解消したら良いか不明のためご教授していただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

原因は、(あなたが悪いわけではなく)openpyxl.drawing.imageの実装にあります。

openpyxl.drawing.image — openpyxl 3.0.7 documentation

を見ると、Imageクラスの__init__(コンストラクター)に、以下のコードが書かれています。

Python

1 try: 2 self.format = image.format.lower() 3 except AttributeError: 4 self.format = "png"

imageは、インスタンス生成時に引数で指定された文字列や画像オブジェクトから、対応する画像オブジェクトを読み込んだものです。

質問のコードのように、np.arrayからPIL.Image.fromarrayで生成された画像オブジェクトの場合は、オブジェクトの種類がPIL.Image.Imageになります。このオブジェクトはformatプロパティを持っておらず(Noneなので)、上記のコードではAttributeErrorを捕捉して、self.formatに(なぜか)"png"が設定されます。

すると、実際にデータを読み込む処理に書かれているコード、

Python

1 if self.format in ['gif', 'jpeg', 'png']: 2 img.fp.seek(0) 3 fp = img.fp 4 else: 5 fp = BytesIO() 6 img.save(fp, format="png") 7 fp.seek(0) 8 9 return fp.read()

のif文が成立してimg.fpにアクセスしようとしますが、実際にはPIL.Image.Imagefpプロパティを持たないため、AttributeError: fpが発生します。

上記のコードには、ちゃんとfpが存在しない場合の処理も(else以下に)書いてあるのですが、__init__の処理でformat"png"に設定されているため、使われません。

さて、この不具合はどうやら2年ほど放置されているようで、「Compatibility to PIL, ERROR while save Image create from PIL.Image.Image object (#1144) · Issues · openpyxl / openpyxl · GitLab」にも同じような議論が行なわれていました。

そこで、自分でなんとかするコードを以下に示します。
まず、先頭付近にfrom io import BytesIOを追加してください。

そして、書き込むfor文を以下のように変更します。

Python

1for index, row in df.iterrows(): 2 img_pil = Image_pil.fromarray((row[0]).astype(np.uint8)) 3 fp = BytesIO() 4 img_pil.save(fp, format="png") 5 fp.seek(0) 6 img_png = Image_pil.open(fp) 7 img_xl = Image_xl(img_png) 8 ws.add_image(img_xl, 'E' + str(index + 1)) 9 10wb.save(out_excel_path) 11wb.close()

上記の不具合を回避するために、img_pilに得られたPIL.Image.Imageの画像オブジェクトを、メモリ上にPNG形式で保存して読み込んでいます。img_pngに得られた画像オブジェクトはPIL.PngImagePlugin.PngImageFileという種類で、こいつは現状のopenpyxl.drawing.imageの実装でも問題なく扱えます。

なお、wb.saveは、for文の処理が終わってから一度だけ実行したほうがよいでしょう。

投稿2021/08/27 00:51

Daregada

総合スコア11990

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

unser

2021/08/27 11:30

ありがとうございます!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問