Commit 56f7a1283c671a4c09f79436ae308dde88cfb46a

Authored by tfmoraes
1 parent 295c16a0

Adapted camera positions to coronal and sagital cases, and added the option to flip image

invesalius/constants.py
... ... @@ -109,11 +109,11 @@ IMPORT_INTERVAL = [_("Keep all slices"), _("Skip 1 for each 2 slices"),
109 109 AXIAL_SLICE_CAM_POSITION = {"AXIAL":(0, 0, 1), "CORONAL":(0, -1, 0), "SAGITAL":(1, 0, 0)}
110 110 AXIAL_SLICE_CAM_VIEW_UP = {"AXIAL":(0, 1, 0), "CORONAL":(0, 0, 1), "SAGITAL":(0, 0, 1)}
111 111  
112   -SAGITAL_SLICE_CAM_POSITION = {"AXIAL":(0, 1, 0), "CORONAL":(1, 0, 0), "SAGITAL":(0, 0, -1)}
113   -SAGITAL_SLICE_CAM_VIEW_UP = {"AXIAL":(-1, 0, 0), "CORONAL":(0, 1, 0), "SAGITAL":(0, 1, 0)}
  112 +SAGITAL_SLICE_CAM_POSITION = {"AXIAL":(0, 0, 1), "CORONAL":(0, 1, 0), "SAGITAL":(-1, 0, 0)}
  113 +SAGITAL_SLICE_CAM_VIEW_UP = {"AXIAL":(0, -1, 0), "CORONAL":(0, 0, 1), "SAGITAL":(0, 0, 1)}
114 114  
115   -CORONAL_SLICE_CAM_POSITION = {"AXIAL":(0, 1, 0), "CORONAL":(0, 0, 1), "SAGITAL":(1, 0, 0)}
116   -CORONAL_SLICE_CAM_VIEW_UP = {"AXIAL":(0, 0, -1), "CORONAL":(0, 1, 0), "SAGITAL":(0, 1, 0)}
  115 +CORONAL_SLICE_CAM_POSITION = {"AXIAL":(0, 0, 1), "CORONAL":(0, 1, 0), "SAGITAL":(-1, 0, 0)}
  116 +CORONAL_SLICE_CAM_VIEW_UP = {"AXIAL":(0, -1, 0), "CORONAL":(0, 0, 1), "SAGITAL":(0, 0, 1)}
117 117  
118 118 SLICE_POSITION = {AXIAL:[AXIAL_SLICE_CAM_VIEW_UP, AXIAL_SLICE_CAM_POSITION],
119 119 SAGITAL:[SAGITAL_SLICE_CAM_VIEW_UP, SAGITAL_SLICE_CAM_POSITION],
... ... @@ -457,6 +457,9 @@ ID_DICOM_NETWORK] = [wx.NewId() for number in range(14)]
457 457 ID_ABOUT = wx.NewId()
458 458 ID_START = wx.NewId()
459 459  
  460 +ID_FLIP_X = wx.NewId()
  461 +ID_FLIP_Y = wx.NewId()
  462 +ID_FLIP_Z = wx.NewId()
460 463 #---------------------------------------------------------
461 464 STATE_DEFAULT = 1000
462 465 STATE_WL = 1001
... ...
invesalius/control.py
... ... @@ -432,6 +432,7 @@ class Controller():
432 432 header['glmax'])
433 433 proj.window = proj.threshold_range[1] - proj.threshold_range[0]
434 434 proj.level = (0.5 * (proj.threshold_range[1] + proj.threshold_range[0]))
  435 + proj.spacing = header['pixdim'][1:4]
435 436  
436 437 self.Slice = sl.Slice()
437 438 self.Slice.matrix = matrix
... ... @@ -495,8 +496,11 @@ class Controller():
495 496 if file_range != None and file_range[1] > file_range[0]:
496 497 filelist = filelist[file_range[0]:file_range[1] + 1]
497 498  
498   - print ">>>>>>>>>>>>>>>>>>",filelist
499 499 zspacing = dicom_group.zspacing * interval
  500 +
  501 + print "\n======================================="
  502 + print ">>>>>>>>>>>>>>>>>> zspacing", zspacing, interval
  503 + print "\n======================================="
