Commit 3b8f8e1ff7464fd3e9c576eda3d24dae75511c4e

Authored by Renan
Committed by GitHub
2 parents 2d8f59df 6e312c12
Exists in master

Merge pull request #403 from sotodela/278_add_visualization_for_the_direction_of_the_marker

278 add visualization for the direction of the marker
invesalius/constants.py
... ... @@ -671,7 +671,7 @@ Z_COLUMN = 6
671 671  
672 672 MARKER_COLOUR = (1.0, 1.0, 0.)
673 673 MARKER_SIZE = 2
674   -
  674 +ARROW_MARKER_SIZE = 10
675 675 CALIBRATION_TRACKER_SAMPLES = 10
676 676 FIDUCIAL_REGISTRATION_ERROR_THRESHOLD = 3.0
677 677  
... ...
invesalius/data/styles.py
... ... @@ -486,7 +486,7 @@ class CrossInteractorStyle(DefaultInteractorStyle):
486 486 x, y, z = self.viewer.get_coordinate_cursor(mouse_x, mouse_y, self.picker)
487 487 self.viewer.UpdateSlicesPosition([x, y, z])
488 488 # This "Set cross" message is needed to update the cross in the other slices
489   - Publisher.sendMessage('Set cross focal point', position=[x, y, z, 0., 0., 0.])
  489 + Publisher.sendMessage('Set cross focal point', position=[x, y, z, None, None, None])
490 490 Publisher.sendMessage('Update slice viewer')
491 491  
492 492 def OnScrollBar(self, *args, **kwargs):
... ... @@ -494,7 +494,7 @@ class CrossInteractorStyle(DefaultInteractorStyle):
494 494 # the actual orientation.
495 495 x, y, z = self.viewer.cross.GetFocalPoint()
496 496 self.viewer.UpdateSlicesPosition([x, y, z])
497   - Publisher.sendMessage('Set cross focal point', position=[x, y, z, 0., 0., 0.])
  497 + Publisher.sendMessage('Set cross focal point', position=[x, y, z, None, None, None])
498 498 Publisher.sendMessage('Update slice viewer')
499 499  
500 500  
... ...
invesalius/data/viewer_volume.py
... ... @@ -70,8 +70,8 @@ class Viewer(wx.Panel):
70 70  
71 71 self.initial_focus = None
72 72  
73   - self.staticballs = []
74   -
  73 + self.static_markers = []
  74 + self.static_arrows = []
75 75 self.style = None
76 76  
77 77 interactor = wxVTKRenderWindowInteractor(self, -1, size = self.GetSize())
... ... @@ -278,6 +278,7 @@ class Viewer(wx.Panel):
278 278  
279 279 # Related to marker creation in navigation tools
280 280 Publisher.subscribe(self.AddMarker, 'Add marker')
  281 + Publisher.subscribe(self.AddMarkerwithOrientation, 'Add arrow marker')
281 282 Publisher.subscribe(self.HideAllMarkers, 'Hide all markers')
282 283 Publisher.subscribe(self.ShowAllMarkers, 'Show all markers')
283 284 Publisher.subscribe(self.RemoveAllMarkers, 'Remove all markers')
... ... @@ -579,7 +580,7 @@ class Viewer(wx.Panel):
579 580 """
580 581 Set all markers, overwriting the previous markers.
581 582 """
582   - self.RemoveAllMarkers(len(self.staticballs))
  583 + self.RemoveAllMarkers(len(self.static_markers))
583 584  
584 585 target_selected = False
585 586 for marker in markers:
... ... @@ -607,6 +608,21 @@ class Viewer(wx.Panel):
607 608  
608 609 self.UpdateRender()
609 610  
  611 + def AddMarkerwithOrientation(self, arrow_id, size, color, coord):
  612 + """
  613 + Markers arrow with orientation created by navigation tools and rendered in volume viewer.
  614 + """
  615 + self.arrow_marker_id = arrow_id
  616 + coord_flip = list(coord)
  617 + coord_flip[1] = -coord_flip[1]
  618 +
  619 + arrow_actor = self.Add_ObjectArrow(coord_flip[:3], coord_flip[3:6], color, size)
  620 + self.static_markers.append(arrow_actor)
  621 + self.ren.AddActor(self.static_markers[self.arrow_marker_id])
  622 + self.arrow_marker_id += 1
  623 +
  624 + self.Refresh()
  625 +
