I have to classify a large number of images at work. I feel that free software can be used for efficient sorting, but unfortunately we prohibit free software in principle. Then, in the spirit of making it, I made ** an image viewer with a simple sorting function **.
Example: A dog image that has been confused in a cat image is extracted with a label "1".
Click here for the main body of the sorting tool ↓
Sorting tool.py
from file_walker import folder_walker #Self-made function
from folder_selecter import file_selecter #Self-made function
import os
import shutil
import subprocess
import tkinter as tk
from tkinter import Label, Tk, StringVar
from PIL import Image, ImageTk #External library
#File reading- - - - - - - - - - - - - - - - - - - - - - - -
def load_file(event):
global img_num, item, dir_name
#Read the file
tex_var.set("Reading a file...")
dir_name = file_selecter(dir_select=True)
if not dir_name == None:
file_list = folder_walker(dir_name)
#List the images that can be read from the file
for f in file_list:
try:
img_lst.append(Image.open(f))
filename_lst.append(f)
except:
pass
#Redefine canvas size to fit window size
window_resize()
#Image conversion
for f in img_lst:
#Resized to fit inside the canvas
resized_img = img_resize_for_canvas(f, image_canvas)
#Image conversion so that it can be displayed with tkinter
tk_img_lst.append(ImageTk.PhotoImage(
image=resized_img, master=image_canvas))
#Get the center of the canvas
c_width_half = round(int(image_canvas["width"]) / 2)
c_height_half = round(int(image_canvas["height"]) / 2)
#Display on canvas
img_num = 0
item = image_canvas.create_image(
c_width_half, c_height_half, image=tk_img_lst[0], anchor=tk.CENTER)
#Label rewriting
tex_var.set(filename_lst[img_num])
#Hide the load button
load_btn.pack_forget()
#Arrangement of sorting execution buttons
assort_btn.pack()
#To the next image- - - - - - - - - - - - - - - - - - - - - - - -
def next_img(event):
global img_num
#Get the number of images being loaded
img_count = len(tk_img_lst)
#Determine if the image is not the last
if img_num >= img_count - 1:
pass
else:
#Image No. being displayed.Update and display
img_num += 1
image_canvas.itemconfig(item, image=tk_img_lst[img_num])
#Label rewriting
tex_var.set(filename_lst[img_num])
#Show labeling
if filename_lst[img_num] in assort_dict:
assort_t_var.set(assort_dict[filename_lst[img_num]])
else:
assort_t_var.set("")
#To the previous image- - - - - - - - - - - - - - - - - - - - - - - -
def prev_img(event):
global img_num
#Determine if the image is not the first
if img_num <= 0:
pass
else:
#Image No. being displayed.Update and display
img_num -= 1
image_canvas.itemconfig(item, image=tk_img_lst[img_num])
#Label rewriting
tex_var.set(filename_lst[img_num])
#Show labeling
if filename_lst[img_num] in assort_dict:
assort_t_var.set(assort_dict[filename_lst[img_num]])
else:
assort_t_var.set("")
#Redefine canvas size from window size- - - - - - - - - - - - - - - - -
def window_resize():
image_canvas["width"] = image_canvas.winfo_width()
image_canvas["height"] = image_canvas.winfo_height()
#Shrink the image to fit the canvas size- - - - - - - - - - - - - - - - - - - -
def img_resize_for_canvas(img, canvas, expand=False):
size_retio_w = int(canvas["width"]) / img.width
size_retio_h = int(canvas["height"]) / img.height
if expand == True:
size_retio = min(size_retio_w, size_retio_h)
else:
size_retio = min(size_retio_w, size_retio_h, 1)
resized_img = img.resize((round(img.width * size_retio),
round(img.height * size_retio)))
return resized_img
#Image display- - - - - - - - - - - - - - - - - - - - - - - -
def image_show(event):
img_lst[img_num].show()
#Labeling for images- - - - - - - - - - - - - - - - - - - - - - - -
def file_assort(event):
if str(event.keysym) in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]:
assort_dict[filename_lst[img_num]] = str(event.keysym)
elif str(event.keysym) == "0":
del assort_dict[filename_lst[img_num]]
#Show labeling
if filename_lst[img_num] in assort_dict:
assort_t_var.set(assort_dict[filename_lst[img_num]])
else:
assort_t_var.set("")
print(assort_dict[filename_lst[img_num]])
#Folder division execution- - - - - - - - - - - - - - - - - - - - - - - -
def assort_go(event):
global f_dir
for f in assort_dict:
#Get file names and folder names before and after sorting
f_dir = os.path.dirname(f)
f_basename = os.path.basename(f)
new_dir = os.path.join(f_dir, assort_dict[f])
new_path = os.path.join(new_dir, f_basename)
#Directory existence check
if not os.path.exists(new_dir):
os.mkdir(new_dir)
#File move execution
shutil.move(f, new_path)
#Show / hide various buttons
assort_btn.pack_forget()
open_folder_btn.pack()
print(new_path)
#Open folder- - - - - - - - - - - - - - - - - - - - - - - -
def folder_open(event):
#Convert path to open in Explorer
open_dir_name = f_dir.replace("/", "\\")
#Open in explorer
subprocess.Popen(['explorer', open_dir_name])
#Close tkinter window
root.destroy()
print(open_dir_name)
#Main processing-------------------------------------------------------
if __name__ == "__main__":
#Global variables
img_lst, tk_img_lst = [], []
filename_lst = []
assort_file_list = []
assort_dict = {}
img_num = 0
f_basename = ""
#tkinter drawing settings
root = tk.Tk()
root.title(u"Display / sorting tool")
root.option_add("*font", ("Meiryo UI", 11))
#Load button drawing settings
load_btn = tk.Button(root, text="Read")
load_btn.bind("<Button-1>", load_file)
load_btn.pack()
#Canvas drawing settings
image_canvas = tk.Canvas(root,
width=640,
height=480)
image_canvas.pack(expand=True, fill="both")
#Sorting result display
assort_t_var = tk.StringVar()
assort_label = tk.Label(
root, textvariable=assort_t_var, font=("Meiryo UI", 14))
assort_label.pack()
#File name label drawing settings
tex_var = tk.StringVar()
tex_var.set("file name")
lbl = tk.Label(root, textvariable=tex_var, font=("Meiryo UI", 8))
lbl["foreground"] = "gray"
lbl.pack()
#Operation setting to feed images with the right and left keys
root.bind("<Key-Right>", next_img)
root.bind("<Key-Left>", prev_img)
# 「Ctrl」+Image display with "P"
root.bind("<Control-Key-p>", image_show)
#Sorting target setting with number keys
root.bind("<Key>", file_assort)
#Sort execution button
assort_btn = tk.Button(root, text="Sorting execution")
assort_btn.bind("<Button-1>", assort_go)
#Button to open folder
open_folder_btn = tk.Button(root,text="Open folder")
open_folder_btn.bind("<Button-1>", folder_open)
root.mainloop()
The created self-made function was used for the operation of specifying the folder and the operation of acquiring the file list from the specified folder. Store the following .py file in the same folder as the above sorting tool .py.
file_selector.py
import os
import sys
import tkinter as f_tk
from tkinter import filedialog
def file_selecter(ini_folder_path = str(os.path.dirname(sys.argv[0])),
multiple= False, dir_select = False):
"""
Open a dialog and select a file or folder.
If you do not specify an initial folder, the folder of the file itself is opened.
You can select folder selection and file selection (multiple / single) as options.
Parameters
----------
ini_folder_path : str
The folder to open initially. The default value is the folder path of the executable file
multiple : bool
Whether to make multiple files selectable. The default value is False, which is a single selection.
dir_select : bool
Folder selection mode. The default value is False, which is in file selection mode.
"""
root_fileselect=f_tk.Tk()
root_fileselect.withdraw() #Hide window
if os.path.isfile(ini_folder_path):
ini_folder_path = os.path.dirname(ini_folder_path) #If the file name is included in the initial folder specification, the file folder is returned.
if dir_select:
select_item = f_tk.filedialog.askdirectory(initialdir=ini_folder_path) #Directory selection mode
elif multiple:
select_item = f_tk.filedialog.askopenfilenames(initialdir=ini_folder_path) #File (multiple) selection mode
else:
select_item = f_tk.filedialog.askopenfilename(initialdir=ini_folder_path) #File (single) selection mode
root_fileselect.destroy()
if not select_item =="":
return select_item
file_walker.py
import os
import pathlib
def folder_walker(folder_path, recursive = True, file_ext = ".*"):
"""
Get the file list of the specified folder.
It can be obtained recursively or non-recursively by specifying an argument.
Parameters
----------
folder_path : str
Target folder path
recursive : bool
Whether to get recursively. The default value is True, which is recursively acquired.
file_ext : str
Specify the extension of the file to be read. Example:".jpg "Need a period like. The default value is".*"Not specified in
"""
p = pathlib.Path(folder_path)
if recursive:
return list(p.glob("**/*" + file_ext)) # **/*Recursively get files with
else:
return list(p.glob("*" + file_ext)) #Do not recursively retrieve files
I also use Tkinter's filedialog
to select the folder, but at first I tried to draw the root of the main window after selecting the folder. Then, I had a hard time because the main root was not displayed well or became inactive, probably because of inheritance.
After all, if I called filedialog after drawing the main root, I could draw the root without any problem.
At first, I was impatient because the contents of the canvas and the label with tk.StringVar ()
embedded were not displayed at all.
As a result of various investigations, python has a concept of garbage collection, and the contents of variables that are judged to be unnecessary (= inaccessible) are automatically deleted. As a result, when the canvas tried to refer to the object, it could not be drawn because its contents were lost.
To avoid this, you can either load it as a global variable in advance or have an instance of the class load the object. See below for details.
Reference page: [Python] What to do when objects such as images cannot be drawn with Tkinter
https://daeudaeu.com/create_image_problem/
I had a hard time finding a way to reference the parameters of the widget. After all, all I had to do was get it with ** widget name ["parameter name"] ** as shown below.
canvas_w = int(image_canvas["width"])
(In this case, since the window size was made variable, it was necessary to obtain the canvas size so that the center of the canvas could be determined.)
I thought it would be easy to make because it was a simple operation, but I had a hard time unexpectedly due to lack of knowledge. In the future, I would like to make various modifications based on the one created this time to make it a more user-friendly tool. In particular,
etc.
Recommended Posts