Esempio n. 1
0
 def export_project_to_nifti(self, filename, save_masks=True):
     import invesalius.data.slice_ as slc
     import nibabel as nib
     s = slc.Slice()
     img_nifti = nib.Nifti1Image(np.swapaxes(np.fliplr(s.matrix), 0, 2), None)
     img_nifti.header.set_zooms(s.spacing)
     img_nifti.header.set_dim_info(slice=0)
     nib.save(img_nifti, filename)
     if save_masks:
         for index in self.mask_dict:
             mask = self.mask_dict[index]
             s.do_threshold_to_all_slices(mask)
             mask_nifti = nib.Nifti1Image(np.swapaxes(np.fliplr(mask.matrix), 0, 2), None)
             mask_nifti.header.set_zooms(s.spacing)
             if filename.lower().endswith('.nii'):
                 basename = filename[:-4]
                 ext = filename[-4::]
             elif filename.lower().endswith('.nii.gz'):
                 basename = filename[:-7]
                 ext = filename[-7::]
             else:
                 ext = '.nii'
                 basename = filename
             nib.save(mask_nifti, "{}_mask_{}_{}{}".format(basename, mask.index, mask.name, ext))
Esempio n. 2
0
    def OpenOtherFiles(self, group):
        # Retreaving matrix from image data
        self.matrix, scalar_range, self.filename = image_utils.img2memmap(
            group)

        hdr = group.header
        hdr.set_data_dtype('int16')
        dims = hdr.get_zooms()
        dimsf = tuple([float(s) for s in dims])

        wl = float((scalar_range[0] + scalar_range[1]) * 0.5)
        ww = float((scalar_range[1] - scalar_range[0]))

        self.Slice = sl.Slice()
        self.Slice.matrix = self.matrix
        self.Slice.matrix_filename = self.filename

        self.Slice.spacing = dimsf
        self.Slice.window_level = wl
        self.Slice.window_width = ww

        scalar_range = int(scalar_range[0]), int(scalar_range[1])
        Publisher.sendMessage('Update threshold limits list', scalar_range)
        return self.matrix, self.filename
Esempio n. 3
0
    def OnPopup(self, evt):
        id = evt.GetId()
        item = self.ID_TO_TOOL_ITEM[evt.GetId()]
        key = item.GetLabel()
        if (key in const.WINDOW_LEVEL.keys()):
            window, level = const.WINDOW_LEVEL[key]
            Publisher.sendMessage('Bright and contrast adjustment image',
                                  (window, level))
            Publisher.sendMessage('Update window level value',\
               (window, level))
            Publisher.sendMessage('Update window and level text',\
                           "WL: %d  WW: %d"%(level, window))
            Publisher.sendMessage('Update slice viewer')

            #Necessary update the slice plane in the volume case exists
            Publisher.sendMessage('Render volume viewer')

        elif (key in const.SLICE_COLOR_TABLE.keys()):
            values = const.SLICE_COLOR_TABLE[key]
            Publisher.sendMessage('Change colour table from background image',
                                  values)
            Publisher.sendMessage('Update slice viewer')

            if sys.platform.startswith('linux'):
                for i in self.pseudo_color_items:
                    it = self.pseudo_color_items[i]
                    if it.IsChecked():
                        it.Toggle()

                item.Toggle()
            self.HideClutDialog()
            self._gen_event = True

        elif key in self.plist_presets:
            values = presets.get_wwwl_preset_colours(self.plist_presets[key])
            Publisher.sendMessage(
                'Change colour table from background image from plist', values)
            Publisher.sendMessage('Update slice viewer')

            if sys.platform.startswith('linux'):
                for i in self.pseudo_color_items:
                    it = self.pseudo_color_items[i]
                    if it.IsChecked():
                        it.Toggle()

                item.Toggle()
            self.HideClutDialog()
            self._gen_event = True

        elif (key in const.IMAGE_TILING.keys()):
            values = const.IMAGE_TILING[key]
            Publisher.sendMessage('Set slice viewer layout', values)
            Publisher.sendMessage('Update slice viewer')

        elif key in PROJECTIONS_ID:
            pid = PROJECTIONS_ID[key]
            Publisher.sendMessage('Set projection type', pid)
            Publisher.sendMessage('Reload actual slice')

        elif key == _('Custom'):
            if self.cdialog is None:
                slc = sl.Slice()
                histogram = slc.histogram
                init = slc.matrix.min()
                end = slc.matrix.max()
                nodes = slc.nodes
                self.cdialog = ClutImagedataDialog(histogram, init, end, nodes)
                self.cdialog.Show()
            else:
                self.cdialog.Show(self._gen_event)

            if sys.platform.startswith('linux'):
                for i in self.pseudo_color_items:
                    it = self.pseudo_color_items[i]
                    if it.IsChecked():
                        it.Toggle()

                item.Toggle()
            item = self.ID_TO_TOOL_ITEM[evt.GetId()]
            item.Check(True)
            self._gen_event = False

        evt.Skip()