610 626 def AddMarker(self, ball_id, size, colour, coord):
611 627 """
612 628 Markers created by navigation tools and rendered in volume viewer.
... ... @@ -626,12 +642,12 @@ class Viewer(wx.Panel):
626 642 prop.SetColor(colour)
627 643  
628 644 # adding a new actor for the present ball
629   - self.staticballs.append(vtk.vtkActor())
  645 + self.static_markers.append(vtk.vtkActor())
630 646  
631   - self.staticballs[self.ball_id].SetMapper(mapper)
632   - self.staticballs[self.ball_id].SetProperty(prop)
  647 + self.static_markers[self.ball_id].SetMapper(mapper)
  648 + self.static_markers[self.ball_id].SetProperty(prop)
633 649  
634   - self.ren.AddActor(self.staticballs[self.ball_id])
  650 + self.ren.AddActor(self.static_markers[self.ball_id])
635 651 self.ball_id += 1
636 652  
637 653 #self.UpdateRender()
... ... @@ -667,33 +683,33 @@ class Viewer(wx.Panel):
667 683 def HideAllMarkers(self, indexes):
668 684 ballid = indexes
669 685 for i in range(0, ballid):
670   - self.staticballs[i].SetVisibility(0)
  686 + self.static_markers[i].SetVisibility(0)
671 687 self.UpdateRender()
672 688  
673 689 def ShowAllMarkers(self, indexes):
674 690 ballid = indexes
675 691 for i in range(0, ballid):
676   - self.staticballs[i].SetVisibility(1)
  692 + self.static_markers[i].SetVisibility(1)
677 693 self.UpdateRender()
678 694  
679 695 def RemoveAllMarkers(self, indexes):
680 696 ballid = indexes
681 697 for i in range(0, ballid):
682   - self.ren.RemoveActor(self.staticballs[i])
683   - self.staticballs = []
  698 + self.ren.RemoveActor(self.static_markers[i])
  699 + self.static_markers = []
684 700 self.UpdateRender()
685 701  
686 702 def RemoveMultipleMarkers(self, index):
687 703 for i in reversed(index):
688   - self.ren.RemoveActor(self.staticballs[i])
689   - del self.staticballs[i]
  704 + self.ren.RemoveActor(self.static_markers[i])
  705 + del self.static_markers[i]
690 706 self.ball_id = self.ball_id - 1
691 707 self.UpdateRender()
692 708  
693 709 def BlinkMarker(self, index):
694 710 if self.timer:
695 711 self.timer.Stop()
696   - self.staticballs[self.index].SetVisibility(1)
  712 + self.static_markers[self.index].SetVisibility(1)
697 713 self.index = index
698 714 self.timer = wx.Timer(self)
699 715 self.Bind(wx.EVT_TIMER, self.OnBlinkMarker, self.timer)
... ... @@ -701,7 +717,7 @@ class Viewer(wx.Panel):
701 717 self.timer_count = 0
702 718  
703 719 def OnBlinkMarker(self, evt):
704   - self.staticballs[self.index].SetVisibility(int(self.timer_count % 2))
  720 + self.static_markers[self.index].SetVisibility(int(self.timer_count % 2))
705 721 self.Refresh()
706 722 self.timer_count += 1
707 723  
... ... @@ -709,20 +725,20 @@ class Viewer(wx.Panel):
709 725 if self.timer:
710 726 self.timer.Stop()
711 727 if index is None:
712   - self.staticballs[self.index].SetVisibility(1)
  728 + self.static_markers[self.index].SetVisibility(1)
713 729 self.Refresh()
714 730 self.index = False
715 731  
716 732 def SetNewColor(self, index, color):
717   - self.staticballs[index].GetProperty().SetColor([round(s/255.0, 3) for s in color])
  733 + self.static_markers[index].GetProperty().SetColor([round(s / 255.0, 3) for s in color])
718 734 self.Refresh()
719 735  
720 736 def OnTargetMarkerTransparency(self, status, index):
721 737 if status:
722   - self.staticballs[index].GetProperty().SetOpacity(1)
  738 + self.static_markers[index].GetProperty().SetOpacity(1)
723 739 # self.staticballs[index].GetProperty().SetOpacity(0.4)
724 740 else:
725   - self.staticballs[index].GetProperty().SetOpacity(1)
  741 + self.static_markers[index].GetProperty().SetOpacity(1)
726 742  
727 743 def OnUpdateAngleThreshold(self, angle):
728 744 self.anglethreshold = angle
... ... @@ -992,16 +1008,10 @@ class Viewer(wx.Panel):
992 1008 self.RemoveTarget()
993 1009 self.DisableCoilTracker()
994 1010  
995   - def CreateTargetAim(self):
996   - if self.aim_actor:
997   - self.RemoveTargetAim()
998   - self.aim_actor = None
999   -
1000   - vtk_colors = vtk.vtkNamedColors()
1001   -
  1011 + def CreateVTKObjectMatrix(self, direction, orientation):
1002 1012 m_img = dco.coordinates_to_transformation_matrix(
1003   - position=self.target_coord[:3],
1004   - orientation=self.target_coord[3:],
  1013 + position=direction,
  1014 + orientation=orientation,
1005 1015 axes='sxyz',
1006 1016 )
1007 1017 m_img = np.asmatrix(m_img)
... ... @@ -1012,7 +1022,16 @@ class Viewer(wx.Panel):
1012 1022 for col in range(0, 4):
1013 1023 m_img_vtk.SetElement(row, col, m_img[row, col])
1014 1024  
1015   - self.m_img_vtk = m_img_vtk
  1025 + return m_img_vtk
  1026 +
  1027 + def CreateTargetAim(self):
  1028 + if self.aim_actor:
  1029 + self.RemoveTargetAim()
  1030 + self.aim_actor = None
  1031 +
  1032 + vtk_colors = vtk.vtkNamedColors()
  1033 +
  1034 + self.m_img_vtk = self.CreateVTKObjectMatrix(self.target_coord[:3], self.target_coord[3:])
1016 1035  
1017 1036 filename = os.path.join(inv_paths.OBJ_DIR, "aim.stl")
1018 1037  
... ... @@ -1023,7 +1042,7 @@ class Viewer(wx.Panel):
1023 1042  
1024 1043 # Transform the polydata
1025 1044 transform = vtk.vtkTransform()
1026   - transform.SetMatrix(m_img_vtk)
  1045 + transform.SetMatrix(self.m_img_vtk)
1027 1046 transformPD = vtk.vtkTransformPolyDataFilter()
1028 1047 transformPD.SetTransform(transform)
1029 1048 transformPD.SetInputConnection(reader.GetOutputPort())
... ... @@ -1071,7 +1090,7 @@ class Viewer(wx.Panel):
1071 1090 self.dummy_coil_actor.GetProperty().SetSpecularPower(10)
1072 1091 self.dummy_coil_actor.GetProperty().SetOpacity(.3)
1073 1092 self.dummy_coil_actor.SetVisibility(1)
1074   - self.dummy_coil_actor.SetUserMatrix(m_img_vtk)
  1093 + self.dummy_coil_actor.SetUserMatrix(self.m_img_vtk)
1075 1094  
1076 1095 self.ren.AddActor(self.dummy_coil_actor)
1077 1096  
... ... @@ -1424,8 +1443,9 @@ class Viewer(wx.Panel):
1424 1443 actor.GetProperty().SetLineWidth(5)
1425 1444 actor.AddPosition(0, 0, 0)
1426 1445 actor.SetScale(size)
1427   - actor.SetPosition(direction)
1428   - actor.SetOrientation(orientation)
  1446 +
  1447 + m_img_vtk = self.CreateVTKObjectMatrix(direction, orientation)
  1448 + actor.SetUserMatrix(m_img_vtk)
1429 1449  
1430 1450 return actor
1431 1451  
... ...
invesalius/gui/task_navigator.py
... ... @@ -359,7 +359,7 @@ class NeuronavigationPanel(wx.Panel):
359 359  
360 360 self.nav_status = False
361 361 self.tracker_fiducial_being_set = None
362   - self.current_coord = 0, 0, 0
  362 + self.current_coord = 0, 0, 0, None, None, None
363 363  
364 364 # Initialize list of buttons and numctrls for wx objects
365 365 self.btns_set_fiducial = [None, None, None, None, None, None]
... ... @@ -704,7 +704,7 @@ class NeuronavigationPanel(wx.Panel):
704 704 if self.btns_set_fiducial[n].GetValue():
705 705 coord = self.numctrls_fiducial[n][0].GetValue(),\
706 706 self.numctrls_fiducial[n][1].GetValue(),\
707   - self.numctrls_fiducial[n][2].GetValue(), 0, 0, 0
  707 + self.numctrls_fiducial[n][2].GetValue(), None, None, None
708 708  
709 709 Publisher.sendMessage('Set image fiducial', fiducial_name=fiducial_name, coord=coord[0:3])
710 710  
... ... @@ -1144,9 +1144,9 @@ class MarkersPanel(wx.Panel):
1144 1144 x : float = 0
1145 1145 y : float = 0
1146 1146 z : float = 0
1147   - alpha : float = 0
1148   - beta : float = 0
1149   - gamma : float = 0
  1147 + alpha : float = dataclasses.field(default = None)
  1148 + beta : float = dataclasses.field(default = None)
  1149 + gamma : float = dataclasses.field(default = None)
1150 1150 r : float = 0
1151 1151 g : float = 1
1152 1152 b : float = 0
... ... @@ -1161,7 +1161,7 @@ class MarkersPanel(wx.Panel):
1161 1161 # x, y, z, alpha, beta, gamma can be jointly accessed as coord
1162 1162 @property
1163 1163 def coord(self):
1164   - return list((self.x, self.y, self.z, self.alpha, self.beta, self.gamma),)
  1164 + return list((self.x, self.y, self.z, self.alpha, self.beta, self.gamma))
1165 1165  
1166 1166 @coord.setter
1167 1167 def coord(self, new_coord):
... ... @@ -1201,11 +1201,18 @@ class MarkersPanel(wx.Panel):
1201 1201 else:
1202 1202 res += ('%s\t' % str(getattr(self, field.name)))
1203 1203  
1204   - # Add world coordinates (in addition to the internal ones).
1205   - position_world, orientation_world = imagedata_utils.convert_invesalius_to_world(
1206   - position=[self.x, self.y, self.z],
1207   - orientation=[self.alpha, self.beta, self.gamma],
1208   - )
  1204 + if self.alpha is not None and self.beta is not None and self.gamma is not None:
  1205 + # Add world coordinates (in addition to the internal ones).
  1206 + position_world, orientation_world = imagedata_utils.convert_invesalius_to_world(
  1207 + position=[self.x, self.y, self.z],
  1208 + orientation=[self.alpha, self.beta, self.gamma],
  1209 + )
  1210 +
  1211 + else:
  1212 + position_world, orientation_world = imagedata_utils.convert_invesalius_to_world(
  1213 + position=[self.x, self.y, self.z],
  1214 + orientation=[0,0,0],
  1215 + )
1209 1216  
1210 1217 res += '\t'.join(map(lambda x: 'N/A' if x is None else str(x), (*position_world, *orientation_world)))
1211 1218 return res
... ... @@ -1215,8 +1222,10 @@ class MarkersPanel(wx.Panel):
1215 1222 properly formatted, might throw an exception and leave the object
1216 1223 in an inconsistent state."""
1217 1224 for field, str_val in zip(dataclasses.fields(self.__class__), inp_str.split('\t')):
1218   - if field.type is float:
  1225 + if field.type is float and str_val != 'None':
