Commit 85de3a6aac8b62562168030f1da47975dbb5ceec
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
Showing
2 changed files
with
81 additions
and
82 deletions
Show diff stats
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): | ... | ... |