Esempio n. 4
0
    def OpenDicomGroup(self, dicom_group, interval, file_range, gui=True):
        # Retrieve general DICOM headers
        dicom = dicom_group.GetDicomSample()

        # Create imagedata
        interval += 1
        filelist = dicom_group.GetFilenameList()[::interval]
        if not filelist:
            utils.debug("Not used the IPPSorter")
            filelist = [
                i.image.file
                for i in dicom_group.GetHandSortedList()[::interval]
            ]

        if file_range is not None and file_range[
                0] is not None and file_range[1] > file_range[0]:
            filelist = filelist[file_range[0]:file_range[1] + 1]

        zspacing = dicom_group.zspacing * interval

        size = dicom.image.size
        bits = dicom.image.bits_allocad
        sop_class_uid = dicom.acquisition.sop_class_uid
        xyspacing = dicom.image.spacing
        orientation = dicom.image.orientation_label

        wl = float(dicom.image.level)
        ww = float(dicom.image.window)

        if sop_class_uid == '1.2.840.10008.5.1.4.1.1.7':  #Secondary Capture Image Storage
            use_dcmspacing = 1
        else:
            use_dcmspacing = 0

        imagedata = None

        if dicom.image.number_of_frames == 1:
            sx, sy = size
            n_slices = len(filelist)
            resolution_percentage = utils.calculate_resizing_tofitmemory(
                int(sx), int(sy), n_slices, bits / 8)

            if resolution_percentage < 1.0 and gui:
                re_dialog = dialog.ResizeImageDialog()
                re_dialog.SetValue(int(resolution_percentage * 100))
                re_dialog_value = re_dialog.ShowModal()
                re_dialog.Close()

                if re_dialog_value == wx.ID_OK:
                    percentage = re_dialog.GetValue()
                    resolution_percentage = percentage / 100.0
                else:
                    return

            xyspacing = xyspacing[0] / resolution_percentage, xyspacing[
                1] / resolution_percentage

            self.matrix, scalar_range, self.filename = image_utils.dcm2memmap(
                filelist, size, orientation, resolution_percentage)

            if orientation == 'AXIAL':
                spacing = xyspacing[0], xyspacing[1], zspacing
            elif orientation == 'CORONAL':
                spacing = xyspacing[0], zspacing, xyspacing[1]
            elif orientation == 'SAGITTAL':
                spacing = zspacing, xyspacing[1], xyspacing[0]
        else:
            print(">>>>>> filelist", filelist)
            self.matrix, scalar_range, spacing, self.filename = image_utils.dcmmf2memmap(
                filelist[0], orientation)

        print(">>>>>> spacing", spacing)

        self.Slice = sl.Slice()
        self.Slice.matrix = self.matrix
        self.Slice.matrix_filename = self.filename

        self.Slice.spacing = spacing

        # 1(a): Fix gantry tilt, if any
        tilt_value = dicom.acquisition.tilt
        if (tilt_value) and (gui):
            # Tell user gantry tilt and fix, according to answer
            message = _("Fix gantry tilt applying the degrees below")
            value = -1 * tilt_value
            tilt_value = dialog.ShowNumberDialog(message, value)
            image_utils.FixGantryTilt(self.matrix, self.Slice.spacing,
                                      tilt_value)
        elif (tilt_value) and not (gui):
            tilt_value = -1 * tilt_value
            image_utils.FixGantryTilt(self.matrix, self.Slice.spacing,
                                      tilt_value)

        self.Slice.window_level = wl
        self.Slice.window_width = ww

        scalar_range = int(self.matrix.min()), int(self.matrix.max())

        Publisher.sendMessage('Update threshold limits list',
                              threshold_range=scalar_range)

        return self.matrix, self.filename, dicom
Esempio n. 5
0
    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)
Esempio n. 6
0
    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')