1219 1226 setattr(self, field.name, float(str_val))
  1227 + if field.type is float and str_val == 'None':
  1228 + setattr(self, field.name, None)
1220 1229 if field.type is int:
1221 1230 setattr(self, field.name, int(str_val))
1222 1231 if field.type is str:
... ... @@ -1253,8 +1262,7 @@ class MarkersPanel(wx.Panel):
1253 1262  
1254 1263 self.session = ses.Session()
1255 1264  
1256   - self.current_coord = 0, 0, 0, 0, 0, 0
1257   - self.current_angle = 0, 0, 0
  1265 + self.current_coord = 0, 0, 0, None, None, None
1258 1266 self.current_seed = 0, 0, 0
1259 1267 self.current_robot_target_matrix = [None] * 9
1260 1268 self.markers = []
... ... @@ -1264,6 +1272,7 @@ class MarkersPanel(wx.Panel):
1264 1272  
1265 1273 self.marker_colour = const.MARKER_COLOUR
1266 1274 self.marker_size = const.MARKER_SIZE
  1275 + self.arrow_marker_size = const.ARROW_MARKER_SIZE
1267 1276 self.current_session = 1
1268 1277  
1269 1278 # Change marker size
... ... @@ -1431,13 +1440,11 @@ class MarkersPanel(wx.Panel):
1431 1440 return list(itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS)))
1432 1441  
1433 1442 def UpdateCurrentCoord(self, position):
1434   - self.current_coord = position
1435   - #self.current_angle = pubsub_evt.data[1][3:]
  1443 + self.current_coord = list(position)
