PythonとPySimpleGUIで2画面Viewerを作ってみた(改良版)

今日は2画面Viewer(改良版)を紹介します。
2画面Viewerとは、画像を二つの画面に独立して表示させることができるViewerです。
画像を比較したいときに使うと便利です。また、気に入った画像だけ別フォルダに再保存(Capture)することができます。元画像には変更を加えないため、誤ってオリジナル画像を消去してしまう心配はありません。
また、サムネイル画面でフォルダ内の画像を確認し、比較したい画像を素早く探すこともできます。
ソフトの機能としては、前回作成したソフトとほぼ同じですが、画像の再読み込みボタンを追加したり、ソースコードに新たな手法を取り入れ使いやすくしています。
興味のある方は是非参考にしてください。
本プログラムの特徴
- 画像を左右二画面で比較できる
- 気に入った画像があればその画像をCaptureボタンで指定のフォルダに保存できる
- Captureした画像のサイズは読み込んだ画像と同じ
- 読み込んだ画像はサムネイル表示できる
- サムネイル画像は送りボタン(<<, >>)で10枚づつ更新できる
- Openボタンを追加し、画像の再読み込みができる
- 画像背景に使用しているgray画像をbase64のbytesデータとしてソースコードに埋め込み、gray画像の作成や再読み込みを不要とした
実行画面
以下の映像をご覧ください。
プログラム
以下のコードをみてください。
import io
import os
import sys
import glob
import base64
import numpy as np
from io import BytesIO
import PySimpleGUI as sg
from PIL import Image, ImageTk
# window の色を設定
sg.theme('Dark Blue 3')
# パラメータ設定
count = 0
count_2 = 0
a = 0
a1 = 0
### メイン画面用の画像を作成する関数 ###
def get_img_data(img,w,h,nx1,maxsize=(748, 748), first=False):
global count, count_2
print('読み込んだ画像枚数 =', nx1)
print('Image 1 No.:', count+1)
print('image 2 No.:', count_2+1)
if w > h:
img_back = Image.new(img.mode, (w, w), (128, 128, 128))
img_back.paste(img, (0, (w - h)//2))
img = img_back.resize((748, 748), Image.LANCZOS)
elif h > w:
img_back = Image.new(img.mode, (h, h), (128, 128, 128))
img_back.paste(img, ((h - w)//2, 0))
img = img_back.resize((748, 748), Image.LANCZOS) # 画像をリサイズ
else:
img = img.resize((748, 748), Image.LANCZOS)
img.thumbnail(maxsize) # 画像のサムネイルを作成
if first:
bio = io.BytesIO()
img.save(bio, format="PNG")
del img
return bio.getvalue()
return ImageTk.PhotoImage(img)
### サムネイル用の画像を作成する関数 ###
def get_img_data_1(img_1,sw,sh,maxsize=(150, 150), first=False):
#print('sw=', sw, 'sh=', sh)
if sw > sh:
img_back_1 = Image.new(img_1.mode, (sw, sw), (128, 128, 128))
img_back_1.paste(img_1, (0, (sw - sh)//2))
img_1 = img_back_1.resize((150, 150), Image.LANCZOS)
elif sh > sw:
img_back_1 = Image.new(img_1.mode, (sh, sh), (128, 128, 128))
img_back_1.paste(img_1, ((sh - sw)//2, 0))
img_1 = img_back_1.resize((150, 150), Image.LANCZOS)
else:
img_1 = img_1.resize((150, 150), Image.LANCZOS)
img_1.thumbnail(maxsize)
if first:
bio = io.BytesIO()
img_1.save(bio, format="PNG")
del img_1
return bio.getvalue()
return ImageTk.PhotoImage(img_1)
# 画像が入ったフォルダーを開く
fname = sg.popup_get_folder('画像が入ったフォルダーを選択してください')
files = glob.glob(fname + '/*')
#print('folder=', fname)
# Capture画像の保存先フォルダ指定
save_path = sg.popup_get_folder('Capture画像の保存先フォルダを指定してください')
# 読み込んだ画像をリスト化
x1 = [] # 画像1用
x2 = [] # 画像2用
x_s= [] # サムネイル用
num = 0
for f in files:
title, ext = os.path.splitext(f)
if ext in ['.jpg', '.png', '.JPG']:
if num >= len(files):
break
# 画像枚数カウント
num += 1
# 画面1に表示する画像リスト
img_x1 = Image.open(f)
w_x1, h_x1 = img_x1.size
x1.append([img_x1,w_x1,h_x1,f]) # [画像、画像幅、画像高さ]の情報をパックしてリスト化
# 画面2に表示する画像リスト
img_x2 = Image.open(f)
w_x2, h_x2 = img_x2.size
x2.append([img_x2,w_x2,h_x2,f]) # [画像、画像幅、画像高さ]の情報をパックしてリスト化
# サムネイルに表示する画像リスト
img_s = Image.open(f)
w_s, h_s = img_s.size
x_s.append([img_s,w_s,h_s,f]) # [画像、画像幅、画像高さ]の情報をパックしてリスト化
x1_org = len(x1)
print('読み込んだ画像枚数 : ', x1_org)
print('サムネイル画像枚数 : ', len(x_s))
### gray画像を作成(空欄を埋める画像) ###
# base64のbytesデータとしてソースコードに埋め込む(サイズ:180 x 180ピクセル)
# メリットはgray画像の読み込みや作成が不要となる
icon = 'iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAIAAACyr5FlAAACMklEQVR4Ae3BAQEAAACCoHru9A40Q\
SgQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Rm65w7E9ESUiQAAAABJRU5ErkJggg=='
# 上記base64データを開く
img_base64 = Image.open(BytesIO(base64.b64decode(icon)))
w_64, h_64 = img_base64.size
#### サムネイル画面の空欄数を判定して空欄にgray画像を追加 ###
""" 2×5枚のサムネイル画像を表示
読み込んだ画像枚数len(x)を10で割ってあまりを出し、
その数を判定に用いて足りない枚数をリストに足す。
"""
gray = []
if len(x1) == 1 or len(x1) % 10 == 1: gray = [[img_base64,w_64,h_64,0] for i in range(9)]
elif len(x1) == 2 or len(x1) % 10 == 2: gray = [[img_base64,w_64,h_64,0] for i in range(8)]
elif len(x1) == 3 or len(x1) % 10 == 3: gray = [[img_base64,w_64,h_64,0] for i in range(7)]
elif len(x1) == 4 or len(x1) % 10 == 4: gray = [[img_base64,w_64,h_64,0] for i in range(6)]
elif len(x1) == 5 or len(x1) % 10 == 5: gray = [[img_base64,w_64,h_64,0] for i in range(5)]
elif len(x1) == 6 or len(x1) % 10 == 6: gray = [[img_base64,w_64,h_64,0] for i in range(4)]
elif len(x1) == 7 or len(x1) % 10 == 7: gray = [[img_base64,w_64,h_64,0] for i in range(3)]
elif len(x1) == 8 or len(x1) % 10 == 8: gray = [[img_base64,w_64,h_64,0] for i in range(2)]
elif len(x1) == 9 or len(x1) % 10 == 9: gray = [[img_base64,w_64,h_64,0] for i in range(1)]
### リストの結合 ###
x1 += gray
x2 += gray
x_s += gray
print('結合後の画像枚数 : ', len(x1))
print('結合後のサムネイル画像枚数 : ', len(x_s))
### メイン画面のレイアウト作成 ###
# 実行ボタン設定
s_button_1 = sg.Submit(button_text='<< image 1', size=(29, 2)) # Previous image
s_button_2 = sg.Submit(button_text='image 1 >>', size=(29, 2)) # Next image
s_button_3 = sg.Submit(button_text='<< image 2', size=(29, 2)) # Previous image
s_button_4 = sg.Submit(button_text='image 2 >>', size=(29, 2)) # Next image
s_button_5 = sg.Submit(button_text='<< thumbnail', size=(15, 2)) # Previous thumbnail
s_button_6 = sg.Submit(button_text='thumbnail >>', size=(15, 2)) # Next thumbnail
"""
ボタンの色は引数二つで指定
第一引数は文字の色(英語で指定)、第二引数は背景の色(16進数で指定)
"""
# 画面を閉じるQuitボタン
s_button_7 = sg.Submit(button_text='Quit', size=(11, 10), button_color=('black', '#FFE4E1'))
# 画像を再読み込みするOpenボタン
s_button_10 = sg.Submit(button_text='Open', size=(11, 10), button_color=('black', '#4adcd6'))
# 画像Captureボタン
s_button_8 = sg.Submit(button_text='Capture 1', size=(20, 2), button_color=('black', '#F0E68C')) # Capture 1
s_button_9 = sg.Submit(button_text='Capture 2', size=(20, 2), button_color=('black', '#F0E68C')) # Capture 2
### メイン画面用の画像を準備 ###
ae_1 = sg.Image(data=get_img_data(x1[0][0],x1[0][1],x1[0][2],x1_org, first=True))
ae_2 = sg.Image(data=get_img_data(x2[0][0],x2[0][1],x2[0][2],x1_org, first=True))
### メイン画面のレイアウト設定 ###
layout_0 = sg.Frame(layout=[[ae_1],
[s_button_1, s_button_8, s_button_2]
],
title='Image 1',
title_color='white',
font=('メイリオ', 10),
relief=sg.RELIEF_SUNKEN,
element_justification='center')
layout_1 = sg.Frame(layout=[[ae_2],
[s_button_3, s_button_9, s_button_4]
],
title='Image 2',
title_color='white',
font=('メイリオ', 10),
relief=sg.RELIEF_SUNKEN,
element_justification='center')
### 画像のリスト X からサムネイル画像を作成(関数使用)###
pp_0 = sg.Image(data=get_img_data_1(x_s[0][0],x_s[0][1],x_s[0][2], first=True))
pp_1 = sg.Image(data=get_img_data_1(x_s[1][0],x_s[1][1],x_s[1][2], first=True))
pp_2 = sg.Image(data=get_img_data_1(x_s[2][0],x_s[2][1],x_s[2][2], first=True))
pp_3 = sg.Image(data=get_img_data_1(x_s[3][0],x_s[3][1],x_s[3][2], first=True))
pp_4 = sg.Image(data=get_img_data_1(x_s[4][0],x_s[4][1],x_s[4][2], first=True))
pp_5 = sg.Image(data=get_img_data_1(x_s[5][0],x_s[5][1],x_s[5][2], first=True))
pp_6 = sg.Image(data=get_img_data_1(x_s[6][0],x_s[6][1],x_s[6][2], first=True))
pp_7 = sg.Image(data=get_img_data_1(x_s[7][0],x_s[7][1],x_s[7][2], first=True))
pp_8 = sg.Image(data=get_img_data_1(x_s[8][0],x_s[8][1],x_s[8][2], first=True))
pp_9 = sg.Image(data=get_img_data_1(x_s[9][0],x_s[9][1],x_s[9][2], first=True))
### 画面全体のレイアウト ###
# 画像数の文字を表示
ml = (f'{i+1}' for i in range(10)) # ジェネレーター式を使用
sm = [sg.Text(i, size=(14, 1), font=('メイリオ', 10), justification='center') for i in ml]
sm_3p = [[sm[0+i*2], sm[1+i*2]] for i in range(5)]
# 画面にラインを表示
s_sen = sg.Text('_' * 192)
### サムネイルのレイアウトを作成 ###
layout_x0 = sg.Frame(layout=[[layout_0, layout_1],
[s_sen],
[sg.Output(size=(161,10), key='-OUTPUT-'), s_button_10, s_button_7]],
title='',
title_color='white',
font=('メイリオ', 10),
relief=sg.RELIEF_SUNKEN, element_justification='left'
)
layout_x1 = sg.Frame(layout=[sm_3p[0],
[pp_0,pp_1],
sm_3p[1],
[pp_2,pp_3],
sm_3p[2],
[pp_4,pp_5],
sm_3p[3],
[pp_6,pp_7],
sm_3p[4],
[pp_8,pp_9],
[s_button_5, s_button_6]],
title='thumbnail',
title_color='white',
font=('メイリオ', 10),
relief=sg.RELIEF_SUNKEN, element_justification='center'
)
### レイアウトを作成 ###
layout = [
[layout_x0, layout_x1]
]
### 画面表示の設定 ###
window = sg.Window('Image Viewer', layout, size=(1900, 980),
location=(0, 10),
alpha_channel=1.0,
no_titlebar=False,
grab_anywhere=False).Finalize()
window.Maximize()
### イベントループ ###
while True:
event, values = window.read()
if event is None:
print('exit')
break
if event == 'Open':
# フォルダーを開く
fname = sg.popup_get_folder('画像が入ったフォルダーを選択してください')
print('folder=', fname)
files = glob.glob(fname + '/*')
# 再読み込みのため、count,count_2,a,a1を0にしてリセット
count = 0
count_2 = 0
a = 0
a1 = 0
### 読み込んだ画像をリスト化 ###
x1 = []
x2 = []
x_s= []
num = 0
for f in files:
title, ext = os.path.splitext(f)
if ext in ['.jpg', '.png', '.JPG']:
if num >= len(files):
break
num += 1
img_x1 = Image.open(f)
w_x1, h_x1 = img_x1.size
x1.append([img_x1,w_x1,h_x1,f]) # [画像、画像幅、画像高さ]の情報をパックしてリスト化
img_x2 = Image.open(f)
w_x2, h_x2 = img_x2.size
x2.append([img_x2,w_x2,h_x2,f]) # [画像、画像幅、画像高さ]の情報をパックしてリスト化
img_s = Image.open(f)
w_s, h_s = img_s.size
x_s.append([img_s,w_s,h_s,f]) # [画像、画像幅、画像高さ]の情報をパックしてリスト化
x1_org = len(x1)
x2_org = len(x2)
gray = []
if len(x1) == 1 or len(x1) % 10 == 1: gray = [[img_base64,w_64,h_64,0] for i in range(9)]
elif len(x1) == 2 or len(x1) % 10 == 2: gray = [[img_base64,w_64,h_64,0] for i in range(8)]
elif len(x1) == 3 or len(x1) % 10 == 3: gray = [[img_base64,w_64,h_64,0] for i in range(7)]
elif len(x1) == 4 or len(x1) % 10 == 4: gray = [[img_base64,w_64,h_64,0] for i in range(6)]
elif len(x1) == 5 or len(x1) % 10 == 5: gray = [[img_base64,w_64,h_64,0] for i in range(5)]
elif len(x1) == 6 or len(x1) % 10 == 6: gray = [[img_base64,w_64,h_64,0] for i in range(4)]
elif len(x1) == 7 or len(x1) % 10 == 7: gray = [[img_base64,w_64,h_64,0] for i in range(3)]
elif len(x1) == 8 or len(x1) % 10 == 8: gray = [[img_base64,w_64,h_64,0] for i in range(2)]
elif len(x1) == 9 or len(x1) % 10 == 9: gray = [[img_base64,w_64,h_64,0] for i in range(1)]
# リストの結合
x1 += gray
x2 += gray
x_s += gray
# 確認
print('結合後の画像枚数 : ', len(x1))
print('サムネイル画像枚数 : ', len(x_s))
# メイン画像の更新
ae_1.update(data=get_img_data(x1[0][0],x1[0][1],x1[0][2],x1_org, first=True))
ae_2.update(data=get_img_data(x2[0][0],x2[0][1],x2[0][2],x1_org, first=True))
# サムネイル画像の更新
pp_0.update(data=get_img_data_1(x_s[0][0],x_s[0][1],x_s[0][2], first=True))
pp_1.update(data=get_img_data_1(x_s[1][0],x_s[1][1],x_s[1][2], first=True))
pp_2.update(data=get_img_data_1(x_s[2][0],x_s[2][1],x_s[2][2], first=True))
pp_3.update(data=get_img_data_1(x_s[3][0],x_s[3][1],x_s[3][2], first=True))
pp_4.update(data=get_img_data_1(x_s[4][0],x_s[4][1],x_s[4][2], first=True))
pp_5.update(data=get_img_data_1(x_s[5][0],x_s[5][1],x_s[5][2], first=True))
pp_6.update(data=get_img_data_1(x_s[6][0],x_s[6][1],x_s[6][2], first=True))
pp_7.update(data=get_img_data_1(x_s[7][0],x_s[7][1],x_s[7][2], first=True))
pp_8.update(data=get_img_data_1(x_s[8][0],x_s[8][1],x_s[8][2], first=True))
pp_9.update(data=get_img_data_1(x_s[9][0],x_s[9][1],x_s[9][2], first=True))
### image 1の更新 ###
if event == 'image 1 >>':
if len(x1) == count + 1:
count = 0
ae_1.update(data=get_img_data(x1[0][0],x1[0][1],x1[0][2],x1_org, first=True))
else:
count += 1
ae_1.update(data=get_img_data(x1[count][0],x1[count][1],x1[count][2],x1_org, first=True))
if event == '<< image 1':
if count <= 0:
count = 0
ae_1.update(data=get_img_data(x1[0][0],x1[0][1],x1[0][2],x1_org, first=True))
else:
count -= 1
ae_1.update(data=get_img_data(x1[count][0],x1[count][1],x1[count][2],x1_org, first=True))
if event == 'Capture 1':
if count == 0:
Image.open(x1[0][3]).save(save_path + '/' + str('cap_1_') +str(1) + '.jpg')
else:
Image.open(x1[count][3]).save(save_path + '/' + str('cap_1_') + str(count+1) + '.jpg')
### image 2 の更新 ###
if event == 'image 2 >>':
if len(x2) == count_2+1:
count_2 = 0
ae_2.update(data=get_img_data(x2[0][0],x2[0][1],x2[0][2],x1_org, first=True))
else:
count_2 += 1
ae_2.update(data=get_img_data(x2[count_2][0],x2[count_2][1],x2[count_2][2],x1_org, first=True))
if event == '<< image 2':
if count_2 <= 0:
count_2 = 0
ae_2.update(data=get_img_data(x2[0][0],x2[0][1],x2[0][2],x1_org, first=True))
else:
count_2 -= 1
ae_2.update(data=get_img_data(x2[count_2][0],x2[count_2][1],x2[count_2][2],x1_org, first=True))
if event == 'Capture 2':
if count_2 == 0:
Image.open(x2[0][3]).save(save_path + '/' + str('cap_2_') +str(1) + '.jpg')
else:
Image.open(x2[count_2][3]).save(save_path + '/' + str('cap_2_') + str(count_2+1) + '.jpg')
### サムネイル画像の更新 ###
# 次の画像に進む
if event == 'thumbnail >>':
a += 1
a1 = 10*a
if len(x1) > a1:
pp_0.update(data=get_img_data_1(x_s[0+a1][0],x_s[0+a1][1],x_s[0+a1][2], first=True))
pp_1.update(data=get_img_data_1(x_s[1+a1][0],x_s[1+a1][1],x_s[1+a1][2], first=True))
pp_2.update(data=get_img_data_1(x_s[2+a1][0],x_s[2+a1][1],x_s[2+a1][2], first=True))
pp_3.update(data=get_img_data_1(x_s[3+a1][0],x_s[3+a1][1],x_s[3+a1][2], first=True))
pp_4.update(data=get_img_data_1(x_s[4+a1][0],x_s[4+a1][1],x_s[4+a1][2], first=True))
pp_5.update(data=get_img_data_1(x_s[5+a1][0],x_s[5+a1][1],x_s[5+a1][2], first=True))
pp_6.update(data=get_img_data_1(x_s[6+a1][0],x_s[6+a1][1],x_s[6+a1][2], first=True))
pp_7.update(data=get_img_data_1(x_s[7+a1][0],x_s[7+a1][1],x_s[7+a1][2], first=True))
pp_8.update(data=get_img_data_1(x_s[8+a1][0],x_s[8+a1][1],x_s[8+a1][2], first=True))
pp_9.update(data=get_img_data_1(x_s[9+a1][0],x_s[9+a1][1],x_s[9+a1][2], first=True))
else:
pp_0.update(data=get_img_data_1(x_s[0][0],x_s[0][1],x_s[0][2], first=True))
pp_1.update(data=get_img_data_1(x_s[1][0],x_s[1][1],x_s[1][2], first=True))
pp_2.update(data=get_img_data_1(x_s[2][0],x_s[2][1],x_s[2][2], first=True))
pp_3.update(data=get_img_data_1(x_s[3][0],x_s[3][1],x_s[3][2], first=True))
pp_4.update(data=get_img_data_1(x_s[4][0],x_s[4][1],x_s[4][2], first=True))
pp_5.update(data=get_img_data_1(x_s[5][0],x_s[5][1],x_s[5][2], first=True))
pp_6.update(data=get_img_data_1(x_s[6][0],x_s[6][1],x_s[6][2], first=True))
pp_7.update(data=get_img_data_1(x_s[7][0],x_s[7][1],x_s[7][2], first=True))
pp_8.update(data=get_img_data_1(x_s[8][0],x_s[8][1],x_s[8][2], first=True))
pp_9.update(data=get_img_data_1(x_s[9][0],x_s[9][1],x_s[9][2], first=True))
# a、a1 を初期化
a = 0
a1 = 0
# 一つ前の画像に戻る
if event == '<< thumbnail':
if a1 > 0:
a -= 1
a1 = 10*a
pp_0.update(data=get_img_data_1(x_s[0+a1][0],x_s[0+a1][1],x_s[0+a1][2], first=True))
pp_1.update(data=get_img_data_1(x_s[1+a1][0],x_s[1+a1][1],x_s[1+a1][2], first=True))
pp_2.update(data=get_img_data_1(x_s[2+a1][0],x_s[2+a1][1],x_s[2+a1][2], first=True))
pp_3.update(data=get_img_data_1(x_s[3+a1][0],x_s[3+a1][1],x_s[3+a1][2], first=True))
pp_4.update(data=get_img_data_1(x_s[4+a1][0],x_s[4+a1][1],x_s[4+a1][2], first=True))
pp_5.update(data=get_img_data_1(x_s[5+a1][0],x_s[5+a1][1],x_s[5+a1][2], first=True))
pp_6.update(data=get_img_data_1(x_s[6+a1][0],x_s[6+a1][1],x_s[6+a1][2], first=True))
pp_7.update(data=get_img_data_1(x_s[7+a1][0],x_s[7+a1][1],x_s[7+a1][2], first=True))
pp_8.update(data=get_img_data_1(x_s[8+a1][0],x_s[8+a1][1],x_s[8+a1][2], first=True))
pp_9.update(data=get_img_data_1(x_s[9+a1][0],x_s[9+a1][1],x_s[9+a1][2], first=True))
else:
pp_0.update(data=get_img_data_1(x_s[0][0],x_s[0][1],x_s[0][2], first=True))
pp_1.update(data=get_img_data_1(x_s[1][0],x_s[1][1],x_s[1][2], first=True))
pp_2.update(data=get_img_data_1(x_s[2][0],x_s[2][1],x_s[2][2], first=True))
pp_3.update(data=get_img_data_1(x_s[3][0],x_s[3][1],x_s[3][2], first=True))
pp_4.update(data=get_img_data_1(x_s[4][0],x_s[4][1],x_s[4][2], first=True))
pp_5.update(data=get_img_data_1(x_s[5][0],x_s[5][1],x_s[5][2], first=True))
pp_6.update(data=get_img_data_1(x_s[6][0],x_s[6][1],x_s[6][2], first=True))
pp_7.update(data=get_img_data_1(x_s[7][0],x_s[7][1],x_s[7][2], first=True))
pp_8.update(data=get_img_data_1(x_s[8][0],x_s[8][1],x_s[8][2], first=True))
pp_9.update(data=get_img_data_1(x_s[9][0],x_s[9][1],x_s[9][2], first=True))
# a、a1 を初期化
a = 0
a1 = 0
if event == 'Quit':
print('Quit')
break
window.close()
ポイント解説
今回新しい手法を取り入れた部分について解説します。
1.gray画像をbase64のbytesデータとしてソースコードに埋め込む
前回作成したソフトではgray画像を以下の流れで作成していました。
- gray画像を以下のコードで作成
- 指定フォルダに保存
- gray画像のパスを読み込みリスト化 y = [ ]
img_gray = np.zeros((180, 180, 3), np.uint8)
img_gray[:,:,:] = 128
cv2.imwrite('./cnn_act/capture/gray/img_gray.png', img_gray)
y = [i for i in glob.glob('./cnn_act/capture/gray/img_gray.png')]
パスをリスト化しておいて、gray画像が必要なときに直前で画像に変換して使用していました。
これに対し、今回作成したソフトでは以下のコードでgray画像を準備します。
icon = 'iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAIAAACyr5FlAAACMklEQVR4Ae3BAQEAAACCoHru9A40Q\
SgQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Sk\
Q6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6SkQ6Rm65w7E9ESUiQAAAABJRU5ErkJggg=='
# 上記base64データを開く
img_base64 = Image.open(BytesIO(base64.b64decode(icon)))
あらかじめ gray画像をbase64のbytesデータに変換してコード内に記載しておきます。
それを、Image.open(BytesIO(base64.b64decode(icon))) で画像に変換し img_base64 に代入しておきます。
これで gray画像が作成できます。
この手法のメリットは
- gray画像を指定フォルダに保存する必要がない
- 再読み込みする必要がない
- 上記の不要な処理を省くことで実行速度が向上する
ちなみに、本当のメリットは本ソフトを exe化したときに実感します。
市販ソフトでもフォルダの階層構造が崩れるとエラーになることがあります。
gray画像を作成して指定フォルダに一度保存しなければならない事やそれを再度読み込まないといけない事から、本ソフトの置き場所を変えるとフォルダの階層構造が崩れることがあります。
今回のソフトは、exe化することを前提に作ったので、gray画像を埋め込むことにしました。
2.画像リスト作成時の工夫
Image 1 画面に表示する画像のリストと Image 2 画面に表示する画像のリストには同じ画像が入ります。
注意点として、Image 1 と Image 2 の各画面は独立してページ送りしたいため、リスト化する際、読み込んだ画像のアドレスが Image 1 と Image 2 で異なるようにリスト化する必要があります。
以下のような工夫をしています。
# Image 1 画面に表示する画像のリスト
img_x1 = Image.open(f)
w_x1, h_x1 = img_x1.size
x1.append([img_x1,w_x1,h_x1,f]) # [画像、画像幅、画像高さ]の情報をパックしてリスト化
# Image 2 画面に表示する画像のリスト
img_x2 = Image.open(f)
w_x2, h_x2 = img_x2.size
x2.append([img_x2,w_x2,h_x2,f]) # [画像、画像幅、画像高さ]の情報をパックしてリスト化
# サムネイルに表示する画像のリスト
img_s = Image.open(f)
w_s, h_s = img_s.size
x_s.append([img_s,w_s,h_s,f]) # [画像、画像幅、画像高さ]の情報をパックしてリスト化
一見、同じようなことを3回繰り返しているように見えます。
じつは中身が全然違います。
Image.open ( f ) で画像リストを作成するたびに、リストにストックされる画像のアドレスは異なります。
つまり、同じ画像を読み込んでも、x1、x2、x_s のリストにはアドレスの異なる画像としてストックされています。ここがポイントです。
これにより、Image 1 の画面と Image 2 の画面は独立してページ送りができるようになります。
ちなみに、どんな情報をリスト化するかによって、処理の仕方は変わります。
例えば、画像のパス情報(./xxx/yyy.jpg)をリスト化する場合は、前回ソフトのやり方で問題なく左右画面に独立して画像表示させることができます。
今回のように画像情報そのものをリスト化する場合は、リスト化した画像のアドレスに注意する必要があります。
ちょっと分かりにくいですが、色々試してみるとよく分かります。
3.必要な情報はパックしてリスト化
先ほどのコードで、x1.append ( [ img_x1,w_x1,h_x1,f ] ) と記載した箇所がありました。
この意味は、[ img_x1,w_x1,h_x1,f ] でパックしたリストを x1 のリストにストックしなさいということです。
リストをリスト化するということです。
おもしろいのは、種類の異なる情報をリストで一つのパックにできることです。
例えば、img_x1 は画像の情報、w_x1 は画像の幅の値、h_x1 は画像の高さの値、f は画像を読み込んだときのパスの情報です。
種類の異なる情報をリストでパックして、それをまた別なリストにストックすることが可能です。
こうしておくと、情報のかたまりが崩れず、後で利用するときに便利です。
情報を取り出すとき、例えば img_x1 の一番最初の情報を取り出したいときは、
x1[0][0] と書けば良いです。
img_x1 の二番目の情報を取り出したいときは、
x1[1][0] と書けば良いです。
また、w_x1 の一番最初の情報を取り出したいときは、
x1[0][1] と書き、
w_x1の二番目の情報を取り出したいときは、
x1[1][1] と書けば良いです。
まとめ
- gray画像をbase64のbytesデータとしてソースコードに埋め込むことで、gray画像作成、フォルダ保存、再読み込みが不要になる
- 画像を二画面に独立表示させる場合は、画像のアドレスに気を付ける
- 必要な情報はパックしてリスト化しておくと便利
ディスカッション
コメント一覧
まだ、コメントがありません