Esempio n. 7
0
    def StartNavigation(self, tracker):
        tracker_fiducials, tracker_fiducials_raw = tracker.GetTrackerFiducials(
        )

        # initialize jobs list
        jobs_list = []

        if self.event.is_set():
            self.event.clear()

        vis_components = [
            self.serial_port_in_use, self.view_tracts, self.peel_loaded
        ]
        vis_queues = [
            self.coord_queue, self.serial_port_queue, self.tracts_queue,
            self.icp_queue, self.robot_target_queue
        ]

        Publisher.sendMessage("Navigation status",
                              nav_status=True,
                              vis_status=vis_components)

        self.all_fiducials = np.vstack(
            [self.image_fiducials, tracker_fiducials])

        # fiducials matrix
        m_change = tr.affine_matrix_from_points(self.all_fiducials[3:, :].T,
                                                self.all_fiducials[:3, :].T,
                                                shear=False,
                                                scale=False)
        self.m_change = m_change

        errors = False

        if self.track_obj:
            # if object tracking is selected
            if self.obj_reg is None:
                # check if object registration was performed
                wx.MessageBox(
                    _("Perform coil registration before navigation."),
                    _("InVesalius 3"))
                errors = True
            else:
                # if object registration was correctly performed continue with navigation
                # obj_reg[0] is object 3x3 fiducial matrix and obj_reg[1] is 3x3 orientation matrix
                obj_fiducials, obj_orients, obj_ref_mode, obj_name = self.obj_reg

                coreg_data = [m_change, obj_ref_mode]

                if self.ref_mode_id:
                    coord_raw, markers_flag = tracker.TrackerCoordinates.GetCoordinates(
                    )
                else:
                    coord_raw = np.array([None])

                obj_data = db.object_registration(obj_fiducials, obj_orients,
                                                  coord_raw, m_change)
                coreg_data.extend(obj_data)

                queues = [
                    self.coord_queue, self.coord_tracts_queue, self.icp_queue,
                    self.object_at_target_queue
                ]
                jobs_list.append(
                    dcr.CoordinateCorregistrate(self.ref_mode_id, tracker,
                                                coreg_data, self.view_tracts,
                                                queues, self.event,
                                                self.sleep_nav,
                                                tracker.tracker_id,
                                                self.target))
        else:
            coreg_data = (m_change, 0)
            queues = [
                self.coord_queue, self.coord_tracts_queue, self.icp_queue
            ]
            jobs_list.append(
                dcr.CoordinateCorregistrateNoObject(self.ref_mode_id, tracker,
                                                    coreg_data,
                                                    self.view_tracts, queues,
                                                    self.event,
                                                    self.sleep_nav))

        if not errors:
            #TODO: Test the serial port thread
            if self.serial_port_in_use:
                self.serial_port_connection = spc.SerialPortConnection(
                    com_port=self.com_port,
                    baud_rate=self.baud_rate,
                    serial_port_queue=self.serial_port_queue,
                    event=self.event,
                    sleep_nav=self.sleep_nav,
                )
                self.serial_port_connection.Connect()
                jobs_list.append(self.serial_port_connection)

            if self.view_tracts:
                # initialize Trekker parameters
                # TODO: This affine and affine_vtk are created 4 times. To improve, create a new affine object inside
                #  Slice() that contains the transformation with the img_shift. Rename it to avoid confusion to the
                #  affine, for instance it can be: affine_world_to_invesalius_vtk
                slic = sl.Slice()
                prj_data = prj.Project()
                matrix_shape = tuple(prj_data.matrix_shape)
                spacing = tuple(prj_data.spacing)
                img_shift = spacing[1] * (matrix_shape[1] - 1)
                affine = slic.affine.copy()
                affine[1, -1] -= img_shift
                affine_vtk = vtk_utils.numpy_to_vtkMatrix4x4(affine)

                Publisher.sendMessage("Update marker offset state",
                                      create=True)

                self.trk_inp = self.trekker, affine, self.seed_offset, self.n_tracts, self.seed_radius,\
                                self.n_threads, self.act_data, affine_vtk, img_shift
                # print("Appending the tract computation thread!")
                queues = [self.coord_tracts_queue, self.tracts_queue]
                if self.enable_act:
                    jobs_list.append(
                        dti.ComputeTractsACTThread(self.trk_inp, queues,
                                                   self.event, self.sleep_nav))
                else:
                    jobs_list.append(
                        dti.ComputeTractsThread(self.trk_inp, queues,
                                                self.event, self.sleep_nav))

            jobs_list.append(
                UpdateNavigationScene(
                    vis_queues=vis_queues,
                    vis_components=vis_components,
                    event=self.event,
                    sle=self.sleep_nav,
                    neuronavigation_api=self.neuronavigation_api,
                ))

            for jobs in jobs_list:
                # jobs.daemon = True
                jobs.start()
                # del jobs

            if self.pedal_connection is not None:
                self.pedal_connection.add_callback(
                    name='navigation', callback=self.PedalStateChanged)