500 504 size = dicom.image.size
501 505 bits = dicom.image.bits_allocad
502 506 sop_class_uid = dicom.acquisition.sop_class_uid
... ...
invesalius/data/imagedata_utils.py
... ... @@ -457,6 +457,9 @@ def dcm2memmap(files, slice_size, orientation):
457 457 matrix[:, n, :] = array
458 458 elif orientation == 'SAGITTAL':
459 459 array.shape = matrix.shape[0], matrix.shape[1]
  460 + # TODO: Verify if it's necessary to add the slices swapped only in
  461 + # sagittal rmi or only in # Rasiane's case or is necessary in all
  462 + # sagittal cases.
460 463 matrix[:, :, n] = array
461 464 else:
462 465 print array.shape, matrix.shape
... ...
invesalius/data/mask.py
... ... @@ -29,6 +29,8 @@ import vtk
29 29 import constants as const
30 30 import imagedata_utils as iu
31 31  
  32 +from wx.lib.pubsub import pub as Publisher
  33 +
32 34 class Mask():
33 35 general_index = -1
34 36 def __init__(self):
... ... @@ -43,6 +45,10 @@ class Mask():
43 45 self.is_shown = 1
44 46 self.edited_points = {}
45 47 self.was_edited = False
  48 + self.__bind_events()
  49 +
  50 + def __bind_events(self):
  51 + Publisher.subscribe(self.OnFlipVolume, 'Flip volume')
46 52  
47 53 def SavePlist(self, filename):
48 54 mask = {}
... ... @@ -79,6 +85,19 @@ class Mask():
79 85 path = os.path.join(dirpath, mask_file)
80 86 self._open_mask(path, tuple(shape))
81 87  
  88 + def OnFlipVolume(self, pubsub_evt):
  89 + axis = pubsub_evt.data
  90 + submatrix = self.matrix[1:, 1:, 1:]
  91 + if axis == 0:
  92 + submatrix[:] = submatrix[::-1]
  93 + self.matrix[1::, 0, 0] = self.matrix[:0:-1, 0, 0]
  94 + elif axis == 1:
  95 + submatrix[:] = submatrix[:, ::-1]
  96 + self.matrix[0, 1::, 0] = self.matrix[0, :0:-1, 0]
  97 + elif axis == 2:
  98 + submatrix[:] = submatrix[:, :, ::-1]
  99 + self.matrix[0, 0, 1::] = self.matrix[0, 0, :0:-1]
  100 +
82 101 def _save_mask(self, filename):
83 102 shutil.copyfile(self.temp_file, filename)
84 103  
... ...
invesalius/data/slice_.py
... ... @@ -132,6 +132,8 @@ class Slice(object):
132 132 Publisher.subscribe(self.OnRemoveMasks, 'Remove masks')
133 133 Publisher.subscribe(self.OnDuplicateMasks, 'Duplicate masks')
134 134 Publisher.subscribe(self.UpdateSlice3D,'Update slice 3D')
  135 +
  136 + Publisher.subscribe(self.OnFlipVolume, 'Flip volume')
135 137  
136 138 def GetMaxSliceNumber(self, orientation):
137 139 shape = self.matrix.shape
... ... @@ -703,15 +705,15 @@ class Slice(object):
703 705 cast.ClampOverflowOn()
704 706 cast.Update()
705 707  
706   - if (original_orientation == const.AXIAL):
707   - flip = vtk.vtkImageFlip()
708   - flip.SetInput(cast.GetOutput())
709   - flip.SetFilteredAxis(1)
710   - flip.FlipAboutOriginOn()
711   - flip.Update()
712   - widget.SetInput(flip.GetOutput())
713   - else:
714   - widget.SetInput(cast.GetOutput())
  708 + #if (original_orientation == const.AXIAL):
  709 + flip = vtk.vtkImageFlip()
  710 + flip.SetInput(cast.GetOutput())
  711 + flip.SetFilteredAxis(1)
  712 + flip.FlipAboutOriginOn()
  713 + flip.Update()
  714 + widget.SetInput(flip.GetOutput())
  715 + #else:
  716 + #widget.SetInput(cast.GetOutput())
715 717  
716 718 def UpdateSlice3D(self, pubsub_evt):
717 719 widget, orientation = pubsub_evt.data
... ... @@ -723,15 +725,15 @@ class Slice(object):
723 725 cast.ClampOverflowOn()
724 726 cast.Update()
725 727  
726   - if (original_orientation == const.AXIAL):
727   - flip = vtk.vtkImageFlip()
728   - flip.SetInput(cast.GetOutput())
729   - flip.SetFilteredAxis(1)
730   - flip.FlipAboutOriginOn()
731   - flip.Update()
732   - widget.SetInput(flip.GetOutput())
733   - else:
734   - widget.SetInput(cast.GetOutput())
  728 + #if (original_orientation == const.AXIAL):
  729 + flip = vtk.vtkImageFlip()
  730 + flip.SetInput(cast.GetOutput())
  731 + flip.SetFilteredAxis(1)
  732 + flip.FlipAboutOriginOn()
  733 + flip.Update()
  734 + widget.SetInput(flip.GetOutput())
  735 + #else:
  736 + #widget.SetInput(cast.GetOutput())
