diff --git a/example/params_file.yml b/example/params_file.yml index ced4336..72fd173 100644 --- a/example/params_file.yml +++ b/example/params_file.yml @@ -1,8 +1,8 @@ - calibration: 3D_model: Tsai - camera_name: cam3 + camera_name: cam1 resolution: 1280, 1024 - calibration_image: ./Calibration/cal3.tif + calibration_image: ./Calibration/cal1.tif target_file: ./Calibration/target_file - analyze_calibration_error: diff --git a/example/workflow.py b/example/workflow.py index 8b71192..26c074b 100644 --- a/example/workflow.py +++ b/example/workflow.py @@ -233,7 +233,9 @@ def initial_calibration(self): elif model_name == 'extendedZolof': - ... + from myptv.extendedZolof.gui_intial_cal import initial_cal_gui + image = imread(cal_image) + gui = initial_cal_gui(cam_name, cal_image, target_file) else: @@ -301,7 +303,28 @@ def final_calibration(self): elif model_name == 'extendedZolof': - ... + from myptv.extendedZolof.camera import camera_extendedZolof + from myptv.extendedZolof.calibrate import calibrate_extendedZolof + from myptv.extendedZolof.gui_final_cal import cal_gui + + try: + cam = camera_extendedZolof(cam_name, cal_points_fname = blob_file) + except: + msg = 'Calibration point file (%s) is not right!'%blob_file + msg2 = 'check that the file exists and that it has no errors.' + raise ValueError(msg+msg2) + + cam.load('.') + print('camera data loaded successfully.') + cal = calibrate_extendedZolof(cam, + cam.image_points, + cam.lab_points) + print('Starting calibration gui') + gui = cal_gui(calibrate_obj=cal) + #cal.calibrate() + err = cal.mean_squared_err() + print('Calibration finished. The calibration error is: %.3e'%err) + #cam.save('.') else: @@ -578,45 +601,6 @@ def calibration_with_particles(self): print('starting calibration GIU using calibration with particles\n') gui = cal_gui(cal, cal_image) - - # print('\n', 'ready to calibrate') - # print('initial error: %.3f pixels'%(cal.mean_squared_err())) - # print('') - - # user = True - # print('Starting calibration sequence:') - # while user != '9': - # print("enter '1' for external parameters calibration") - # print("enter '2' for internal correction ('fine') calibration") - # print("enter '3' to show current camera external parameters") - # print("enter '4' to plot the calibration points' projection") - # print("enter '8' to save the results") - # print("enter '9' to quit") - # user = input('') - - # if user == '1': - # print('\n', 'Iterating to minimize external parameters') - # cal.searchCalibration(maxiter=2000) - # err = cal.mean_squared_err() - # print('\n','calibration error: %.3f pixels'%(err),'\n') - - # if user == '2': - # print('\n', 'Iterating to minimize correction terms') - # cal.fineCalibration() - # err = cal.mean_squared_err() - # print('\n','calibration error:', err,'\n') - - # if user == '3': - # print('\n', cam, '\n') - - # if user == '4': - # fig, ax = subplots() - # cal.plot_proj(ax=ax) - # show() - # if user == '8': - # print('\n', 'Saving results') - # cam.save('.') - def do_segmentation(self): diff --git a/myptv/extendedZolof/calibrate.py b/myptv/extendedZolof/calibrate.py index 27343c8..b5df155 100644 --- a/myptv/extendedZolof/calibrate.py +++ b/myptv/extendedZolof/calibrate.py @@ -133,6 +133,42 @@ def mean_squared_err(self): + def plot_err_distribution(self, ax = None): + import matplotlib.pyplot as plt + from numpy import sum as npsum + + if ax == None: + fig, ax = plt.subplots() + + imc = array(self.x_list) + z_lst = array([self.cam.projection(x) for x in self.X_list]) + err = npsum((imc-z_lst)**2, axis=1)**0.5 + + h = ax.hist( err, bins='auto') + ax.set_xlabel('Camera projection err [px]') + ax.set_ylabel('Counts') + + + + + def plot_proj(self, ax = None): + import matplotlib.pyplot as plt + + if ax == None: + fig, ax = plt.subplots() + + imc = array(self.x_list) + ax.plot(imc[:,0], imc[:,1], 'ob') + for i in range(imc.shape[0]): + ax.text(imc[i,0], imc[i,1], '%d'%i, color = 'b') + + z_lst = array([self.cam.projection(x) for x in self.X_list]) + ax.plot( z_lst[:,0], z_lst[:,1], 'xr' ) + for i in range(z_lst.shape[0]): + ax.text(z_lst[i,0], z_lst[i,1], '%d'%i, color = 'r') + + ax.set_aspect('equal') + \ No newline at end of file diff --git a/myptv/extendedZolof/gui_final_cal.py b/myptv/extendedZolof/gui_final_cal.py new file mode 100755 index 0000000..aa2b364 --- /dev/null +++ b/myptv/extendedZolof/gui_final_cal.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 -*- +""" +Created on April 23, 2022 + +@author: Ron + +A gui for the final calibration of a Tsai model camera. +""" + + +#from PIL import Image, ImageTk +from tkinter import Label, LabelFrame, Entry, Tk, Button, Checkbutton, IntVar +from matplotlib.pyplot import subplots, show, imread + + +class cal_gui(object): + ''' + This is a Tkinter based graphical user interface that can be used to mark + points on a static image, give their lab space coordinates, and then save + the data as a text file in the format used by the calibration processes. + ''' + + + def __init__(self, calibrate_obj=None, cal_image=None): + ''' + input: + + calibrate_obj - An instance of the calibrate class, ready to perform + calibration. + + ''' + self.calibrate_obj = calibrate_obj + self.cal_image = cal_image + + # set the window + self.root = Tk() + self.root.geometry('370x350') + #self.root.resizable(0,0) + self.root.resizable(height = None, width = None) + self.root.title('MyPTV: Extended Zolof calibration GUI') + + + # ============================= + # BUTTONS: + + # buttons frame + button_label = LabelFrame(self.root, padx=10, pady=3, width=100) + button_label.grid(row=0, column=0, padx=(10), pady=3, sticky='nsew') + + + # Buttons frame1 + button_frame1 = Label(button_label) + button_frame1.grid(row=0, column=0, columnspan=1, sticky='nsw', padx=2, + pady=3) + + + + # external params calibration button + calibrate_button = Button(button_frame1, text='Calibrate', + command = self.calibrate, + padx=2, pady=4, width=20) + calibrate_button.grid(row=0, column=0, padx=10, pady=2, sticky='ew') + + + # plot calibration + plot_button = Button(button_frame1, text='Plot calibration', + command = self.plot_calibration, + padx=2, pady=4, width=20) + plot_button.grid(row=1, column=0, padx=10, pady=2, sticky='ew') + + # plot error historam + plot_button = Button(button_frame1, text='Plot error hist.', + command = self.plot_err_hist, + padx=2, pady=4, width=20) + plot_button.grid(row=2, column=0, padx=10, pady=2, sticky='ew') + + + # save points button + save_button = Button(button_frame1, text='Save', + command = self.Save, padx=10, pady=4, width=15) + save_button.grid(row=5, column=0, padx=10, pady=30, sticky='ew') + + + # quit button + quit_button = Button(button_frame1, text='Quit', + command = self.Quit, padx=10, pady=4, width=15) + quit_button.grid(row=5, column=1, padx=10, pady=30, sticky='ew') + + + + + + + + # ============================= + # CALIBRATOR STATUS: + + + status_label = LabelFrame(self.root, padx=10, pady=4, width=30) + status_label.grid(row=1, column=0, padx=(10), pady=4, sticky='nsew') + + self.status = Label(status_label, text='Status:', padx=2, pady=2) + self.status.grid(row=0, column=0, rowspan=1, sticky='nw', padx=2, pady=2) + self.status_show = Label(status_label, text='wating for action', padx=2, pady=2, + width=30, anchor='w', fg='green') + self.status_show.grid(row=0, column=1, rowspan=1, sticky='nw', padx=2, pady=2) + + + + self.camera = Label(status_label, text='Camera:', padx=2, pady=2) + self.camera.grid(row=1, column=0, rowspan=1, sticky='nw', padx=2, pady=2) + + cam = 'camera name' + if self.calibrate_obj is not None: + cam = self.calibrate_obj.cam.name + + self.camera_show = Label(status_label, text=cam, padx=2, pady=2, + width=30, anchor='w') + self.camera_show.grid(row=1, column=1, rowspan=1, sticky='nw', padx=2, pady=2) + + + + + # ============================= + # CAMERA STATUS: + + + + dashboard = LabelFrame(self.root, padx=10, pady=10, width=100) + dashboard.grid(row=2, column=0, padx=(10), pady=10, sticky='nsew') + + + # err_Dashboard frame - where the error is shown + err_dashboard = LabelFrame(dashboard) + err_dashboard.grid(row=2, column=1, columnspan=1, sticky='sw', padx=2, + pady=10) + + + self.error = Label(err_dashboard, text='Error:', padx=2, pady=2) + self.error.grid(row=1, column=0, rowspan=1, sticky='nw', padx=2, pady=2) + self.error_input = Label(err_dashboard, text='0.0', padx=2, pady=2, + width=14, bg='white') + self.error_input.grid(row=1, column=1, rowspan=1, sticky='nw', padx=2, pady=2) + + + + #if self.calibrate_obj is not None: + # self.update_cal_stats() + + + + + # ============================== + # RUN + + # configure hte frames and run main loop + self.root.columnconfigure(0, weight=1) + self.root.rowconfigure(0, weight=1) + button_label.rowconfigure(0, weight=1) + button_label.columnconfigure(0, weight=1) + + self.root.mainloop() + + + + def calibrate(self): + '''Does the searchCalibration, i.e. external calibration''' + print('\n','Iterating to minimize external parameters...','\n') + self.status_show.configure(fg='red', + text='minimizing external parameters...') + self.root.update() + self.calibrate_obj.calibrate() + err = self.calibrate_obj.mean_squared_err() + print('\n','calibration error: %.3f pixels'%(err),'\n') + self.error_input.config(text = '%.3e'%err) + self.status_show.configure(fg='green', text='done! waiting for action') + + + + + def plot_calibration(self): + '''Plots the calibration using plot_proj function''' + self.status_show.configure(fg='red', text='plotting calibration...') + self.root.update() + fig, ax = subplots() + if self.cal_image is not None: + img = imread(self.cal_image) + ax.imshow(img, cmap='gray') + self.calibrate_obj.plot_proj(ax=ax) + show() + self.status_show.configure(fg='green', text='done! waiting for action') + + + def plot_err_hist(self): + '''Plots the calibration error histogram''' + self.status_show.configure(fg='red', text='plotting calibration...') + self.root.update() + fig, ax = subplots() + self.calibrate_obj.plot_err_distribution(ax=ax) + show() + self.status_show.configure(fg='green', text='done! waiting for action') + + + def Save(self): + '''save the calibrated camera''' + self.status_show.configure(fg='red', text='saving results...') + self.root.update() + self.calibrate_obj.cam.save('.') + self.status_show.configure(fg='green', text='done! waiting for action') + + + def Quit(self): + '''quit the app''' + self.status_show.configure(fg='red', text='bye!') + self.root.update() + self.root.destroy() + + + + +if __name__ == '__main__': + gui = cal_gui() + + +