Commit 85de3a6aac8b62562168030f1da47975dbb5ceec

Authored by Renan
Committed by GitHub
1 parent 93ef8d8f
Exists in master

MOD: Refactor ICP to their own class (#307)

-ENH: stop navigation to perform ICP
-FIX: remove ICP thread to avoid vtk freezing
invesalius/gui/dialogs.py
@@ -864,7 +864,8 @@ def ShowNavigationTrackerWarning(trck_id, lib_mode): @@ -864,7 +864,8 @@ def ShowNavigationTrackerWarning(trck_id, lib_mode):
864 """ 864 """
865 Spatial Tracker connection error 865 Spatial Tracker connection error
866 """ 866 """
867 - trck = {const.MTC: 'Claron MicronTracker', 867 + trck = {const.SELECT: 'Tracker',
  868 + const.MTC: 'Claron MicronTracker',
868 const.FASTRAK: 'Polhemus FASTRAK', 869 const.FASTRAK: 'Polhemus FASTRAK',
869 const.ISOTRAKII: 'Polhemus ISOTRAK', 870 const.ISOTRAKII: 'Polhemus ISOTRAK',
870 const.PATRIOT: 'Polhemus PATRIOT', 871 const.PATRIOT: 'Polhemus PATRIOT',
@@ -3611,7 +3612,7 @@ class ICPCorregistrationDialog(wx.Dialog): @@ -3611,7 +3612,7 @@ class ICPCorregistrationDialog(wx.Dialog):
3611 btn_reset.Bind(wx.EVT_BUTTON, self.OnReset) 3612 btn_reset.Bind(wx.EVT_BUTTON, self.OnReset)
3612 3613
3613 btn_apply_icp = wx.Button(self, -1, label=_('Apply registration')) 3614 btn_apply_icp = wx.Button(self, -1, label=_('Apply registration'))
3614 - btn_apply_icp.Bind(wx.EVT_BUTTON, self.thread_ICP_start, btn_apply_icp) 3615 + btn_apply_icp.Bind(wx.EVT_BUTTON, self.OnICP)
3615 btn_apply_icp.Enable(False) 3616 btn_apply_icp.Enable(False)
3616 self.btn_apply_icp = btn_apply_icp 3617 self.btn_apply_icp = btn_apply_icp
3617 3618
@@ -3848,12 +3849,13 @@ class ICPCorregistrationDialog(wx.Dialog): @@ -3848,12 +3849,13 @@ class ICPCorregistrationDialog(wx.Dialog):
3848 self.RemoveActor() 3849 self.RemoveActor()
3849 self.LoadActor() 3850 self.LoadActor()
3850 3851
3851 - def OnICP(self): 3852 + def OnICP(self, evt):
3852 if self.cont_point: 3853 if self.cont_point:
3853 self.cont_point.SetValue(False) 3854 self.cont_point.SetValue(False)
3854 self.OnContinuousAcquisition(evt=None, btn=self.cont_point) 3855 self.OnContinuousAcquisition(evt=None, btn=self.cont_point)
3855 3856
3856 self.SetProgress(0.3) 3857 self.SetProgress(0.3)
  3858 + time.sleep(1)
3857 3859
3858 sourcePoints = np.array(self.point_coord) 3860 sourcePoints = np.array(self.point_coord)
3859 sourcePoints_vtk = vtk.vtkPoints() 3861 sourcePoints_vtk = vtk.vtkPoints()
@@ -3926,11 +3928,6 @@ class ICPCorregistrationDialog(wx.Dialog): @@ -3926,11 +3928,6 @@ class ICPCorregistrationDialog(wx.Dialog):
3926 3928
3927 self.btn_ok.Enable(True) 3929 self.btn_ok.Enable(True)
3928 3930
3929 - def thread_ICP_start(self, evt):  
3930 - import threading  
3931 - th = threading.Thread(target=self.OnICP, args=[])  
3932 - th.start()  
3933 -  
3934 def GetValue(self): 3931 def GetValue(self):
3935 return self.m_icp, self.point_coord, self.transformed_points, self.prev_error, self.final_error 3932 return self.m_icp, self.point_coord, self.transformed_points, self.prev_error, self.final_error
3936 3933
invesalius/gui/task_navigator.py
@@ -310,6 +310,8 @@ class Navigation(): @@ -310,6 +310,8 @@ class Navigation():
310 self.trigger_state = False 310 self.trigger_state = False
311 self.obj_reg = None 311 self.obj_reg = None
312 self.track_obj = False 312 self.track_obj = False
  313 + self.m_change = None
  314 + self.all_fiducials = np.zeros((6, 6))
313 315
314 self.event = threading.Event() 316 self.event = threading.Event()
315 self.coord_queue = QueueCustom(maxsize=1) 317 self.coord_queue = QueueCustom(maxsize=1)
@@ -331,12 +333,6 @@ class Navigation(): @@ -331,12 +333,6 @@ class Navigation():
331 self.seed_radius = const.SEED_RADIUS 333 self.seed_radius = const.SEED_RADIUS
332 self.sleep_nav = const.SLEEP_NAVIGATION 334 self.sleep_nav = const.SLEEP_NAVIGATION
333 335
334 - # ICP related variables  
335 - self.use_icp = False  
336 - self.m_icp = None  
337 - self.fre = None  
338 - self.icp_fre = None  
339 -  
340 def SetImageFiducial(self, fiducial_index, coord): 336 def SetImageFiducial(self, fiducial_index, coord):
341 self.image_fiducials[fiducial_index, :] = coord 337 self.image_fiducials[fiducial_index, :] = coord
342 338
@@ -349,16 +345,12 @@ class Navigation(): @@ -349,16 +345,12 @@ class Navigation():
349 tracker_fiducials, tracker_fiducials_raw = tracker.GetTrackerFiducials() 345 tracker_fiducials, tracker_fiducials_raw = tracker.GetTrackerFiducials()
350 ref_mode_id = tracker.GetReferenceMode() 346 ref_mode_id = tracker.GetReferenceMode()
351 347
352 - all_fiducials = np.vstack([self.image_fiducials, tracker_fiducials])  
353 -  
354 - # fiducials matrix  
355 - m_change = tr.affine_matrix_from_points(all_fiducials[3:, :].T, all_fiducials[:3, :].T,  
356 - shear=False, scale=False) 348 + self.all_fiducials = np.vstack([self.image_fiducials, tracker_fiducials])
357 349
358 - self.fre = db.calculate_fre(tracker_fiducials_raw, all_fiducials, ref_mode_id, m_change) 350 + self.fre = db.calculate_fre(tracker_fiducials_raw, self.all_fiducials, ref_mode_id, self.m_change)
359 351
360 - def GetFiducialRegistrationError(self):  
361 - fre = self.icp_fre if self.use_icp else self.fre 352 + def GetFiducialRegistrationError(self, icp):
  353 + fre = icp.icp_fre if icp.use_icp else self.fre
362 return fre, fre <= 3 354 return fre, fre <= 3
363 355
364 def StartNavigation(self, tracker): 356 def StartNavigation(self, tracker):
@@ -376,11 +368,12 @@ class Navigation(): @@ -376,11 +368,12 @@ class Navigation():
376 368
377 Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components) 369 Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components)
378 370
379 - all_fiducials = np.vstack([self.image_fiducials, tracker_fiducials]) 371 + self.all_fiducials = np.vstack([self.image_fiducials, tracker_fiducials])
380 372
381 # fiducials matrix 373 # fiducials matrix
382 - m_change = tr.affine_matrix_from_points(all_fiducials[3:, :].T, all_fiducials[:3, :].T, 374 + m_change = tr.affine_matrix_from_points(self.all_fiducials[3:, :].T, self.all_fiducials[:3, :].T,
383 shear=False, scale=False) 375 shear=False, scale=False)
  376 + self.m_change = m_change
384 377
385 errors = False 378 errors = False
386 379
@@ -449,16 +442,6 @@ class Navigation(): @@ -449,16 +442,6 @@ class Navigation():
449 jobs.start() 442 jobs.start()
450 # del jobs 443 # del jobs
451 444
452 - # TODO: Separate ICP-related code from Navigation class. The first step would be to have this stuff  
453 - # in a separate function or so.  
454 - #  
455 - if not self.use_icp:  
456 - if dlg.ICPcorregistration(self.fre):  
457 - m_icp = self.OnICP(tracker, m_change)  
458 - self.icp_fre = db.calculate_fre(tracker_fiducials_raw, all_fiducials, ref_mode_id,  
459 - m_change, m_icp)  
460 - self.SetICP(True)  
461 -  
462 def StopNavigation(self): 445 def StopNavigation(self):
463 self.event.set() 446 self.event.set()
464 447
@@ -478,42 +461,6 @@ class Navigation(): @@ -478,42 +461,6 @@ class Navigation():
478 vis_components = [self.trigger_state, self.view_tracts] 461 vis_components = [self.trigger_state, self.view_tracts]
479 Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components) 462 Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components)
480 463
481 - # ICP-related functions  
482 - #  
483 - # TODO: Refactor to preferably their own class.  
484 - #  
485 - def OnICP(self, tracker, m_change):  
486 - ref_mode_id = tracker.GetReferenceMode()  
487 -  
488 - dialog = dlg.ICPCorregistrationDialog(nav_prop=(m_change, tracker.tracker_id, tracker.trk_init, ref_mode_id))  
489 -  
490 - if dialog.ShowModal() == wx.ID_OK:  
491 - self.m_icp, point_coord, transformed_points, prev_error, final_error = dialog.GetValue()  
492 - # TODO: checkbox in the dialog to transfer the icp points to 3D viewer  
493 - #create markers  
494 - # for i in range(len(point_coord)):  
495 - # img_coord = point_coord[i][0],-point_coord[i][1],point_coord[i][2], 0, 0, 0  
496 - # transf_coord = transformed_points[i][0],-transformed_points[i][1],transformed_points[i][2], 0, 0, 0  
497 - # Publisher.sendMessage('Create marker', coord=img_coord, marker_id=None, colour=(1,0,0))  
498 - # Publisher.sendMessage('Create marker', coord=transf_coord, marker_id=None, colour=(0,0,1))  
499 - if self.m_icp is not None:  
500 - dlg.ReportICPerror(prev_error, final_error)  
501 - self.use_icp = True  
502 - else:  
503 - self.use_icp = False  
504 -  
505 - return self.m_icp  
506 -  
507 - def SetICP(self, use_icp):  
508 - self.use_icp = use_icp  
509 - self.icp_queue.put_nowait([self.use_icp, self.m_icp])  
510 -  
511 - def ResetICP(self):  
512 - self.use_icp = False  
513 - self.m_icp = None  
514 - self.fre = None  
515 - self.icp_fre = None  
516 -  
517 464
518 class Tracker(): 465 class Tracker():
519 def __init__(self): 466 def __init__(self):
@@ -689,6 +636,58 @@ class Tracker(): @@ -689,6 +636,58 @@ class Tracker():
689 def get_trackers(self): 636 def get_trackers(self):
690 return const.TRACKERS 637 return const.TRACKERS
691 638
  639 +class ICP():
  640 + def __init__(self):
  641 + self.use_icp = False
  642 + self.m_icp = None
  643 + self.icp_fre = None
  644 +
  645 + def StartICP(self, navigation, tracker):
  646 + if not self.use_icp:
  647 + if dlg.ICPcorregistration(navigation.fre):
  648 + Publisher.sendMessage('Stop navigation')
  649 + use_icp, self.m_icp = self.OnICP(tracker, navigation.m_change)
  650 + if use_icp:
  651 + self.icp_fre = db.calculate_fre(tracker.tracker_fiducials_raw, navigation.all_fiducials,
  652 + tracker.ref_mode_id, navigation.m_change, self.m_icp)
  653 + self.SetICP(navigation, use_icp)
  654 + else:
  655 + print("ICP canceled")
  656 + Publisher.sendMessage('Start navigation')
  657 +
  658 + def OnICP(self, tracker, m_change):
  659 + ref_mode_id = tracker.GetReferenceMode()
  660 +
  661 + dialog = dlg.ICPCorregistrationDialog(nav_prop=(m_change, tracker.tracker_id, tracker.trk_init, ref_mode_id))
  662 +
  663 + if dialog.ShowModal() == wx.ID_OK:
  664 + m_icp, point_coord, transformed_points, prev_error, final_error = dialog.GetValue()
  665 + # TODO: checkbox in the dialog to transfer the icp points to 3D viewer
  666 + #create markers
  667 + # for i in range(len(point_coord)):
  668 + # img_coord = point_coord[i][0],-point_coord[i][1],point_coord[i][2], 0, 0, 0
  669 + # transf_coord = transformed_points[i][0],-transformed_points[i][1],transformed_points[i][2], 0, 0, 0
  670 + # Publisher.sendMessage('Create marker', coord=img_coord, marker_id=None, colour=(1,0,0))
  671 + # Publisher.sendMessage('Create marker', coord=transf_coord, marker_id=None, colour=(0,0,1))
  672 + if m_icp is not None:
  673 + dlg.ReportICPerror(prev_error, final_error)
  674 + use_icp = True
  675 + else:
  676 + use_icp = False
  677 +
  678 + return use_icp, m_icp
  679 +
  680 + else:
  681 + return self.use_icp, self.m_icp
  682 +
  683 + def SetICP(self, navigation, use_icp):
  684 + self.use_icp = use_icp
  685 + navigation.icp_queue.put_nowait([self.use_icp, self.m_icp])
  686 +
  687 + def ResetICP(self):
  688 + self.use_icp = False
  689 + self.m_icp = None
  690 + self.icp_fre = None