735 737  
736 738  
737 739  
... ... @@ -966,6 +968,18 @@ class Slice(object):
966 968 self.matrix = numpy.memmap(filename, shape=shape, dtype=dtype,
967 969 mode='r+')
968 970  
  971 + def OnFlipVolume(self, pubsub_evt):
  972 + axis = pubsub_evt.data
  973 + if axis == 0:
  974 + self.matrix[:] = self.matrix[::-1]
  975 + elif axis == 1:
  976 + self.matrix[:] = self.matrix[:, ::-1]
  977 + elif axis == 2:
  978 + self.matrix[:] = self.matrix[:, :, ::-1]
  979 +
  980 + for buffer_ in self.buffer_slices.values():
  981 + buffer_.discard_buffer()
  982 +
969 983 def OnExportMask(self, pubsub_evt):
970 984 #imagedata = self.current_mask.imagedata
971 985 imagedata = self.imagedata
... ...
invesalius/data/surface_process.py
... ... @@ -97,6 +97,8 @@ class SurfaceProcess(multiprocessing.Process):
97 97 flip.FlipAboutOriginOn()
98 98 flip.Update()
99 99  
  100 + image = flip.GetOutput()
  101 +
100 102 #filename = tempfile.mktemp(suffix='_%s.vti' % (self.pid))
101 103 #writer = vtk.vtkXMLImageDataWriter()
102 104 #writer.SetInput(mask_vtk)
... ... @@ -110,7 +112,8 @@ class SurfaceProcess(multiprocessing.Process):
110 112 #if self.mode == "CONTOUR":
111 113 #print "Contour"
112 114 contour = vtk.vtkContourFilter()
113   - contour.SetInput(flip.GetOutput())
  115 + contour.SetInput(image)
  116 + #contour.SetInput(flip.GetOutput())
114 117 if self.from_binary:
115 118 contour.SetValue(0, 127) # initial threshold
116 119 else:
... ...
invesalius/data/viewer_slice.py
... ... @@ -1311,7 +1311,7 @@ class Viewer(wx.Panel):
1311 1311 def __update_camera(self):
1312 1312 orientation = self.orientation
1313 1313 proj = project.Project()
1314   - orig_orien = 1 #proj.original_orientation
  1314 + orig_orien = proj.original_orientation
1315 1315  
1316 1316 self.cam.SetFocalPoint(0, 0, 0)
1317 1317 self.cam.SetViewUp(const.SLICE_POSITION[orig_orien][0][self.orientation])
... ...
invesalius/data/viewer_volume.py
... ... @@ -180,8 +180,6 @@ class Viewer(wx.Panel):
180 180  
181 181 Publisher.subscribe(self.RemoveVolume, 'Remove Volume')
182 182  
183   -
184   -
185 183 def SetStereoMode(self, pubsub_evt):
186 184 mode = pubsub_evt.data
187 185 ren_win = self.interactor.GetRenderWindow()
... ... @@ -210,7 +208,6 @@ class Viewer(wx.Panel):
210 208  
211 209 self.interactor.Render()
212 210  
213   -
214 211 def CreateBallReference(self):
215 212 self.ball_reference = vtk.vtkSphereSource()
216 213 self.ball_reference.SetRadius(5)
... ... @@ -271,7 +268,6 @@ class Viewer(wx.Panel):
271 268  
272 269 image = image.GetOutput()
273 270  
274   -
275 271 # write image file
276 272 if (filetype == const.FILETYPE_BMP):
277 273 writer = vtk.vtkBMPWriter()
... ... @@ -289,8 +285,6 @@ class Viewer(wx.Panel):
289 285 writer.SetFileName(filename)
290 286 writer.Write()
291 287 Publisher.sendMessage('End busy cursor')
292   -
293   -
294 288  
295 289 def OnCloseProject(self, pubsub_evt):
296 290 if self.raycasting_volume:
... ... @@ -844,8 +838,8 @@ class SlicePlane:
844 838 def Create(self):
845 839 plane_x = self.plane_x = vtk.vtkImagePlaneWidget()
846 840 plane_x.InteractionOff()
847   - Publisher.sendMessage('Input Image in the widget',
848   - (plane_x, 'SAGITAL'))
  841 + #Publisher.sendMessage('Input Image in the widget',
  842 + #(plane_x, 'SAGITAL'))