Esempio n. 8
0
 def apply_segment_threshold(self):
     threshold = self.sld_threshold.GetValue() / 100.0
     if self.ps is not None:
         self.ps.apply_segment_threshold(threshold)
         slc.Slice().discard_all_buffers()
         Publisher.sendMessage("Reload actual slice")
Esempio n. 9
0
    def CreateSurfaceFromPolydata(self, polydata, overwrite=False, index=None,
                                  name=None, colour=None, transparency=None,
                                  volume=None, area=None, scalar=False):

        normals = vtk.vtkPolyDataNormals()
        normals.SetInputData(polydata)
        normals.SetFeatureAngle(80)
        normals.AutoOrientNormalsOn()
        normals.Update()

        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputData(normals.GetOutput())
        if scalar:
            mapper.ScalarVisibilityOn()
        else:
            mapper.ScalarVisibilityOff()
        #  mapper.ImmediateModeRenderingOn() # improve performance

        actor = vtk.vtkActor()
        actor.SetMapper(mapper)
        actor.GetProperty().SetBackfaceCulling(1)

        if self.convert2inv:
            # convert between invesalius and world space with shift in the Y coordinate
            affine = sl.Slice().affine
            # 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 affine is not None:
                matrix_shape = sl.Slice().matrix.shape
                spacing = sl.Slice().spacing
                img_shift = spacing[1] * (matrix_shape[1] - 1)
                affine = sl.Slice().affine.copy()
                affine[1, -1] -= img_shift
                affine_vtk = vtk_utils.numpy_to_vtkMatrix4x4(affine)

                actor.SetUserMatrix(affine_vtk)

        if overwrite:
            if index is None:
                index = self.last_surface_index
            surface = Surface(index=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)

        if overwrite and self.actors_dict.keys():
            try:
                old_actor = self.actors_dict[index]
                Publisher.sendMessage('Remove surface actor from viewer', actor=old_actor)
            except KeyError:
                pass

        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
Esempio n. 10
0
    def segment(self,
                image,
                prob_threshold,
                backend,
                device_id,
                use_gpu,
                progress_callback=None,
                after_segment=None):
        print("backend", backend)
        if backend.lower() == 'plaidml':
            os.environ["KERAS_BACKEND"] = "plaidml.keras.backend"
            os.environ["PLAIDML_DEVICE_IDS"] = device_id
        elif backend.lower() == 'theano':
            os.environ["KERAS_BACKEND"] = "theano"
            if use_gpu:
                os.environ["THEANO_FLAGS"] = "device=cuda0"
                print("Use GPU theano", os.environ["THEANO_FLAGS"])
            else:
                os.environ["THEANO_FLAGS"] = "device=cpu"
        else:
            raise TypeError("Wrong backend")

        import keras
        import invesalius.data.slice_ as slc

        image = imagedata_utils.image_normalize(image, 0.0, 1.0)

        # Loading model
        folder = pathlib.Path(__file__).parent.resolve()
        with open(folder.joinpath("model.json"), "r") as json_file:
            model = keras.models.model_from_json(json_file.read())
        model.load_weights(str(folder.joinpath("model.h5")))
        model.compile("Adam", "binary_crossentropy")

        # segmenting by patches
        msk = np.zeros_like(image, dtype="float32")
        sums = np.zeros_like(image)
        for completion, sub_image, patch in gen_patches(image, SIZE, OVERLAP):
            if self.stop:
                self.stop = False
                return

            if progress_callback is not None:
                progress_callback(completion)
            print("completion", completion)
            (iz, ez), (iy, ey), (ix, ex) = patch
            sub_mask = predict_patch(sub_image, patch, model, SIZE)
            msk[iz:ez, iy:ey, ix:ex] += sub_mask
            sums[iz:ez, iy:ey, ix:ex] += 1

        propability_array = msk / sums

        mask = slc.Slice().create_new_mask()
        mask.was_edited = True
        mask.matrix[:] = 1
        mask.matrix[1:, 1:, 1:] = (propability_array >= prob_threshold) * 255

        self.mask = mask
        self.propability_array = propability_array
        self.segmented = True
        if after_segment is not None:
            after_segment()