Beispiel #1
0
    def update_legend(self, vmax=17.):
        ''' 
		Update the legend with the electrode devices.
		'''
        self.legend_handles = []
        for i in self.devices:
            QtCore.pyqtRemoveInputHook()
            cmap = self.elec_colors
            num = self.devices.index(i)
            c = cmap(num / vmax)
            color_patch = mpatches.Patch(color=c, label=i)
            self.legend_handles.append(color_patch)
            plt.legend(handles=self.legend_handles,
                       loc='upper right',
                       fontsize='x-small')
Beispiel #2
0
    def update_legend(self, vmax=17.):
        ''' 
        Update the legend with the electrode devices.

        Parameters
        ----------
        vmax : int
            Maximum number of devices (is used to set the color scale)

        '''
        self.legend_handles = []
        for i in self.devices:
            QtCore.pyqtRemoveInputHook()
            cmap = self.elec_colors
            num = self.devices.index(i)
            c = cmap(num / vmax)
            color_patch = mpatches.Patch(color=c, label=i)
            self.legend_handles.append(color_patch)
            plt.legend(handles=self.legend_handles,
                       fontsize='x-small',
                       borderaxespad=3,
                       bbox_to_anchor=self.ax[3].get_position())
Beispiel #3
0
    def __init__(self, subj_dir, img):
        '''
		Initialize the electrode picker with the user-defined MRI and co-registered
		CT scan in [subj_dir].  Images will be displayed using orientation information 
		obtained from the image header. Images will be resampled to dimensions 
		[256,256,256] for display.
		We will also listen for keyboard and mouse events so the user can interact
		with each of the subplot panels (zoom/pan) and add/remove electrodes with a 
		keystroke.
		'''
        QtCore.pyqtRemoveInputHook()
        self.subj_dir = subj_dir
        self.img = nib.load(img)

        # Get affine transform
        self.affine = self.img.affine
        self.fsVox2RAS = np.array([[-1., 0., 0., 128.], [0., 0., 1., -128.],
                                   [0., -1., 0., 128.], [0., 0., 0., 1.]])

        # Apply orientation to the MRI so that the order of the dimensions will be
        # sagittal, coronal, axial
        self.codes = nib.orientations.axcodes2ornt(
            nib.orientations.aff2axcodes(self.affine))
        img_data = nib.orientations.apply_orientation(self.img.get_data(),
                                                      self.codes)
        self.voxel_sizes = nib.affines.voxel_sizes(self.affine)
        nx, ny, nz = np.array(img_data.shape, dtype='float')

        self.inv_affine = np.linalg.inv(self.affine)
        self.img_clim = np.percentile(img_data, (1., 99.))

        self.img_data = img_data
        self.elec_data = np.nan + np.zeros((img_data.shape))
        self.bin_mat = ''  # binary mask for electrodes
        self.device_num = 0  # Start with device 0, increment when we add a new electrode name type
        self.device_name = ''
        self.devices = [
        ]  # This will be a list of the devices (grids, strips, depths)
        self.elec_num = dict()
        self.elecmatrix = dict()  # This will be the electrode coordinates
        self.legend_handles = []  # This will hold legend entries
        self.elec_added = False  # Whether we're in an electrode added state

        self.imsz = [256, 256, 256]

        self.current_slice = np.array(
            [self.imsz[0] / 2, self.imsz[1] / 2, self.imsz[2] / 2],
            dtype=np.float)

        self.fig = plt.figure(figsize=(12, 10))
        self.fig.canvas.set_window_title('Electrode Picker')
        thismanager = plt.get_current_fig_manager()
        thismanager.window.setWindowIcon(
            QtGui.QIcon((os.path.join('icons', 'leftbrain_blackbg.png'))))

        self.im = []
        self.elec_im = []
        self.pial_im = []

        self.cursor = []
        self.cursor2 = []

        im_ranges = [[0, self.imsz[1], 0, self.imsz[2]],
                     [0, self.imsz[0], 0, self.imsz[2]],
                     [0, self.imsz[0], 0, self.imsz[1]]]
        im_labels = [['Inferior', 'Posterior'], ['Inferior', 'Left'],
                     ['Posterior', 'Left']]

        self.ax = []
        self.contour = [False, False, False]
        self.pial_surf_on = True  # Whether pial surface is visible or not
        self.T1_on = True  # Whether T1 is visible or not

        # This is the current slice for indexing (as integers so python doesnt complain)
        cs = np.round(self.current_slice).astype(np.int)

        # Plot sagittal, coronal, and axial views
        for i in np.arange(3):
            self.ax.append(self.fig.add_subplot(2, 2, i + 1))
            self.ax[i].set_axis_bgcolor('k')
            if i == 0:
                imdata = img_data[cs[0], :, :].T
                ctdat = ct_data[cs[0], :, :].T
                edat = self.elec_data[cs[0], :, :].T
                pdat = self.pial_data[cs[0], :, :].T
            elif i == 1:
                imdata = img_data[:, cs[1], :].T
                ctdat = ct_data[:, cs[1], :].T
                edat = self.elec_data[:, cs[1], :].T
                pdat = self.pial_data[:, cs[1], :].T
            elif i == 2:
                imdata = img_data[:, :, cs[2]].T
                ctdat = ct_data[:, :, cs[2]].T
                edat = self.elec_data[:, :, cs[2]].T
                pdat = self.pial_data[:, :, cs[2]].T

            # Show the MRI data in grayscale
            self.im.append(plt.imshow(imdata, cmap=cm.gray, aspect='auto'))

            # Overlay the CT on top in "hot" colormap, slightly transparent
            self.ct_im.append(
                plt.imshow(ctdat,
                           cmap=cm.hot,
                           aspect='auto',
                           alpha=0.5,
                           vmin=1000,
                           vmax=3000))

            # Overlay the electrodes image on top (starts as NaNs, is eventually filled in)
            self.elec_colors = mcolors.LinearSegmentedColormap.from_list(
                'elec_colors',
                np.vstack(
                    (cm.Set1(np.linspace(0., 1,
                                         9)), cm.Set2(np.linspace(0., 1, 8)))))
            self.elec_im.append(
                plt.imshow(edat,
                           cmap=self.elec_colors,
                           aspect='auto',
                           alpha=1,
                           vmin=0,
                           vmax=17))

            # Overlay the pial surface
            self.pial_im.append(self.ax[i].contour(pdat,
                                                   linewidths=0.5,
                                                   colors='y'))
            self.contour[i] = True

            # Plot a green cursor
            self.cursor.append(
                plt.plot([cs[1], cs[1]], [
                    self.ax[i].get_ylim()[0] + 1, self.ax[i].get_ylim()[1] - 1
                ],
                         color=[0, 1, 0]))
            self.cursor2.append(
                plt.plot([
                    self.ax[i].get_xlim()[0] + 1, self.ax[i].get_xlim()[1] - 1
                ], [cs[2], cs[2]],
                         color=[0, 1, 0]))

            # Flip the y axis so brains are the correct side up
            plt.gca().invert_yaxis()

            # Get rid of tick labels
            self.ax[i].set_xticks([])
            self.ax[i].set_yticks([])

            # Label the axes
            self.ax[i].set_xlabel(im_labels[i][0])
            self.ax[i].set_ylabel(im_labels[i][1])
            self.ax[i].axis(im_ranges[i])

        # Plot the maximum intensity projection
        self.ct_slice = 's'  # Show sagittal MIP to start
        self.ax.append(self.fig.add_subplot(2, 2, 4))
        self.ax[3].set_axis_bgcolor('k')
        self.im.append(
            plt.imshow(np.nanmax(ct_data[cs[0] - 15:cs[0] + 15, :, :],
                                 axis=0).T,
                       cmap=cm.gray,
                       aspect='auto'))
        self.cursor.append(
            plt.plot(
                [cs[1], cs[1]],
                [self.ax[3].get_ylim()[0] + 1, self.ax[3].get_ylim()[1] - 1],
                color=[0, 1, 0]))
        self.cursor2.append(
            plt.plot(
                [self.ax[3].get_xlim()[0] + 1, self.ax[3].get_xlim()[1] - 1],
                [cs[2], cs[2]],
                color=[0, 1, 0]))
        self.ax[3].set_xticks([])
        self.ax[3].set_yticks([])
        plt.gca().invert_yaxis()
        self.ax[3].axis([0, self.imsz[1], 0, self.imsz[2]])

        self.elec_im.append(
            plt.imshow(self.elec_data[cs[0], :, :].T,
                       cmap=self.elec_colors,
                       aspect='auto',
                       alpha=1,
                       vmin=0,
                       vmax=17))
        plt.gcf().suptitle(
            "Press 'n' to enter device name in console, press 'e' to add an electrode at crosshair, press 'h' for more options",
            fontsize=14)

        plt.tight_layout()
        plt.subplots_adjust(top=0.9)
        cid2 = self.fig.canvas.mpl_connect('scroll_event', self.on_scroll)
        cid3 = self.fig.canvas.mpl_connect('button_press_event', self.on_click)
        cid = self.fig.canvas.mpl_connect('key_press_event', self.on_key)
        #cid4 = self.fig.canvas.mpl_connect('key_release_event', self.on_key)

        plt.show()
        self.fig.canvas.draw()
