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 864 """
865 865 Spatial Tracker connection error
866 866 """
867   - trck = {const.MTC: 'Claron MicronTracker',
  867 + trck = {const.SELECT: 'Tracker',
  868 + const.MTC: 'Claron MicronTracker',
868 869 const.FASTRAK: 'Polhemus FASTRAK',
869 870 const.ISOTRAKII: 'Polhemus ISOTRAK',
870 871 const.PATRIOT: 'Polhemus PATRIOT',
... ... @@ -3611,7 +3612,7 @@ class ICPCorregistrationDialog(wx.Dialog):
3611 3612 btn_reset.Bind(wx.EVT_BUTTON, self.OnReset)
3612 3613  
3613 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 3616 btn_apply_icp.Enable(False)
3616 3617 self.btn_apply_icp = btn_apply_icp
3617 3618  
... ... @@ -3848,12 +3849,13 @@ class ICPCorregistrationDialog(wx.Dialog):
3848 3849 self.RemoveActor()
3849 3850 self.LoadActor()
3850 3851  
3851   - def OnICP(self):
  3852 + def OnICP(self, evt):
3852 3853 if self.cont_point:
3853 3854 self.cont_point.SetValue(False)
3854 3855 self.OnContinuousAcquisition(evt=None, btn=self.cont_point)
3855 3856  
3856 3857 self.SetProgress(0.3)
  3858 + time.sleep(1)
3857 3859  
3858 3860 sourcePoints = np.array(self.point_coord)
3859 3861 sourcePoints_vtk = vtk.vtkPoints()
... ... @@ -3926,11 +3928,6 @@ class ICPCorregistrationDialog(wx.Dialog):
3926 3928  
3927 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 3931 def GetValue(self):
3935 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 310 self.trigger_state = False
311 311 self.obj_reg = None
312 312 self.track_obj = False
  313 + self.m_change = None
  314 + self.all_fiducials = np.zeros((6, 6))
313 315  
314 316 self.event = threading.Event()
315 317 self.coord_queue = QueueCustom(maxsize=1)
... ... @@ -331,12 +333,6 @@ class Navigation():
331 333 self.seed_radius = const.SEED_RADIUS
332 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 336 def SetImageFiducial(self, fiducial_index, coord):
341 337 self.image_fiducials[fiducial_index, :] = coord
342 338  
... ... @@ -349,16 +345,12 @@ class Navigation():
349 345 tracker_fiducials, tracker_fiducials_raw = tracker.GetTrackerFiducials()
350 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 354 return fre, fre <= 3
363 355  
364 356 def StartNavigation(self, tracker):
... ... @@ -376,11 +368,12 @@ class Navigation():
376 368  
377 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 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 375 shear=False, scale=False)
  376 + self.m_change = m_change
384 377  
385 378 errors = False
386 379  
... ... @@ -449,16 +442,6 @@ class Navigation():
449 442 jobs.start()
450 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 445 def StopNavigation(self):
463 446 self.event.set()
464 447  
... ... @@ -478,42 +461,6 @@ class Navigation():
478 461 vis_components = [self.trigger_state, self.view_tracts]
479 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 465 class Tracker():
519 466 def __init__(self):
... ... @@ -689,6 +636,58 @@ class Tracker():
689 636 def get_trackers(self):
690 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 692 class NeuronavigationPanel(wx.Panel):
694 693 def __init__(self, parent):
... ... @@ -707,6 +706,7 @@ class NeuronavigationPanel(wx.Panel):
707 706 self.pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None
708 707 self.tracker = Tracker()
709 708 self.navigation = Navigation()
  709 + self.icp = ICP()
710 710  
711 711 self.nav_status = False
712 712  
... ... @@ -915,11 +915,12 @@ class NeuronavigationPanel(wx.Panel):
915 915  
916 916 self.tracker.SetTrackerFiducial(fiducial_index)
917 917  
  918 + self.ResetICP()
918 919 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_coord[3:6], self.txtctrl_fre)
919 920  
920 921 def UpdateNavigationStatus(self, nav_status, vis_status):
921 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 924 self.checkbox_icp.Enable(True)
924 925 else:
925 926 self.checkbox_icp.Enable(False)
... ... @@ -974,7 +975,7 @@ class NeuronavigationPanel(wx.Panel):
974 975 self.navigation.trigger_state = trigger_state
975 976  
976 977 def ResetICP(self):
977   - self.navigation.ResetICP()
  978 + self.icp.ResetICP()
978 979 self.checkbox_icp.Enable(False)
979 980 self.checkbox_icp.SetValue(False)
980 981  
... ... @@ -1041,7 +1042,7 @@ class NeuronavigationPanel(wx.Panel):
1041 1042  
1042 1043 def UpdateFiducialRegistrationError(self):
1043 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 1047 self.txtctrl_fre.SetValue(str(round(fre, 2)))
1047 1048 if fre_ok:
... ... @@ -1063,10 +1064,6 @@ class NeuronavigationPanel(wx.Panel):
1063 1064 errors = True
1064 1065  
1065 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 1067 # Prepare GUI for navigation.
1071 1068 Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS)
1072 1069 Publisher.sendMessage("Hide current mask")
... ... @@ -1079,13 +1076,17 @@ class NeuronavigationPanel(wx.Panel):
1079 1076  
1080 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 1085 self.checkbox_icp.Enable(True)
1088 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 1091 def OnNavigate(self, evt, btn_nav):
1091 1092 select_tracker_elem = self.select_tracker_elem
... ... @@ -1113,7 +1114,7 @@ class NeuronavigationPanel(wx.Panel):
1113 1114 self.numctrls_coord[m][n].SetValue(0.0)
1114 1115  
1115 1116 def OnCheckboxICP(self, evt, ctrl):
1116   - self.navigation.SetICP(ctrl.GetValue())
  1117 + self.icp.SetICP(self.navigation, ctrl.GetValue())
1117 1118 self.UpdateFiducialRegistrationError()
1118 1119  
1119 1120 def OnCloseProject(self):
... ... @@ -1130,6 +1131,7 @@ class NeuronavigationPanel(wx.Panel):
1130 1131 self.navigation.StopNavigation()
1131 1132 self.navigation.__init__()
1132 1133 self.tracker.__init__()
  1134 + self.icp.__init__()
1133 1135  
1134 1136  
1135 1137 class ObjectRegistrationPanel(wx.Panel):
... ...