Ejemplo n.º 1
0
def launch_sagittal_viewer(img: Image,
                           labels: Sequence[int],
                           msg: str,
                           previous_points: Sequence[Coordinate] = None,
                           output_img: Image = None) -> Image:
    from spinalcordtoolbox.gui import base
    from spinalcordtoolbox.gui.sagittal import launch_sagittal_dialog
    params = base.AnatomicalParams()
    params.vertebraes = labels
    params.input_file_name = img.absolutepath

    if output_img is not None:
        params.output_file_name = output_img.absolutepath
    else:
        params.output_file_name = img.absolutepath

    params.subtitle = msg

    if previous_points is not None:
        params.message_warn = 'Please select the label you want to add \nor correct in the list below before clicking \non the image'

    out = zeros_like(img, dtype='uint8')
    out.absolutepath = params.output_file_name
    launch_sagittal_dialog(img, out, params, previous_points)

    return out
Ejemplo n.º 2
0
    def launch_sagittal_viewer(self, labels):
        from spinalcordtoolbox.gui import base
        from spinalcordtoolbox.gui.sagittal import launch_sagittal_dialog

        params = base.AnatomicalParams()
        params.vertebraes = labels
        params.input_file_name = self.image_input.absolutepath
        params.output_file_name = self.fname_output
        params.subtitle = self.msg
        output = msct_image.zeros_like(self.image_input)
        output.absolutepath = self.fname_output
        launch_sagittal_dialog(self.image_input, output, params)

        return output
    def launch_sagittal_viewer(self, labels):
        from spinalcordtoolbox.gui import base
        from spinalcordtoolbox.gui.sagittal import launch_sagittal_dialog

        params = base.AnatomicalParams()
        params.vertebraes = labels
        params.input_file_name = self.image_input.absolutepath
        params.output_file_name = self.fname_output
        params.subtitle = self.msg
        output = msct_image.zeros_like(self.image_input)
        output.absolutepath = self.fname_output
        launch_sagittal_dialog(self.image_input, output, params)

        return output
Ejemplo n.º 4
0
    def launch_sagittal_viewer(self, labels):
        from spinalcordtoolbox.gui import base
        from spinalcordtoolbox.gui.sagittal import launch_sagittal_dialog

        params = base.AnatomicalParams()
        params.vertebraes = labels
        params.input_file_name = self.image_input.file_name
        params.output_file_name = self.fname_output
        output = self.image_input.copy()
        output.data *= 0
        output.setFileName(self.fname_output)
        launch_sagittal_dialog(self.image_input, output, params)

        return output
Ejemplo n.º 5
0
    def launch_sagittal_viewer(self, labels, previous_points=None):
        from spinalcordtoolbox.gui import base
        from spinalcordtoolbox.gui.sagittal import launch_sagittal_dialog

        params = base.AnatomicalParams()
        params.vertebraes = labels
        params.input_file_name = self.image_input.absolutepath
        params.output_file_name = self.fname_output
        params.subtitle = self.msg
        if previous_points is not None:
            params.message_warn = 'Please select the label you want to add \nor correct in the list below before clicking \non the image'
        output = msct_image.zeros_like(self.image_input)
        output.absolutepath = self.fname_output
        launch_sagittal_dialog(self.image_input, output, params,
                               previous_points)

        return output