Beispiel #4
0
    def __init__(self, subj_dir, hem):
        '''
        Initialize the electrode picker with the user-defined MRI and co-registered
        CT scan in [subj_dir].  Images will be displayed using orientation information 
        obtained from the image header. Images will be resampled to dimensions 
        [256,256,256] for display.
        We will also listen for keyboard and mouse events so the user can interact
        with each of the subplot panels (zoom/pan) and add/remove electrodes with a 
        keystroke.

        Parameters
        ----------
        subj_dir : str
            Path to freesurfer subjects
        hem : {'lh', 'rh', 'stereo'}
            Hemisphere of implantation.

        Attributes
        ----------
        subj_dir : str
            Path to freesurfer subjects
        hem : {'lh','rh','stereo'}
            Hemisphere of implantation
        img : nibabel image
            Data from brain.mgz T1 MRI scan
        ct : nibabel image
            Data from rCT.nii registered CT scan
        pial_img : nibabel image
            Filled pial image
        affine : array-like
            Affine transform for img
        fsVox2RAS : array-like
            Freesurfer voxel to RAS coordinate affine transform
        codes : nibabel orientation codes
        voxel_sizes : array-like
            nibabel voxel size
        inv_affine : array-like
            Inverse of self.affine transform
        img_clim : array-like
            1st and 99th percentile of the image data (for color scaling)
        pial_codes : orientation codes for pial 
        ct_codes : orientation codes for CT
        elec_data : array-like
            Mask for the electrodes
        bin_mat : array-like
            Temporary mask for populating elec_data
        device_num : int
            Number of current device that has been added
        device_name : str
            Name of current device
        devices : list
            List of devices (grids, strips, depths)
        elec_num : dict
                    Indexed by device_name, which number electrode we are on for
            that particular device
        elecmatrix : dict
            Dictionary of electrode coordinates 
        legend_handles : list
            Holds legend entries
        elec_added : bool
            Whether we're in an electrode added state
        imsz : array-like
            image size (brain.mgz)
        ctsz : array-like
            CT size (rCT.nii)
        current_slice : array-like
            Which 3D slice coordinate the user clicked
        fig : figure window
            The current figure window
        im : 
            Contains data for each axis with MRI data values.
        ct_im : 
            Contains CT data for each axis
        elec_im : 
            Contains electrode data for each axis
        pial_im :
            Contains data for the pial surface on each axis
        cursor : array-like
            Cross hair
        cursor2 : array-like
            Cross hair
        ax : 
            which of the axes we're on
        contour : list of bool
            Whether pial surface contour is displayed in each view
        pial_surf_on : bool
            Whether pial surface is visible or not
        T1_on : bool
            Whether T1 is visible or not
        ct_slice : {'s','c','a'}
            How to slice CT maximum intensity projection (sagittal, coronal, or axial)

        '''
        QtCore.pyqtRemoveInputHook()
        self.subj_dir = subj_dir
        if hem == 'stereo':
            hem = 'lh'  # For now, set to lh because hemisphere isn't used in stereo case
        self.hem = hem
        self.img = nib.load(os.path.join(subj_dir, 'mri', 'brain.mgz'))
        self.ct = nib.load(os.path.join(subj_dir, 'CT', 'rCT.nii'))
        pial_fill = os.path.join(subj_dir, 'surf',
                                 '%s.pial.filled.mgz' % (self.hem))
        if not os.path.isfile(pial_fill):
            pial_surf = os.path.join(subj_dir, 'surf', '%s.pial' % (self.hem))
            mris_fill = os.path.join(os.environ['FREESURFER_HOME'], 'bin',
                                     'mris_fill')
            os.system('%s -c -r 1 %s %s' % (mris_fill, pial_surf, pial_fill))
        self.pial_img = nib.load(pial_fill)

        #self.slider = QSlider(Qt.Horizontal)

        # Get affine transform
        self.affine = self.img.affine
        self.fsVox2RAS = np.array([[-1., 0., 0., 128.], [0., 0., 1., -128.],
                                   [0., -1., 0., 128.], [0., 0., 0., 1.]])

        # Apply orientation to the MRI so that the order of the dimensions will be
        # sagittal, coronal, axial
        self.codes = nib.orientations.axcodes2ornt(
            nib.orientations.aff2axcodes(self.affine))
        img_data = nib.orientations.apply_orientation(self.img.get_data(),
                                                      self.codes)
        self.voxel_sizes = nib.affines.voxel_sizes(self.affine)
        nx, ny, nz = np.array(img_data.shape, dtype='float')

        self.inv_affine = np.linalg.inv(self.affine)
        self.img_clim = np.percentile(img_data, (1., 99.))

        # Apply orientation to pial surface fill
        self.pial_codes = nib.orientations.axcodes2ornt(
            nib.orientations.aff2axcodes(self.pial_img.affine))
        pial_data = nib.orientations.apply_orientation(
            self.pial_img.get_data(), self.pial_codes)
        pial_data = scipy.ndimage.binary_closing(pial_data)

        # Apply orientation to the CT so that the order of the dimensions will be
        # sagittal, coronal, axial
        self.ct_codes = nib.orientations.axcodes2ornt(
            nib.orientations.aff2axcodes(self.ct.affine))
        ct_data = nib.orientations.apply_orientation(self.ct.get_data(),
                                                     self.ct_codes)

        cx, cy, cz = np.array(ct_data.shape, dtype='float')

        # Resample both images to the highest resolution
        voxsz = (256, 256, 256)
        if ct_data.shape != voxsz:
            print("Resizing voxels in CT")
            ct_data = scipy.ndimage.zoom(
                ct_data, [voxsz[0] / cx, voxsz[1] / cy, voxsz[2] / cz])
            print(ct_data.shape)
        if self.img.shape != voxsz:
            print("Resizing voxels in MRI")
            img_data = scipy.ndimage.zoom(
                img_data, [voxsz[0] / nx, voxsz[1] / ny, voxsz[2] / nz])
            print(img_data.shape)

        # Threshold the CT so only bright objects (electrodes) are visible
        ct_data[ct_data < 1000] = np.nan

        self.ct_data = ct_data
        self.img_data = img_data
        self.pial_data = pial_data
        self.elec_data = np.nan + np.zeros((img_data.shape))
        self.bin_mat = ''  # binary mask for electrodes
        self.device_num = 0  # Start with device 0, increment when we add a new electrode name type
        self.device_name = ''
        self.devices = [
        ]  # This will be a list of the devices (grids, strips, depths)
        self.elec_num = dict()
        self.elecmatrix = dict()  # This will be the electrode coordinates
        self.legend_handles = []  # This will hold legend entries
        self.elec_added = False  # Whether we're in an electrode added state

        self.imsz = [256, 256, 256]
        self.ctsz = [256, 256, 256]

        self.current_slice = np.array(
            [self.imsz[0] / 2, self.imsz[1] / 2, self.imsz[2] / 2],
            dtype=np.float)

        self.fig = plt.figure(figsize=(12, 10))
        self.fig.canvas.set_window_title('Electrode Picker')
        thismanager = plt.get_current_fig_manager()
        thismanager.window.setWindowIcon(
            QtGui.QIcon((os.path.join('icons', 'leftbrain_blackbg.png'))))

        self.im = []
        self.ct_im = []
        self.elec_im = []
        self.pial_im = []

        self.cursor = []
        self.cursor2 = []

        im_ranges = [[0, self.imsz[1], 0, self.imsz[2]],
                     [0, self.imsz[0], 0, self.imsz[2]],
                     [0, self.imsz[0], 0, self.imsz[1]]]
        im_labels = [['Inferior', 'Posterior'], ['Inferior', 'Left'],
                     ['Posterior', 'Left']]

        self.ax = []
        self.contour = [False, False, False]
        self.pial_surf_on = True  # Whether pial surface is visible or not
        self.T1_on = True  # Whether T1 is visible or not

        # This is the current slice for indexing (as integers so python doesnt complain)
        cs = np.round(self.current_slice).astype(np.int)

        # Plot sagittal, coronal, and axial views
        for i in np.arange(3):
            self.ax.append(self.fig.add_subplot(2, 2, i + 1))
            self.ax[i].set_axis_bgcolor('k')
            if i == 0:
                imdata = img_data[cs[0], :, :].T
                ctdat = ct_data[cs[0], :, :].T
                edat = self.elec_data[cs[0], :, :].T
                pdat = self.pial_data[cs[0], :, :].T
            elif i == 1:
                imdata = img_data[:, cs[1], :].T
                ctdat = ct_data[:, cs[1], :].T
                edat = self.elec_data[:, cs[1], :].T
                pdat = self.pial_data[:, cs[1], :].T
            elif i == 2:
                imdata = img_data[:, :, cs[2]].T
                ctdat = ct_data[:, :, cs[2]].T
                edat = self.elec_data[:, :, cs[2]].T
                pdat = self.pial_data[:, :, cs[2]].T

            # Show the MRI data in grayscale
            self.im.append(plt.imshow(imdata, cmap=cm.gray, aspect='auto'))

            # Overlay the CT on top in "hot" colormap, slightly transparent
            self.ct_im.append(
                plt.imshow(ctdat,
                           cmap=cm.hot,
                           aspect='auto',
                           alpha=0.5,
                           vmin=1000,
                           vmax=3000))

            # Overlay the electrodes image on top (starts as NaNs, is eventually filled in)
            self.elec_colors = mcolors.LinearSegmentedColormap.from_list(
                'elec_colors',
                np.vstack(
                    (cm.Set1(np.linspace(0., 1,
                                         9)), cm.Set2(np.linspace(0., 1, 8)))))
            self.elec_im.append(
                plt.imshow(edat,
                           cmap=self.elec_colors,
                           aspect='auto',
                           alpha=1,
                           vmin=0,
                           vmax=17))

            # Overlay the pial surface
            self.pial_im.append(self.ax[i].contour(pdat,
                                                   linewidths=0.5,
                                                   colors='y'))
            self.contour[i] = True

            # Plot a green cursor
            self.cursor.append(
                plt.plot([cs[1], cs[1]], [
                    self.ax[i].get_ylim()[0] + 1, self.ax[i].get_ylim()[1] - 1
                ],
                         color=[0, 1, 0]))
            self.cursor2.append(
                plt.plot([
                    self.ax[i].get_xlim()[0] + 1, self.ax[i].get_xlim()[1] - 1
                ], [cs[2], cs[2]],
                         color=[0, 1, 0]))

            # Flip the y axis so brains are the correct side up
            plt.gca().invert_yaxis()

            # Get rid of tick labels
            self.ax[i].set_xticks([])
            self.ax[i].set_yticks([])

            # Label the axes
            self.ax[i].set_xlabel(im_labels[i][0])
            self.ax[i].set_ylabel(im_labels[i][1])
            self.ax[i].axis(im_ranges[i])

        # Plot the maximum intensity projection
        self.ct_slice = 's'  # Show sagittal MIP to start
        self.ax.append(self.fig.add_subplot(2, 2, 4))
        self.ax[3].set_axis_bgcolor('k')
        self.im.append(
            plt.imshow(np.nanmax(ct_data[cs[0] - 15:cs[0] + 15, :, :],
                                 axis=0).T,
                       cmap=cm.gray,
                       aspect='auto'))
        self.cursor.append(
            plt.plot(
                [cs[1], cs[1]],
                [self.ax[3].get_ylim()[0] + 1, self.ax[3].get_ylim()[1] - 1],
                color=[0, 1, 0]))
        self.cursor2.append(
            plt.plot(
                [self.ax[3].get_xlim()[0] + 1, self.ax[3].get_xlim()[1] - 1],
                [cs[2], cs[2]],
                color=[0, 1, 0]))
        self.ax[3].set_xticks([])
        self.ax[3].set_yticks([])
        plt.gca().invert_yaxis()
        self.ax[3].axis([0, self.imsz[1], 0, self.imsz[2]])

        self.elec_im.append(
            plt.imshow(self.elec_data[cs[0], :, :].T,
                       cmap=self.elec_colors,
                       aspect='auto',
                       alpha=1,
                       vmin=0,
                       vmax=17))
        plt.gcf().suptitle(
            "Press 'n' to enter device name, press 'e' to add an electrode at crosshair, press 'h' for more options",
            fontsize=14)

        plt.tight_layout()
        plt.subplots_adjust(top=0.9)
        cid2 = self.fig.canvas.mpl_connect('scroll_event', self.on_scroll)
        cid3 = self.fig.canvas.mpl_connect('button_press_event', self.on_click)
        cid = self.fig.canvas.mpl_connect('key_press_event', self.on_key)
        #cid4 = self.fig.canvas.mpl_connect('key_release_event', self.on_key)

        slider_ax = plt.axes([
            self.ax[0].get_position().bounds[0] + 0.06,
            self.ax[0].get_position().bounds[1] + 0.42,
            self.ax[0].get_position().bounds[2] - 0.1, 0.02
        ])
        self.mri_slider = Slider(slider_ax,
                                 'MRI max',
                                 img_data.min(),
                                 img_data.max(),
                                 facecolor=[0.3, 0.3, 0.3])
        self.mri_slider.on_changed(self.update_mri)

        ct_slider_ax = plt.axes([
            self.ax[1].get_position().bounds[0] + 0.06,
            self.ax[1].get_position().bounds[1] + 0.42,
            self.ax[1].get_position().bounds[2] - 0.1, 0.02
        ])
        print(np.nanmax(ct_data))
        self.ct_slider = Slider(ct_slider_ax,
                                'CT max',
                                1000,
                                np.nanmax(ct_data),
                                facecolor=[0.8, 0.1, 0.1])
        self.ct_slider.on_changed(self.update_ct)

        plt.show()
        self.fig.canvas.draw()