ImageOptimizer/main.py
2025-06-09 16:47:55 +08:00

634 lines
25 KiB
Python

import os
import uuid
import tempfile
import subprocess
import seaborn as sns
import cv2 as cv
import tkinter as tk
from tkinter import filedialog, messagebox
import customtkinter as ctk
import numpy as np
import pandas as pd
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import analyze_func as img_func
import dataset_predict as predict
import dataset_analysis
def raise_frame(next_frame):
next_frame.tkraise()
def on_close():
root.quit()
root.destroy()
def select_img():
# Select image file
filename = filedialog.askopenfilename(
title="Select An Image",
filetypes=(("Image", "*.png"), ("Image", "*.jpg"), ("Image", "*.jpeg"))
)
if filename:
# Read image file
global img_name
global original_img
img_name = os.path.basename(filename)
img_pil = Image.open(filename)
img = cv.cvtColor(np.array(img_pil), cv.COLOR_RGB2BGR)
original_img = img
if img is not None:
raise_frame(f_main)
plot_img_hist(img, f_main_left_top, 7, 3)
display_image(img, f_main_right_top)
# Get image parameter values
WB_red, WB_green, WB_blue = img_func.get_white_balance(img)
average_brightness = img_func.get_brightness(img)
contrast = img_func.get_contrast(img)
average_hue = img_func.get_hue(img)
average_saturation = img_func.get_saturation(img)
average_perceived_brightness = img_func.get_perceived_avg_brightness(img)
average_sharpen = img_func.get_sharpness(img)
average_highlights = img_func.get_highlights(img)
average_shadow = img_func.get_shadows(img)
average_temperature = img_func.get_color_temperature(img)
average_noisy = img_func.get_noise(img)
average_exposure = img_func.get_exposure(img)
# Display image parameter values
parameters = {
"Red": WB_red,
"Green": WB_green,
"Blue": WB_blue,
"Contrast": contrast,
"Brightness": average_brightness,
"Perceived Brightness": average_perceived_brightness,
"Hue": average_hue,
"Saturation": average_saturation,
"Sharpness": average_sharpen,
"Highlight": average_highlights,
"Shadow": average_shadow,
"Temperature": average_temperature,
"Noise": average_noisy,
"Exposure": average_exposure
}
display_parameters(f_main_left_bottom, parameters, img)
def display_parameters(frame, parameters, img):
# Clear previous content
for widget in frame.winfo_children():
widget.destroy()
global img_buffer
img_buffer = img
# Create a scrollable canvas
canvas = ctk.CTkCanvas(frame, highlightthickness=0, bg="white")
scrollbar = ctk.CTkScrollbar(frame, fg_color="white", command=canvas.yview)
scrollable_frame = ctk.CTkFrame(canvas, fg_color="white")
scrollable_frame.bind("<Configure>", lambda e=None: canvas.configure(scrollregion=canvas.bbox("all")))
# Function to change background color on focus
def on_focus_in(event):
event.widget.configure(foreground="black")
def on_focus_out(event):
event.widget.configure(foreground="gray")
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
# Pack the canvas and scrollbar
canvas.pack(side=ctk.LEFT, fill=ctk.BOTH, expand=True)
scrollbar.pack(side=ctk.LEFT, fill=ctk.Y)
# Adding parameters to the scrollable frame
for i, (param, value) in enumerate(parameters.items()):
# Parameter's name label
label = ctk.CTkLabel(scrollable_frame, text=f"{param}", anchor="w", fg_color="white")
label.grid(row=i, column=0, padx=10, pady=5, sticky="w")
# Parameter's value textbox
value_str = f"{value}"
textbox = ctk.CTkTextbox(scrollable_frame, height=1, width=160, wrap="none", fg_color="#f0f0f0",
text_color="gray")
textbox.insert("1.0", value_str)
textbox.grid(row=i, column=1, padx=10, pady=5, sticky="w")
# Bind focus in and focus out events
textbox.bind("<FocusIn>", on_focus_in)
textbox.bind("<FocusOut>", on_focus_out)
textbox.bind("<FocusOut>",
lambda event=None, param1=param, textbox1=textbox: param_updator(param1, textbox1))
scrollable_frame.update_idletasks()
def param_updator(param, textbox):
# Get new parameter value
try:
new_val = float(textbox.get("1.0", "end-1c"))
except ValueError:
messagebox.showerror("Invalid input", f"Invalid value for {param}: {textbox.get('1.0', 'end-1c')}")
return
# Pass it to its corresponding 'modify' function
global img_buffer
modified_img = None
if param == "Red":
modified_img = img_func.modify_white_balance(img_buffer, new_val, -1, -1)
elif param == "Green":
modified_img = img_func.modify_white_balance(img_buffer, -1, new_val, -1)
elif param == "Blue":
modified_img = img_func.modify_white_balance(img_buffer, -1, -1, new_val)
elif param == "Contrast":
modified_img = img_func.modify_contrast(img_buffer, new_val)
elif param == "Brightness":
modified_img = img_func.modify_brightness(img_buffer, new_val)
elif param == "Perceived Brightness":
modified_img = img_func.modify_perceived_avg_brightness(img_buffer, new_val)
elif param == "Hue":
modified_img = img_func.modify_hue(img_buffer, new_val)
elif param == "Saturation":
modified_img = img_func.modify_saturation(img_buffer, new_val)
elif param == "Sharpness":
modified_img = img_func.modify_sharpness(img_buffer, new_val)
elif param == "Highlight":
modified_img = img_func.modify_highlights(img_buffer, new_val)
elif param == "Shadow":
modified_img = img_func.modify_shadows(img_buffer, new_val)
elif param == "Temperature":
modified_img = img_func.modify_color_temperature(img_buffer, new_val)
if param == "Noise":
modified_img = img_func.modify_noise(img_buffer, new_val)
elif param == "Exposure":
modified_img = img_func.modify_exposure(img_buffer, new_val)
else:
pass
# Update img_buffer & Refresh the window
if modified_img is not None:
img_buffer = modified_img
plot_img_hist(img_buffer, f_main_left_top, 7, 3)
display_image(img_buffer, f_main_right_top)
def display_image(img, frame):
# Clear previous image
for widget in frame.winfo_children():
widget.destroy()
# Convert the image to RGB format
img_rgb = cv.cvtColor(img, cv.COLOR_BGR2RGB)
img_pil = Image.fromarray(img_rgb)
# Get the height and width of the image
height, width = img.shape[:2]
# Calculate the maximum width and height
if height > width:
max_width = 380
else:
max_width = 500
img_ratio = width / height
max_height = int(max_width / img_ratio)
# Display image in the frame
img_tk = ctk.CTkImage(img_pil, size=(max_width, max_height))
label = ctk.CTkLabel(frame, image=img_tk, text="")
label.image = img_tk
label.pack(padx=10, pady=10, fill='x')
def plot_img_hist(img, frame, width, height):
# Clear previous plots
for widget in frame.winfo_children():
widget.destroy()
plt.figure(figsize=(width, height))
# Plot grayscale hist
plt.hist(img.ravel(), 256, [0, 256])
# Plot color hist
color = ('blue', 'green', 'red')
for i, color in enumerate(color):
hist = cv.calcHist([img], [i], None, [256], [0, 256])
plt.plot(hist, color=color)
plt.xlabel("Bins")
plt.ylabel("Pixel Number")
# Hide the top and right spines
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Get the current figure & draw & close
figure = plt.gcf()
canvas = FigureCanvasTkAgg(figure, master=frame)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
plt.close()
def revert_to_original():
# Revert to original_img
global original_img
plot_img_hist(original_img, f_main_left_top, 7, 3)
display_image(original_img, f_main_right_top)
# Get image parameter values
WB_red, WB_green, WB_blue = img_func.get_white_balance(original_img)
average_brightness = img_func.get_brightness(original_img)
contrast = img_func.get_contrast(original_img)
average_hue = img_func.get_hue(original_img)
average_saturation = img_func.get_saturation(original_img)
average_perceived_brightness = img_func.get_perceived_avg_brightness(original_img)
average_sharpen = img_func.get_sharpness(original_img)
average_highlights = img_func.get_highlights(original_img)
average_shadow = img_func.get_shadows(original_img)
average_temperature = img_func.get_color_temperature(original_img)
average_noisy = img_func.get_noise(original_img)
average_exposure = img_func.get_exposure(original_img)
# Display image parameter values
parameters = {
"Red": WB_red,
"Green": WB_green,
"Blue": WB_blue,
"Contrast": contrast,
"Brightness": average_brightness,
"Perceived Brightness": average_perceived_brightness,
"Hue": average_hue,
"Saturation": average_saturation,
"Sharpness": average_sharpen,
"Highlight": average_highlights,
"Shadow": average_shadow,
"Temperature": average_temperature,
"Noise": average_noisy,
"Exposure": average_exposure
}
display_parameters(f_main_left_bottom, parameters, original_img)
def auto_optimize():
# Get current image
global img_buffer
# Check filter data availability
json_dataset_path = ".\\dataset\\" + combobox.get() + "\\" + combobox.get() + "_result.json"
if not os.path.exists(json_dataset_path):
tk.messagebox.showinfo("Cannot find dataset",
"Cannot find json data file for this dataset. Check & Update Dataset.")
return
# Get image parameter values
WB_red, WB_green, WB_blue = img_func.get_white_balance(img_buffer)
avg_brightness = img_func.get_brightness(img_buffer)
contrast = img_func.get_contrast(img_buffer)
avg_hue = img_func.get_hue(img_buffer)
avg_saturation = img_func.get_saturation(img_buffer)
avg_perceived_brightness = img_func.get_perceived_avg_brightness(img_buffer)
avg_sharpness = img_func.get_sharpness(img_buffer)
avg_highlights = img_func.get_highlights(img_buffer)
avg_shadow = img_func.get_shadows(img_buffer)
avg_temperature = img_func.get_color_temperature(img_buffer)
avg_noisy = img_func.get_noise(img_buffer)
avg_exposure = img_func.get_exposure(img_buffer)
# Pass the parameters & Predict optimal values
optimal_vals = predict.optimal_val_predict(json_dataset_path, contrast, WB_red, WB_green, WB_blue,
avg_brightness, avg_perceived_brightness, avg_hue,
avg_saturation, avg_sharpness, avg_highlights, avg_shadow,
avg_temperature, avg_noisy, avg_exposure)
# Ensure optimal_vals contains scalar values
optimal_vals = {key: val.item() if isinstance(val, pd.Series) else val for key, val in optimal_vals.items()}
# Modify image with optimal values
modified_img = img_func.modify_hue(img_buffer, optimal_vals["avg_hue"])
modified_img = img_func.modify_sharpness(modified_img, optimal_vals["avg_sharpness"])
modified_img = img_func.modify_color_temperature(modified_img, optimal_vals["avg_temperature"])
modified_img = img_func.modify_exposure(modified_img, optimal_vals["avg_exposure"])
modified_img = img_func.modify_white_balance(modified_img, optimal_vals["WB_red"],
optimal_vals["WB_green"], optimal_vals["WB_blue"])
modified_img = img_func.modify_contrast(modified_img, optimal_vals["contrast"])
modified_img = img_func.modify_saturation(modified_img, optimal_vals["avg_saturation"])
modified_img = img_func.modify_highlights(modified_img, optimal_vals["avg_highlights"])
modified_img = img_func.modify_noise(modified_img, optimal_vals["avg_noisy"])
modified_img = img_func.modify_brightness(modified_img, optimal_vals["avg_brightness"])
modified_img = img_func.modify_shadows(modified_img, optimal_vals["avg_shadow"])
modified_img = img_func.modify_perceived_avg_brightness(modified_img, optimal_vals["avg_perceived_brightness"])
# Update Window
if modified_img is not None:
img_buffer = modified_img
plot_img_hist(img_buffer, f_main_left_top, 7, 3)
display_image(img_buffer, f_main_right_top)
# Display image parameter values
parameters = {
"Red": WB_red,
"Green": WB_green,
"Blue": WB_blue,
"Contrast": contrast,
"Brightness": avg_brightness,
"Perceived Brightness": avg_perceived_brightness,
"Hue": avg_hue,
"Saturation": avg_saturation,
"Sharpness": avg_sharpness,
"Highlight": avg_highlights,
"Shadow": avg_shadow,
"Temperature": avg_temperature,
"Noise": avg_noisy,
"Exposure": avg_exposure
}
display_parameters(f_main_left_bottom, parameters, img_buffer)
def export_img():
if img_buffer is not None:
# Create a temporary address & Pass the temporarily stored image file
with tempfile.TemporaryDirectory() as temp_dir:
# Encode the original image name using UTF-8
if all(ord(char) < 128 for char in img_name):
# If all characters in img_name are ASCII characters
encoded_img_name = img_name.encode('utf-8')
else:
# If img_name contains non-ASCII characters
name, extension = os.path.splitext(img_name)
uuid_bytes = str(uuid.uuid4()).encode('utf-8')
encoded_img_name = uuid_bytes + extension.encode('utf-8')
encoded_img_name_str = encoded_img_name.decode('utf-8')
tmp_file_path = os.path.join(temp_dir, encoded_img_name_str)
cv.imwrite(tmp_file_path, img_buffer)
# Start Export Window
try:
subprocess.run(['python', 'export_func.py', tmp_file_path], capture_output=True, text=True)
except Exception as e:
messagebox.showerror("Exception", "Error occurred: " + str(e))
def dataset_select(value):
# Load the dataset
json_dataset_path = os.path.join(".\\dataset", value, f"{value}_result.json")
if not os.path.exists(json_dataset_path):
tk.messagebox.showinfo("Cannot find dataset",
"Cannot find json data file for this dataset. Check & Update Dataset.")
return
stats, original_stats = dataset_analysis.dataset_desc(json_dataset_path)
# Create a combobox to select the parameter
parameter_label = ctk.CTkLabel(f_dataset, text="Select Parameter:")
parameter_label.pack(padx=20, pady=10, anchor="w")
# Clear previous widgets
for widget in f_dataset.winfo_children():
if widget != home and widget != update and widget != crawler and widget != combobox_m:
widget.destroy()
# Display the box plot of all numeric columns
plt.figure(figsize=(18, 4))
plt.xticks(fontsize=8)
sns.boxplot(data=original_stats, color='blue')
plt.ylabel('Values')
plt.grid(True)
plt.tight_layout()
boxplot_canvas = FigureCanvasTkAgg(plt.gcf(), master=f_dataset)
boxplot_canvas.draw()
boxplot_canvas.get_tk_widget().pack()
plt.close()
# Update & Pack parameter_combobox
global parameter_combobox
parameter_combobox = ctk.CTkComboBox(f_dataset, values=stats['Parameter'].tolist(),
command=lambda param: display_parameter_stats(param, stats,
parameter_combobox))
parameter_combobox.pack(padx=20, pady=10, fill="x")
# Initial display of the first parameter's stats
if not stats.empty:
display_parameter_stats(stats['Parameter'].iloc[0], stats, parameter_combobox)
def display_parameter_stats(param, stats, parameter_combobox):
# Find the row corresponding to the selected parameter
param_stats = stats[stats['Parameter'] == param]
if param_stats.empty:
return
# Extract the parameter stats
param_values = param_stats.iloc[0].to_dict()
for widget in f_dataset.winfo_children():
if isinstance(widget, (ctk.CTkCanvas, ctk.CTkScrollbar)) and widget != parameter_combobox:
widget.destroy()
# Create a canvas and a scrollbar
canvas = ctk.CTkCanvas(f_dataset, bg="white", highlightthickness=0)
scrollbar = ctk.CTkScrollbar(f_dataset, fg_color="white", command=canvas.yview)
scrollable_frame = ctk.CTkFrame(canvas, fg_color="white")
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")
)
)
# Place the scrollable frame in the canvas
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
# Pack the canvas and scrollbar
canvas.pack(side=ctk.LEFT, fill=ctk.BOTH, expand=True)
scrollbar.pack(side=ctk.RIGHT, fill=ctk.Y)
# Display the stats for the selected parameter inside the scrollable frame
for key, value in param_values.items():
if key == 'Parameter':
continue
# Parameter name label
label = ctk.CTkLabel(scrollable_frame, text=f"{key}:", anchor="w", fg_color="white")
label.pack(padx=20, pady=5, anchor="w")
# Parameter value textbox
value_str = f"{value:.4f}" if isinstance(value, (int, float)) else f"{value}"
textbox = ctk.CTkTextbox(scrollable_frame, height=1, width=160, wrap="none", fg_color="#f0f0f0",
text_color="black")
textbox.insert("1.0", value_str)
textbox.pack(padx=20, pady=5, anchor="w")
textbox.configure(state="disabled")
# Display the histogram for the parameter
plt.figure(figsize=(6, 4))
sns.histplot(data=stats[key].dropna(), kde=True, bins=30, color='blue')
plt.ylabel('Frequency')
plt.grid(True)
plt.tight_layout()
hist_canvas = FigureCanvasTkAgg(plt.gcf(), master=scrollable_frame)
hist_canvas.draw()
hist_canvas.get_tk_widget().pack()
plt.close()
def dataset_update():
# Create waiting window
update.configure(text="Processing", fg_color="red")
update.update()
# Start Tasks
img_func.process_images_in_folders(root_dir)
global subfolders
subfolders = [sub_folder for sub_folder in os.listdir(root_dir) if
os.path.isdir(os.path.join(root_dir, sub_folder))]
combobox.configure(values=subfolders)
combobox_m.configure(values=subfolders)
combobox.update()
combobox_m.update()
dataset_select(combobox_m.get())
# End Waiting Window
update.configure(text="Update Dataset", fg_color="#3b8ed0")
update.update()
if __name__ == "__main__":
# Main window
img_buffer = None
bg_color = "white"
root = ctk.CTk(bg_color)
root.title("Image Optimizer")
root.minsize(800, 500)
root.iconbitmap('./icon/icon.ico')
root.protocol("WM_DELETE_WINDOW", on_close)
# Frames
f_wizard = ctk.CTkFrame(root, fg_color=bg_color)
f_main = ctk.CTkFrame(root, fg_color=bg_color)
f_dataset = ctk.CTkFrame(root, fg_color=bg_color)
for f in (f_wizard, f_main, f_dataset):
f.grid(row=0, column=0, sticky="nsew")
# Configure grid to expand
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
# Predefined root directory
root_dir = "./dataset"
subfolders = [sub_folder for sub_folder in os.listdir(root_dir) if os.path.isdir(os.path.join(root_dir, sub_folder))]
# Wizard Interface
f_wizard_bg_color = "white"
# Wizard Interface Left
f_wizard_left = ctk.CTkFrame(f_wizard, fg_color=f_wizard_bg_color)
f_wizard_left.grid(row=0, column=0, sticky="nsew")
f_wizard_left.grid_rowconfigure(0, weight=1)
f_wizard_left.grid_columnconfigure(0, weight=1)
# Wizard Interface Right
f_wizard_right = ctk.CTkFrame(f_wizard, fg_color=f_wizard_bg_color)
f_wizard_right.grid(row=0, column=1, sticky="nsew")
f_wizard_right.grid_rowconfigure(0, weight=1)
f_wizard_right.grid_columnconfigure(0, weight=1)
# Configure grid in f_wizard
f_wizard.grid_rowconfigure(0, weight=1)
f_wizard.grid_columnconfigure(0, weight=1)
f_wizard.grid_columnconfigure(1, weight=1)
# Load Wizard Interface Icons
icon_select = ctk.CTkImage(Image.open("./icon/select.png"), size=(100, 100))
icon_db_man = ctk.CTkImage(Image.open("./icon/edit.png"), size=(100, 100))
# Buttons with icons and custom styles
select_img_button = ctk.CTkButton(
f_wizard_left, text="Select An Image", command=select_img,
image=icon_select, compound="top", fg_color="transparent", text_color="black", hover_color="lightblue"
)
select_img_button.grid(row=0, column=0, padx=20, pady=20, sticky="nsew")
db_manage_button = ctk.CTkButton(
f_wizard_right, text="Dataset Management", command=lambda: raise_frame(f_dataset),
image=icon_db_man, compound="top", fg_color="transparent", text_color="black", hover_color="lightblue"
)
db_manage_button.grid(row=0, column=0, padx=20, pady=20, sticky="nsew")
# Main Interface
f_main_bg_color = "white"
# Main Interface Left
f_main_left = ctk.CTkFrame(f_main, fg_color=f_main_bg_color)
f_main_left.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
f_main_left.grid_rowconfigure(0, weight=1)
f_main_left.grid_rowconfigure(1, weight=1) # Button
f_main_left.grid_columnconfigure(0, weight=1)
# Main Interface Left Top
f_main_left_top = ctk.CTkFrame(f_main_left, fg_color=f_main_bg_color)
f_main_left_top.grid(row=0, column=0, sticky="nsew")
f_main_left_top.grid_rowconfigure(0, weight=1)
f_main_left_top.grid_columnconfigure(0, weight=1)
# Main Interface Left Bottom
f_main_left_bottom = ctk.CTkFrame(f_main_left, fg_color=f_main_bg_color)
f_main_left_bottom.grid(row=1, column=0, sticky="nsew") # Button
f_main_left_bottom.grid_rowconfigure(0, weight=1)
f_main_left_bottom.grid_columnconfigure(0, weight=1)
# Main Interface Right
f_main_right = ctk.CTkFrame(f_main, fg_color=f_main_bg_color)
f_main_right.grid(row=0, column=1, padx=10, pady=10, sticky="nsew")
f_main_right.grid_rowconfigure(0, weight=1)
f_main_right.grid_rowconfigure(1, weight=1)
f_main_right.grid_columnconfigure(0, weight=1)
# Main Interface Right Top
f_main_right_top = ctk.CTkFrame(f_main_right, fg_color=f_main_bg_color)
f_main_right_top.grid(row=0, column=0, sticky="nsew")
f_main_right_top.grid_rowconfigure(0, weight=1)
f_main_right_top.grid_columnconfigure(0, weight=1)
# Main Interface Right Bottom
f_main_right_bottom = ctk.CTkFrame(f_main_right, fg_color=f_main_bg_color)
f_main_right_bottom.grid(row=1, column=0, sticky="nsew")
f_main_right_bottom.grid_rowconfigure(0, weight=1)
f_main_right_bottom.grid_columnconfigure(0, weight=1)
# Combobox to display sub-folder names
combobox = ctk.CTkComboBox(f_main_right_bottom, values=subfolders)
combobox.grid(row=1, column=0, padx=20, pady=10, sticky="ew")
auto_button = ctk.CTkButton(f_main_right_bottom, text="Auto Optimization", command=auto_optimize)
auto_button.grid(row=2, column=0, padx=20, pady=10, sticky="ew")
revert_button = ctk.CTkButton(f_main_right_bottom, text="Revert To Original", command=lambda: revert_to_original())
revert_button.grid(row=3, column=0, padx=20, pady=10, sticky="ew")
export_button = ctk.CTkButton(f_main_right_bottom, text="Export Image", command=export_img)
export_button.grid(row=4, column=0, padx=20, pady=10, sticky="ew")
export_button = ctk.CTkButton(f_main_right_bottom, text="Discard", command=lambda: raise_frame(f_wizard))
export_button.grid(row=5, column=0, padx=20, pady=10, sticky="ew")
# Configure grid in f_main
f_main.grid_rowconfigure(0, weight=1)
f_main.grid_columnconfigure(0, weight=1)
f_main.grid_columnconfigure(1, weight=1)
# Database Management Interface
# Back To Wizard Button
home = ctk.CTkButton(f_dataset, text="Back To Wizard", command=lambda: raise_frame(f_wizard))
home.pack(padx=20, pady=5, fill='x')
# Crawler Button
crawler = ctk.CTkButton(f_dataset, text="Get More Images from Unsplash",
command=lambda: subprocess.run(['python', 'crawler.py']))
crawler.pack(padx=20, pady=5, fill='x')
# Dataset Update Button
update = ctk.CTkButton(f_dataset, text="Update Dataset", command=dataset_update)
update.pack(padx=20, pady=5, fill='x')
# Combobox to display sub-folder names
combobox_m = ctk.CTkComboBox(f_dataset, values=subfolders, command=lambda value: dataset_select(value))
combobox_m.pack(padx=20, pady=5, fill='x')
dataset_select(combobox_m.get())
raise_frame(f_wizard)
root.mainloop()