import os
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
from sys import exit
[docs]class ParserDirectory:
"""
GUI for parsing initial directories for processing from scratch.
"""
def __init__(self):
"""
Initialize the window.
"""
# Window
self.master = tk.Tk()
self.master.title("Select directoy")
self.master.protocol("WM_DELETE_WINDOW", self.close_window)
self.master.lift()
self.master.focus_force()
# Variables
self.skip = False
self.image_dir = tk.StringVar()
self.model_states = tk.StringVar()
self.destination_dir = tk.StringVar()
self.file_name = tk.StringVar()
self.frame_limit = tk.IntVar()
self.only_segmentation = tk.BooleanVar(value=False)
# Input fields
tk.Label(
self.master, text="Images:"
).grid(row=0, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.image_dir, width=40
).grid(row=0, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_source
).grid(row=0, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Model states:"
).grid(row=1, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.model_states, width=40
).grid(row=1, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_states
).grid(row=1, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Save in:"
).grid(row=2, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.destination_dir, width=40
).grid(row=2, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_saving
).grid(row=2, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Save as:"
).grid(row=3, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.file_name, width=40
).grid(row=3, column=1, padx=10, pady=5)
tk.Label(
self.master, text="Until frame:"
).grid(row=4, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.frame_limit, width=40
).grid(row=4, column=1, padx=10, pady=5)
tk.Label(
self.master,
text="Only run image segmentation and save data for later editing."
).grid(row=5, column=0, columnspan=2, padx=10, pady=5)
checkbox_original = ttk.Checkbutton(
self.master, variable=self.only_segmentation)
checkbox_original.grid(row=5, column=2, padx=10, pady=5)
# Progress buttons
frame = tk.Frame(self.master)
frame.grid(
row=6, column=0, columnspan=3, sticky='nsew', padx=10, pady=5)
frame.grid_columnconfigure(0, weight=1)
frame.grid_columnconfigure(1, weight=1)
frame.grid_columnconfigure(2, weight=1)
frame.grid_rowconfigure(1, weight=1)
self.confirm = tk.Button(frame, text="Ok", command=self.parse)
reset = tk.Button(frame, text="Reset", command=self.reset_inputs)
skip = tk.Button(frame, text="Skip", command=self.skip_window)
self.confirm.grid(row=0, column=0, padx=5, pady=5, sticky='ew')
reset.grid(row=0, column=1, padx=5, pady=5, sticky='ew')
skip.grid(row=0, column=2, padx=5, pady=5, sticky='ew')
# Return key bind
self.master.bind('<Return>', self.invoke_confirm)
# Start
self.master.mainloop()
[docs] def select_source(self):
"""
Select the image directory.
"""
dir_ = filedialog.askdirectory()
if dir_:
self.image_dir.set(dir_)
[docs] def select_saving(self):
"""
Select the directory for saving.
"""
dir_ = filedialog.askdirectory()
if dir_:
self.destination_dir.set(dir_)
[docs] def select_states(self):
"""
Select the model states.
"""
path = filedialog.askopenfilename()
if path:
self.model_states.set(path)
[docs] def skip_window(self):
"""
Skip this window and go to the next step.
"""
self.skip = True
self.master.destroy()
[docs] def invoke_confirm(self, event):
"""
Invoke the Ok button with Return to progress.
"""
self.confirm.invoke()
[docs] def parse(self):
"""
Parse and check inputs and close window if no error.
"""
source_dir = self.image_dir.get()
destination_dir = self.destination_dir.get()
states = self.model_states.get()
save_as_name = self.file_name.get()
if not os.path.isdir(source_dir):
messagebox.showerror("Error", "Invalid source directory!")
return
if not os.path.isdir(destination_dir):
messagebox.showerror("Error", "Invalid destination directory!")
return
if not os.path.isfile(states):
messagebox.showerror("Error", "Invalid states file selected!")
return
if not save_as_name :
messagebox.showerror("Error", "Save as file name is required!")
return
self.master.destroy()
[docs] def close_window(self):
"""
Confirm abrupt exit.
"""
if messagebox.askyesno(
"Confirm Exit", "Are you sure you want to exit?"):
self.master.destroy()
self.window_exception()
[docs] def window_exception(self):
"""
Exit immediately.
"""
messagebox.showerror(
"Window closed.",
"Window was closed unexpectedly. The program terminates now.")
exit()
[docs]class ParserSkip:
"""
GUI for parsing directories for processing from tracking.
"""
def __init__(self):
"""
Initialize the window.
"""
# Window
self.master = tk.Tk()
self.master.title("Select directory")
self.master.protocol("WM_DELETE_WINDOW", self.close_window)
self.master.lift()
self.master.focus_force()
# Variables
self.skip = False
self.segmentation_name = tk.StringVar()
self.video_name = tk.StringVar()
self.destination_dir = tk.StringVar()
self.file_name = tk.StringVar()
self.limit = tk.IntVar()
self.file_name.set('Experiment1')
self.limit.set(0)
# Input fields
tk.Label(
self.master, text="Segmentation:"
).grid(row=0, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.segmentation_name, width=40
).grid(row=0, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_segmentation
).grid(row=0, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Video file:"
).grid(row=1, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.video_name, width=40
).grid(row=1, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_video
).grid(row=1, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Save in:"
).grid(row=2, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.destination_dir, width=40
).grid(row=2, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_saving
).grid(row=2, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Save as:"
).grid(row=3, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.file_name, width=40
).grid(row=3, column=1, padx=10, pady=5)
tk.Label(
self.master, text="Frame limit:"
).grid(row=4, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.limit, width=40
).grid(row=4, column=1, padx=10, pady=5)
# Progress buttons
frame = tk.Frame(self.master)
frame.grid(
row=5, column=0, columnspan=3, sticky='nsew', padx=10, pady=5)
frame.grid_columnconfigure(0, weight=1)
frame.grid_columnconfigure(1, weight=1)
frame.grid_columnconfigure(2, weight=1)
frame.grid_rowconfigure(1, weight=1)
self.confirm = tk.Button(frame, text="Ok", command=self.parse)
reset = tk.Button(frame, text="Reset", command=self.reset_inputs)
skip = tk.Button(frame, text="Skip", command=self.skip_window)
self.confirm.grid(row=0, column=0, padx=5, pady=5, sticky='ew')
reset.grid(row=0, column=1, padx=5, pady=5, sticky='ew')
skip.grid(row=0, column=2, padx=5, pady=5, sticky='ew')
# Return key bind
self.master.bind('<Return>', self.invoke_confirm)
# Start
self.master.mainloop()
[docs] def select_segmentation(self):
"""
Select segmentation data.
"""
path = filedialog.askopenfilename()
if path:
self.segmentation_name.set(path)
[docs] def select_video(self):
"""
Select video file.
"""
path = filedialog.askopenfilename()
if path:
self.video_name.set(path)
[docs] def select_saving(self):
"""
Select directory for saving.
"""
dir_ = filedialog.askdirectory()
if dir_:
self.destination_dir.set(dir_)
[docs] def select_states(self):
"""
Select model states.
"""
path = filedialog.askopenfilename()
if path:
self.model_states.set(path)
[docs] def skip_window(self):
"""
Skip this window and go to the next step.
"""
self.skip = True
self.master.destroy()
[docs] def invoke_confirm(self, event):
"""
Invoke the Ok button with Return to progress.
"""
self.confirm.invoke()
[docs] def parse(self):
"""
Parse and check inputs and close window if no error.
"""
segmentation = self.segmentation_name.get()
video = self.video_name.get()
destination_dir = self.destination_dir.get()
save_as_name = self.file_name.get()
if not os.path.isfile(segmentation):
messagebox.showerror(
"Error", "Invalid segmentation file selected!")
return
if (
segmentation.split('.')[-1] != 'pkl' and
segmentation.split('.')[-1] != 'csv'
):
messagebox.showerror(
"Error", "Segmentation file needs to be .pkl or .csv!")
return
if not os.path.isfile(video):
messagebox.showerror(
"Error", "Invalid segmentation file selected!")
return
if not os.path.isdir(destination_dir):
messagebox.showerror("Error", "Invalid destination directory!")
return
if not save_as_name :
messagebox.showerror("Error", "Save as file name is required!")
return
try:
_ = self.limit.get()
except:
messagebox.showerror("Error", "Limit needs to be integer!")
return
self.master.destroy()
[docs] def close_window(self):
"""
Confirm abrupt exit.
"""
if messagebox.askyesno(
"Confirm Exit", "Are you sure you want to exit?"):
self.master.destroy()
self.window_exception()
[docs] def window_exception(self):
"""
Exit immediately.
"""
messagebox.showerror(
"Window closed.",
"Window was closed unexpectedly. The program terminates now.")
exit()
[docs]class ParserTrack:
"""
GUI for parsing tracking parameters.
"""
def __init__(self):
"""
Initialize the window.
"""
# Window
self.master = tk.Tk()
self.master.title("Tracker setup")
self.master.protocol("WM_DELETE_WINDOW", self.close_window)
self.master.lift()
self.master.focus_force()
# Variables
self.age = tk.IntVar()
self.gap = tk.IntVar()
self.split = tk.IntVar()
self.offset = tk.IntVar()
self.age.set(500)
self.gap.set(200)
self.split.set(50)
self.offset.set(80)
# Create input fields and buttons
tk.Label(
self.master, text="Enter maximum age:"
).grid(row=0, column=0, padx=10, pady=5, sticky='w')
ttk.Entry(
self.master, textvariable=self.age, width=20
).grid(row=0, column=1, padx=10, pady=5)
tk.Label(
self.master, text="Enter offset from prediction:"
).grid(row=1, column=0, padx=10, pady=5, sticky='w')
ttk.Entry(
self.master, textvariable=self.offset, width=20
).grid(row=1, column=1, padx=10, pady=5)
tk.Label(
self.master, text="Enter distance after splitting:"
).grid(row=2, column=0, padx=10, pady=5, sticky='w')
ttk.Entry(
self.master, textvariable=self.split, width=20
).grid(row=2, column=1, padx=10, pady=5)
tk.Label(
self.master, text="Enter gap closing distance:"
).grid(row=3, column=0, padx=10, pady=5, sticky='w')
ttk.Entry(
self.master, textvariable=self.gap, width=20
).grid(row=3, column=1, padx=10, pady=5)
# Progress buttons
frame = tk.Frame(self.master)
frame.grid(
row=4, column=0, columnspan=3, sticky='nsew', padx=10, pady=5)
frame.grid_columnconfigure(0, weight=1)
frame.grid_columnconfigure(1, weight=1)
self.confirm = tk.Button(frame, text="Ok", command=self.parse)
reset = tk.Button(frame, text="Reset", command=self.reset_inputs)
self.confirm.grid(row=0, column=0, padx=5, pady=5, sticky='ew')
reset.grid(row=0, column=1, padx=5, pady=5, sticky='ew')
# Return key bind
self.master.bind('<Return>', self.invoke_confirm)
# Start
self.master.mainloop()
[docs] def invoke_confirm(self, event):
"""
Invoke the Ok button with Return to progress.
"""
self.confirm.invoke()
[docs] def parse(self):
"""
Parse and check inputs and close window if no error.
"""
try:
_ = self.age.get()
_ = self.offset.get()
_ = self.split.get()
_ = self.gap.get()
except:
messagebox.showerror("Error", "Entry needs to be integer!")
return
self.master.destroy()
[docs] def close_window(self):
"""
Confirm abrupt exit.
"""
if messagebox.askyesno(
"Confirm Exit", "Are you sure you want to exit?"):
self.master.destroy()
self.window_exception()
[docs] def window_exception(self):
"""
Exit immediately.
"""
messagebox.showerror(
"Window closed.",
"Window was closed unexpectedly. The program terminates now.")
exit()
[docs]class ParserEditor:
"""
GUI for parsing directories and files for editor.
"""
def __init__(self):
"""
Initialize the window.
"""
# Window
self.master = tk.Tk()
self.master.title("Open previous experiment")
self.master.protocol("WM_DELETE_WINDOW", self.close_window)
self.master.lift()
self.master.focus_force()
# Variables
self.track_video_name = tk.StringVar()
self.video_name = tk.StringVar()
self.track_name = tk.StringVar()
self.destination_dir = tk.StringVar()
self.file_name = tk.StringVar()
# Input fields
tk.Label(
self.master, text="Video track file:"
).grid(row=0, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.track_video_name, width=40
).grid(row=0, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_track_video
).grid(row=0, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Or"
).grid(row=1, column=0, columnspan=3, padx=10, pady=10)
tk.Label(
self.master, text="Video file:"
).grid(row=2, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.video_name, width=40
).grid(row=2, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_video
).grid(row=2, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Track file:"
).grid(row=3, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.track_name, width=40
).grid(row=3, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_track
).grid(row=3, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Save in:"
).grid(row=4, column=0, padx=10, pady=5, sticky='e')
tk.Entry(
self.master, textvariable=self.destination_dir, width=40
).grid(row=4, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_saving
).grid(row=4, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Save as:"
).grid(row=5, column=0, padx=10, pady=5, sticky='e')
ttk.Entry(
self.master, textvariable=self.file_name, width=40
).grid(row=5, column=1, padx=10, pady=5)
# Progress buttons
frame = tk.Frame(self.master)
frame.grid(
row=6, column=0, columnspan=3, sticky='nsew', padx=10, pady=5)
frame.grid_columnconfigure(0, weight=1)
frame.grid_columnconfigure(1, weight=1)
self.confirm = tk.Button(frame, text="Ok", command=self.parse)
reset = tk.Button(frame, text="Reset", command=self.reset_inputs)
self.confirm.grid(row=0, column=0, padx=5, pady=5, sticky='ew')
reset.grid(row=0, column=1, padx=5, pady=5, sticky='ew')
# Return key bind
self.master.bind('<Return>', self.invoke_confirm)
# Start
self.master.mainloop()
[docs] def select_track_video(self):
"""
Select track video file.
"""
path = filedialog.askopenfilename()
if path:
name, ext = os.path.split(path)[-1].split('.')
if ext == 'tvf':
self.track_video_name.set(path)
self.file_name.set(name)
self.video_name.set('')
self.track_name.set('')
else:
messagebox.showerror(
"Error", "Selected file needs to be .tvf file.")
[docs] def select_video(self):
"""
Select video file.
"""
path = filedialog.askopenfilename()
if path:
name, ext = os.path.split(path)[-1].split('.')
if ext == 'avi':
self.video_name.set(path)
self.file_name.set(name)
self.track_video_name.set('')
else:
messagebox.showerror(
"Error", "Selected file needs to be .avi file.")
[docs] def select_track(self):
"""
Select track data.
"""
path = filedialog.askopenfilename()
if path:
name, ext = os.path.split(path)[-1].split('.')
if ext == 'csv':
self.track_name.set(path)
self.file_name.set(name)
self.track_video_name.set('')
else:
messagebox.showerror(
"Error", "Selected file needs to be .csv file.")
[docs] def select_saving(self):
"""
Select directory for saving.
"""
dir_ = filedialog.askdirectory()
if dir_:
self.destination_dir.set(dir_)
[docs] def invoke_confirm(self, event):
"""
Invoke the Ok button with Return to progress.
"""
self.confirm.invoke()
[docs] def parse(self):
"""
Parse and check inputs and close window if no error.
"""
if (not os.path.isfile(self.track_video_name.get()) and
not os.path.isfile(self.video_name.get()) and
not os.path.isfile(self.track_name.get())):
messagebox.showerror("Error", "No files selected!")
return
if os.path.isfile(self.track_video_name.get()):
pass
elif not os.path.isfile(self.video_name.get()):
messagebox.showerror("Error", "Invalid video file selected!")
return
elif not os.path.isfile(self.track_name.get()):
messagebox.showerror("Error", "Invalid track file selected!")
return
if not os.path.isdir(self.destination_dir.get()):
messagebox.showerror("Error", "Invalid destination directory!")
return
try:
_ = self.file_name.get()
except:
messagebox.showerror("Error", "File name is required!")
return
self.master.destroy()
[docs] def close_window(self):
"""
Confirm abrupt exit.
"""
if messagebox.askyesno(
"Confirm Exit", "Are you sure you want to exit?"):
self.master.destroy()
self.window_exception()
[docs] def window_exception(self):
"""
Exit immediately.
"""
messagebox.showerror(
"Window closed.",
"Window was closed unexpectedly. The program terminates now.")
exit()
[docs]def parse_segmentation():
"""
Get the paths and directories for segmentation and additional information,
None if parsing / step is skipped.
Returns
-------
tuple or None
Parsed directories, paths and parameters, or None.
"""
p = ParserDirectory()
if p.skip == False:
source_dir = p.image_dir.get().replace('/', '\\')
destination_dir = p.destination_dir.get().replace('/', '\\')
states = p.model_states.get().replace('/', '\\')
save_as_name = p.file_name.get()
limit = p.frame_limit.get()
only_seg = p.only_segmentation.get()
return (
source_dir, destination_dir, states, save_as_name, limit,
only_seg, True)
else:
p = ParserSkip()
if p.skip == False:
segmentation_name = p.segmentation_name.get().replace('/', '\\')
video_name = p.video_name.get().replace('/', '\\')
destination_dir = p.destination_dir.get().replace('/', '\\')
save_as_name = p.file_name.get()
limit = p.limit.get()
return (
segmentation_name, video_name, destination_dir, save_as_name,
limit, False)
else:
return None
[docs]def parse_tracker():
"""
Get the tracking parameters.
Returns
-------
age : int
Maximum frames an object may be undetected.
offset : int
The maximum distance in Euclidean tracking.
split : int
The maximum distance to consider splitting.
gap : int
The maximum distance for matching after detection gap.
"""
p = ParserTrack()
age = p.age.get()
offset = p.offset.get()
split = p.split.get()
gap = p.gap.get()
return age, offset, split, gap
[docs]def parse_editor():
"""
Get the file and saving information for the editor.
Returns
-------
tuple
Paths to video, track data or combined as track video file, and save
directory, and save name.
"""
p = ParserEditor()
destination_dir = p.destination_dir.get().replace('/', '\\')
save_as_name = p.file_name.get()
if len(p.track_video_name.get()) > 0:
track_video = p.track_video_name.get().replace('/', '\\')
return track_video, destination_dir, save_as_name
else:
video = p.video_name.get().replace('/', '\\')
track = p.track_name.get().replace('/', '\\')
return video, track, destination_dir, save_as_name