849 843 plane_x.SetPlaneOrientationToXAxes()
850 844 plane_x.TextureVisibilityOn()
851 845 plane_x.SetLeftButtonAction(0)
... ... @@ -856,8 +850,8 @@ class SlicePlane:
856 850  
857 851 plane_y = self.plane_y = vtk.vtkImagePlaneWidget()
858 852 plane_y.DisplayTextOff()
859   - Publisher.sendMessage('Input Image in the widget',
860   - (plane_y, 'CORONAL'))
  853 + #Publisher.sendMessage('Input Image in the widget',
  854 + #(plane_y, 'CORONAL'))
861 855 plane_y.SetPlaneOrientationToYAxes()
862 856 plane_y.TextureVisibilityOn()
863 857 plane_y.SetLeftButtonAction(0)
... ... @@ -869,8 +863,8 @@ class SlicePlane:
869 863  
870 864 plane_z = self.plane_z = vtk.vtkImagePlaneWidget()
871 865 plane_z.InteractionOff()
872   - Publisher.sendMessage('Input Image in the widget',
873   - (plane_z, 'AXIAL'))
  866 + #Publisher.sendMessage('Input Image in the widget',
  867 + #(plane_z, 'AXIAL'))
874 868 plane_z.SetPlaneOrientationToZAxes()
875 869 plane_z.TextureVisibilityOn()
876 870 plane_z.SetLeftButtonAction(0)
... ...
invesalius/data/volume.py
... ... @@ -85,6 +85,7 @@ class Volume():
85 85 self.plane = None
86 86 self.plane_on = False
87 87 self.volume = None
  88 + self.image = None
88 89 self.loaded_image = 0
89 90 self.__bind_events()
90 91  
... ... @@ -109,6 +110,8 @@ class Volume():
109 110  
110 111 Publisher.subscribe(self.ResetRayCasting, 'Reset Reaycasting')
111 112  
  113 + Publisher.subscribe(self.OnFlipVolume, 'Flip volume')
  114 +
112 115 def ResetRayCasting(self, pub_evt):
113 116 if self.exist:
114 117 self.exist = None
... ... @@ -171,6 +174,13 @@ class Volume():
171 174 colour = self.GetBackgroundColour()
172 175 Publisher.sendMessage('Change volume viewer background colour', colour)
173 176 Publisher.sendMessage('Change volume viewer gui colour', colour)
  177 +
  178 + def OnFlipVolume(self, pubsub_evt):
  179 + print "Flipping Volume"
  180 + self.loaded_image = False
  181 + del self.image
  182 + self.image = None
  183 + self.exist = None
174 184  
175 185 def __load_preset_config(self):
176 186 self.config = prj.Project().raycasting_preset
... ... @@ -461,19 +471,15 @@ class Volume():
461 471 return imagedata
462 472  
463 473 def LoadImage(self):
464   -
465   -
466 474 slice_data = slice_.Slice()
467 475 n_array = slice_data.matrix
468 476 spacing = slice_data.spacing
469 477 slice_number = 0
470 478 orientation = 'AXIAL'
471 479  
472   -
473 480 image = converters.to_vtk(n_array, spacing, slice_number, orientation)
474 481 self.image = image
475 482  
476   -
477 483 def LoadVolume(self):
478 484 proj = prj.Project()
479 485 #image = imagedata_utils.to_vtk(n_array, spacing, slice_number, orientation)
... ... @@ -491,19 +497,19 @@ class Volume():
491 497 else:
492 498 flip_image = False
493 499  
494   - if (flip_image):
495   - update_progress= vtk_utils.ShowProgress(2 + number_filters)
496   - # Flip original vtkImageData
497   - flip = vtk.vtkImageFlip()
498   - flip.SetInput(image)
499   - flip.SetFilteredAxis(1)
500   - flip.FlipAboutOriginOn()
501   - flip.AddObserver("ProgressEvent", lambda obj,evt:
502   - update_progress(flip, "Rendering..."))
503   - flip.Update()
504   - image = flip.GetOutput()
505   - else:
506   - update_progress= vtk_utils.ShowProgress(1 + number_filters)
  500 + #if (flip_image):
  501 + update_progress= vtk_utils.ShowProgress(2 + number_filters)
  502 + # Flip original vtkImageData
  503 + flip = vtk.vtkImageFlip()
  504 + flip.SetInput(image)
  505 + flip.SetFilteredAxis(1)
  506 + flip.FlipAboutOriginOn()
  507 + flip.AddObserver("ProgressEvent", lambda obj,evt:
  508 + update_progress(flip, "Rendering..."))
  509 + flip.Update()
  510 + image = flip.GetOutput()
  511 + #else:
  512 + #update_progress= vtk_utils.ShowProgress(1 + number_filters)
