Commit 3b8f8e1ff7464fd3e9c576eda3d24dae75511c4e
Committed by
GitHub
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
Showing
4 changed files
with
95 additions
and
60 deletions
Show diff stats
invesalius/constants.py
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 | ... | ... |