import tkinter as tk
from tkinter import ttk
from data_treat.reconstruction_3d import reconstruct_3d
from data_treat.make_report import make_report
from data_treat.data_pp import get_init_angle, get_impact_position, get_velocity
import os
from gui.gui_utils import makeform, popupmsg
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
from PIL import Image
import glob
import numpy as np
[docs]def ana_tab(root,frame, notebook, cam_top, cam_left, traj_3d):
"""Setup the analysis tab
:param root: root tk window
:param frame: frame of the tab to draw in
:param notebook: notebook object the tab belongs to
:param cam_top,cam_left: top and left camera objects
:param traj_3d: Experiment object
"""
show_traj = tk.IntVar()
is_batch = tk.IntVar()
cam_frames = tk.Frame(frame)
top_cam = tk.Frame(cam_frames, width=250)
left_cam = tk.Frame(cam_frames, width=250)
div_params = tk.Frame(frame)
option_box = tk.Frame(div_params)
cam_factors = tk.Frame(frame)
#cam_factors.hidden = 1
cam_top_lab = tk.Label(cam_factors, text="Top pixel to cm ratio")
cam_top_lab.pack(side=tk.LEFT)
cam_top_factor = tk.Entry(cam_factors)
cam_top_factor.insert(tk.END, '{:.04e}'.format(1 / 141.1))
cam_top_factor.pack(side=tk.LEFT)
cam_left_lab = tk.Label(cam_factors, text="Left pixel to cm ratio")
cam_left_lab.pack(side=tk.LEFT)
cam_left_factor = tk.Entry(cam_factors)
cam_left_factor.insert(tk.END, '{:.04e}'.format(1 / 148.97))
cam_left_factor.pack(side=tk.LEFT)
batch_options = tk.Frame(frame)
batch_label = tk.Label(batch_options, text="Batch folder path:")
batch_label.pack()
batch_folder = tk.Entry(batch_options, width=100)
batch_folder.pack()
batch_warning = tk.Label(batch_options,
text="Each folder to analyse in the batch folder should contain two folders with "
"the name specified for the top and left camera above\nAll the report files will"
"be saved in a 'RESULT' folder created in the batch folder.")
batch_warning.pack()
batch_params = tk.Button(batch_options, text="Batch set-up", command=(lambda t3d=traj_3d: set_pp_params(t3d)))
batch_params.pack(side=tk.RIGHT)
batch_switch = tk.Checkbutton(option_box, text="Batch mode", variable=is_batch,
command=(lambda e=is_batch, bo=batch_options: batch_option_active(e, bo)))
w = ttk.Combobox(option_box, values=['No perspective', 'Perspective simple', 'Perspective optimized'])
#w.bind("<<ComboboxSelected>>", (lambda val=w.get(), camf=cam_factors: method_change(val, camf)))
w.insert(tk.END, 'Perspective optimized')
cb = tk.Checkbutton(option_box, text="Show detected points", variable=show_traj)
exp_params_fr = tk.Frame(div_params)
exp_param = makeform(exp_params_fr, ['Shot type', 'Sample name', 'Input pressure (psi)'],
["0.5mm", "Aluminum 6050", "50"])
top_base = tk.Frame(top_cam)
titleTop = tk.Label(top_base, text="Top camera parameters")
titleTop.pack(side=tk.LEFT)
top = makeform(top_cam, ['Calibration folder',"Picture folder", 'First picture ID', 'framerate',
'Screen width', 'Screen height', "Acquisition width", "Acquisition height",
'Detection threshold'],
["calibration/res", "tests/single/camTop", "0", '15000',
"1280", "800", "1280", "800", "20."], pos=tk.BOTTOM)
b1 = tk.Button(top_base, text='Set mask', command=(lambda ct=cam_top, t=top: set_mask(ct, t)))
b1.pack(side=tk.RIGHT)
top_base.pack(side=tk.TOP)
left_base = tk.Frame(left_cam)
titleLeft = tk.Label(left_base, text="Left camera parameters")
titleLeft.pack(side=tk.LEFT)
left = makeform(left_cam, ['Calibration folder',"Picture folder", 'First picture ID', 'framerate',
'Screen width', 'Screen height', "Acquisition width", "Acquisition height",
'Detection threshold'],
["calibration/res", "tests/single/camLeft", "0", '15000',
"1280", "800", "1280", "800", '20.'], pos=tk.BOTTOM)
b1 = tk.Button(left_base, text='Set mask', command=(lambda cl=cam_left, l=left: set_mask(cl, l)))
b1.pack(side=tk.RIGHT)
left_base.pack(side=tk.TOP)
b1 = tk.Button(frame, text='Launch Analysis !',
command=(lambda t=top, l=left, n=notebook, wval=w, s=show_traj, ct=cam_top,
cl=cam_left, traj=traj_3d, ratTop=cam_top_factor, ratLeft=cam_left_factor,
bo=is_batch, bf=batch_folder, ep=exp_param:
launch_analysis(t, l, n, wval,ct, cl, traj, s, ratTop, ratLeft, bo, bf, ep)))
exp_params_fr.pack(side=tk.LEFT)
b1.pack(side=tk.BOTTOM, padx=5, pady=5)
w.pack(side=tk.LEFT)
cb.pack(side=tk.RIGHT)
batch_switch.pack(side=tk.RIGHT)
option_box.pack(side=tk.RIGHT)
div_params.pack(side=tk.TOP)
warning_label = tk.Label(frame, text="Warning, picture name must be in the following format: 'Name_number.jpg'")
warning_label.pack(side=tk.BOTTOM)
top_cam.pack(side=tk.LEFT, padx=5, pady=5)
left_cam.pack(side=tk.RIGHT, padx=5, pady=5)
cam_frames.pack(side=tk.TOP)
cam_factors.pack(side=tk.BOTTOM)
[docs]def set_mask(cam, form):
"""Generate the subwindow to set a camera mask
:param cam: camera object
:param form: camera form values to get the first picture path
"""
mask_w = tk.IntVar()
mask_h = tk.IntVar()
cam_pics = glob.glob(form[1][1].get() + "/*.tif")
if len(cam_pics) == 0:
cam_pics = glob.glob(form[1][1].get() + "/*.jpg")
try:
im_act = Image.open(cam_pics[0])
except:
popupmsg("Wrong camera picture folder name")
else:
root = tk.Tk()
root.title("Set mask")
root.geometry("600x600")
root.wm_iconbitmap('gui/logo-lm2-f_0.ico')
fig = Figure(figsize=(5, 4), dpi=100)
im_act = np.array(im_act)
fig_plot = fig.add_subplot(111).imshow(im_act)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
wd_lab = tk.Label(root, text="width")
mask_val_w = tk.Scale(root, from_=0, to=im_act.shape[1], orient=tk.HORIZONTAL, variable=mask_w,
command=(lambda ma=mask_w, p=im_act, c=canvas: update_fig(ma, p, c, 0)))
wd_lab.pack(side=tk.LEFT)
mask_val_w.pack(side=tk.LEFT)
hi_lab = tk.Label(root, text="height")
mask_val_h = tk.Scale(root, from_=0, to=im_act.shape[0], orient=tk.HORIZONTAL, variable=mask_h,
command=(lambda ma=mask_h, p=im_act, c=canvas: update_fig(ma, p, c, 1)))
hi_lab.pack(side=tk.LEFT)
mask_val_h.pack(side=tk.LEFT)
b1 = tk.Button(root, text='Set mask',
command=(lambda r=root, c=cam, mw=mask_val_w, mh=mask_val_h: set_cam_mask(r, c, mw, mh)))
b1.pack()
[docs]def set_cam_mask(root, cam, mask_w, mask_h):
"""sets the camera mask and closes the mask selection window"""
cam.set_mask(int(mask_w.get()), int(mask_h.get()))
root.destroy()
[docs]def update_fig(mask, im_act, canvas, ind):
"""update the mask selection picture as the slider moves
:param mask: mask value
:param im_act: plotted image ndarray
:param canvas: canvas to plot the modified image onto
:param ind: 0 for the mask to act on the pictur width, 1 for the picture height
"""
pic_act = np.copy(im_act)
if ind == 0:
pic_act[:, :int(mask)] = 0
elif ind == 1:
pic_act[im_act.shape[0] - int(mask):, :] = 0
canvas.figure.clear()
canvas.figure.add_subplot(111).imshow(pic_act)
canvas.draw()
[docs]def set_pp_params(traj_3d):
"""Generate the batch PP parameter subwindow
:param traj_3d: Experience object to change
"""
param_win = tk.Tk()
param_win.title("Batch post-processing parameters")
param_win.geometry("600x400")
param_win.wm_iconbitmap('gui/logo-lm2-f_0.ico')
vel_frame = tk.Frame(param_win)
title = tk.Label(vel_frame, text="Velocity determination parameters")
title.pack(side=tk.TOP)
vels = makeform(vel_frame, ['Velocity detection factor', 'Initial index', 'Minimum number of points'],
[traj_3d.vel_det_fac, traj_3d.vel_init_ind, traj_3d.vel_min_pt])
angle_frame = tk.Frame(param_win)
title = tk.Label(angle_frame, text="Angle determination parameters")
title.pack(side=tk.TOP)
angles = makeform(angle_frame, ['Initial index', 'End index'], [traj_3d.ang_min_ind, traj_3d.ang_end_ind])
pos_frame = tk.Frame(param_win)
title = tk.Label(angle_frame, text="Impact position parameters")
title.pack(side=tk.TOP)
pos = makeform(angle_frame, ['Threshold'], [traj_3d.imp_thres])
vel_frame.pack(side=tk.TOP)
angle_frame.pack(side=tk.TOP)
pos_frame.pack(side=tk.TOP)
b1 = tk.Button(param_win, text='Ok', command=(lambda t3d=traj_3d, v=vels, a=angles, p=pos, f=param_win:
save_pp_params(t3d, v, a, p, f)))
b1.pack(side=tk.BOTTOM, padx=5, pady=5)
[docs]def save_pp_params(traj_3d, vels, angles, pos, param_win):
"""saves PP parameters in an Experiment object and closes the window
:param traj_3d: Experiment object
:param vels: velocity determination parameters
:param angles: angle determination parameters
:param pos: impact position determination parameters
:param param_win: PP parameter window to shut down
:return:
"""
traj_3d.vel_det_fac = float(vels[0][1].get())
traj_3d.vel_init_ind = int(vels[1][1].get())
traj_3d.vel_min_pt = int(vels[2][1].get())
traj_3d.ang_min_ind = int(angles[0][1].get())
traj_3d.ang_end_ind = int(angles[1][1].get())
traj_3d.imp_thres = float(pos[0][1].get())
param_win.destroy()
[docs]def batch_option_active(switch_val, batch_options):
"""Check if the batch mode was activate and plots the batch options accordingly
:param switch_val: batch mode switch
:param batch_options: batch option tk Frame
"""
if switch_val.get():
batch_options.pack(side=tk.TOP)
else:
batch_options.pack_forget()
[docs]def method_change(val, cam_factors):
"""Checks if the method was changed to no-perspective and changes the GUI accordingly
:param val: method choice tk combobox object
:param cam_factors: method parameters to display
"""
if val.widget.get() == 'No perspective':
cam_factors.pack(side=tk.BOTTOM)
else:
cam_factors.pack_forget()
[docs]def create_camera(entries, name, cam, pic_to_cm=None):
"""Create a camera object based on the values filled in the form
:param entries: form entries
:param name: camera name (top or left)
:param cam: camera object
:param pic_to_cm: picture to cm ratio if the no-perspective mode is used
:return: initialized camera object
"""
cam.load_calibration_file(entries[0][1].get()+'/cam_'+name)
cam.dir = entries[1][1].get()
cam.firstPic = int(entries[2][1].get())
cam.pic_to_cm = 1 / 141.1
cam.framerate = float(entries[3][1].get())
cam.camRes = (int(entries[4][1].get()), int(entries[5][1].get()))
cam.res = (int(entries[6][1].get()), int(entries[7][1].get()))
cam.cam_thres = float(entries[8][1].get())
if not pic_to_cm is None:
cam.pic_to_cm = pic_to_cm
return cam
[docs]def launch_analysis(top_entry, left_entry, notebook, method, cam_top, cam_left, traj_3d, show_traj,
ratTop, ratLeft, isbatch, batch_folder, exp_param):
"""Launch a 3D trajectory analysis
:param top_entry,left_entry: form entries for the top and left cameras
:param notebook: GUI notebook object (to enable the pp tab at the end
:param method: analysis method combobox object
:param cam_top,cam_left: camera top and left objects
:param traj_3d: Experiment object
:param show_traj: Trajectory display checkbox object
:param ratTop,ratLeft: pixel to cm ratio form objects for the top and left cameras
:param isbatch: checkbox object enabling (or not) batch mode
:param batch_folder: batch folder path form object
:param exp_param: experimental parameter form object
:return:
"""
traj_3d.set_exp_params(exp_param[0][1].get(), exp_param[1][1].get(), exp_param[2][1].get(), "Trajectory.txt")
if method.get() == "No perspective":
meth = 'no-persp'
elif method.get() == "Perspective simple":
meth = 'persp'
else:
meth = 'persp-opti'
if isbatch.get():
foldList = os.listdir(batch_folder.get())
ana_fold = batch_folder.get()+'/'
if 'RESULTS' in foldList:
foldList.remove('RESULTS')
os.system('cd ' + ana_fold + ' && mkdir RESULTS')
savedir = ana_fold + 'RESULTS/'
else:
foldList = ['']
ana_fold = ''
savedir = 'data_treat/Reproj_error.png'
for elem in foldList:
print("************ "+elem)
try:
if meth == 'no-persp':
ptc_top = float(ratTop.get())
ptc_left = float(ratLeft.get())
else:
ptc_top = None
ptc_left = None
create_camera(top_entry, 'top', cam_top, ptc_top)
create_camera(left_entry, 'left', cam_left, ptc_left)
if not elem == '':
cam_top.dir = ana_fold + elem + '/' + cam_top.dir
cam_left.dir = ana_fold + elem + '/' + cam_left.dir
cam_top.set_crop_size()
cam_left.set_crop_size()
except NameError:
popupmsg("One of the camera folder name you entered was either incorrect or empty.")
else:
if isbatch.get():
savedir += elem+'_reproj.png'
#try:
X, Y, Z, timespan, err = reconstruct_3d(cam_top, cam_left,
splitSymb="_", numsplit=-1, method=meth,
plotTraj=show_traj.get(), plot=not (isbatch.get()), isgui=True,
savedir=savedir)
#except:
notebook.tab(3, state='disable')
#popupmsg("Something went wrong in the analysis. Check out the console messages for more detail.")
#else:
traj_3d.set_trajectory(timespan, X, Y, Z, yerr=err)
notebook.tab(3, state='normal')
if isbatch.get():
try:
alpha = get_init_angle(X, Y, Z, timespan, cam_top, cam_left, plot=False,
saveDir=ana_fold+'RESULTS\\'+elem+'-', init=traj_3d.ang_min_ind, end=traj_3d.ang_end_ind)
xi, yi, zi, imp_i = get_impact_position(X, Y, Z, cam_left, cam_top, plot=False,
saveDir=ana_fold+'RESULTS\\'+elem+'-', threshold= traj_3d.imp_thres)
Vinit, Vend = get_velocity(timespan, X, Y, Z, thres=traj_3d.vel_det_fac, plot=False,
saveDir=ana_fold+'RESULTS\\'+elem+'-', init=traj_3d.vel_init_ind, pt_num=traj_3d.vel_min_pt)
except:
popupmsg("Wrong batch post-processing parameters. Please adjust.")
else:
traj_3d.set_pp(alpha, Vinit, Vend, [xi, yi, zi], imp_i)
traj_3d.save_dir = ana_fold+'RESULTS/'+elem+'.txt'
make_report(traj_3d, cam_top, cam_left, "data_treat/report_template.txt")