def OnSplitSurface(self, pubsub_evt): """ Create n new surfaces, based on the last selected surface, according to their connectivity. """ index = self.last_surface_index proj = prj.Project() surface = proj.surface_dict[index] index_list = [] new_polydata_list = pu.SplitDisconectedParts(surface.polydata) for polydata in new_polydata_list: index = self.CreateSurfaceFromPolydata(polydata) index_list.append(index) #self.ShowActor(index, True) Publisher.sendMessage('Show multiple surfaces', (index_list, True))
def CloseProject(self): Publisher.sendMessage('Set slice interaction style', style=const.STATE_DEFAULT) Publisher.sendMessage('Hide content panel') Publisher.sendMessage('Close project data') if self.img_type == 1: Publisher.sendMessage('Show import panel in frame') if self.img_type == 2: Publisher.sendMessage('Show import bitmap panel in frame') proj = prj.Project() proj.Close() session = ses.Session() session.CloseProject()
def _remove_measurements(self, indexes): for index in indexes: m, mr = self.measures.pop(index) try: mr.Remove() except AttributeError: # The is not being displayed pass prj.Project().RemoveMeasurement(index) if m.location == const.SURFACE: Publisher.sendMessage('Remove actors ' + str(m.location), actors=mr.GetActors()) Publisher.sendMessage('Redraw canvas') Publisher.sendMessage('Render volume viewer') session = ses.Session() session.ChangeProject()
def CreateBitmapProject(self, bmp_data, rec_data, matrix, matrix_filename): name_to_const = { "AXIAL": const.AXIAL, "CORONAL": const.CORONAL, "SAGITTAL": const.SAGITAL } name = rec_data[0] orientation = rec_data[1] sp_x = float(rec_data[2]) sp_y = float(rec_data[3]) sp_z = float(rec_data[4]) interval = int(rec_data[5]) bits = bmp_data.GetFirstPixelSize() sx, sy = size = bmp_data.GetFirstBitmapSize() proj = prj.Project() proj.name = name proj.modality = 'UNKNOWN' proj.SetAcquisitionModality(proj.modality) proj.matrix_shape = matrix.shape proj.matrix_dtype = matrix.dtype.name proj.matrix_filename = matrix_filename #proj.imagedata = imagedata #proj.dicom_sample = dicom proj.original_orientation =\ name_to_const[orientation.upper()] proj.window = float(matrix.max()) proj.level = float(matrix.max() / 4) proj.threshold_range = int(matrix.min()), int(matrix.max()) #const.THRESHOLD_RANGE = proj.threshold_range proj.spacing = self.Slice.spacing ###### session = ses.Session() filename = proj.name + ".inv3" filename = filename.replace("/", "") #Fix problem case other/Skull_DICOM dirpath = session.CreateProject(filename)
def OnDuplicate(self, surface_indexes): proj = prj.Project() surface_dict = proj.surface_dict for index in surface_indexes: original_surface = surface_dict[index] # compute copy name name = original_surface.name names_list = [surface_dict[i].name for i in surface_dict.keys()] new_name = utl.next_copy_name(name, names_list) # create new mask self.CreateSurfaceFromPolydata( polydata=original_surface.polydata, overwrite=False, name=new_name, colour=original_surface.colour, transparency=original_surface.transparency, volume=original_surface.volume, area=original_surface.area)
def CloseProject(self): Publisher.sendMessage('Enable style', style=const.STATE_DEFAULT) Publisher.sendMessage('Hide content panel') Publisher.sendMessage('Close project data') if self.img_type == 1: Publisher.sendMessage('Show import panel in frame') if self.img_type == 2: Publisher.sendMessage('Show import bitmap panel in frame') proj = prj.Project() proj.Close() session = ses.Session() session.CloseProject() Publisher.sendMessage('Update status text in GUI', label=_("Ready"))
def OnLinkExportSurface(self, evt=None): "OnLinkExportSurface" project = proj.Project() n_surface = 0 for index in project.surface_dict: if project.surface_dict[index].is_shown: n_surface += 1 if n_surface: if sys.platform == 'win32': project_name = project.name else: project_name = project.name+".stl" dlg = wx.FileDialog(None, _("Save 3D surface as..."), # title "", # last used directory project_name, # filename WILDCARD_SAVE_3D, wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) dlg.SetFilterIndex(3) # default is STL if dlg.ShowModal() == wx.ID_OK: filetype_index = dlg.GetFilterIndex() filetype = INDEX_TO_TYPE_3D[filetype_index] filename = dlg.GetPath() extension = INDEX_TO_EXTENSION[filetype_index] if sys.platform != 'win32': if filename.split(".")[-1] != extension: filename = filename + "."+ extension Publisher.sendMessage('Export surface to file', (filename, filetype)) else: dlg = wx.MessageDialog(None, _("You need to create a surface and make it ") + _("visible before exporting it."), 'InVesalius 3', wx.OK | wx.ICON_INFORMATION) try: dlg.ShowModal() finally: dlg.Destroy()
def CreateBallReference(self): MRAD = 3.0 proj = prj.Project() s = proj.spacing # The sphere's radius will be MRAD times bigger than the media of the # spacing values. r = (s[0] + s[1] + s[2]) / 3.0 * MRAD self.ball_reference = vtk.vtkSphereSource() self.ball_reference.SetRadius(r) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(self.ball_reference.GetOutputPort()) p = vtk.vtkProperty() p.SetColor(1, 0, 0) self.ball_actor = vtk.vtkActor() self.ball_actor.SetMapper(mapper) self.ball_actor.SetProperty(p)
def start_new_inv_instance(self, image, name, spacing, modality, orientation, window_width, window_level): p = prj.Project() project_folder = tempfile.mkdtemp() p.create_project_file(name, spacing, modality, orientation, window_width, window_level, image, folder=project_folder) err_msg = '' try: sp = subprocess.Popen([sys.executable, sys.argv[0], '--import-folder', project_folder], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=os.getcwd()) except Exception as err: err_msg = str(err) else: try: if sp.wait(2): err_msg = sp.stderr.read().decode('utf8') sp.terminate() except subprocess.TimeoutExpired: pass if err_msg: dialog.MessageBox(None, "It was not possible to launch new instance of InVesalius3 dsfa dfdsfa sdfas fdsaf asdfasf dsaa", err_msg)
def LoadImagedataInfo(self): proj = prj.Project() thresh_modes = proj.threshold_modes.keys() thresh_modes = sorted(thresh_modes) default_threshold = const.THRESHOLD_PRESETS_INDEX if proj.mask_dict: keys = proj.mask_dict.keys() last = max(keys) (a, b) = proj.mask_dict[last].threshold_range default_threshold = [a, b] min_ = proj.threshold_range[0] max_ = proj.threshold_range[1] if default_threshold[0] < min_: default_threshold[0] = min_ if default_threshold[1] > max_: default_threshold[1] = max_ [a, b] = default_threshold default_threshold = (a, b) Publisher.sendMessage('Set threshold modes', (thresh_modes, default_threshold))
def CreateOtherProject(self, name, matrix, matrix_filename): name_to_const = { "AXIAL": const.AXIAL, "CORONAL": const.CORONAL, "SAGITTAL": const.SAGITAL } proj = prj.Project() proj.name = name proj.modality = 'MRI' proj.SetAcquisitionModality('MRI') proj.matrix_shape = matrix.shape proj.matrix_dtype = matrix.dtype.name proj.matrix_filename = matrix_filename # Orientation must be CORONAL in order to as_closes_canonical and # swap axis in img2memmap to work in a standardized way. # TODO: Create standard import image for all acquisition orientations orientation = 'CORONAL' proj.original_orientation =\ name_to_const[orientation] proj.window = self.Slice.window_width proj.level = self.Slice.window_level proj.threshold_range = int(matrix.min()), int(matrix.max()) proj.spacing = self.Slice.spacing # TODO: Check that this is needed with the new way of using affine # now the affine should be at least the identity(4) and never None if self.Slice.affine is not None: proj.affine = self.Slice.affine.tolist() ###### session = ses.Session() filename = proj.name + ".inv3" filename = filename.replace("/", "") # Fix problem case other/Skull_DICOM dirpath = session.CreateProject(filename)
def OnInsertLinearMeasurePoint(self, obj, evt): x, y = self.interactor.GetEventPosition() self.measure_picker.Pick(x, y, 0, self.ren) x, y, z = self.measure_picker.GetPickPosition() proj = prj.Project() radius = min(proj.spacing) * PROP_MEASURE if self.measure_picker.GetActor(): # if not self.measures or self.measures[-1].IsComplete(): # m = measures.LinearMeasure(self.ren) # m.AddPoint(x, y, z) # self.measures.append(m) # else: # m = self.measures[-1] # m.AddPoint(x, y, z) # if m.IsComplete(): # Publisher.sendMessage("Add measure to list", # (u"3D", _(u"%.3f mm" % m.GetValue()))) Publisher.sendMessage( "Add measurement point", ((x, y, z), const.LINEAR, const.SURFACE, radius)) self.interactor.Render()
def OpenProject(self, filepath): Publisher.sendMessage('Begin busy cursor') path = os.path.abspath(filepath) proj = prj.Project() proj.OpenPlistProject(path) proj.SetAcquisitionModality(proj.modality) self.Slice = sl.Slice() self.Slice._open_image_matrix(proj.matrix_filename, tuple(proj.matrix_shape), proj.matrix_dtype) self.Slice.window_level = proj.level self.Slice.window_width = proj.window Publisher.sendMessage('Update threshold limits list', proj.threshold_range) self.LoadProject() session = ses.Session() session.OpenProject(filepath) Publisher.sendMessage("Enable state project", True)
def CreateBallReference(self): """ Red sphere on volume visualization to reference center of cross in slice planes. The sphere's radius will be scale times bigger than the average of image spacing values. """ scale = 3.0 proj = prj.Project() s = proj.spacing r = (s[0] + s[1] + s[2]) / 3.0 * scale ball_source = vtk.vtkSphereSource() ball_source.SetRadius(r) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(ball_source.GetOutputPort()) self.ball_actor = vtk.vtkActor() self.ball_actor.SetMapper(mapper) self.ball_actor.GetProperty().SetColor(1, 0, 0) self.ren.AddActor(self.ball_actor)
def OnRemove(self, surface_indexes): proj = prj.Project() old_dict = self.actors_dict new_dict = {} if surface_indexes: for index in surface_indexes: proj.RemoveSurface(index) if index in old_dict: actor = old_dict[index] for i in old_dict: if i < index: new_dict[i] = old_dict[i] if i > index: new_dict[i-1] = old_dict[i] old_dict = new_dict Publisher.sendMessage('Remove surface actor from viewer', actor=actor) self.actors_dict = new_dict if self.last_surface_index in surface_indexes: if self.actors_dict: self.last_surface_index = 0 else: self.last_surface_index = None
def CreateAnalyzeProject(self, imagedata): header = imagedata.get_header() proj = prj.Project() proj.imagedata = None proj.name = _("Untitled") proj.SetAcquisitionModality("MRI") #TODO: Verify if all Analyse are in AXIAL orientation # To get Z, X, Y (used by InVesaliu), not X, Y, Z matrix, matrix_filename = image_utils.analyze2mmap(imagedata) if header['orient'] == 0: proj.original_orientation = const.AXIAL elif header['orient'] == 1: proj.original_orientation = const.CORONAL elif header['orient'] == 2: proj.original_orientation = const.SAGITAL else: proj.original_orientation = const.SAGITAL proj.threshold_range = (int(header['glmin']), int(header['glmax'])) proj.window = proj.threshold_range[1] - proj.threshold_range[0] proj.level = (0.5 * (proj.threshold_range[1] + proj.threshold_range[0])) proj.spacing = header['pixdim'][1:4] proj.matrix_shape = matrix.shape self.Slice = sl.Slice() self.Slice.matrix = matrix self.Slice.matrix_filename = matrix_filename self.Slice.window_level = proj.level self.Slice.window_width = proj.window self.Slice.spacing = header.get_zooms()[:3] Publisher.sendMessage('Update threshold limits list', proj.threshold_range)
def OnRemove(self, pubsub_evt): selected_items = pubsub_evt.data proj = prj.Project() old_dict = self.actors_dict new_dict = {} if selected_items: for index in selected_items: proj.RemoveSurface(index) actor = old_dict[index] for i in old_dict: if i < index: new_dict[i] = old_dict[i] if i > index: new_dict[i-1] = old_dict[i] old_dict = new_dict Publisher.sendMessage('Remove surface actor from viewer', actor) self.actors_dict = new_dict if self.last_surface_index in selected_items: if self.actors_dict: self.last_surface_index = 0 else: self.last_surface_index = None
def OnLinkExportMask(self, evt=None): project = proj.Project() if sys.platform == 'win32': project_name = project.name else: project_name = project.name + ".vti" dlg = wx.FileDialog( None, "Save mask as...", # title "", # last used directory project_name, # filename WILDCARD_SAVE_MASK, wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) dlg.SetFilterIndex(0) # default is VTI if dlg.ShowModal() == wx.ID_OK: filename = dlg.GetPath() extension = "vti" if sys.platform != 'win32': if filename.split(".")[-1] != extension: filename = filename + "." + extension filetype = const.FILETYPE_IMAGEDATA Publisher.sendMessage('Export mask to file', (filename, filetype))
def create_project_from_matrix(self, name, matrix, orientation="AXIAL", spacing=(1.0, 1.0, 1.0), modality="CT", window_width=None, window_level=None, new_instance=False): """ Creates a new project from a Numpy 3D array. name: Name of the project. matrix: A Numpy 3D array. It only works with int16 arrays. spacing: The spacing between the center of the voxels in X, Y and Z direction. modality: Imaging modality. """ if window_width is None: window_width = (matrix.max() - matrix.min()) if window_level is None: window_level = (matrix.max() + matrix.min()) // 2 window_width = int(window_width) window_level = int(window_level) name_to_const = { "AXIAL": const.AXIAL, "CORONAL": const.CORONAL, "SAGITTAL": const.SAGITAL } if new_instance: self.start_new_inv_instance(matrix, name, spacing, modality, name_to_const[orientation], window_width, window_level) else: # Verifying if there is a project open s = ses.Session() if s.IsOpen(): Publisher.sendMessage('Close Project') Publisher.sendMessage('Disconnect tracker') # Check if user really closed the project, if not, stop project creation if s.IsOpen(): return mmap_matrix = image_utils.array2memmap(matrix) self.Slice = sl.Slice() self.Slice.matrix = mmap_matrix self.Slice.matrix_filename = mmap_matrix.filename self.Slice.spacing = spacing self.Slice.window_width = window_width self.Slice.window_level = window_level proj = prj.Project() proj.name = name proj.modality = modality proj.SetAcquisitionModality(modality) proj.matrix_shape = matrix.shape proj.matrix_dtype = matrix.dtype.name proj.matrix_filename = self.Slice.matrix_filename proj.window = window_width proj.level = window_level proj.original_orientation =\ name_to_const[orientation] proj.threshold_range = int(matrix.min()), int(matrix.max()) proj.spacing = self.Slice.spacing Publisher.sendMessage('Update threshold limits list', threshold_range=proj.threshold_range) ###### session = ses.Session() filename = proj.name + ".inv3" filename = filename.replace("/", "") dirpath = session.CreateProject(filename) self.LoadProject() Publisher.sendMessage("Enable state project", state=True)
def SetBitmapSpacing(self, pubsub_evt): proj = prj.Project() proj.spacing = pubsub_evt.data
def LoadProject(self): proj = prj.Project() const.THRESHOLD_OUTVALUE = proj.threshold_range[0] const.THRESHOLD_INVALUE = proj.threshold_range[1] const.THRESHOLD_RANGE = proj.threshold_modes[_("Bone")] const.WINDOW_LEVEL[_('Default')] = (proj.window, proj.level) const.WINDOW_LEVEL[_('Manual')] = (proj.window, proj.level) self.Slice = sl.Slice() self.Slice.spacing = proj.spacing Publisher.sendMessage('Load slice to viewer', mask_dict=proj.mask_dict) Publisher.sendMessage('Load slice plane') Publisher.sendMessage('Bright and contrast adjustment image', window=proj.window, level=proj.level) Publisher.sendMessage('Update window level value', window=proj.window, level=proj.level) Publisher.sendMessage('Set project name', proj_name=proj.name) Publisher.sendMessage('Load surface dict', surface_dict=proj.surface_dict) Publisher.sendMessage('Hide surface items', surface_dict=proj.surface_dict) self.LoadImagedataInfo() # TODO: where do we insert this <<<? Publisher.sendMessage('Show content panel') Publisher.sendMessage('Update AUI') if len(proj.mask_dict): mask_index = len(proj.mask_dict) - 1 for m in proj.mask_dict.values(): Publisher.sendMessage('Add mask', mask=m) if m.is_shown: self.Slice.current_mask = proj.mask_dict[mask_index] Publisher.sendMessage('Show mask', index=m.index, value=True) Publisher.sendMessage('Change mask selected', index=m.index) else: mask_name = const.MASK_NAME_PATTERN % (1, ) if proj.modality != "UNKNOWN": thresh = const.THRESHOLD_RANGE else: thresh = proj.threshold_range colour = const.MASK_COLOUR[0] Publisher.sendMessage('Create new mask', mask_name=mask_name, thresh=thresh, colour=colour) Publisher.sendMessage('Load measurement dict', measurement_dict=proj.measurement_dict, spacing=self.Slice.spacing) Publisher.sendMessage(('Set scroll position', 'AXIAL'), index=proj.matrix_shape[0] / 2) Publisher.sendMessage(('Set scroll position', 'SAGITAL'), index=proj.matrix_shape[1] / 2) Publisher.sendMessage(('Set scroll position', 'CORONAL'), index=proj.matrix_shape[2] / 2) Publisher.sendMessage('End busy cursor')
def __load_preset_config(self): self.config = prj.Project().raycasting_preset
def SetBitmapSpacing(self, spacing): proj = prj.Project() proj.spacing = spacing
def ShowBooleanOpDialog(self): dlg = dialogs.MaskBooleanDialog(prj.Project().mask_dict) dlg.Show()
def OnChangeSurfaceName(self, index, name): proj = prj.Project() proj.surface_dict[index].name = name
def _add_point(self, pubsub_evt): position = pubsub_evt.data[0] type = pubsub_evt.data[1] # Linear or Angular location = pubsub_evt.data[2] # 3D, AXIAL, SAGITAL, CORONAL if location == const.SURFACE: slice_number = 0 try: radius = pubsub_evt.data[3] except IndexError: radius = const.PROP_MEASURE else: try: slice_number = pubsub_evt.data[3] except IndexError: slice_number = 0 try: radius = pubsub_evt.data[4] except IndexError: radius = const.PROP_MEASURE to_remove = False if self.current is None: to_create = True elif self.current[0].location != location: to_create = True to_remove = True elif self.current[0].slice_number != slice_number: to_create = True to_remove = True else: to_create = False if to_create: m = Measurement() m.index = len(self.measures) m.location = location m.slice_number = slice_number m.type = type representation = CirclePointRepresentation(m.colour, radius) if type == const.LINEAR: mr = LinearMeasure(m.colour, representation) else: mr = AngularMeasure(m.colour, representation) if to_remove: # actors = self.current[1].GetActors() # slice_number = self.current[0].slice_number # Publisher.sendMessage(('Remove actors ' + str(self.current[0].location)), # (actors, slice_number)) self.measures.pop()[1].Remove() if self.current[0].location == const.SURFACE: Publisher.sendMessage('Render volume viewer') else: Publisher.sendMessage('Redraw canvas') session = ses.Session() session.ChangeProject() self.current = (m, mr) mr = self.current[1] m = self.current[0] x, y, z = position actors = mr.AddPoint(x, y, z) m.points.append(position) if m.location == const.SURFACE: Publisher.sendMessage("Add actors " + str(location), (actors, m.slice_number)) if self.current not in self.measures: self.measures.append(self.current) if mr.IsComplete(): index = prj.Project().AddMeasurement(m) #m.index = index # already done in proj name = m.name colour = m.colour m.value = mr.GetValue() type_ = TYPE[type] location = LOCATION[location] if type == const.LINEAR: value = u"%.3f mm" % m.value else: value = u"%.3f°" % m.value msg = 'Update measurement info in GUI', Publisher.sendMessage( msg, (index, name, colour, location, type_, value)) self.current = None
def LoadVolume(self): proj = prj.Project() #image = imagedata_utils.to_vtk(n_array, spacing, slice_number, orientation) if not self.loaded_image: self.LoadImage() self.loaded_image = 1 image = self.image number_filters = len(self.config['convolutionFilters']) if (prj.Project().original_orientation == const.AXIAL): flip_image = True else: flip_image = False #if (flip_image): update_progress = vtk_utils.ShowProgress(2 + number_filters) # Flip original vtkImageData flip = vtk.vtkImageFlip() flip.SetInputData(image) flip.SetFilteredAxis(1) flip.FlipAboutOriginOn() # flip.ReleaseDataFlagOn() flip_ref = weakref.ref(flip) flip_ref().AddObserver( "ProgressEvent", lambda obj, evt: update_progress(flip_ref(), "Rendering...")) flip.Update() image = flip.GetOutput() scale = image.GetScalarRange() self.scale = scale cast = vtk.vtkImageShiftScale() cast.SetInputData(image) cast.SetShift(abs(scale[0])) cast.SetOutputScalarTypeToUnsignedShort() # cast.ReleaseDataFlagOn() cast_ref = weakref.ref(cast) cast_ref().AddObserver( "ProgressEvent", lambda obj, evt: update_progress(cast_ref(), "Rendering...")) cast.Update() image2 = cast self.imagedata = image2 if self.config['advancedCLUT']: self.Create16bColorTable(scale) self.CreateOpacityTable(scale) else: self.Create8bColorTable(scale) self.Create8bOpacityTable(scale) image2 = self.ApplyConvolution(image2.GetOutput(), update_progress) self.final_imagedata = image2 # Changed the vtkVolumeRayCast to vtkFixedPointVolumeRayCastMapper # because it's faster and the image is better # TODO: To test if it's true. if const.TYPE_RAYCASTING_MAPPER: volume_mapper = vtk.vtkVolumeRayCastMapper() #volume_mapper.AutoAdjustSampleDistancesOff() #volume_mapper.SetInput(image2) #volume_mapper.SetVolumeRayCastFunction(composite_function) #volume_mapper.SetGradientEstimator(gradientEstimator) volume_mapper.IntermixIntersectingGeometryOn() self.volume_mapper = volume_mapper else: if int(ses.Session().rendering) == 0: volume_mapper = vtk.vtkFixedPointVolumeRayCastMapper() #volume_mapper.AutoAdjustSampleDistancesOff() self.volume_mapper = volume_mapper volume_mapper.IntermixIntersectingGeometryOn() else: volume_mapper = vtk.vtkGPUVolumeRayCastMapper() volume_mapper.UseJitteringOn() self.volume_mapper = volume_mapper self.SetTypeRaycasting() volume_mapper.SetInputData(image2) # TODO: Look to this #volume_mapper_hw = vtk.vtkVolumeTextureMapper3D() #volume_mapper_hw.SetInput(image2) #Cut Plane #CutPlane(image2, volume_mapper) #self.color_transfer = color_transfer volume_properties = vtk.vtkVolumeProperty() #volume_properties.IndependentComponentsOn() volume_properties.SetInterpolationTypeToLinear() volume_properties.SetColor(self.color_transfer) try: volume_properties.SetScalarOpacity(self.opacity_transfer_func) except NameError: pass if not self.volume_mapper.IsA("vtkGPUVolumeRayCastMapper"): # Using these lines to improve the raycasting quality. These values # seems related to the distance from ray from raycasting. # TODO: Need to see values that improve the quality and don't decrease # the performance. 2.0 seems to be a good value to pix_diag pix_diag = 2.0 volume_mapper.SetImageSampleDistance(0.25) volume_mapper.SetSampleDistance(pix_diag / 5.0) volume_properties.SetScalarOpacityUnitDistance(pix_diag) self.volume_properties = volume_properties self.SetShading() volume = vtk.vtkVolume() volume.SetMapper(volume_mapper) volume.SetProperty(volume_properties) self.volume = volume colour = self.GetBackgroundColour() self.exist = 1 if self.plane: self.plane.SetVolumeMapper(volume_mapper) Publisher.sendMessage('Load volume into viewer', volume=volume, colour=colour, ww=self.ww, wl=self.wl) del flip del cast
def CreateSurfaceFromPolydata(self, polydata, overwrite=False, name=None, colour=None, transparency=None, volume=None, area=None): normals = vtk.vtkPolyDataNormals() normals.SetInputData(polydata) normals.SetFeatureAngle(80) normals.AutoOrientNormalsOn() normals.Update() mapper = vtk.vtkPolyDataMapper() mapper.SetInputData(normals.GetOutput()) mapper.ScalarVisibilityOff() mapper.ImmediateModeRenderingOn() # improve performance actor = vtk.vtkActor() actor.SetMapper(mapper) if overwrite: surface = Surface(index=self.last_surface_index) else: surface = Surface() if not colour: surface.colour = random.choice(const.SURFACE_COLOUR) else: surface.colour = colour surface.polydata = polydata if transparency: surface.transparency = transparency if name: surface.name = name # Append surface into Project.surface_dict proj = prj.Project() if overwrite: proj.ChangeSurface(surface) else: index = proj.AddSurface(surface) surface.index = index self.last_surface_index = index # Set actor colour and transparency actor.GetProperty().SetColor(surface.colour) actor.GetProperty().SetOpacity(1 - surface.transparency) self.actors_dict[surface.index] = actor session = ses.Session() session.ChangeProject() # The following lines have to be here, otherwise all volumes disappear if not volume or not area: triangle_filter = vtk.vtkTriangleFilter() triangle_filter.SetInputData(polydata) triangle_filter.Update() measured_polydata = vtk.vtkMassProperties() measured_polydata.SetInputConnection( triangle_filter.GetOutputPort()) measured_polydata.Update() volume = measured_polydata.GetVolume() area = measured_polydata.GetSurfaceArea() surface.volume = volume surface.area = area print(">>>>", surface.volume) else: surface.volume = volume surface.area = area self.last_surface_index = surface.index Publisher.sendMessage('Load surface actor into viewer', actor=actor) Publisher.sendMessage('Update surface info in GUI', surface=surface) return surface.index
def OnLinkExportSurface(self, evt=None): "OnLinkExportSurface" project = proj.Project() n_surface = 0 for index in project.surface_dict: if project.surface_dict[index].is_shown: n_surface += 1 if n_surface: if sys.platform == 'win32': project_name = project.name else: project_name = project.name + ".stl" session = ses.Session() last_directory = session.get('paths', 'last_directory_3d_surface', '') dlg = wx.FileDialog( None, _("Save 3D surface as..."), # title last_directory, # last used directory project_name, # filename WILDCARD_SAVE_3D, wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) dlg.SetFilterIndex(3) # default is STL if dlg.ShowModal() == wx.ID_OK: filetype_index = dlg.GetFilterIndex() filetype = INDEX_TO_TYPE_3D[filetype_index] filename = dlg.GetPath() extension = INDEX_TO_EXTENSION[filetype_index] if sys.platform != 'win32': if filename.split(".")[-1] != extension: filename = filename + "." + extension if filename: session['paths'][ 'last_directory_3d_surface'] = os.path.split( filename)[0] session.WriteSessionFile() Publisher.sendMessage('Export surface to file', filename=filename, filetype=filetype) if not os.path.exists(filename): dlg = wx.MessageDialog( None, _("It was not possible to save the surface."), _("Error saving surface"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() else: dlg = wx.MessageDialog( None, _("You need to create a surface and make it ") + _("visible before exporting it."), 'InVesalius 3', wx.OK | wx.ICON_INFORMATION) try: dlg.ShowModal() finally: dlg.Destroy()
def AddNewActor(self, slice_, mask, surface_parameters): """ Create surface actor, save into project and send it to viewer. """ matrix = slice_.matrix filename_img = slice_.matrix_filename spacing = slice_.spacing algorithm = surface_parameters['method']['algorithm'] options = surface_parameters['method']['options'] surface_name = surface_parameters['options']['name'] quality = surface_parameters['options']['quality'] fill_holes = surface_parameters['options']['fill'] keep_largest = surface_parameters['options']['keep_largest'] mode = 'CONTOUR' # 'GRAYSCALE' min_value, max_value = mask.threshold_range colour = mask.colour[:3] try: overwrite = surface_parameters['options']['overwrite'] except KeyError: overwrite = False mask.matrix.flush() if quality in const.SURFACE_QUALITY.keys(): imagedata_resolution = const.SURFACE_QUALITY[quality][0] smooth_iterations = const.SURFACE_QUALITY[quality][1] smooth_relaxation_factor = const.SURFACE_QUALITY[quality][2] decimate_reduction = const.SURFACE_QUALITY[quality][3] #if imagedata_resolution: #imagedata = iu.ResampleImage3D(imagedata, imagedata_resolution) pipeline_size = 4 if decimate_reduction: pipeline_size += 1 if (smooth_iterations and smooth_relaxation_factor): pipeline_size += 1 if fill_holes: pipeline_size += 1 if keep_largest: pipeline_size += 1 ## Update progress value in GUI UpdateProgress = vu.ShowProgress(pipeline_size) UpdateProgress(0, _("Creating 3D surface...")) language = ses.Session().language if (prj.Project().original_orientation == const.CORONAL): flip_image = False else: flip_image = True n_processors = multiprocessing.cpu_count() pipe_in, pipe_out = multiprocessing.Pipe() o_piece = 1 piece_size = 2000 n_pieces = int(round(matrix.shape[0] / piece_size + 0.5, 0)) q_in = multiprocessing.Queue() q_out = multiprocessing.Queue() p = [] for i in range(n_processors): sp = surface_process.SurfaceProcess( pipe_in, filename_img, matrix.shape, matrix.dtype, mask.temp_file, mask.matrix.shape, mask.matrix.dtype, spacing, mode, min_value, max_value, decimate_reduction, smooth_relaxation_factor, smooth_iterations, language, flip_image, q_in, q_out, algorithm != 'Default', algorithm, imagedata_resolution) p.append(sp) sp.start() for i in range(n_pieces): init = i * piece_size end = init + piece_size + o_piece roi = slice(init, end) q_in.put(roi) print("new_piece", roi) for i in p: q_in.put(None) none_count = 1 while 1: msg = pipe_out.recv() if (msg is None): none_count += 1 else: UpdateProgress(msg[0] / (n_pieces * pipeline_size), msg[1]) if none_count > n_pieces: break polydata_append = vtk.vtkAppendPolyData() # polydata_append.ReleaseDataFlagOn() t = n_pieces while t: filename_polydata = q_out.get() reader = vtk.vtkXMLPolyDataReader() reader.SetFileName(filename_polydata) # reader.ReleaseDataFlagOn() reader.Update() # reader.GetOutput().ReleaseDataFlagOn() polydata = reader.GetOutput() # polydata.SetSource(None) polydata_append.AddInputData(polydata) del reader del polydata t -= 1 polydata_append.Update() # polydata_append.GetOutput().ReleaseDataFlagOn() polydata = polydata_append.GetOutput() #polydata.Register(None) # polydata.SetSource(None) del polydata_append if algorithm == 'ca_smoothing': normals = vtk.vtkPolyDataNormals() normals_ref = weakref.ref(normals) normals_ref().AddObserver( "ProgressEvent", lambda obj, evt: UpdateProgress( normals_ref(), _("Creating 3D surface..."))) normals.SetInputData(polydata) # normals.ReleaseDataFlagOn() #normals.SetFeatureAngle(80) #normals.AutoOrientNormalsOn() normals.ComputeCellNormalsOn() # normals.GetOutput().ReleaseDataFlagOn() normals.Update() del polydata polydata = normals.GetOutput() # polydata.SetSource(None) del normals clean = vtk.vtkCleanPolyData() # clean.ReleaseDataFlagOn() # clean.GetOutput().ReleaseDataFlagOn() clean_ref = weakref.ref(clean) clean_ref().AddObserver( "ProgressEvent", lambda obj, evt: UpdateProgress( clean_ref(), _("Creating 3D surface..."))) clean.SetInputData(polydata) clean.PointMergingOn() clean.Update() del polydata polydata = clean.GetOutput() # polydata.SetSource(None) del clean # try: # polydata.BuildLinks() # except TypeError: # polydata.BuildLinks(0) # polydata = ca_smoothing.ca_smoothing(polydata, options['angle'], # options['max distance'], # options['min weight'], # options['steps']) mesh = cy_mesh.Mesh(polydata) cy_mesh.ca_smoothing(mesh, options['angle'], options['max distance'], options['min weight'], options['steps']) # polydata = mesh.to_vtk() # polydata.SetSource(None) # polydata.DebugOn() else: #smoother = vtk.vtkWindowedSincPolyDataFilter() smoother = vtk.vtkSmoothPolyDataFilter() smoother_ref = weakref.ref(smoother) smoother_ref().AddObserver( "ProgressEvent", lambda obj, evt: UpdateProgress( smoother_ref(), _("Creating 3D surface..."))) smoother.SetInputData(polydata) smoother.SetNumberOfIterations(smooth_iterations) smoother.SetRelaxationFactor(smooth_relaxation_factor) smoother.SetFeatureAngle(80) #smoother.SetEdgeAngle(90.0) #smoother.SetPassBand(0.1) smoother.BoundarySmoothingOn() smoother.FeatureEdgeSmoothingOn() #smoother.NormalizeCoordinatesOn() #smoother.NonManifoldSmoothingOn() # smoother.ReleaseDataFlagOn() # smoother.GetOutput().ReleaseDataFlagOn() smoother.Update() del polydata polydata = smoother.GetOutput() #polydata.Register(None) # polydata.SetSource(None) del smoother if decimate_reduction: print("Decimating", decimate_reduction) decimation = vtk.vtkQuadricDecimation() # decimation.ReleaseDataFlagOn() decimation.SetInputData(polydata) decimation.SetTargetReduction(decimate_reduction) decimation_ref = weakref.ref(decimation) decimation_ref().AddObserver( "ProgressEvent", lambda obj, evt: UpdateProgress( decimation_ref(), _("Creating 3D surface..."))) #decimation.PreserveTopologyOn() #decimation.SplittingOff() #decimation.BoundaryVertexDeletionOff() # decimation.GetOutput().ReleaseDataFlagOn() decimation.Update() del polydata polydata = decimation.GetOutput() #polydata.Register(None) # polydata.SetSource(None) del decimation #to_measure.Register(None) # to_measure.SetSource(None) if keep_largest: conn = vtk.vtkPolyDataConnectivityFilter() conn.SetInputData(polydata) conn.SetExtractionModeToLargestRegion() conn_ref = weakref.ref(conn) conn_ref().AddObserver( "ProgressEvent", lambda obj, evt: UpdateProgress( conn_ref(), _("Creating 3D surface..."))) conn.Update() # conn.GetOutput().ReleaseDataFlagOn() del polydata polydata = conn.GetOutput() #polydata.Register(None) # polydata.SetSource(None) del conn #Filter used to detect and fill holes. Only fill boundary edges holes. #TODO: Hey! This piece of code is the same from #polydata_utils.FillSurfaceHole, we need to review this. if fill_holes: filled_polydata = vtk.vtkFillHolesFilter() # filled_polydata.ReleaseDataFlagOn() filled_polydata.SetInputData(polydata) filled_polydata.SetHoleSize(300) filled_polydata_ref = weakref.ref(filled_polydata) filled_polydata_ref().AddObserver( "ProgressEvent", lambda obj, evt: UpdateProgress( filled_polydata_ref(), _("Creating 3D surface..."))) filled_polydata.Update() # filled_polydata.GetOutput().ReleaseDataFlagOn() del polydata polydata = filled_polydata.GetOutput() #polydata.Register(None) # polydata.SetSource(None) # polydata.DebugOn() del filled_polydata to_measure = polydata # If InVesalius is running without GUI if wx.GetApp() is None: proj = prj.Project() #Create Surface instance if overwrite: surface = Surface(index=self.last_surface_index) proj.ChangeSurface(surface) else: surface = Surface(name=surface_name) index = proj.AddSurface(surface) surface.index = index self.last_surface_index = index surface.colour = colour surface.polydata = polydata # With GUI else: normals = vtk.vtkPolyDataNormals() # normals.ReleaseDataFlagOn() normals_ref = weakref.ref(normals) normals_ref().AddObserver( "ProgressEvent", lambda obj, evt: UpdateProgress( normals_ref(), _("Creating 3D surface..."))) normals.SetInputData(polydata) normals.SetFeatureAngle(80) normals.AutoOrientNormalsOn() # normals.GetOutput().ReleaseDataFlagOn() normals.Update() del polydata polydata = normals.GetOutput() #polydata.Register(None) # polydata.SetSource(None) del normals # Improve performance stripper = vtk.vtkStripper() # stripper.ReleaseDataFlagOn() stripper_ref = weakref.ref(stripper) stripper_ref().AddObserver( "ProgressEvent", lambda obj, evt: UpdateProgress( stripper_ref(), _("Creating 3D surface..."))) stripper.SetInputData(polydata) stripper.PassThroughCellIdsOn() stripper.PassThroughPointIdsOn() # stripper.GetOutput().ReleaseDataFlagOn() stripper.Update() del polydata polydata = stripper.GetOutput() #polydata.Register(None) # polydata.SetSource(None) del stripper # Map polygonal data (vtkPolyData) to graphics primitives. mapper = vtk.vtkPolyDataMapper() mapper.SetInputData(polydata) mapper.ScalarVisibilityOff() # mapper.ReleaseDataFlagOn() mapper.ImmediateModeRenderingOn() # improve performance # Represent an object (geometry & properties) in the rendered scene actor = vtk.vtkActor() actor.SetMapper(mapper) del mapper #Create Surface instance if overwrite: surface = Surface(index=self.last_surface_index) else: surface = Surface(name=surface_name) surface.colour = colour surface.polydata = polydata del polydata # Set actor colour and transparency actor.GetProperty().SetColor(colour) actor.GetProperty().SetOpacity(1 - surface.transparency) prop = actor.GetProperty() interpolation = int(ses.Session().surface_interpolation) prop.SetInterpolation(interpolation) proj = prj.Project() if overwrite: proj.ChangeSurface(surface) else: index = proj.AddSurface(surface) surface.index = index self.last_surface_index = index session = ses.Session() session.ChangeProject() measured_polydata = vtk.vtkMassProperties() # measured_polydata.ReleaseDataFlagOn() measured_polydata.SetInputData(to_measure) volume = float(measured_polydata.GetVolume()) area = float(measured_polydata.GetSurfaceArea()) surface.volume = volume surface.area = area self.last_surface_index = surface.index del measured_polydata del to_measure Publisher.sendMessage('Load surface actor into viewer', actor=actor) # Send actor by pubsub to viewer's render if overwrite and self.actors_dict.keys(): old_actor = self.actors_dict[self.last_surface_index] Publisher.sendMessage('Remove surface actor from viewer', actor=old_actor) # Save actor for future management tasks self.actors_dict[surface.index] = actor Publisher.sendMessage('Update surface info in GUI', surface=surface) #When you finalize the progress. The bar is cleaned. UpdateProgress = vu.ShowProgress(1) UpdateProgress(0, _("Ready")) Publisher.sendMessage('Update status text in GUI', label=_("Ready")) Publisher.sendMessage('End busy cursor') del actor