1436 1444  
1437 1445 def UpdateNavigationStatus(self, nav_status, vis_status):
1438 1446 if not nav_status:
1439   - sleep(0.5)
1440   - #self.current_coord[3:] = 0, 0, 0
  1447 + self.current_coord[3:] = None, None, None
1441 1448 self.nav_status = False
1442 1449 else:
1443 1450 self.nav_status = True
... ... @@ -1615,8 +1622,8 @@ class MarkersPanel(wx.Panel):
1615 1622 if marker.is_target:
1616 1623 self.__set_marker_as_target(len(self.markers) - 1)
1617 1624  
1618   - except:
1619   - wx.MessageBox(_("Invalid markers file."), _("InVesalius 3"))
  1625 + except Exception as e:
  1626 + wx.MessageBox(_("Invalid markers file."), _("InVesalius 3"))
1620 1627  
1621 1628 def OnMarkersVisibility(self, evt, ctrl):
1622 1629 if ctrl.GetValue():
... ... @@ -1681,10 +1688,18 @@ class MarkersPanel(wx.Panel):
1681 1688 new_robot_marker.robot_target_matrix = self.current_robot_target_matrix
1682 1689  
1683 1690 # Note that ball_id is zero-based, so we assign it len(self.markers) before the new marker is added
1684   - Publisher.sendMessage('Add marker', ball_id=len(self.markers),
1685   - size=new_marker.size,
1686   - colour=new_marker.colour,
1687   - coord=new_marker.coord[:3])
  1691 + if all([elem is not None for elem in new_marker.coord[3:]]):
  1692 + Publisher.sendMessage('Add arrow marker', arrow_id=len(self.markers),
  1693 + size=self.arrow_marker_size,
  1694 + color=new_marker.colour,
  1695 + coord=new_marker.coord)
  1696 + else:
  1697 + Publisher.sendMessage('Add marker', ball_id=len(self.markers),
  1698 + size=new_marker.size,
  1699 + colour=new_marker.colour,
  1700 + coord=new_marker.coord[:3])
  1701 +
  1702 +
1688 1703 self.markers.append(new_marker)
1689 1704 self.robot_markers.append(new_robot_marker)
1690 1705  
... ...