692 691
693 class NeuronavigationPanel(wx.Panel): 692 class NeuronavigationPanel(wx.Panel):
694 def __init__(self, parent): 693 def __init__(self, parent):
@@ -707,6 +706,7 @@ class NeuronavigationPanel(wx.Panel): @@ -707,6 +706,7 @@ class NeuronavigationPanel(wx.Panel):
707 self.pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None 706 self.pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None
708 self.tracker = Tracker() 707 self.tracker = Tracker()
709 self.navigation = Navigation() 708 self.navigation = Navigation()
  709 + self.icp = ICP()
710 710
711 self.nav_status = False 711 self.nav_status = False
712 712
@@ -915,11 +915,12 @@ class NeuronavigationPanel(wx.Panel): @@ -915,11 +915,12 @@ class NeuronavigationPanel(wx.Panel):
915 915
916 self.tracker.SetTrackerFiducial(fiducial_index) 916 self.tracker.SetTrackerFiducial(fiducial_index)
917 917
  918 + self.ResetICP()
918 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_coord[3:6], self.txtctrl_fre) 919 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_coord[3:6], self.txtctrl_fre)
919 920
920 def UpdateNavigationStatus(self, nav_status, vis_status): 921 def UpdateNavigationStatus(self, nav_status, vis_status):
921 self.nav_status = nav_status 922 self.nav_status = nav_status
922 - if nav_status and self.navigation.m_icp is not None: 923 + if nav_status and self.icp.m_icp is not None:
923 self.checkbox_icp.Enable(True) 924 self.checkbox_icp.Enable(True)
924 else: 925 else:
925 self.checkbox_icp.Enable(False) 926 self.checkbox_icp.Enable(False)
@@ -974,7 +975,7 @@ class NeuronavigationPanel(wx.Panel): @@ -974,7 +975,7 @@ class NeuronavigationPanel(wx.Panel):
974 self.navigation.trigger_state = trigger_state 975 self.navigation.trigger_state = trigger_state
975 976
976 def ResetICP(self): 977 def ResetICP(self):
977 - self.navigation.ResetICP() 978 + self.icp.ResetICP()
978 self.checkbox_icp.Enable(False) 979 self.checkbox_icp.Enable(False)
979 self.checkbox_icp.SetValue(False) 980 self.checkbox_icp.SetValue(False)
980 981
@@ -1041,7 +1042,7 @@ class NeuronavigationPanel(wx.Panel): @@ -1041,7 +1042,7 @@ class NeuronavigationPanel(wx.Panel):
1041 1042
1042 def UpdateFiducialRegistrationError(self): 1043 def UpdateFiducialRegistrationError(self):
1043 self.navigation.UpdateFiducialRegistrationError(self.tracker) 1044 self.navigation.UpdateFiducialRegistrationError(self.tracker)
1044 - fre, fre_ok = self.navigation.GetFiducialRegistrationError() 1045 + fre, fre_ok = self.navigation.GetFiducialRegistrationError(self.icp)
1045 1046
1046 self.txtctrl_fre.SetValue(str(round(fre, 2))) 1047 self.txtctrl_fre.SetValue(str(round(fre, 2)))
1047 if fre_ok: 1048 if fre_ok:
@@ -1063,10 +1064,6 @@ class NeuronavigationPanel(wx.Panel): @@ -1063,10 +1064,6 @@ class NeuronavigationPanel(wx.Panel):
1063 errors = True 1064 errors = True
1064 1065
1065 else: 1066 else:
1066 - if not self.UpdateFiducialRegistrationError():  
1067 - # TODO: Exhibit FRE in a warning dialog and only starts navigation after user clicks ok  
1068 - print("WARNING: Fiducial registration error too large.")  
1069 -  
1070 # Prepare GUI for navigation. 1067 # Prepare GUI for navigation.
1071 Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS) 1068 Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS)
1072 Publisher.sendMessage("Hide current mask") 1069 Publisher.sendMessage("Hide current mask")
@@ -1079,13 +1076,17 @@ class NeuronavigationPanel(wx.Panel): @@ -1079,13 +1076,17 @@ class NeuronavigationPanel(wx.Panel):
1079 1076
1080 self.navigation.StartNavigation(self.tracker) 1077 self.navigation.StartNavigation(self.tracker)
1081 1078
1082 - # Update FRE once more after starting the navigation, due to the optional use of ICP,  
1083 - # which improves FRE.  
1084 - self.UpdateFiducialRegistrationError() 1079 + if not self.UpdateFiducialRegistrationError():
  1080 + # TODO: Exhibit FRE in a warning dialog and only starts navigation after user clicks ok
  1081 + print("WARNING: Fiducial registration error too large.")