507 513  
508 514 scale = image.GetScalarRange()
509 515 self.scale = scale
... ...
invesalius/gui/dicom_preview_panel.py
... ... @@ -813,7 +813,12 @@ class SingleImagePreview(wx.Panel):
813 813  
814 814 ## Text related to slice position
815 815 value1 = STR_SPC %(dicom.image.spacing[2])
816   - value2 = STR_LOCAL %(dicom.image.position[2])
  816 + if dicom.image.orientation_label == 'AXIAL':
  817 + value2 = STR_LOCAL %(dicom.image.position[2])
  818 + elif dicom.image.orientation_label == 'CORONAL':
  819 + value2 = STR_LOCAL %(dicom.image.position[1])
  820 + elif dicom.image.orientation_label == 'SAGITTAL':
  821 + value2 = STR_LOCAL %(dicom.image.position[0])
817 822 value = "%s\n%s" %(value1, value2)
818 823 self.text_image_location.SetValue(value)
819 824  
... ...
invesalius/gui/frame.py
... ... @@ -339,6 +339,11 @@ class Frame(wx.Frame):
339 339 self.ShowPreferences()
340 340 elif id == const.ID_DICOM_NETWORK:
341 341 self.ShowRetrieveDicomPanel()
  342 + elif id in (const.ID_FLIP_X, const.ID_FLIP_Y, const.ID_FLIP_Z):
  343 + axis = {const.ID_FLIP_X: 2,
  344 + const.ID_FLIP_Y: 1,
  345 + const.ID_FLIP_Z: 0}[id]
  346 + self.FlipVolume(axis)
342 347  
343 348 def OnSize(self, evt):
344 349 """
... ... @@ -409,6 +414,10 @@ class Frame(wx.Frame):
409 414 """
410 415 Publisher.sendMessage('Show analyze dialog', True)
411 416  
  417 + def FlipVolume(self, axis):
  418 + Publisher.sendMessage('Flip volume', axis)
  419 + Publisher.sendMessage('Reload actual slice')
  420 +
412 421 # ------------------------------------------------------------------
413 422 # ------------------------------------------------------------------
414 423 # ------------------------------------------------------------------
... ... @@ -474,12 +483,23 @@ class MenuBar(wx.MenuBar):
474 483 #file_menu.AppendSeparator()
475 484 app(const.ID_EXIT, _("Exit"))
476 485  
477   - # EDIT
478   - #file_edit = wx.Menu()
479   - #app = file_edit.Append
  486 +
  487 + ############################### EDIT###############################
  488 + # Flip
  489 + flip_menu = wx.Menu()
  490 + app = flip_menu.Append
  491 + app(const.ID_FLIP_X, _("R <-> L"))
  492 + app(const.ID_FLIP_Y, _("A <-> P"))
  493 + app(const.ID_FLIP_Z, _("T <-> B"))
  494 +
  495 + file_edit = wx.Menu()
  496 + app = file_edit.Append
  497 + file_edit.AppendMenu(wx.NewId(), _('Flip'), flip_menu)
480 498 #app(wx.ID_UNDO, "Undo\tCtrl+Z")
481 499 #app(wx.ID_REDO, "Redo\tCtrl+Y")
482 500 #app(const.ID_EDIT_LIST, "Show Undo List...")
  501 + #################################################################
  502 +
483 503  
484 504 # VIEW
485 505 #view_tool_menu = wx.Menu()
... ... @@ -527,7 +547,7 @@ class MenuBar(wx.MenuBar):
527 547  
528 548 # Add all menus to menubar
529 549 self.Append(file_menu, _("File"))
530   - #self.Append(file_edit, "Edit")
  550 + self.Append(file_edit, "Edit")
531 551 #self.Append(view_menu, "View")
532 552 #self.Append(tools_menu, "Tools")
533 553 self.Append(options_menu, _("Options"))
... ...