Ejemplo n.º 6
0
    def get_bbox_from_gui(self):
        """
        Launch a GUI. The medial sagittal plane of the image is shown. User selects two points: top-left and bottom-
        right of the cropping window.
        Note: There is no cropping along the right-left direction.
        :return:
        """
        from spinalcordtoolbox.gui import base
        from spinalcordtoolbox.gui.sagittal import launch_sagittal_dialog

        # Change orientation to SAL (for displaying sagittal view in the GUI)
        native_orientation = self.img_in.orientation
        self.img_in.change_orientation('SAL')

        # Launch GUI
        params = base.AnatomicalParams()
        params.vertebraes = [
            1, 2
        ]  # TODO: Have user draw a sliding rectangle instead (more intuitive)
        params.subtitle = "Click on the top-left (Label 1) and bottom-right (Label 2) of the image to select your " \
                          "cropping window."
        img_labels = zeros_like(self.img_in)
        launch_sagittal_dialog(self.img_in, img_labels, params)

        # Extract coordinates
        img_labels.change_orientation(native_orientation)
        cropping_coord = img_labels.getNonZeroCoordinates(sorting='value')
        # Since there is no cropping along the R-L direction, xmin/xmax are based on image dimension
        self.bbox.xmin, self.bbox.ymin, self.bbox.zmin = (
            0,
            min(cropping_coord[0].y, cropping_coord[1].y),
            min(cropping_coord[0].z, cropping_coord[1].z),
        )
        self.bbox.xmax, self.bbox.ymax, self.bbox.zmax = (
            img_labels.dim[0],
            max(cropping_coord[0].y, cropping_coord[1].y),
            max(cropping_coord[0].z, cropping_coord[1].z),
        )
        # Put back input image in native orientation
        self.img_in.change_orientation(native_orientation)