1085 1082
1086 - if self.navigation.use_icp: 1083 + self.icp.StartICP(self.navigation, self.tracker)
  1084 + if self.icp.use_icp:
1087 self.checkbox_icp.Enable(True) 1085 self.checkbox_icp.Enable(True)
1088 self.checkbox_icp.SetValue(True) 1086 self.checkbox_icp.SetValue(True)
  1087 + # Update FRE once more after starting the navigation, due to the optional use of ICP,
  1088 + # which improves FRE.
  1089 + self.UpdateFiducialRegistrationError()
1089 1090
1090 def OnNavigate(self, evt, btn_nav): 1091 def OnNavigate(self, evt, btn_nav):
1091 select_tracker_elem = self.select_tracker_elem 1092 select_tracker_elem = self.select_tracker_elem
@@ -1113,7 +1114,7 @@ class NeuronavigationPanel(wx.Panel): @@ -1113,7 +1114,7 @@ class NeuronavigationPanel(wx.Panel):
1113 self.numctrls_coord[m][n].SetValue(0.0) 1114 self.numctrls_coord[m][n].SetValue(0.0)
1114 1115
1115 def OnCheckboxICP(self, evt, ctrl): 1116 def OnCheckboxICP(self, evt, ctrl):
1116 - self.navigation.SetICP(ctrl.GetValue()) 1117 + self.icp.SetICP(self.navigation, ctrl.GetValue())
1117 self.UpdateFiducialRegistrationError() 1118 self.UpdateFiducialRegistrationError()
1118 1119
1119 def OnCloseProject(self): 1120 def OnCloseProject(self):
@@ -1130,6 +1131,7 @@ class NeuronavigationPanel(wx.Panel): @@ -1130,6 +1131,7 @@ class NeuronavigationPanel(wx.Panel):
1130 self.navigation.StopNavigation() 1131 self.navigation.StopNavigation()
1131 self.navigation.__init__() 1132 self.navigation.__init__()
1132 self.tracker.__init__() 1133 self.tracker.__init__()
  1134 + self.icp.__init__()
1133 1135
1134 1136
1135 class ObjectRegistrationPanel(wx.Panel): 1137 class ObjectRegistrationPanel(wx.Panel):