Ejemplo n.º 7
0
def vertebral_detection(fname,
                        fname_seg,
                        contrast,
                        param,
                        init_disc,
                        verbose=1,
                        path_template='',
                        initc2='auto',
                        path_output='../'):
    """
    Find intervertebral discs in straightened image using template matching
    :param fname: file name of straigthened spinal cord
    :param fname_seg: file name of straigthened spinal cord segmentation
    :param contrast: t1 or t2
    :param param:  advanced parameters
    :param init_disc:
    :param verbose:
    :param path_template:
    :param path_output: output path for verbose=2 pictures
    :return:
    """
    sct.printv('\nLook for template...', verbose)
    sct.printv('Path template: ' + path_template, verbose)

    # adjust file names if MNI-Poly-AMU template is used (by default: PAM50)
    fname_level = get_file_label(os.path.join(path_template, 'template'),
                                 'vertebral labeling',
                                 output='filewithpath')
    fname_template = get_file_label(os.path.join(path_template, 'template'),
                                    contrast.upper() + '-weighted template',
                                    output='filewithpath')

    # Open template and vertebral levels
    sct.printv('\nOpen template and vertebral levels...', verbose)
    data_template = Image(fname_template).data
    data_disc_template = Image(fname_level).data

    # open anatomical volume
    im_input = Image(fname)
    data = im_input.data

    # smooth data
    data = gaussian_filter(data,
                           param.smooth_factor,
                           output=None,
                           mode="reflect")

    # get dimension of src
    nx, ny, nz = data.shape
    # define xc and yc (centered in the field of view)
    xc = int(np.round(nx / 2))  # direction RL
    yc = int(np.round(ny / 2))  # direction AP
    # get dimension of template
    nxt, nyt, nzt = data_template.shape
    # define xc and yc (centered in the field of view)
    xct = int(np.round(nxt / 2))  # direction RL
    yct = int(np.round(nyt / 2))  # direction AP

    # define mean distance (in voxel) between adjacent discs: [C1/C2 -> C2/C3], [C2/C3 -> C4/C5], ..., [L1/L2 -> L2/L3]
    centerline_level = data_disc_template[xct, yct, :]
    # attribute value to each disc. Starts from max level, then decrease.
    # NB: value 2 means disc C2/C3 (and so on and so forth).
    min_level = centerline_level[centerline_level.nonzero()].min()
    max_level = centerline_level[centerline_level.nonzero()].max()
    list_disc_value_template = list(range(min_level, max_level))
    # add disc above top one
    list_disc_value_template.insert(int(0), min_level - 1)
    sct.printv('\nDisc values from template: ' + str(list_disc_value_template),
               verbose)
    # get diff to find transitions (i.e., discs)
    diff_centerline_level = np.diff(centerline_level)
    # get disc z-values
    list_disc_z_template = diff_centerline_level.nonzero()[0].tolist()
    list_disc_z_template.reverse()
    sct.printv('Z-values for each disc: ' + str(list_disc_z_template), verbose)
    list_distance_template = (
        np.diff(list_disc_z_template) *
        (-1)).tolist()  # multiplies by -1 to get positive distances
    sct.printv(
        'Distances between discs (in voxel): ' + str(list_distance_template),
        verbose)

    # if manual mode, open viewer for user to click on C2/C3 disc
    if init_disc == [] and initc2 == 'manual':
        from spinalcordtoolbox.gui.base import AnatomicalParams
        from spinalcordtoolbox.gui.sagittal import launch_sagittal_dialog

        params = AnatomicalParams()
        params.num_points = 1
        params.vertebraes = [
            3,
        ]
        params.subtitle = 'Click at the posterior tip of C2-C3 disc\n'
        input_file = Image(fname)
        output_file = msct_image.zeros_like(input_file)
        output_file.absolutepath = os.path.join(path_output, 'labels.nii.gz')
        controller = launch_sagittal_dialog(input_file, output_file, params)
        mask_points = controller.as_string()
        # assign new init_disc_z value
        # Note: there is a discrepancy between the label value (3) and the disc value (2). As of mid-2017, the SCT convention for disc C2-C3 is value=3. Before that it was value=2.
        init_disc = [int(mask_points.split(',')[2]), 2]

    # display init disc
    if verbose == 2:
        import matplotlib
        matplotlib.use('Agg')
        import matplotlib.pyplot as plt
        # get percentile for automatic contrast adjustment
        data_display = np.mean(data[xc - param.size_RL:xc +
                                    param.size_RL, :, :],
                               axis=0).transpose()
        percmin = np.percentile(data_display, 10)
        percmax = np.percentile(data_display, 90)
        # display image
        plt.matshow(data_display,
                    fignum=50,
                    cmap=plt.cm.gray,
                    clim=[percmin, percmax],
                    origin='lower')
        plt.title('Anatomical image')
        plt.autoscale(
            enable=False)  # to prevent autoscale of axis when displaying plot
        plt.figure(50), plt.scatter(yc + param.shift_AP_visu,
                                    init_disc[0],
                                    c='yellow',
                                    s=50)
        plt.text(yc + param.shift_AP_visu + 4,
                 init_disc[0],
                 str(init_disc[1]) + '/' + str(init_disc[1] + 1),
                 verticalalignment='center',
                 horizontalalignment='left',
                 color='pink',
                 fontsize=15), plt.draw()
        # plt.ion()  # enables interactive mode

    # FIND DISCS
    # ===========================================================================
    sct.printv('\nDetect intervertebral discs...', verbose)
    # assign initial z and disc
    current_z = init_disc[0]
    current_disc = init_disc[1]
    # create list for z and disc
    list_disc_z = []
    list_disc_value = []
    zrange = list(range(-10, 10))
    direction = 'superior'
    search_next_disc = True
    while search_next_disc:
        sct.printv(
            'Current disc: ' + str(current_disc) + ' (z=' + str(current_z) +
            '). Direction: ' + direction, verbose)
        try:
            # get z corresponding to current disc on template
            current_z_template = list_disc_z_template[current_disc]
        except:
            # in case reached the bottom (see issue #849)
            sct.printv(
                'WARNING: Reached the bottom of the template. Stop searching.',
                verbose, 'warning')
            break
        # find next disc
        # N.B. Do not search for C1/C2 disc (because poorly visible), use template distance instead
        if current_disc != 1:
            current_z = compute_corr_3d(data,
                                        data_template,
                                        x=xc,
                                        xshift=0,
                                        xsize=param.size_RL,
                                        y=yc,
                                        yshift=param.shift_AP,
                                        ysize=param.size_AP,
                                        z=current_z,
                                        zshift=0,
                                        zsize=param.size_IS,
                                        xtarget=xct,
                                        ytarget=yct,
                                        ztarget=current_z_template,
                                        zrange=zrange,
                                        verbose=verbose,
                                        save_suffix='_disc' +
                                        str(current_disc),
                                        gaussian_std=999,
                                        path_output=path_output)

        # display new disc
        if verbose == 2:
            plt.figure(50), plt.scatter(yc + param.shift_AP_visu,
                                        current_z,
                                        c='yellow',
                                        s=50)
            plt.text(yc + param.shift_AP_visu + 4,
                     current_z,
                     str(current_disc) + '/' + str(current_disc + 1),
                     verticalalignment='center',
                     horizontalalignment='left',
                     color='yellow',
                     fontsize=15), plt.draw()

        # append to main list
        if direction == 'superior':
            # append at the beginning
            list_disc_z.insert(0, current_z)
            list_disc_value.insert(0, current_disc)
        elif direction == 'inferior':
            # append at the end
            list_disc_z.append(current_z)
            list_disc_value.append(current_disc)

        # adjust correcting factor based on already-identified discs
        if len(list_disc_z) > 1:
            # compute distance between already-identified discs
            list_distance_current = (np.diff(list_disc_z) * (-1)).tolist()
            # retrieve the template distance corresponding to the already-identified discs
            index_disc_identified = [
                i for i, j in enumerate(list_disc_value_template)
                if j in list_disc_value[:-1]
            ]
            list_distance_template_identified = [
                list_distance_template[i] for i in index_disc_identified
            ]
            # divide subject and template distances for the identified discs
            list_subject_to_template_distance = [
                float(list_distance_current[i]) /
                list_distance_template_identified[i]
                for i in range(len(list_distance_current))
            ]
            # average across identified discs to obtain an average correcting factor
            correcting_factor = np.mean(list_subject_to_template_distance)
            sct.printv('.. correcting factor: ' + str(correcting_factor),
                       verbose)
        else:
            correcting_factor = 1
        # update list_distance specific for the subject
        list_distance = [
            int(np.round(list_distance_template[i] * correcting_factor))
            for i in range(len(list_distance_template))
        ]

        # assign new current_z and disc value
        if direction == 'superior':
            try:
                approx_distance_to_next_disc = list_distance[
                    list_disc_value_template.index(current_disc - 1)]
            except ValueError:
                sct.printv(
                    'WARNING: Disc value not included in template. Using previously-calculated distance: '
                    + str(approx_distance_to_next_disc))
            # assign new current_z and disc value
            current_z = current_z + approx_distance_to_next_disc
            current_disc = current_disc - 1
        elif direction == 'inferior':
            try:
                approx_distance_to_next_disc = list_distance[
                    list_disc_value_template.index(current_disc)]
            except:
                sct.printv(
                    'WARNING: Disc value not included in template. Using previously-calculated distance: '
                    + str(approx_distance_to_next_disc))
            # assign new current_z and disc value
            current_z = current_z - approx_distance_to_next_disc
            current_disc = current_disc + 1

        # if current_z is larger than searching zone, switch direction (and start from initial z minus approximate
        # distance from updated template distance)
        if current_z >= nz or current_disc == 0:
            sct.printv('.. Switching to inferior direction.', verbose)
            direction = 'inferior'
            current_disc = init_disc[1] + 1
            current_z = init_disc[0] - list_distance[
                list_disc_value_template.index(current_disc)]
        # if current_z is lower than searching zone, stop searching
        if current_z <= 0:
            search_next_disc = False

    # if upper disc is not 1, add disc above top disc based on mean_distance_adjusted
    upper_disc = min(list_disc_value)
    # if not upper_disc == 1:
    sct.printv(
        'Adding top disc based on adjusted template distance: #' +
        str(upper_disc - 1), verbose)
    approx_distance_to_next_disc = list_distance[
        list_disc_value_template.index(upper_disc - 1)]
    next_z = max(list_disc_z) + approx_distance_to_next_disc
    sct.printv('.. approximate distance: ' + str(approx_distance_to_next_disc),
               verbose)
    # make sure next disc does not go beyond FOV in superior direction
    if next_z > nz:
        list_disc_z.insert(0, nz)
    else:
        list_disc_z.insert(0, next_z)
    # assign disc value
    list_disc_value.insert(0, upper_disc - 1)

    # Label segmentation
    label_segmentation(fname_seg,
                       list_disc_z,
                       list_disc_value,
                       verbose=verbose)

    # save figure
    if verbose == 2:
        plt.figure(50), plt.savefig(
            os.path.join(path_output, "fig_anat_straight_with_labels.png"))