Exemplo n.º 1
0
    def execute(self):
        """
        This method executes the symmetry detection
        :return: returns the symmetry data
        """
        img = Image(self.input_image)
        raw_orientation = img.change_orientation()
        data = np.squeeze(img.data)
        dim = data.shape
        section_length = dim[1]/self.nb_sections

        result = np.zeros(dim)

        for i in range(0, self.nb_sections):
            if (i+1)*section_length > dim[1]:
                y_length = (i+1)*section_length - ((i+1)*section_length - dim[1])
                result[:, i*section_length:i*section_length + y_length, :] = symmetry_detector_right_left(data[:, i*section_length:i*section_length + y_length, :],  cropped_xy=self.crop_xy)
            sym = symmetry_detector_right_left(data[:, i*section_length:(i+1)*section_length, :], cropped_xy=self.crop_xy)
            result[:, i*section_length:(i+1)*section_length, :] = sym

        result_image = Image(img)
        if len(result_image.data) == 4:
            result_image.data = result[:,:,:,np.newaxis]
        else:
            result_image.data = result

        result_image.change_orientation(raw_orientation)

        return result_image.data
def create_label_z(fname_seg, z, value):
    """
    Create a label at coordinates x_center, y_center, z
    :param fname_seg: segmentation
    :param z: int
    :return: fname_label
    """
    fname_label = 'labelz.nii.gz'
    nii = Image(fname_seg)
    orientation_origin = nii.change_orientation(
        'RPI')  # change orientation to RPI
    nx, ny, nz, nt, px, py, pz, pt = nii.dim  # Get dimensions
    # find x and y coordinates of the centerline at z using center of mass
    from scipy.ndimage.measurements import center_of_mass
    x, y = center_of_mass(nii.data[:, :, z])
    x, y = int(round(x)), int(round(y))
    nii.data[:, :, :] = 0
    nii.data[x, y, z] = value
    # dilate label to prevent it from disappearing due to nearestneighbor interpolation
    from sct_maths import dilate
    nii.data = dilate(nii.data, [3])
    nii.setFileName(fname_label)
    nii.change_orientation(
        orientation_origin)  # put back in original orientation
    nii.save()
    return fname_label
Exemplo n.º 3
0
def set_orientation(im, orientation, data_inversion=False, filename=False, fname_out=''):
    """
    Set orientation on image
    :param im: either Image object or file name. Carefully set param filename.
    :param orientation:
    :param data_inversion:
    :param filename:
    :return:
    """

    if fname_out:
        pass
    elif filename:
        path, fname, ext = extract_fname(im)
        fname_out = fname + '_' + orientation + ext
    else:
        fname_out = im.file_name + '_' + orientation + im.ext

    if not data_inversion:
        from sct_utils import run
        if filename:
            run('isct_orientation3d -i ' + im + ' -orientation ' + orientation + ' -o ' + fname_out, 0)
            im_out = fname_out
        else:
            fname_in = im.absolutepath
            if not os.path.exists(fname_in):
                im.save()
            run('isct_orientation3d -i ' + im.absolutepath + ' -orientation ' + orientation + ' -o ' + fname_out, 0)
            im_out = Image(fname_out)
    else:
        im_out = im.copy()
        im_out.change_orientation(orientation, True)
        im_out.setFileName(fname_out)
    return im_out
Exemplo n.º 4
0
def load_level(list_slices_target, fname_level):
    verbose = 1
    path_level, file_level, ext_level = extract_fname(fname_level)

    #  ####### Check if the level file is an image or a text file
    # Level file is an image
    if ext_level in ['.nii', '.nii.gz']:
        im_level = Image(fname_level)
        im_level.change_orientation('IRP')

        list_level = []
        list_med_level = []
        for slice_level in im_level.data:
            try:
                # vertebral level of the slice
                l = np.mean(slice_level[slice_level > 0])
                # median of the vertebral level of the slice: if all voxels are int, med will be an int.
                med = np.median(slice_level[slice_level > 0])
                # change med in int if it is an int
                med = int(med) if int(med)==med else med
            except Exception, e:
                printv('WARNING: ' + str(e) + '\nNo level label found. Level will be set to 0 for this slice', verbose, 'warning')
                l = 0
                med = 0
            list_level.append(l)
            list_med_level.append(med)

        # if all median of level are int for all slices : consider level as int
        if all([isinstance(med, int) for med in list_med_level]):
            # level as int are placed in the middle of each vertebra (that's why there is a "+0.5")
            list_level = [int(round(l))+0.5 for l in list_level]
def load_level(list_slices_target, fname_level):
    verbose = 1
    path_level, file_level, ext_level = extract_fname(fname_level)

    #  ####### Check if the level file is an image or a text file
    # Level file is an image
    if ext_level in ['.nii', '.nii.gz']:
        im_level = Image(fname_level)
        im_level.change_orientation('IRP')

        list_level = []
        list_med_level = []
        for slice_level in im_level.data:
            try:
                # vertebral level of the slice
                l = np.mean(slice_level[slice_level > 0])
                # median of the vertebral level of the slice: if all voxels are int, med will be an int.
                med = np.median(slice_level[slice_level > 0])
                # change med in int if it is an int
                med = int(med) if int(med) == med else med
            except Exception, e:
                printv(
                    'WARNING: ' + str(e) +
                    '\nNo level label found. Level will be set to 0 for this slice',
                    verbose, 'warning')
                l = 0
                med = 0
            list_level.append(l)
            list_med_level.append(med)

        # if all median of level are int for all slices : consider level as int
        if all([isinstance(med, int) for med in list_med_level]):
            # level as int are placed in the middle of each vertebra (that's why there is a "+0.5")
            list_level = [int(round(l)) + 0.5 for l in list_level]
Exemplo n.º 6
0
def set_orientation(im, orientation, data_inversion=False, filename=False, fname_out=''):
    """
    Set orientation on image
    :param im: either Image object or file name. Carefully set param filename.
    :param orientation:
    :param data_inversion:
    :param filename:
    :return:
    """

    if fname_out:
        pass
    elif filename:
        path, fname, ext = extract_fname(im)
        fname_out = fname+'_'+orientation+ext
    else:
        fname_out = im.file_name+'_'+orientation+im.ext

    if not data_inversion:
        from sct_utils import run
        if filename:
            run('isct_orientation3d -i '+im+' -orientation '+orientation+' -o '+fname_out, 0)
            im_out = fname_out
        else:
            run('isct_orientation3d -i '+im.absolutepath+' -orientation '+orientation+' -o '+fname_out, 0)
            im_out = Image(fname_out)
    else:
        im_out = im.copy()
        im_out.change_orientation(orientation, True)
        im_out.setFileName(fname_out)
    return im_out
Exemplo n.º 7
0
def get_minimum_path_nii(fname):
    from msct_image import Image
    data=Image(fname)
    vesselness_data = data.data
    raw_orient=data.change_orientation()
    data.data=get_minimum_path(data.data, invert=1)
    data.change_orientation(raw_orient)
    data.file_name += '_minimalpath'
    data.save()
def get_minimum_path_nii(fname):
    from msct_image import Image
    data = Image(fname)
    vesselness_data = data.data
    raw_orient = data.change_orientation()
    result, J1, J2 = get_minimum_path(data.data, invert=1)
    data.data = result
    data.change_orientation(raw_orient)
    data.file_name += '_minimalpath'
    data.save()
def set_orientation(fname_in, orientation, fname_out, inversion=False):
    if not inversion:
        sct.run('isct_orientation3d -i '+fname_in+' -orientation '+orientation+' -o '+fname_out, 0)
    else:
        from msct_image import Image
        input_image = Image(fname_in)
        input_image.change_orientation(orientation, True)
        input_image.setFileName(fname_out)
        input_image.save()
    # return full path
    return os.path.abspath(fname_out)
Exemplo n.º 10
0
def set_orientation(fname_in, orientation, fname_out, inversion=False):
    if not inversion:
        sct.run('isct_orientation3d -i '+fname_in+' -orientation '+orientation+' -o '+fname_out, 0)
    else:
        from msct_image import Image
        input_image = Image(fname_in)
        input_image.change_orientation(orientation, True)
        input_image.setFileName(fname_out)
        input_image.save()
    # return full path
    return os.path.abspath(fname_out)
Exemplo n.º 11
0
def main(fname_anat, fname_centerline, degree_poly, centerline_fitting, interp, remove_temp_files, verbose):

    # load input image
    im_anat = Image(fname_anat)
    nx, ny, nz, nt, px, py, pz, pt = im_anat.dim
    # re-oriente to RPI
    orientation_native = im_anat.change_orientation('RPI')

    # load centerline
    im_centerline = Image(fname_centerline)
    im_centerline.change_orientation('RPI')

    # smooth centerline and return fitted coordinates in voxel space
    x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline(
        im_centerline, algo_fitting=centerline_fitting, type_window='hanning', window_length=50,
        nurbs_pts_number=3000, phys_coordinates=False, verbose=verbose, all_slices=True)

    # compute translation for each slice, such that the flattened centerline is centered in the medial plane (R-L) and
    # avoid discontinuity in slices where there is no centerline (in which case, simply copy the translation of the
    # closest Z).
    # first, get zmin and zmax spanned by the centerline (i.e. with non-zero values)
    indz_centerline = np.where([np.sum(im_centerline.data[:, :, iz]) for iz in range(nz)])[0]
    zmin, zmax = indz_centerline[0], indz_centerline[-1]
    # then, extend the centerline by padding values below zmin and above zmax
    x_centerline_extended = np.concatenate([np.ones(zmin) * x_centerline_fit[0], x_centerline_fit, np.ones(nz-zmax-1) * x_centerline_fit[-1]])

    # loop across slices and apply translation
    im_anat_flattened = im_anat.copy()
    # change type to float32 because of subsequent conversion (img_as_float). See #1790
    im_anat_flattened.changeType('float32')
    for iz in range(nz):
        # compute translation along x (R-L)
        translation_x = x_centerline_extended[iz] - round(nx/2.0)
        # apply transformation to 2D image with linear interpolation
        # tform = tf.SimilarityTransform(scale=1, rotation=0, translation=(translation_x, 0))
        tform = transform.SimilarityTransform(translation=(0, translation_x))
        # important to force input in float to skikit image, because it will output float values
        img = img_as_float(im_anat.data[:, :, iz])
        img_reg = transform.warp(img, tform)
        im_anat_flattened.data[:, :, iz] = img_reg  # img_as_uint(img_reg)

    # change back to native orientation
    im_anat_flattened.change_orientation(orientation_native)
    # save output
    fname_out = sct.add_suffix(fname_anat, '_flatten')
    im_anat_flattened.setFileName(fname_out)
    im_anat_flattened.save()

    sct.display_viewer_syntax([fname_anat, fname_out])
Exemplo n.º 12
0
def set_orientation(im,
                    orientation,
                    data_inversion=False,
                    filename=False,
                    fname_out=''):
    """
    Set orientation on image
    :param im: either Image object or file name. Carefully set param filename.
    :param orientation:
    :param data_inversion:
    :param filename:
    :return:
    """

    if fname_out:
        pass
    elif filename:
        path, fname, ext = extract_fname(im)
        fname_out = fname + '_' + orientation + ext
    else:
        fname_out = im.file_name + '_' + orientation + im.ext

    if not data_inversion:
        if filename:
            sct.run([
                'isct_orientation3d', '-i', im, '-orientation', orientation,
                '-o', fname_out
            ],
                    verbose=0)
            im_out = fname_out
        else:
            fname_in = im.absolutepath
            if not os.path.exists(fname_in):
                im.save()
            sct.run([
                'isct_orientation3d', '-i', im.absolutepath, '-orientation',
                orientation, '-o', fname_out
            ],
                    verbose=0)
            im_out = Image(fname_out)
    else:
        im_out = im.copy()
        im_out.change_orientation(orientation, True)
        im_out.setFileName(fname_out)
    return im_out
 def output_debug_file(self, img, data, file_name):
     """
     This method writes a nifti file that corresponds to a step in the algorithm for easy debug.
     The new nifti file uses the header from the the image passed as parameter
     :param data: data to be written to file
     :param file_name: filename...
     :return: None
     """
     if self.verbose == 2:
         current_folder = os.getcwd()
         # os.chdir(self.path_tmp)
         try:
             img = Image(img)
             img.data = data
             img.change_orientation(self.raw_orientation)
             img.file_name = file_name
             img.save()
         except Exception, e:
             print e
 def output_debug_file(self, img, data, file_name):
     """
     This method writes a nifti file that corresponds to a step in the algorithm for easy debug.
     The new nifti file uses the header from the the image passed as parameter
     :param data: data to be written to file
     :param file_name: filename...
     :return: None
     """
     if self.produce_output:
         current_folder = os.getcwd()
         os.chdir(self.debug_folder)
         try:
             img = Image(img)
             img.data = data
             img.change_orientation(self.raw_orientation)
             img.file_name = file_name
             img.save()
         except Exception, e:
             print e
         os.chdir(current_folder)
def label_discs(fname_seg_labeled, verbose=1):
    """
    Label discs from labaled_segmentation
    :param fname_seg_labeld: fname of the labeled segmentation
    :param verbose:
    :return:
    """
    # open labeled segmentation
    im_seg_labeled = Image(fname_seg_labeled)
    orientation_native = im_seg_labeled.change_orientation('RPI')
    nx, ny, nz = im_seg_labeled.dim[0], im_seg_labeled.dim[
        1], im_seg_labeled.dim[2]
    data_disc = np.zeros([nx, ny, nz])
    vertebral_level_previous = np.max(im_seg_labeled.data)
    # loop across z
    for iz in range(nz):
        # get 2d slice
        slice = im_seg_labeled.data[:, :, iz]
        # check if at least one voxel is non-zero
        if np.any(slice):
            slice_one = np.copy(slice)
            # set all non-zero values to 1
            slice_one[slice.nonzero()] = 1
            # compute center of mass
            cx, cy = [
                int(x) for x in np.round(center_of_mass(slice_one)).tolist()
            ]
            # retrieve vertebral level
            vertebral_level = slice[cx, cy]
            # if smaller than previous level, then labeled as a disc
            if vertebral_level < vertebral_level_previous:
                # label disc
                # sct.printv('iz='+iz+', disc='+vertebral_level)
                data_disc[cx, cy, iz] = vertebral_level
            # update variable
            vertebral_level_previous = vertebral_level
    # save disc labeled file
    im_seg_labeled.file_name += '_disc'
    im_seg_labeled.data = data_disc
    im_seg_labeled.change_orientation(orientation_native)
    im_seg_labeled.save()
Exemplo n.º 16
0
def label_discs(fname_seg_labeled, verbose=1):
    """
    Label discs from labaled_segmentation
    :param fname_seg_labeld: fname of the labeled segmentation
    :param verbose:
    :return:
    """
    # open labeled segmentation
    im_seg_labeled = Image(fname_seg_labeled)
    orientation_native = im_seg_labeled.change_orientation('RPI')
    nx, ny, nz = im_seg_labeled.dim[0], im_seg_labeled.dim[1], im_seg_labeled.dim[2]
    data_disc = np.zeros([nx, ny, nz])
    vertebral_level_previous = np.max(im_seg_labeled.data)
    # loop across z
    for iz in range(nz):
        # get 2d slice
        slice = im_seg_labeled.data[:, :, iz]
        # check if at least one voxel is non-zero
        if np.any(slice):
            slice_one = np.copy(slice)
            # set all non-zero values to 1
            slice_one[slice.nonzero()] = 1
            # compute center of mass
            cx, cy = [int(x) for x in np.round(center_of_mass(slice_one)).tolist()]
            # retrieve vertebral level
            vertebral_level = slice[cx, cy]
            # if smaller than previous level, then labeled as a disc
            if vertebral_level < vertebral_level_previous:
                # label disc
                # print 'iz='+iz+', disc='+vertebral_level
                data_disc[cx, cy, iz] = vertebral_level
            # update variable
            vertebral_level_previous = vertebral_level
    # save disc labeled file
    im_seg_labeled.file_name += '_disc'
    im_seg_labeled.data = data_disc
    im_seg_labeled.change_orientation(orientation_native)
    im_seg_labeled.save()
    def execute(self):
        """
        This method executes the symmetry detection
        :return: returns the symmetry data
        """
        img = Image(self.input_image)
        raw_orientation = img.change_orientation()
        data = np.squeeze(img.data)
        dim = data.shape
        section_length = dim[1] / self.nb_sections

        result = np.zeros(dim)

        for i in range(0, self.nb_sections):
            if (i + 1) * section_length > dim[1]:
                y_length = (i + 1) * section_length - (
                    (i + 1) * section_length - dim[1])
                result[:, i * section_length:i * section_length +
                       y_length, :] = symmetry_detector_right_left(
                           data[:, i * section_length:i * section_length +
                                y_length, :],
                           cropped_xy=self.crop_xy)
            sym = symmetry_detector_right_left(
                data[:, i * section_length:(i + 1) * section_length, :],
                cropped_xy=self.crop_xy)
            result[:, i * section_length:(i + 1) * section_length, :] = sym

        result_image = Image(img)
        if len(result_image.data) == 4:
            result_image.data = result[:, :, :, np.newaxis]
        else:
            result_image.data = result

        result_image.change_orientation(raw_orientation)

        return result_image.data
Exemplo n.º 18
0
def create_label_z(fname_seg, z, value):
    """
    Create a label at coordinates x_center, y_center, z
    :param fname_seg: segmentation
    :param z: int
    :return: fname_label
    """
    fname_label = 'labelz.nii.gz'
    nii = Image(fname_seg)
    orientation_origin = nii.change_orientation('RPI')  # change orientation to RPI
    nx, ny, nz, nt, px, py, pz, pt = nii.dim  # Get dimensions
    # find x and y coordinates of the centerline at z using center of mass
    from scipy.ndimage.measurements import center_of_mass
    x, y = center_of_mass(nii.data[:, :, z])
    x, y = int(round(x)), int(round(y))
    nii.data[:, :, :] = 0
    nii.data[x, y, z] = value
    # dilate label to prevent it from disappearing due to nearestneighbor interpolation
    from sct_maths import dilate
    nii.data = dilate(nii.data, [3])
    nii.setFileName(fname_label)
    nii.change_orientation(orientation_origin)  # put back in original orientation
    nii.save()
    return fname_label
Exemplo n.º 19
0
def test(path_data='', parameters=''):

    if not parameters:
        parameters = '-i t2/t2.nii.gz -c t2 -p auto'

    # parameters
    folder_data = 't2/'
    file_data = [
        't2.nii.gz', 't2_centerline_init.nii.gz',
        't2_centerline_labels.nii.gz', 't2_seg_manual.nii.gz'
    ]

    parser = sct_get_centerline.get_parser()
    dict_param = parser.parse(parameters.split(), check_file_exist=False)
    contrast = dict_param['-c']
    dict_param_with_path = parser.add_path_to_file(dict_param,
                                                   path_data,
                                                   input_file=True)
    param_with_path = parser.dictionary_to_string(dict_param_with_path)

    # Check if input files exist
    if not (os.path.isfile(dict_param_with_path['-i'])):
        status = 200
        output = 'ERROR: the file(s) provided to test function do not exist in folder: ' + path_data
        return status, output, DataFrame(data={
            'status': status,
            'output': output,
            'mse': float('nan'),
            'dist_max': float('nan')
        },
                                         index=[path_data])

    cmd = 'sct_get_centerline ' + param_with_path
    status, output = sct.run(cmd, 0)
    scad_centerline = Image(contrast + "_centerline.nii.gz")
    manual_seg = Image(path_data + folder_data + contrast +
                       '_seg_manual.nii.gz')

    max_distance = 0
    standard_deviation = 0
    average = 0
    root_mean_square = 0
    overall_distance = 0
    max_distance = 0
    overall_std = 0
    rmse = 0

    try:
        if status == 0:
            manual_seg.change_orientation()
            scad_centerline.change_orientation()
            from scipy.ndimage.measurements import center_of_mass
            # find COM
            iterator = range(manual_seg.data.shape[2])
            com_x = [0 for ix in iterator]
            com_y = [0 for iy in iterator]

            for iz in iterator:
                com_x[iz], com_y[iz] = center_of_mass(manual_seg.data[:, :,
                                                                      iz])
            max_distance = {}
            distance = {}
            for iz in range(1, scad_centerline.data.shape[2] - 1):
                ind1 = np.argmax(scad_centerline.data[:, :, iz])
                X, Y = ind2sub(scad_centerline.data[:, :, iz].shape, ind1)
                com_phys = np.array(
                    manual_seg.transfo_pix2phys([[com_x[iz], com_y[iz], iz]]))
                scad_phys = np.array(
                    scad_centerline.transfo_pix2phys([[X, Y, iz]]))
                distance_magnitude = np.linalg.norm([
                    com_phys[0][0] - scad_phys[0][0],
                    com_phys[0][1] - scad_phys[0][1], 0
                ])
                if math.isnan(distance_magnitude):
                    print "Value is nan"
                else:
                    distance[iz] = distance_magnitude

            max_distance = max(distance.values())
            standard_deviation = np.std(np.array(distance.values()))
            average = sum(distance.values()) / len(distance)
            root_mean_square = np.sqrt(np.mean(np.square(distance.values())))
            overall_distance = average
            max_distance = max(distance.values())
            overall_std = standard_deviation
            rmse = root_mean_square

    except Exception, e:
        sct.printv("Exception found while testing scad integrity")
        output = e.message
Exemplo n.º 20
0
def validate_scad(folder_input, contrast):
    """
    Expecting folder to have the following structure :
    errsm_01:
    - t2
    -- errsm_01.nii.gz or t2.nii.gz
    --
    :param folder_input:
    :return:
    """
    from sct_get_centerline import ind2sub
    import time
    import math
    import numpy

    t0 = time.time()

    current_folder = os.getcwd()
    os.chdir(folder_input)

    try:
        patients = next(os.walk('.'))[1]
        overall_distance = {}
        max_distance = {}
        standard_deviation = 0
        overall_std = {}
        rmse = {}
        for i in patients:
            directory = i + "/" + str(contrast)
            try:
                os.chdir(directory)
            except Exception, e:
                print str(i)+" : "+contrast+" directory not found"

            try:
                if os.path.isfile(i+"_"+contrast+".nii.gz"):
                    raw_image = Image(i+"_"+contrast+".nii.gz")
                elif os.path.isfile(contrast+".nii.gz"):
                    raw_image = Image(contrast+".nii.gz")
                else:
                    raise Exception("Patient scan not found")

                if os.path.isfile(i+"_"+contrast+"_manual_segmentation.nii.gz"):
                    raw_orientation = raw_image.change_orientation()
                    scad = SCAD(raw_image, contrast=contrast, rm_tmp_file=1, verbose=1)
                    scad.execute()

                    manual_seg = Image(i+"_"+contrast+"_manual_segmentation.nii.gz")
                    manual_orientation = manual_seg.change_orientation()

                    from scipy.ndimage.measurements import center_of_mass
                    # find COM
                    iterator = range(manual_seg.data.shape[2])
                    com_x = [0 for ix in iterator]
                    com_y = [0 for iy in iterator]

                    for iz in iterator:
                        com_x[iz], com_y[iz] = center_of_mass(manual_seg.data[:, :, iz])

                    centerline_scad = Image(i+"_"+contrast+"_centerline.nii.gz")
                    # os.remove(i+"_"+contrast+"_centerline.nii.gz")

                    centerline_scad.change_orientation()
                    distance = {}
                    for iz in range(1, centerline_scad.data.shape[2]-1):
                        ind1 = np.argmax(centerline_scad.data[:, :, iz])
                        X,Y = ind2sub(centerline_scad.data[:, :, iz].shape,ind1)
                        com_phys = np.array(manual_seg.transfo_pix2phys([[com_x[iz], com_y[iz], iz]]))
                        scad_phys = np.array(centerline_scad.transfo_pix2phys([[X, Y, iz]]))
                        distance_magnitude = np.linalg.norm([com_phys[0][0]-scad_phys[0][0], com_phys[0][1]-scad_phys[0][1], 0])
                        if math.isnan(distance_magnitude):
                            print "Value is nan"
                        else:
                            distance[iz] = distance_magnitude

                    f = open(i+"_"+contrast+"_results.txt", 'w+')
                    f.write("Patient,Slice,Distance")
                    for key, value in distance.items():
                        f.write(i+","+str(key)+","+str(value))

                    standard_deviation = np.std(np.array(distance.values()))
                    average = sum(distance.values())/len(distance)
                    root_mean_square = np.sqrt(np.mean(np.square(distance.values())))

                    f.write("\nAverage : "+str(average))
                    f.write("\nStandard Deviation : "+str(standard_deviation))

                    f.close()

                    overall_distance[i] = average
                    max_distance[i] = max(distance.values())
                    overall_std[i] = standard_deviation
                    rmse[i] = root_mean_square

                else:
                    printv("Cannot find the manual segmentation", type="warning")

            except Exception, e:
                print e.message

            os.chdir(folder_input)
Exemplo n.º 21
0
class ProcessLabels(object):
    def __init__(self, fname_label, fname_output=None, fname_ref=None, cross_radius=5, dilate=False,
                 coordinates=None, verbose=1, vertebral_levels=None, value=None):
        self.image_input = Image(fname_label, verbose=verbose)
        self.image_ref = None
        if fname_ref is not None:
            self.image_ref = Image(fname_ref, verbose=verbose)

        if isinstance(fname_output, list):
            if len(fname_output) == 1:
                self.fname_output = fname_output[0]
            else:
                self.fname_output = fname_output
        else:
            self.fname_output = fname_output
        self.cross_radius = cross_radius
        self.vertebral_levels = vertebral_levels
        self.dilate = dilate
        self.coordinates = coordinates
        self.verbose = verbose
        self.value = value

    def process(self, type_process):
        # for some processes, change orientation of input image to RPI
        change_orientation = False
        if type_process in ['vert-body', 'vert-disc', 'vert-continuous']:
            # get orientation of input image
            input_orientation = self.image_input.orientation
            # change orientation
            self.image_input.change_orientation('RPI')
            change_orientation = True
        if type_process == 'add':
            self.output_image = self.add(self.value)
        if type_process == 'cross':
            self.output_image = self.cross()
        if type_process == 'plan':
            self.output_image = self.plan(self.cross_radius, 100, 5)
        if type_process == 'plan_ref':
            self.output_image = self.plan_ref()
        if type_process == 'increment':
            self.output_image = self.increment_z_inverse()
        if type_process == 'disks':
            self.output_image = self.labelize_from_disks()
        if type_process == 'MSE':
            self.MSE()
            self.fname_output = None
        if type_process == 'remove':
            self.output_image = self.remove_label()
        if type_process == 'remove-symm':
            self.output_image = self.remove_label(symmetry=True)
        if type_process == 'centerline':
            self.extract_centerline()
        if type_process == 'create':
            self.output_image = self.create_label()
        if type_process == 'create-add':
            self.output_image = self.create_label(add=True)
        if type_process == 'display-voxel':
            self.display_voxel()
            self.fname_output = None
        if type_process == 'diff':
            self.diff()
            self.fname_output = None
        if type_process == 'dist-inter':  # second argument is in pixel distance
            self.distance_interlabels(5)
            self.fname_output = None
        if type_process == 'cubic-to-point':
            self.output_image = self.cubic_to_point()
        if type_process == 'vert-body':
            self.output_image = self.label_vertebrae(self.vertebral_levels)
        # if type_process == 'vert-disc':
        #     self.output_image = self.label_disc(self.vertebral_levels)
        # if type_process == 'label-vertebrae-from-disks':
        #     self.output_image = self.label_vertebrae_from_disks(self.vertebral_levels)
        if type_process == 'vert-continuous':
            self.output_image = self.continuous_vertebral_levels()

        # save the output image as minimized integers
        if self.fname_output is not None:
            self.output_image.setFileName(self.fname_output)
            if change_orientation:
                self.output_image.change_orientation(input_orientation)
            if type_process == 'vert-continuous':
                self.output_image.save('float32')
            elif type_process != 'plan_ref':
                self.output_image.save('minimize_int')
            else:
                self.output_image.save()

    def add(self, value):
        """
        This function add a specified value to all non-zero voxels.
        """
        image_output = Image(self.image_input, self.verbose)
        # image_output.data *= 0
        coordinates_input = self.image_input.getNonZeroCoordinates()

        # for all points with non-zeros neighbors, force the neighbors to 0
        for i, coord in enumerate(coordinates_input):
            image_output.data[int(coord.x), int(coord.y), int(coord.z)] = image_output.data[int(coord.x), int(coord.y), int(coord.z)] + float(value)
        return image_output


    def create_label(self, add=False):
        """
        Create an image with labels listed by the user.
        This method works only if the user inserted correct coordinates.

        self.coordinates is a list of coordinates (class in msct_types).
        a Coordinate contains x, y, z and value.
        If only one label is to be added, coordinates must be completed with '[]'
        examples:
        For one label:  object_define=ProcessLabels( fname_label, coordinates=[coordi]) where coordi is a 'Coordinate' object from msct_types
        For two labels: object_define=ProcessLabels( fname_label, coordinates=[coordi1, coordi2]) where coordi1 and coordi2 are 'Coordinate' objects from msct_types
        """
        image_output = self.image_input.copy()
        if not add:
            image_output.data *= 0

        # loop across labels
        for i, coord in enumerate(self.coordinates):
            # display info
            sct.printv('Label #' + str(i) + ': ' + str(coord.x) + ',' + str(coord.y) + ',' + str(coord.z) + ' --> ' +
                       str(coord.value), 1)
            image_output.data[int(coord.x), int(coord.y), int(coord.z)] = coord.value

        return image_output


    def cross(self):
        """
        create a cross.
        :return:
        """
        output_image = Image(self.image_input, self.verbose)
        nx, ny, nz, nt, px, py, pz, pt = Image(self.image_input.absolutepath).dim

        coordinates_input = self.image_input.getNonZeroCoordinates()
        d = self.cross_radius  # cross radius in pixel
        dx = d / px  # cross radius in mm
        dy = d / py

        # clean output_image
        output_image.data *= 0

        cross_coordinates = self.get_crosses_coordinates(coordinates_input, dx, self.image_ref, self.dilate)

        for coord in cross_coordinates:
            output_image.data[int(round(coord.x)), int(round(coord.y)), int(round(coord.z))] = coord.value

        return output_image


    @staticmethod
    def get_crosses_coordinates(coordinates_input, gapxy=15, image_ref=None, dilate=False):
        from msct_types import Coordinate

        # if reference image is provided (segmentation), we draw the cross perpendicular to the centerline
        if image_ref is not None:
            # smooth centerline
            from sct_straighten_spinalcord import smooth_centerline
            x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline(self.image_ref, verbose=self.verbose)

        # compute crosses
        cross_coordinates = []
        for coord in coordinates_input:
            if image_ref is None:
                from sct_straighten_spinalcord import compute_cross
                cross_coordinates_temp = compute_cross(coord, gapxy)
            else:
                from sct_straighten_spinalcord import compute_cross_centerline
                from numpy import where
                index_z = where(z_centerline == coord.z)
                deriv = Coordinate([x_centerline_deriv[index_z][0], y_centerline_deriv[index_z][0], z_centerline_deriv[index_z][0], 0.0])
                cross_coordinates_temp = compute_cross_centerline(coord, deriv, gapxy)

            for i, coord_cross in enumerate(cross_coordinates_temp):
                coord_cross.value = coord.value * 10 + i + 1

            # dilate cross to 3x3x3
            if dilate:
                additional_coordinates = []
                for coord_temp in cross_coordinates_temp:
                    additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y, coord_temp.z+1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y, coord_temp.z-1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y+1.0, coord_temp.z, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y+1.0, coord_temp.z+1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y+1.0, coord_temp.z-1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y-1.0, coord_temp.z, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y-1.0, coord_temp.z+1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x, coord_temp.y-1.0, coord_temp.z-1.0, coord_temp.value]))

                    additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y, coord_temp.z, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y, coord_temp.z+1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y, coord_temp.z-1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y+1.0, coord_temp.z, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y+1.0, coord_temp.z+1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y+1.0, coord_temp.z-1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y-1.0, coord_temp.z, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y-1.0, coord_temp.z+1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x+1.0, coord_temp.y-1.0, coord_temp.z-1.0, coord_temp.value]))

                    additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y, coord_temp.z, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y, coord_temp.z+1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y, coord_temp.z-1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y+1.0, coord_temp.z, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y+1.0, coord_temp.z+1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y+1.0, coord_temp.z-1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y-1.0, coord_temp.z, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y-1.0, coord_temp.z+1.0, coord_temp.value]))
                    additional_coordinates.append(Coordinate([coord_temp.x-1.0, coord_temp.y-1.0, coord_temp.z-1.0, coord_temp.value]))

                cross_coordinates_temp.extend(additional_coordinates)

            cross_coordinates.extend(cross_coordinates_temp)

        cross_coordinates = sorted(cross_coordinates, key=lambda obj: obj.value)
        return cross_coordinates


    def plan(self, width, offset=0, gap=1):
        """
        Create a plane of thickness="width" and changes its value with an offset and a gap between labels.
        """
        image_output = Image(self.image_input, self.verbose)
        image_output.data *= 0
        coordinates_input = self.image_input.getNonZeroCoordinates()

        # for all points with non-zeros neighbors, force the neighbors to 0
        for coord in coordinates_input:
            image_output.data[:, :, int(coord.z) - width:int(coord.z) + width] = offset + gap * coord.value

        return image_output


    def plan_ref(self):
        """
        Generate a plane in the reference space for each label present in the input image
        """

        image_output = Image(self.image_ref, self.verbose)
        image_output.data *= 0

        image_input_neg = Image(self.image_input, self.verbose).copy()
        image_input_pos = Image(self.image_input, self.verbose).copy()
        image_input_neg.data *=0
        image_input_pos.data *=0
        X, Y, Z = (self.image_input.data< 0).nonzero()
        for i in range(len(X)):
            image_input_neg.data[X[i], Y[i], Z[i]] = -self.image_input.data[X[i], Y[i], Z[i]] # in order to apply getNonZeroCoordinates
        X_pos, Y_pos, Z_pos = (self.image_input.data> 0).nonzero()
        for i in range(len(X_pos)):
            image_input_pos.data[X_pos[i], Y_pos[i], Z_pos[i]] = self.image_input.data[X_pos[i], Y_pos[i], Z_pos[i]]

        coordinates_input_neg = image_input_neg.getNonZeroCoordinates()
        coordinates_input_pos = image_input_pos.getNonZeroCoordinates()

        image_output.changeType('float32')
        for coord in coordinates_input_neg:
            image_output.data[:, :, int(coord.z)] = -coord.value #PB: takes the int value of coord.value
        for coord in coordinates_input_pos:
            image_output.data[:, :, int(coord.z)] = coord.value

        return image_output


    def cubic_to_point(self):
        """
        Calculate the center of mass of each group of labels and returns a file of same size with only a
        label by group at the center of mass of this group.
        It is to be used after applying homothetic warping field to a label file as the labels will be dilated.
        Be careful: this algorithm computes the center of mass of voxels with same value, if two groups of voxels with
         the same value are present but separated in space, this algorithm will compute the center of mass of the two
         groups together.
        :return: image_output
        """

        # 0. Initialization of output image
        output_image = self.image_input.copy()
        output_image.data *= 0

        # 1. Extraction of coordinates from all non-null voxels in the image. Coordinates are sorted by value.
        coordinates = self.image_input.getNonZeroCoordinates(sorting='value')

        # 2. Separate all coordinates into groups by value
        groups = dict()
        for coord in coordinates:
            if coord.value in groups:
                groups[coord.value].append(coord)
            else:
                groups[coord.value] = [coord]

        # 3. Compute the center of mass of each group of voxels and write them into the output image
        for value, list_coord in groups.iteritems():
            center_of_mass = sum(list_coord)/float(len(list_coord))
            sct.printv("Value = " + str(center_of_mass.value) + " : ("+str(center_of_mass.x) + ", "+str(center_of_mass.y) + ", " + str(center_of_mass.z) + ") --> ( "+ str(round(center_of_mass.x)) + ", " + str(round(center_of_mass.y)) + ", " + str(round(center_of_mass.z)) + ")", verbose=self.verbose)
            output_image.data[int(round(center_of_mass.x)), int(round(center_of_mass.y)), int(round(center_of_mass.z))] = center_of_mass.value

        return output_image


    def increment_z_inverse(self):
        """
        Take all non-zero values, sort them along the inverse z direction, and attributes the values 1,
        2, 3, etc. This function assuming RPI orientation.
        """
        image_output = Image(self.image_input, self.verbose)
        image_output.data *= 0
        coordinates_input = self.image_input.getNonZeroCoordinates(sorting='z', reverse_coord=True)

        # for all points with non-zeros neighbors, force the neighbors to 0
        for i, coord in enumerate(coordinates_input):
            image_output.data[int(coord.x), int(coord.y), int(coord.z)] = i + 1

        return image_output


    def labelize_from_disks(self):
        """
        Create an image with regions labelized depending on values from reference.
        Typically, user inputs a segmentation image, and labels with disks position, and this function produces
        a segmentation image with vertebral levels labelized.
        Labels are assumed to be non-zero and incremented from top to bottom, assuming a RPI orientation
        """
        image_output = Image(self.image_input, self.verbose)
        image_output.data *= 0
        coordinates_input = self.image_input.getNonZeroCoordinates()
        coordinates_ref = self.image_ref.getNonZeroCoordinates(sorting='value')

        # for all points in input, find the value that has to be set up, depending on the vertebral level
        for i, coord in enumerate(coordinates_input):
            for j in range(0, len(coordinates_ref)-1):
                if coordinates_ref[j+1].z < coord.z <= coordinates_ref[j].z:
                    image_output.data[int(coord.x), int(coord.y), int(coord.z)] = coordinates_ref[j].value

        return image_output


    def label_vertebrae(self, levels_user=None):
        """
        Find the center of mass of vertebral levels specified by the user.
        :return: image_output: Image with labels.
        """
        # get center of mass of each vertebral level
        image_cubic2point = self.cubic_to_point()
        # get list of coordinates for each label
        list_coordinates = image_cubic2point.getNonZeroCoordinates(sorting='value')
        # if user did not specify levels, include all:
        if levels_user[0] == 0:
            levels_user = [int(i.value) for i in list_coordinates]
        # loop across labels and remove those that are not listed by the user
        for i_label in range(len(list_coordinates)):
            # check if this level is NOT in levels_user
            if not levels_user.count(int(list_coordinates[i_label].value)):
                # if not, set value to zero
                image_cubic2point.data[int(list_coordinates[i_label].x), int(list_coordinates[i_label].y), int(list_coordinates[i_label].z)] = 0
        # list all labels
        return image_cubic2point


    # FUNCTION BELOW REMOVED BY JULIEN ON 2016-07-04 BECAUSE SEEMS NOT TO BE USED (AND DUPLICATION WITH ABOVE)
    # def label_vertebrae_from_disks(self, levels_user):
    #     """
    #     Find the center of mass of vertebral levels specified by the user.
    #     :param levels_user:
    #     :return:
    #     """
    #     image_cubic2point = self.cubic_to_point()
    #     # get list of coordinates for each label
    #     list_coordinates_disks = image_cubic2point.getNonZeroCoordinates(sorting='value')
    #     image_cubic2point.data *= 0
    #     # compute vertebral labels from disk labels
    #     list_coordinates_vertebrae = []
    #     for i_label in range(len(list_coordinates_disks)-1):
    #         list_coordinates_vertebrae.append((list_coordinates_disks[i_label] + list_coordinates_disks[i_label+1]) / 2.0)
    #     # loop across labels and remove those that are not listed by the user
    #     for i_label in range(len(list_coordinates_vertebrae)):
    #         # check if this level is NOT in levels_user
    #         if levels_user.count(int(list_coordinates_vertebrae[i_label].value)):
    #             image_cubic2point.data[int(list_coordinates_vertebrae[i_label].x), int(list_coordinates_vertebrae[i_label].y), int(list_coordinates_vertebrae[i_label].z)] = list_coordinates_vertebrae[i_label].value
    #
    #     return image_cubic2point


    # BELOW: UNFINISHED BUSINESS (JULIEN)
    # def label_disc(self, levels_user=None):
    #     """
    #     Find the edge of vertebral labeling file and assign value corresponding to middle coordinate between two levels.
    #     Assumes RPI orientation.
    #     :return: image_output: Image with labels.
    #     """
    #     from msct_types import Coordinate
    #     # get dim
    #     nx, ny, nz, nt, px, py, pz, pt = self.image_input.dim
    #     # initialize disc as a coordinate variable
    #     disc = []
    #     # get center of mass of each vertebral level
    #     image_cubic2point = self.cubic_to_point()
    #     # get list of coordinates for each label
    #     list_centermass = image_cubic2point.getNonZeroCoordinates(sorting='value')
    #     # if user did not specify levels, include all:
    #     if levels_user[0] == 0:
    #         levels_user = [int(i.value) for i in list_centermass]
    #     # get list of all coordinates
    #     list_coordinates = self.display_voxel()
    #     # loop across labels and remove those that are not listed by the user
    #     # for i_label in range(len(list_centermass)):
    #
    #     # TOP DISC
    #     # get coordinates for value i_level
    #     list_i_level = [list_coordinates[i] for i in xrange(len(list_coordinates)) if int(list_coordinates[i].value) == levels_user[0]]
    #     # get max z-value
    #     zmax = max([list_i_level[i].z for i in xrange(len(list_i_level))])
    #     # get coordinates corresponding to bottom voxels
    #     list_i_level_top = [list_i_level[i] for i in xrange(len(list_i_level)) if list_i_level[i].z == zmax]
    #     # get center of mass of the top and bottom voxels
    #     arr_voxels_around_disc = np.array([[list_i_level_top[i].x, list_i_level_top[i].y, list_i_level_top[i].z] for i in range(len(list_i_level_top))])
    #     centermass = list(np.mean(arr_voxels_around_disc, 0))
    #     centermass.append(levels_user[0]-1)
    #     disc.append(Coordinate(centermass))
    #     # if minimum level corresponds to z=nz, then remove it (likely corresponds to top edge of the FOV)
    #     if disc[0].z == nz:
    #         sct.printv('WARNING: Maximum level corresponds to z=0. Removing it (likely corresponds to edge of the FOV)', 1, 'warning')
    #         # remove last element of the list
    #         disc.pop()
    #
    #     # ALL DISCS
    #     # loop across values
    #     for i_level in levels_user:
    #         # get coordinates for value i_level
    #         list_i_level = [list_coordinates[i] for i in xrange(len(list_coordinates)) if int(list_coordinates[i].value) == i_level]
    #         # get min z-value
    #         zmin = min([list_i_level[i].z for i in xrange(len(list_i_level))])
    #         # get coordinates corresponding to bottom voxels
    #         list_i_level_bottom = [list_i_level[i] for i in xrange(len(list_i_level)) if list_i_level[i].z == zmin]
    #         # get center of mass
    #         # arr_i_level_bottom = np.array([[list_i_level_bottom[i].x, list_i_level_bottom[i].y] for i in range(len(list_i_level_bottom))])
    #         # centermass_i_level = ndimage.measurements.center_of_mass()
    #         try:
    #             # get coordinates for value i_level+1
    #             list_i_level_plus_one = [list_coordinates[i] for i in xrange(len(list_coordinates)) if int(list_coordinates[i].value) == i_level+1]
    #             # get max z-value
    #             zmax = max([list_i_level_plus_one[i].z for i in xrange(len(list_i_level_plus_one))])
    #             # get coordinates corresponding to top voxels
    #             list_i_level_plus_one_top = [list_i_level_plus_one[i] for i in xrange(len(list_i_level_plus_one)) if list_i_level_plus_one[i].z == zmax]
    #         except:
    #             # if maximum level was reached, ignore it and disc will be located at the centermass of the bottom z.
    #             list_i_level_plus_one_top = []
    #         # stack bottom and top voxels
    #         list_voxels_around_disc = list_i_level_bottom + list_i_level_plus_one_top
    #         # get center of mass of the top and bottom voxels
    #         arr_voxels_around_disc = np.array([[list_voxels_around_disc[i].x, list_voxels_around_disc[i].y, list_voxels_around_disc[i].z] for i in range(len(list_voxels_around_disc))])
    #         centermass = list(np.mean(arr_voxels_around_disc, 0))
    #         centermass.append(i_level)
    #         disc.append(Coordinate(centermass))
    #     # if maximum level corresponds to z=0, then remove it (likely corresponds to edge of the FOV)
    #     if disc[-1].z == 0.0:
    #         sct.printv('WARNING: Maximum level corresponds to z=0. Removing it (likely corresponds to edge of the FOV)', 1, 'warning')
    #         # remove last element of the list
    #         disc.pop()
    #
    #     # loop across labels and assign voxels in image
    #     image_cubic2point.data[:, :, :] = 0
    #     for i_label in range(len(disc)):
    #         image_cubic2point.data[int(round(disc[i_label].x)),
    #                                int(round(disc[i_label].y)),
    #                                int(round(disc[i_label].z))] = disc[i_label].value
    #
    #     # return image of labels
    #     return image_cubic2point


    def MSE(self, threshold_mse=0):
        """
        Compute the Mean Square Distance Error between two sets of labels (input and ref).
        Moreover, a warning is generated for each label mismatch.
        If the MSE is above the threshold provided (by default = 0mm), a log is reported with the filenames considered here.
        """
        coordinates_input = self.image_input.getNonZeroCoordinates()
        coordinates_ref = self.image_ref.getNonZeroCoordinates()

        # check if all the labels in both the images match
        if len(coordinates_input) != len(coordinates_ref):
            sct.printv('ERROR: labels mismatch', 1, 'warning')
        for coord in coordinates_input:
            if round(coord.value) not in [round(coord_ref.value) for coord_ref in coordinates_ref]:
                sct.printv('ERROR: labels mismatch', 1, 'warning')
        for coord_ref in coordinates_ref:
            if round(coord_ref.value) not in [round(coord.value) for coord in coordinates_input]:
                sct.printv('ERROR: labels mismatch', 1, 'warning')

        result = 0.0
        for coord in coordinates_input:
            for coord_ref in coordinates_ref:
                if round(coord_ref.value) == round(coord.value):
                    result += (coord_ref.z - coord.z) ** 2
                    break
        result = math.sqrt(result / len(coordinates_input))
        sct.printv('MSE error in Z direction = ' + str(result) + ' mm')

        if result > threshold_mse:
            f = open(self.image_input.path + 'error_log_' + self.image_input.file_name + '.txt', 'w')
            f.write(
                'The labels error (MSE) between ' + self.image_input.file_name + ' and ' + self.image_ref.file_name + ' is: ' + str(
                    result))
            f.close()

        return result


    @staticmethod
    def remove_label_coord(coord_input, coord_ref, symmetry=False):
        """
        coord_input and coord_ref should be sets of CoordinateValue in order to improve speed of intersection
        :param coord_input: set of CoordinateValue
        :param coord_ref: set of CoordinateValue
        :param symmetry: boolean,
        :return: intersection of CoordinateValue: list
        """
        from msct_types import CoordinateValue
        if isinstance(coord_input[0], CoordinateValue) and isinstance(coord_ref[0], CoordinateValue) and symmetry:
            coord_intersection = list(set(coord_input).intersection(set(coord_ref)))
            result_coord_input = [coord for coord in coord_input if coord in coord_intersection]
            result_coord_ref = [coord for coord in coord_ref if coord in coord_intersection]
        else:
            result_coord_ref = coord_ref
            result_coord_input = [coord for coord in coord_input if filter(lambda x: x.value == coord.value, coord_ref)]
            if symmetry:
                result_coord_ref = [coord for coord in coord_ref if filter(lambda x: x.value == coord.value, result_coord_input)]

        return result_coord_input, result_coord_ref


    def remove_label(self, symmetry=False):
        """
        Compare two label images and remove any labels in input image that are not in reference image.
        The symmetry option enables to remove labels from reference image that are not in input image
        """
        # image_output = Image(self.image_input.dim, orientation=self.image_input.orientation, hdr=self.image_input.hdr, verbose=self.verbose)
        image_output = Image(self.image_input, verbose=self.verbose)
        image_output.data *= 0  # put all voxels to 0

        result_coord_input, result_coord_ref = self.remove_label_coord(self.image_input.getNonZeroCoordinates(coordValue=True),
                                                                       self.image_ref.getNonZeroCoordinates(coordValue=True), symmetry)

        for coord in result_coord_input:
            image_output.data[int(coord.x), int(coord.y), int(coord.z)] = int(round(coord.value))

        if symmetry:
            # image_output_ref = Image(self.image_ref.dim, orientation=self.image_ref.orientation, hdr=self.image_ref.hdr, verbose=self.verbose)
            image_output_ref = Image(self.image_ref, verbose=self.verbose)
            for coord in result_coord_ref:
                image_output_ref.data[int(coord.x), int(coord.y), int(coord.z)] = int(round(coord.value))
            image_output_ref.setFileName(self.fname_output[1])
            image_output_ref.save('minimize_int')

            self.fname_output = self.fname_output[0]

        return image_output


    def extract_centerline(self):
        """
        Write a text file with the coordinates of the centerline.
        The image is suppose to be RPI
        """
        coordinates_input = self.image_input.getNonZeroCoordinates(sorting='z')

        fo = open(self.fname_output, "wb")
        for coord in coordinates_input:
            line = (coord.x,coord.y, coord.z)
            fo.write("%i %i %i\n" % line)
        fo.close()


    def display_voxel(self):
        """
        Display all the labels that are contained in the input image.
        The image is suppose to be RPI to display voxels. But works also for other orientations
        """
        coordinates_input = self.image_input.getNonZeroCoordinates(sorting='z')
        useful_notation = ''
        for coord in coordinates_input:
            print 'Position=(' + str(coord.x) + ',' + str(coord.y) + ',' + str(coord.z) + ') -- Value= ' + str(coord.value)
            if useful_notation:
                useful_notation = useful_notation + ':'
            useful_notation = useful_notation + str(coord.x) + ',' + str(coord.y) + ',' + str(coord.z) + ',' + str(coord.value)
        print 'All labels (useful syntax):'
        print useful_notation
        return coordinates_input


    def diff(self):
        """
        Detect any label mismatch between input image and reference image
        """
        coordinates_input = self.image_input.getNonZeroCoordinates()
        coordinates_ref = self.image_ref.getNonZeroCoordinates()

        print "Label in input image that are not in reference image:"
        for coord in coordinates_input:
            isIn = False
            for coord_ref in coordinates_ref:
                if coord.value == coord_ref.value:
                    isIn = True
                    break
            if not isIn:
                print coord.value

        print "Label in ref image that are not in input image:"
        for coord_ref in coordinates_ref:
            isIn = False
            for coord in coordinates_input:
                if coord.value == coord_ref.value:
                    isIn = True
                    break
            if not isIn:
                print coord_ref.value


    def distance_interlabels(self, max_dist):
        """
        Calculate the distances between each label in the input image.
        If a distance is larger than max_dist, a warning message is displayed.
        """
        coordinates_input = self.image_input.getNonZeroCoordinates()

        # for all points with non-zeros neighbors, force the neighbors to 0
        for i in range(0, len(coordinates_input) - 1):
            dist = math.sqrt((coordinates_input[i].x - coordinates_input[i+1].x)**2 + (coordinates_input[i].y - coordinates_input[i+1].y)**2 + (coordinates_input[i].z - coordinates_input[i+1].z)**2)
            if dist < max_dist:
                print 'Warning: the distance between label ' + str(i) + '[' + str(coordinates_input[i].x) + ',' + str(coordinates_input[i].y) + ',' + str(
                    coordinates_input[i].z) + ']=' + str(coordinates_input[i].value) + ' and label ' + str(i+1) + '[' + str(
                    coordinates_input[i+1].x) + ',' + str(coordinates_input[i+1].y) + ',' + str(coordinates_input[i+1].z) + ']=' + str(
                    coordinates_input[i+1].value) + ' is larger than ' + str(max_dist) + '. Distance=' + str(dist)


    def continuous_vertebral_levels(self):
        """
        This function transforms the vertebral levels file from the template into a continuous file.
        Instead of having integer representing the vertebral level on each slice, a continuous value that represents
        the position of the slice in the vertebral level coordinate system.
        The image must be RPI
        :return:
        """
        im_input = Image(self.image_input, self.verbose)
        im_output = Image(self.image_input, self.verbose)
        im_output.data *= 0

        # 1. extract vertebral levels from input image
        #   a. extract centerline
        #   b. for each slice, extract corresponding level
        nx, ny, nz, nt, px, py, pz, pt = im_input.dim
        from sct_straighten_spinalcord import smooth_centerline
        x_centerline_fit, y_centerline_fit, z_centerline_fit, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline(self.image_input, algo_fitting='nurbs', verbose=0)
        value_centerline = np.array([im_input.data[int(x_centerline_fit[it]), int(y_centerline_fit[it]), int(z_centerline_fit[it])] for it in range(len(z_centerline_fit))])

        # 2. compute distance for each vertebral level --> Di for i being the vertebral levels
        vertebral_levels = {}
        for slice_image, level in enumerate(value_centerline):
            if level not in vertebral_levels:
                vertebral_levels[level] = slice_image

        length_levels = {}
        for level in vertebral_levels:
            indexes_slice = np.where(value_centerline == level)
            length_levels[level] = np.sum([math.sqrt(((x_centerline_fit[indexes_slice[0][index_slice + 1]] - x_centerline_fit[indexes_slice[0][index_slice]])*px)**2 +
                                                     ((y_centerline_fit[indexes_slice[0][index_slice + 1]] - y_centerline_fit[indexes_slice[0][index_slice]])*py)**2 +
                                                     ((z_centerline_fit[indexes_slice[0][index_slice + 1]] - z_centerline_fit[indexes_slice[0][index_slice]])*pz)**2)
                                           for index_slice in range(len(indexes_slice[0]) - 1)])

        # 2. for each slice:
        #   a. identify corresponding vertebral level --> i
        #   b. calculate distance of slice from upper vertebral level --> d
        #   c. compute relative distance in the vertebral level coordinate system --> d/Di
        continuous_values = {}
        for it, iz in enumerate(z_centerline_fit):
            level = value_centerline[it]
            indexes_slice = np.where(value_centerline == level)
            indexes_slice = indexes_slice[0][indexes_slice[0] >= it]
            distance_from_level = np.sum([math.sqrt(((x_centerline_fit[indexes_slice[index_slice + 1]] - x_centerline_fit[indexes_slice[index_slice]]) * px * px) ** 2 +
                                                    ((y_centerline_fit[indexes_slice[index_slice + 1]] - y_centerline_fit[indexes_slice[index_slice]]) * py * py) ** 2 +
                                                    ((z_centerline_fit[indexes_slice[index_slice + 1]] - z_centerline_fit[indexes_slice[index_slice]]) * pz * pz) ** 2)
                                          for index_slice in range(len(indexes_slice) - 1)])
            continuous_values[iz] = level + 2.0 * distance_from_level / float(length_levels[level])

        # 3. saving data
        # for each slice, get all non-zero pixels and replace with continuous values
        coordinates_input = self.image_input.getNonZeroCoordinates()
        im_output.changeType('float32')
        # for all points in input, find the value that has to be set up, depending on the vertebral level
        for i, coord in enumerate(coordinates_input):
            im_output.data[int(coord.x), int(coord.y), int(coord.z)] = continuous_values[coord.z]

        return im_output
Exemplo n.º 22
0
    def execute(self):
        print 'Execution of the SCAD algorithm in '+str(os.getcwd())

        original_name = self.input_image.file_name
        vesselness_file_name = "imageVesselNessFilter.nii.gz"
        raw_file_name = "raw.nii"

        self.setup_debug_folder()

        if self.debug:
            import matplotlib.pyplot as plt # import for debug purposes

        # create tmp and copy input
        path_tmp = self.create_temporary_path()
        conv.convert(self.input_image.absolutepath, path_tmp+raw_file_name)

        if self.vesselness_provided:
            sct.run('cp '+vesselness_file_name+' '+path_tmp+vesselness_file_name)
        os.chdir(path_tmp)

        # get input image information
        img = Image(raw_file_name)

        # save original orientation and change image to RPI
        self.raw_orientation = img.change_orientation()

        # get body symmetry
        if self.enable_symmetry:
            from msct_image import change_data_orientation
            sym = SymmetryDetector(raw_file_name, self.contrast, crop_xy=0)
            self.raw_symmetry = sym.execute()
            img.change_orientation(self.raw_orientation)
            self.output_debug_file(img, self.raw_symmetry, "body_symmetry")
            img.change_orientation()

        # vesselness filter
        if not self.vesselness_provided:
            sct.run('sct_vesselness -i '+raw_file_name+' -t ' + self._contrast+" -radius "+str(self.spinalcord_radius))

        # load vesselness filter data and perform minimum path on it
        img = Image(vesselness_file_name)
        img.change_orientation()
        self.minimum_path_data, self.J1_min_path, self.J2_min_path = get_minimum_path(img.data, invert=1, debug=1)
        self.output_debug_file(img, self.minimum_path_data, "minimal_path")
        self.output_debug_file(img, self.J1_min_path, "J1_minimal_path")
        self.output_debug_file(img, self.J2_min_path, "J2_minimal_path")

        # Apply an exponent to the minimum path
        self.minimum_path_powered = np.power(self.minimum_path_data, self.minimum_path_exponent)
        self.output_debug_file(img, self.minimum_path_powered, "minimal_path_power_"+str(self.minimum_path_exponent))

        # Saving in Image since smooth_minimal_path needs pixel dimensions
        img.data = self.minimum_path_powered

        # smooth resulting minimal path
        self.smoothed_min_path = smooth_minimal_path(img)
        self.output_debug_file(img, self.smoothed_min_path.data, "minimal_path_smooth")

        # normalise symmetry values between 0 and 1
        if self.enable_symmetry:
            normalised_symmetry = normalize_array_histogram(self.raw_symmetry)
            self.output_debug_file(img, self.smoothed_min_path.data, "minimal_path_smooth")

        # multiply normalised symmetry data with the minimum path result
            from msct_image import change_data_orientation
            self.spine_detect_data = np.multiply(self.smoothed_min_path.data, change_data_orientation(np.power(normalised_symmetry, self.symmetry_exponent), self.raw_orientation, "RPI"))
            self.output_debug_file(img, self.spine_detect_data, "symmetry_x_min_path")
            # extract the centerline from the minimal path image
            self.centerline_with_outliers = get_centerline(self.spine_detect_data, self.spine_detect_data.shape)
        else:
            # extract the centerline from the minimal path image
            self.centerline_with_outliers = get_centerline(self.smoothed_min_path.data, self.smoothed_min_path.data.shape)
        self.output_debug_file(img, self.centerline_with_outliers, "centerline_with_outliers")

        # saving centerline with outliers to have
        img.data = self.centerline_with_outliers
        img.change_orientation()
        img.file_name = "centerline_with_outliers"
        img.save()

        # use a b-spline to smooth out the centerline
        x, y, z, dx, dy, dz = smooth_centerline("centerline_with_outliers.nii.gz")

        # save the centerline
        nx, ny, nz, nt, px, py, pz, pt = img.dim
        img.data = np.zeros((nx, ny, nz))
        for i in range(0, np.size(x)-1):
            img.data[int(x[i]), int(y[i]), int(z[i])] = 1

        self.output_debug_file(img, img.data, "centerline")
        img.change_orientation(self.raw_orientation)
        img.file_name = "centerline"
        img.save()

        # copy back centerline
        os.chdir('../')
        conv.convert(path_tmp+img.file_name+img.ext, self.output_filename)
        if self.rm_tmp_file == 1:
            import shutil
            shutil.rmtree(path_tmp)
Exemplo n.º 23
0
class ProcessLabels(object):
    def __init__(self,
                 fname_label,
                 fname_output=None,
                 fname_ref=None,
                 cross_radius=5,
                 dilate=False,
                 coordinates=None,
                 verbose=1,
                 vertebral_levels=None,
                 value=None,
                 msg=""):
        """
        Collection of processes that deal with label creation/modification.
        :param fname_label:
        :param fname_output:
        :param fname_ref:
        :param cross_radius:
        :param dilate:
        :param coordinates:
        :param verbose:
        :param vertebral_levels:
        :param value:
        :param msg: string. message to display to the user.
        """
        self.image_input = Image(fname_label, verbose=verbose)
        self.image_ref = None
        if fname_ref is not None:
            self.image_ref = Image(fname_ref, verbose=verbose)

        if isinstance(fname_output, list):
            if len(fname_output) == 1:
                self.fname_output = fname_output[0]
            else:
                self.fname_output = fname_output
        else:
            self.fname_output = fname_output
        self.cross_radius = cross_radius
        self.vertebral_levels = vertebral_levels
        self.dilate = dilate
        self.coordinates = coordinates
        self.verbose = verbose
        self.value = value
        self.msg = msg

    def process(self, type_process):
        # for some processes, change orientation of input image to RPI
        change_orientation = False
        if type_process in ['vert-body', 'vert-disc', 'vert-continuous']:
            # get orientation of input image
            input_orientation = self.image_input.orientation
            # change orientation
            self.image_input.change_orientation('RPI')
            change_orientation = True
        if type_process == 'add':
            self.output_image = self.add(self.value)
        if type_process == 'cross':
            self.output_image = self.cross()
        if type_process == 'plan':
            self.output_image = self.plan(self.cross_radius, 100, 5)
        if type_process == 'plan_ref':
            self.output_image = self.plan_ref()
        if type_process == 'increment':
            self.output_image = self.increment_z_inverse()
        if type_process == 'disks':
            self.output_image = self.labelize_from_disks()
        if type_process == 'MSE':
            self.MSE()
            self.fname_output = None
        if type_process == 'remove':
            self.output_image = self.remove_label()
        if type_process == 'remove-symm':
            self.output_image = self.remove_label(symmetry=True)
        if type_process == 'centerline':
            self.extract_centerline()
        if type_process == 'create':
            self.output_image = self.create_label()
        if type_process == 'create-add':
            self.output_image = self.create_label(add=True)
        if type_process == 'create-seg':
            self.output_image = self.create_label_along_segmentation()
        if type_process == 'display-voxel':
            self.display_voxel()
            self.fname_output = None
        if type_process == 'diff':
            self.diff()
            self.fname_output = None
        if type_process == 'dist-inter':  # second argument is in pixel distance
            self.distance_interlabels(5)
            self.fname_output = None
        if type_process == 'cubic-to-point':
            self.output_image = self.cubic_to_point()
        if type_process == 'vert-body':
            self.output_image = self.label_vertebrae(self.vertebral_levels)
        if type_process == 'vert-continuous':
            self.output_image = self.continuous_vertebral_levels()
        if type_process == 'create-viewer':
            self.output_image = self.launch_sagittal_viewer(self.value)

        # save the output image as minimized integers
        if self.fname_output is not None:
            self.output_image.setFileName(self.fname_output)
            if change_orientation:
                self.output_image.change_orientation(input_orientation)
            if type_process == 'vert-continuous':
                self.output_image.save('float32')
            elif type_process != 'plan_ref':
                self.output_image.save('minimize_int')
            else:
                self.output_image.save()

    def add(self, value):
        """
        This function add a specified value to all non-zero voxels.
        """
        image_output = Image(self.image_input, self.verbose)
        # image_output.data *= 0
        coordinates_input = self.image_input.getNonZeroCoordinates()

        # for all points with non-zeros neighbors, force the neighbors to 0
        for i, coord in enumerate(coordinates_input):
            image_output.data[
                int(coord.x), int(coord.y),
                int(coord.z)] = image_output.data[int(coord.x),
                                                  int(coord.y),
                                                  int(coord.z)] + float(value)
        return image_output

    def create_label(self, add=False):
        """
        Create an image with labels listed by the user.
        This method works only if the user inserted correct coordinates.

        self.coordinates is a list of coordinates (class in msct_types).
        a Coordinate contains x, y, z and value.
        If only one label is to be added, coordinates must be completed with '[]'
        examples:
        For one label:  object_define=ProcessLabels( fname_label, coordinates=[coordi]) where coordi is a 'Coordinate' object from msct_types
        For two labels: object_define=ProcessLabels( fname_label, coordinates=[coordi1, coordi2]) where coordi1 and coordi2 are 'Coordinate' objects from msct_types
        """
        image_output = self.image_input.copy()
        if not add:
            image_output.data *= 0

        # loop across labels
        for i, coord in enumerate(self.coordinates):
            if len(image_output.data.shape) == 3:
                image_output.data[int(coord.x),
                                  int(coord.y),
                                  int(coord.z)] = coord.value
            elif len(image_output.data.shape) == 2:
                assert str(
                    coord.z
                ) == '0', "ERROR: 2D coordinates should have a Z value of 0. Z coordinate is :" + str(
                    coord.z)
                image_output.data[int(coord.x), int(coord.y)] = coord.value
            else:
                sct.printv(
                    'ERROR: Data should be 2D or 3D. Current shape is: ' +
                    str(image_output.data.shape), 1, 'error')
            # display info
            sct.printv(
                'Label #' + str(i) + ': ' + str(coord.x) + ',' + str(coord.y) +
                ',' + str(coord.z) + ' --> ' + str(coord.value), 1)
        return image_output

    def create_label_along_segmentation(self):
        """
        Create an image with labels defined along the spinal cord segmentation (or centerline)
        Example:
        object_define=ProcessLabels(fname_segmentation, coordinates=[coord_1, coord_2, coord_i]), where coord_i='z,value'. If z=-1, then use z=nz/2 (i.e. center of FOV in superior-inferior direction)
        Returns
        -------
        image_output: Image object with labels.
        """
        # copy input Image object (will use the same header)
        image_output = self.image_input.copy()
        # set all voxels to 0
        image_output.data *= 0
        # loop across labels
        for i, coord in enumerate(self.coordinates):
            # split coord string
            list_coord = coord.split(',')
            # convert to int() and assign to variable
            z, value = [int(i) for i in list_coord]
            # if z=-1, replace with nz/2
            if z == -1:
                z = int(round(image_output.dim[2] / 2.0))
            # get center of mass of segmentation at given z
            x, y = ndimage.measurements.center_of_mass(
                np.array(self.image_input.data[:, :, z]))
            # round values to make indices
            x, y = int(round(x)), int(round(y))
            # display info
            sct.printv(
                'Label #' + str(i) + ': ' + str(x) + ',' + str(y) + ',' +
                str(z) + ' --> ' + str(value), 1)
            if len(image_output.data.shape) == 3:
                image_output.data[x, y, z] = value
            elif len(image_output.data.shape) == 2:
                assert str(
                    z
                ) == '0', "ERROR: 2D coordinates should have a Z value of 0. Z coordinate is :" + str(
                    z)
                image_output.data[x, y] = value
        return image_output

    def cross(self):
        """
        create a cross.
        :return:
        """
        output_image = Image(self.image_input, self.verbose)
        nx, ny, nz, nt, px, py, pz, pt = Image(
            self.image_input.absolutepath).dim

        coordinates_input = self.image_input.getNonZeroCoordinates()
        d = self.cross_radius  # cross radius in pixel
        dx = d / px  # cross radius in mm
        dy = d / py

        # clean output_image
        output_image.data *= 0

        cross_coordinates = self.get_crosses_coordinates(
            coordinates_input, dx, self.image_ref, self.dilate)

        for coord in cross_coordinates:
            output_image.data[int(round(coord.x)),
                              int(round(coord.y)),
                              int(round(coord.z))] = coord.value

        return output_image

    @staticmethod
    def get_crosses_coordinates(coordinates_input,
                                gapxy=15,
                                image_ref=None,
                                dilate=False):
        from msct_types import Coordinate

        # if reference image is provided (segmentation), we draw the cross perpendicular to the centerline
        if image_ref is not None:
            # smooth centerline
            from sct_straighten_spinalcord import smooth_centerline
            x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline(
                self.image_ref, verbose=self.verbose)

        # compute crosses
        cross_coordinates = []
        for coord in coordinates_input:
            if image_ref is None:
                from sct_straighten_spinalcord import compute_cross
                cross_coordinates_temp = compute_cross(coord, gapxy)
            else:
                from sct_straighten_spinalcord import compute_cross_centerline
                from numpy import where
                index_z = where(z_centerline == coord.z)
                deriv = Coordinate([
                    x_centerline_deriv[index_z][0],
                    y_centerline_deriv[index_z][0],
                    z_centerline_deriv[index_z][0], 0.0
                ])
                cross_coordinates_temp = compute_cross_centerline(
                    coord, deriv, gapxy)

            for i, coord_cross in enumerate(cross_coordinates_temp):
                coord_cross.value = coord.value * 10 + i + 1

            # dilate cross to 3x3x3
            if dilate:
                additional_coordinates = []
                for coord_temp in cross_coordinates_temp:
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x, coord_temp.y, coord_temp.z + 1.0,
                            coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x, coord_temp.y, coord_temp.z - 1.0,
                            coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x, coord_temp.y + 1.0, coord_temp.z,
                            coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x, coord_temp.y + 1.0,
                            coord_temp.z + 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x, coord_temp.y + 1.0,
                            coord_temp.z - 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x, coord_temp.y - 1.0, coord_temp.z,
                            coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x, coord_temp.y - 1.0,
                            coord_temp.z + 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x, coord_temp.y - 1.0,
                            coord_temp.z - 1.0, coord_temp.value
                        ]))

                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x + 1.0, coord_temp.y, coord_temp.z,
                            coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x + 1.0, coord_temp.y,
                            coord_temp.z + 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x + 1.0, coord_temp.y,
                            coord_temp.z - 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x + 1.0, coord_temp.y + 1.0,
                            coord_temp.z, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x + 1.0, coord_temp.y + 1.0,
                            coord_temp.z + 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x + 1.0, coord_temp.y + 1.0,
                            coord_temp.z - 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x + 1.0, coord_temp.y - 1.0,
                            coord_temp.z, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x + 1.0, coord_temp.y - 1.0,
                            coord_temp.z + 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x + 1.0, coord_temp.y - 1.0,
                            coord_temp.z - 1.0, coord_temp.value
                        ]))

                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x - 1.0, coord_temp.y, coord_temp.z,
                            coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x - 1.0, coord_temp.y,
                            coord_temp.z + 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x - 1.0, coord_temp.y,
                            coord_temp.z - 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x - 1.0, coord_temp.y + 1.0,
                            coord_temp.z, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x - 1.0, coord_temp.y + 1.0,
                            coord_temp.z + 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x - 1.0, coord_temp.y + 1.0,
                            coord_temp.z - 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x - 1.0, coord_temp.y - 1.0,
                            coord_temp.z, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x - 1.0, coord_temp.y - 1.0,
                            coord_temp.z + 1.0, coord_temp.value
                        ]))
                    additional_coordinates.append(
                        Coordinate([
                            coord_temp.x - 1.0, coord_temp.y - 1.0,
                            coord_temp.z - 1.0, coord_temp.value
                        ]))

                cross_coordinates_temp.extend(additional_coordinates)

            cross_coordinates.extend(cross_coordinates_temp)

        cross_coordinates = sorted(cross_coordinates,
                                   key=lambda obj: obj.value)
        return cross_coordinates

    def plan(self, width, offset=0, gap=1):
        """
        Create a plane of thickness="width" and changes its value with an offset and a gap between labels.
        """
        image_output = Image(self.image_input, self.verbose)
        image_output.data *= 0
        coordinates_input = self.image_input.getNonZeroCoordinates()

        # for all points with non-zeros neighbors, force the neighbors to 0
        for coord in coordinates_input:
            image_output.data[:, :,
                              int(coord.z) - width:int(coord.z) +
                              width] = offset + gap * coord.value

        return image_output

    def plan_ref(self):
        """
        Generate a plane in the reference space for each label present in the input image
        """

        image_output = Image(self.image_ref, self.verbose)
        image_output.data *= 0

        image_input_neg = Image(self.image_input, self.verbose).copy()
        image_input_pos = Image(self.image_input, self.verbose).copy()
        image_input_neg.data *= 0
        image_input_pos.data *= 0
        X, Y, Z = (self.image_input.data < 0).nonzero()
        for i in range(len(X)):
            image_input_neg.data[X[i], Y[i], Z[i]] = -self.image_input.data[
                X[i], Y[i], Z[i]]  # in order to apply getNonZeroCoordinates
        X_pos, Y_pos, Z_pos = (self.image_input.data > 0).nonzero()
        for i in range(len(X_pos)):
            image_input_pos.data[X_pos[i], Y_pos[i],
                                 Z_pos[i]] = self.image_input.data[X_pos[i],
                                                                   Y_pos[i],
                                                                   Z_pos[i]]

        coordinates_input_neg = image_input_neg.getNonZeroCoordinates()
        coordinates_input_pos = image_input_pos.getNonZeroCoordinates()

        image_output.changeType('float32')
        for coord in coordinates_input_neg:
            image_output.data[:, :, int(
                coord.z
            )] = -coord.value  # PB: takes the int value of coord.value
        for coord in coordinates_input_pos:
            image_output.data[:, :, int(coord.z)] = coord.value

        return image_output

    def cubic_to_point(self):
        """
        Calculate the center of mass of each group of labels and returns a file of same size with only a
        label by group at the center of mass of this group.
        It is to be used after applying homothetic warping field to a label file as the labels will be dilated.
        Be careful: this algorithm computes the center of mass of voxels with same value, if two groups of voxels with
         the same value are present but separated in space, this algorithm will compute the center of mass of the two
         groups together.
        :return: image_output
        """

        # 0. Initialization of output image
        output_image = self.image_input.copy()
        output_image.data *= 0

        # 1. Extraction of coordinates from all non-null voxels in the image. Coordinates are sorted by value.
        coordinates = self.image_input.getNonZeroCoordinates(sorting='value')

        # 2. Separate all coordinates into groups by value
        groups = dict()
        for coord in coordinates:
            if coord.value in groups:
                groups[coord.value].append(coord)
            else:
                groups[coord.value] = [coord]

        # 3. Compute the center of mass of each group of voxels and write them into the output image
        for value, list_coord in groups.items():
            center_of_mass = sum(list_coord) / float(len(list_coord))
            sct.printv("Value = " + str(center_of_mass.value) + " : (" +
                       str(center_of_mass.x) + ", " + str(center_of_mass.y) +
                       ", " + str(center_of_mass.z) + ") --> ( " +
                       str(round(center_of_mass.x)) + ", " +
                       str(round(center_of_mass.y)) + ", " +
                       str(round(center_of_mass.z)) + ")",
                       verbose=self.verbose)
            output_image.data[
                int(round(center_of_mass.x)),
                int(round(center_of_mass.y)),
                int(round(center_of_mass.z))] = center_of_mass.value

        return output_image

    def increment_z_inverse(self):
        """
        Take all non-zero values, sort them along the inverse z direction, and attributes the values 1,
        2, 3, etc. This function assuming RPI orientation.
        """
        image_output = Image(self.image_input, self.verbose)
        image_output.data *= 0
        coordinates_input = self.image_input.getNonZeroCoordinates(
            sorting='z', reverse_coord=True)

        # for all points with non-zeros neighbors, force the neighbors to 0
        for i, coord in enumerate(coordinates_input):
            image_output.data[int(coord.x), int(coord.y), int(coord.z)] = i + 1

        return image_output

    def labelize_from_disks(self):
        """
        Create an image with regions labelized depending on values from reference.
        Typically, user inputs a segmentation image, and labels with disks position, and this function produces
        a segmentation image with vertebral levels labelized.
        Labels are assumed to be non-zero and incremented from top to bottom, assuming a RPI orientation
        """
        image_output = Image(self.image_input, self.verbose)
        image_output.data *= 0
        coordinates_input = self.image_input.getNonZeroCoordinates()
        coordinates_ref = self.image_ref.getNonZeroCoordinates(sorting='value')

        # for all points in input, find the value that has to be set up, depending on the vertebral level
        for i, coord in enumerate(coordinates_input):
            for j in range(0, len(coordinates_ref) - 1):
                if coordinates_ref[j + 1].z < coord.z <= coordinates_ref[j].z:
                    image_output.data[int(coord.x),
                                      int(coord.y),
                                      int(coord.z)] = coordinates_ref[j].value

        return image_output

    def label_vertebrae(self, levels_user=None):
        """
        Find the center of mass of vertebral levels specified by the user.
        :return: image_output: Image with labels.
        """
        # get center of mass of each vertebral level
        image_cubic2point = self.cubic_to_point()
        # get list of coordinates for each label
        list_coordinates = image_cubic2point.getNonZeroCoordinates(
            sorting='value')
        # if user did not specify levels, include all:
        if levels_user[0] == 0:
            levels_user = [int(i.value) for i in list_coordinates]
        # loop across labels and remove those that are not listed by the user
        for i_label in range(len(list_coordinates)):
            # check if this level is NOT in levels_user
            if not levels_user.count(int(list_coordinates[i_label].value)):
                # if not, set value to zero
                image_cubic2point.data[int(list_coordinates[i_label].x),
                                       int(list_coordinates[i_label].y),
                                       int(list_coordinates[i_label].z)] = 0
        # list all labels
        return image_cubic2point

    def MSE(self, threshold_mse=0):
        """
        Compute the Mean Square Distance Error between two sets of labels (input and ref).
        Moreover, a warning is generated for each label mismatch.
        If the MSE is above the threshold provided (by default = 0mm), a log is reported with the filenames considered here.
        """
        coordinates_input = self.image_input.getNonZeroCoordinates()
        coordinates_ref = self.image_ref.getNonZeroCoordinates()

        # check if all the labels in both the images match
        if len(coordinates_input) != len(coordinates_ref):
            sct.printv('ERROR: labels mismatch', 1, 'warning')
        for coord in coordinates_input:
            if round(coord.value) not in [
                    round(coord_ref.value) for coord_ref in coordinates_ref
            ]:
                sct.printv('ERROR: labels mismatch', 1, 'warning')
        for coord_ref in coordinates_ref:
            if round(coord_ref.value) not in [
                    round(coord.value) for coord in coordinates_input
            ]:
                sct.printv('ERROR: labels mismatch', 1, 'warning')

        result = 0.0
        for coord in coordinates_input:
            for coord_ref in coordinates_ref:
                if round(coord_ref.value) == round(coord.value):
                    result += (coord_ref.z - coord.z)**2
                    break
        result = math.sqrt(result / len(coordinates_input))
        sct.printv('MSE error in Z direction = ' + str(result) + ' mm')

        if result > threshold_mse:
            f = open(
                self.image_input.path + 'error_log_' +
                self.image_input.file_name + '.txt', 'w')
            f.write('The labels error (MSE) between ' +
                    self.image_input.file_name + ' and ' +
                    self.image_ref.file_name + ' is: ' + str(result))
            f.close()

        return result

    @staticmethod
    def remove_label_coord(coord_input, coord_ref, symmetry=False):
        """
        coord_input and coord_ref should be sets of CoordinateValue in order to improve speed of intersection
        :param coord_input: set of CoordinateValue
        :param coord_ref: set of CoordinateValue
        :param symmetry: boolean,
        :return: intersection of CoordinateValue: list
        """
        from msct_types import CoordinateValue
        if isinstance(coord_input[0], CoordinateValue) and isinstance(
                coord_ref[0], CoordinateValue) and symmetry:
            coord_intersection = list(
                set(coord_input).intersection(set(coord_ref)))
            result_coord_input = [
                coord for coord in coord_input if coord in coord_intersection
            ]
            result_coord_ref = [
                coord for coord in coord_ref if coord in coord_intersection
            ]
        else:
            result_coord_ref = coord_ref
            result_coord_input = [
                coord for coord in coord_input
                if list(filter(lambda x: x.value == coord.value, coord_ref))
            ]
            if symmetry:
                result_coord_ref = [
                    coord for coord in coord_ref if list(
                        filter(lambda x: x.value == coord.value,
                               result_coord_input))
                ]

        return result_coord_input, result_coord_ref

    def remove_label(self, symmetry=False):
        """
        Compare two label images and remove any labels in input image that are not in reference image.
        The symmetry option enables to remove labels from reference image that are not in input image
        """
        # image_output = Image(self.image_input.dim, orientation=self.image_input.orientation, hdr=self.image_input.hdr, verbose=self.verbose)
        image_output = Image(self.image_input, verbose=self.verbose)
        image_output.data *= 0  # put all voxels to 0

        result_coord_input, result_coord_ref = self.remove_label_coord(
            self.image_input.getNonZeroCoordinates(coordValue=True),
            self.image_ref.getNonZeroCoordinates(coordValue=True), symmetry)

        for coord in result_coord_input:
            image_output.data[int(coord.x),
                              int(coord.y),
                              int(coord.z)] = int(round(coord.value))

        if symmetry:
            # image_output_ref = Image(self.image_ref.dim, orientation=self.image_ref.orientation, hdr=self.image_ref.hdr, verbose=self.verbose)
            image_output_ref = Image(self.image_ref, verbose=self.verbose)
            for coord in result_coord_ref:
                image_output_ref.data[int(coord.x),
                                      int(coord.y),
                                      int(coord.z)] = int(round(coord.value))
            image_output_ref.setFileName(self.fname_output[1])
            image_output_ref.save('minimize_int')

            self.fname_output = self.fname_output[0]

        return image_output

    def extract_centerline(self):
        """
        Write a text file with the coordinates of the centerline.
        The image is suppose to be RPI
        """
        coordinates_input = self.image_input.getNonZeroCoordinates(sorting='z')

        fo = open(self.fname_output, "wb")
        for coord in coordinates_input:
            line = (coord.x, coord.y, coord.z)
            fo.write("%i %i %i\n" % line)
        fo.close()

    def display_voxel(self):
        """
        Display all the labels that are contained in the input image.
        The image is suppose to be RPI to display voxels. But works also for other orientations
        """
        coordinates_input = self.image_input.getNonZeroCoordinates(
            sorting='value')
        self.useful_notation = ''
        for coord in coordinates_input:
            sct.printv('Position=(' + str(coord.x) + ',' + str(coord.y) + ',' +
                       str(coord.z) + ') -- Value= ' + str(coord.value),
                       verbose=self.verbose)
            if self.useful_notation:
                self.useful_notation = self.useful_notation + ':'
            self.useful_notation += str(coord)
        sct.printv('All labels (useful syntax):', verbose=self.verbose)
        sct.printv(self.useful_notation, verbose=self.verbose)
        return coordinates_input

    def get_physical_coordinates(self):
        """
        This function returns the coordinates of the labels in the physical referential system.
        :return: a list of CoordinateValue, in the physical (scanner) space
        """
        coord = self.image_input.getNonZeroCoordinates(sorting='value')
        phys_coord = []
        for c in coord:
            # convert pixelar coordinates to physical coordinates
            c_p = self.image_input.transfo_pix2phys([[c.x, c.y, c.z]])[0]
            phys_coord.append(
                CoordinateValue([c_p[0], c_p[1], c_p[2], c.value]))
        return phys_coord

    def get_coordinates_in_destination(self, im_dest, type='discrete'):
        """
        This function calculate the position of labels in the pixelar space of a destination image
        :param im_dest: Object Image
        :param type: 'discrete' or 'continuous'
        :return: a list of CoordinateValue, in the pixelar (image) space of the destination image
        """
        phys_coord = self.get_physical_coordinates()
        dest_coord = []
        for c in phys_coord:
            if type is 'discrete':
                c_p = im_dest.transfo_phys2pix([[c.x, c.y, c.y]])[0]
            elif type is 'continuous':
                c_p = im_dest.transfo_phys2continuouspix([[c.x, c.y, c.y]])[0]
            else:
                raise ValueError(
                    "The value of 'type' should either be 'discrete' or 'continuous'."
                )
            dest_coord.append(
                CoordinateValue([c_p[0], c_p[1], c_p[2], c.value]))
        return dest_coord

    def diff(self):
        """
        Detect any label mismatch between input image and reference image
        """
        coordinates_input = self.image_input.getNonZeroCoordinates()
        coordinates_ref = self.image_ref.getNonZeroCoordinates()

        sct.printv("Label in input image that are not in reference image:")
        for coord in coordinates_input:
            isIn = False
            for coord_ref in coordinates_ref:
                if coord.value == coord_ref.value:
                    isIn = True
                    break
            if not isIn:
                sct.printv(coord.value)

        sct.printv("Label in ref image that are not in input image:")
        for coord_ref in coordinates_ref:
            isIn = False
            for coord in coordinates_input:
                if coord.value == coord_ref.value:
                    isIn = True
                    break
            if not isIn:
                sct.printv(coord_ref.value)

    def distance_interlabels(self, max_dist):
        """
        Calculate the distances between each label in the input image.
        If a distance is larger than max_dist, a warning message is displayed.
        """
        coordinates_input = self.image_input.getNonZeroCoordinates()

        # for all points with non-zeros neighbors, force the neighbors to 0
        for i in range(0, len(coordinates_input) - 1):
            dist = math.sqrt(
                (coordinates_input[i].x - coordinates_input[i + 1].x)**2 +
                (coordinates_input[i].y - coordinates_input[i + 1].y)**2 +
                (coordinates_input[i].z - coordinates_input[i + 1].z)**2)
            if dist < max_dist:
                sct.printv('Warning: the distance between label ' + str(i) +
                           '[' + str(coordinates_input[i].x) + ',' +
                           str(coordinates_input[i].y) + ',' +
                           str(coordinates_input[i].z) + ']=' +
                           str(coordinates_input[i].value) + ' and label ' +
                           str(i + 1) + '[' + str(coordinates_input[i + 1].x) +
                           ',' + str(coordinates_input[i + 1].y) + ',' +
                           str(coordinates_input[i + 1].z) + ']=' +
                           str(coordinates_input[i + 1].value) +
                           ' is larger than ' + str(max_dist) + '. Distance=' +
                           str(dist))

    def continuous_vertebral_levels(self):
        """
        This function transforms the vertebral levels file from the template into a continuous file.
        Instead of having integer representing the vertebral level on each slice, a continuous value that represents
        the position of the slice in the vertebral level coordinate system.
        The image must be RPI
        :return:
        """
        im_input = Image(self.image_input, self.verbose)
        im_output = Image(self.image_input, self.verbose)
        im_output.data *= 0

        # 1. extract vertebral levels from input image
        #   a. extract centerline
        #   b. for each slice, extract corresponding level
        nx, ny, nz, nt, px, py, pz, pt = im_input.dim
        from sct_straighten_spinalcord import smooth_centerline
        x_centerline_fit, y_centerline_fit, z_centerline_fit, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline(
            self.image_input, algo_fitting='nurbs', verbose=0)
        value_centerline = np.array([
            im_input.data[int(x_centerline_fit[it]),
                          int(y_centerline_fit[it]),
                          int(z_centerline_fit[it])]
            for it in range(len(z_centerline_fit))
        ])

        # 2. compute distance for each vertebral level --> Di for i being the vertebral levels
        vertebral_levels = {}
        for slice_image, level in enumerate(value_centerline):
            if level not in vertebral_levels:
                vertebral_levels[level] = slice_image

        length_levels = {}
        for level in vertebral_levels:
            indexes_slice = np.where(value_centerline == level)
            length_levels[level] = np.sum([
                math.sqrt(
                    ((x_centerline_fit[indexes_slice[0][index_slice + 1]] -
                      x_centerline_fit[indexes_slice[0][index_slice]]) *
                     px)**2 +
                    ((y_centerline_fit[indexes_slice[0][index_slice + 1]] -
                      y_centerline_fit[indexes_slice[0][index_slice]]) *
                     py)**2 +
                    ((z_centerline_fit[indexes_slice[0][index_slice + 1]] -
                      z_centerline_fit[indexes_slice[0][index_slice]]) *
                     pz)**2)
                for index_slice in range(len(indexes_slice[0]) - 1)
            ])

        # 2. for each slice:
        #   a. identify corresponding vertebral level --> i
        #   b. calculate distance of slice from upper vertebral level --> d
        #   c. compute relative distance in the vertebral level coordinate system --> d/Di
        continuous_values = {}
        for it, iz in enumerate(z_centerline_fit):
            level = value_centerline[it]
            indexes_slice = np.where(value_centerline == level)
            indexes_slice = indexes_slice[0][indexes_slice[0] >= it]
            distance_from_level = np.sum([
                math.sqrt(((x_centerline_fit[indexes_slice[index_slice + 1]] -
                            x_centerline_fit[indexes_slice[index_slice]]) *
                           px * px)**2 +
                          ((y_centerline_fit[indexes_slice[index_slice + 1]] -
                            y_centerline_fit[indexes_slice[index_slice]]) *
                           py * py)**2 +
                          ((z_centerline_fit[indexes_slice[index_slice + 1]] -
                            z_centerline_fit[indexes_slice[index_slice]]) *
                           pz * pz)**2)
                for index_slice in range(len(indexes_slice) - 1)
            ])
            continuous_values[iz] = level + 2.0 * distance_from_level / float(
                length_levels[level])

        # 3. saving data
        # for each slice, get all non-zero pixels and replace with continuous values
        coordinates_input = self.image_input.getNonZeroCoordinates()
        im_output.changeType('float32')
        # for all points in input, find the value that has to be set up, depending on the vertebral level
        for i, coord in enumerate(coordinates_input):
            im_output.data[int(coord.x),
                           int(coord.y),
                           int(coord.z)] = continuous_values[coord.z]

        return im_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.file_name
        params.output_file_name = self.fname_output
        params.subtitle = self.msg
        output = self.image_input.copy()
        output.data *= 0
        output.setFileName(self.fname_output)
        launch_sagittal_dialog(self.image_input, output, params)

        return output
Exemplo n.º 24
0
def validate_scad(folder_input):
    """
    Expecting folder to have the following structure :
    errsm_01:
    - t2
    -- errsm_01.nii.gz or t2.nii.gz
    :param folder_input:
    :return:
    """
    current_folder = os.getcwd()
    os.chdir(folder_input)
    try:
        patients = next(os.walk('.'))[1]
        for i in patients:
            if i != "errsm_01" and i !="errsm_02":
                directory = i + "/t2"
                os.chdir(directory)
                try:
                    if os.path.isfile(i+"_t2.nii.gz"):
                        raw_image = Image(i+"_t2.nii.gz")
                    elif os.path.isfile("t2.nii.gz"):
                        raw_image = Image("t2.nii.gz")
                    else:
                        raise Exception("t2.nii.gz or "+i+"_t2.nii.gz file is not found")

                    raw_orientation = raw_image.change_orientation()
                    SCAD(raw_image, contrast="t2", rm_tmp_file=1, verbose=1).test_debug()

                    manual_seg = Image(i+"_t2_manual_segmentation.nii.gz")
                    manual_orientation = manual_seg.change_orientation()

                    from scipy.ndimage.measurements import center_of_mass
                    # find COM
                    iterator = range(manual_seg.data.shape[2])
                    com_x = [0 for ix in iterator]
                    com_y = [0 for iy in iterator]

                    for iz in iterator:
                        com_x[iz], com_y[iz] = center_of_mass(manual_seg.data[:, :, iz])
                    #raw_image.change_orientation(raw_orientation)
                    #manual_seg.change_orientation(manual_orientation)

                    centerline_scad = Image(i+"_t2_centerline.nii.gz")
                    os.remove(i+"_t2_centerline.nii.gz")

                    centerline_scad.change_orientation()
                    distance = []
                    for iz in range(centerline_scad.data.shape[2]):
                        ind1 = np.argmax(centerline_scad.data[:, :, iz])
                        X,Y = scad.ind2sub(centerline_scad.data[:, :, i].shape,ind1)
                        com_phys = centerline_scad.transfo_pix2phys([[com_x[iz], com_y[iz], iz]])
                        scad_phys = centerline_scad.transfo_pix2phys([[X, Y, iz]])
                        distance_magnitude = np.linalg.norm(com_phys-scad_phys)
                        distance.append(distance_magnitude)



                    os.chdir(folder_input)

                except Exception, e:
                    print e.message
                pass
    except Exception, e:
        print e.message
def project_labels_on_spinalcord(fname_label, fname_seg):
    """
    Project labels orthogonally on the spinal cord centerline. The algorithm works by finding the smallest distance
    between each label and the spinal cord center of mass.
    :param fname_label: file name of labels
    :param fname_seg: file name of cord segmentation (could also be of centerline)
    :return: file name of projected labels
    """
    # build output name
    fname_label_projected = sct.add_suffix(fname_label, "_projected")
    # open labels and segmentation
    im_label = Image(fname_label)
    im_seg = Image(fname_seg)
    # orient to RPI
    native_orient = im_seg.change_orientation('RPI')
    im_label.change_orientation('RPI')
    # smooth centerline and return fitted coordinates in voxel space
    centerline_x, centerline_y, centerline_z, centerline_derivx, centerline_derivy, centerline_derivz = smooth_centerline(
        im_seg,
        algo_fitting="hanning",
        type_window="hanning",
        window_length=50,
        nurbs_pts_number=3000,
        phys_coordinates=False,
        all_slices=True)
    # convert pixel into physical coordinates
    centerline_xyz_transposed = [
        im_seg.transfo_pix2phys(
            [[centerline_x[i], centerline_y[i], centerline_z[i]]])[0]
        for i in range(len(centerline_x))
    ]
    # transpose list
    centerline_phys_x, centerline_phys_y, centerline_phys_z = map(
        list, map(None, *centerline_xyz_transposed))
    # get center of mass of label
    labels = im_label.getCoordinatesAveragedByValue()
    # initialize image of projected labels. Note that we use the space of the seg (not label).
    im_label_projected = im_seg.copy()
    im_label_projected.data = np.zeros(im_label_projected.data.shape,
                                       dtype='uint8')
    # loop across label values
    for label in labels:
        # convert pixel into physical coordinates for the label
        label_phys_x, label_phys_y, label_phys_z = im_label.transfo_pix2phys(
            [[label.x, label.y, label.z]])[0]
        # calculate distance between label and each point of the centerline
        distance_centerline = [
            np.linalg.norm([
                centerline_phys_x[i] - label_phys_x,
                centerline_phys_y[i] - label_phys_y,
                centerline_phys_z[i] - label_phys_z
            ]) for i in range(len(centerline_x))
        ]
        # get the index corresponding to the min distance
        ind_min_distance = np.argmin(distance_centerline)
        # get centerline coordinate (in physical space)
        [min_phy_x, min_phy_y, min_phy_z] = [
            centerline_phys_x[ind_min_distance],
            centerline_phys_y[ind_min_distance],
            centerline_phys_z[ind_min_distance]
        ]
        # convert coordinate to voxel space
        minx, miny, minz = im_seg.transfo_phys2pix(
            [[min_phy_x, min_phy_y, min_phy_z]])[0]
        # use that index to assign projected label in the centerline
        im_label_projected.data[minx, miny, minz] = label.value
    # re-orient projected labels to native orientation and save
    im_label_projected.change_orientation(
        native_orient)  # note: native_orient refers to im_seg (not im_label)
    im_label_projected.setFileName(fname_label_projected)
    im_label_projected.save()
    return fname_label_projected
def main(args=None):

    # initializations
    initz = ''
    initcenter = ''

    # check user arguments
    if not args:
        args = sys.argv[1:]

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(sys.argv[1:])
    fname_in = arguments["-i"]
    fname_seg = arguments['-s']
    # contrast = arguments['-t']
    if '-o' in arguments:
        fname_out = arguments["-o"]
    else:
        fname_out = ''
    if '-initz' in arguments:
        initz = arguments['-initz']
    if '-initcenter' in arguments:
        initcenter = arguments['-initcenter']
    verbose = int(arguments['-v'])
    remove_tmp_files = int(arguments['-r'])
    denoise = int(arguments['-denoise'])
    laplacian = int(arguments['-laplacian'])

    # create temporary folder
    printv('\nCreate temporary folder...', verbose)
    path_tmp = slash_at_the_end('tmp.'+strftime("%y%m%d%H%M%S"), 1)
    run('mkdir '+path_tmp, verbose)

    # Copying input data to tmp folder
    printv('\nCopying input data to tmp folder...', verbose)
    run('sct_convert -i '+fname_in+' -o '+path_tmp+'data.nii')
    run('sct_convert -i '+fname_seg+' -o '+path_tmp+'segmentation.nii.gz')

    # Go go temp folder
    # path_tmp = '/Users/julien/data/biospective/20151013_demo_spinalcordv2.1.b9/200_006_s2_T2/tmp.151013175622/'
    chdir(path_tmp)

    # create label to identify disc
    printv('\nCreate label to identify disc...', verbose)
    if initz:
        create_label_z('segmentation.nii.gz', initz[0], initz[1])  # create label located at z_center
    elif initcenter:
        # find z centered in FOV
        nii = Image('segmentation.nii.gz')
        nii.change_orientation('RPI')  # reorient to RPI
        nx, ny, nz, nt, px, py, pz, pt = nii.dim  # Get dimensions
        z_center = int(round(nz/2))  # get z_center
        create_label_z('segmentation.nii.gz', z_center, initcenter)  # create label located at z_center
    else:
        printv('\nERROR: You need to initialize the disc detection algorithm using one of these two options: -initz, -initcenter\n', 1, 'error')

    # Straighten spinal cord
    printv('\nStraighten spinal cord...', verbose)
    run('sct_straighten_spinalcord -i data.nii -s segmentation.nii.gz -r 0 -param all_labels=0,bspline_meshsize=3x3x5 -qc 0')  # here using all_labels=0 because of issue #610

    # Apply straightening to segmentation
    # N.B. Output is RPI
    printv('\nApply straightening to segmentation...', verbose)
    run('sct_apply_transfo -i segmentation.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear')
    # Threshold segmentation to 0.5
    run('sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz')

    # Apply straightening to z-label
    printv('\nDilate z-label and apply straightening...', verbose)
    run('sct_apply_transfo -i labelz.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn')

    # get z value and disk value to initialize labeling
    printv('\nGet z and disc values from straight label...', verbose)
    init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz')
    printv('.. '+str(init_disc), verbose)

    # denoise data
    if denoise:
        printv('\nDenoise data...', verbose)
        run('sct_maths -i data_straight.nii -denoise h=0.05 -o data_straight.nii')

    # apply laplacian filtering
    if laplacian:
        printv('\nApply Laplacian filter...', verbose)
        run('sct_maths -i data_straight.nii -laplace 1 -o data_straight.nii')

    # detect vertebral levels on straight spinal cord
    vertebral_detection('data_straight.nii', 'segmentation_straight.nii.gz', init_disc, verbose)

    # un-straighten labelled spinal cord
    printv('\nUn-straighten labeling...', verbose)
    run('sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn')

    # Clean labeled segmentation
    printv('\nClean labeled segmentation (correct interpolation errors)...', verbose)
    clean_labeled_segmentation('segmentation_labeled.nii.gz', 'segmentation.nii.gz', 'segmentation_labeled.nii.gz')

    # Build fname_out
    if fname_out == '':
        path_seg, file_seg, ext_seg = extract_fname(fname_seg)
        fname_out = path_seg+file_seg+'_labeled'+ext_seg

    # come back to parent folder
    chdir('..')

    # Generate output files
    printv('\nGenerate output files...', verbose)
    generate_output_file(path_tmp+'segmentation_labeled.nii.gz', fname_out)

    # Remove temporary files
    if remove_tmp_files == 1:
        printv('\nRemove temporary files...', verbose)
        run('rm -rf '+path_tmp)

    # to view results
    printv('\nDone! To view results, type:', verbose)
    printv('fslview '+fname_in+' '+fname_out+' -l Random-Rainbow -t 0.5 &\n', verbose, 'info')
def main(args=None):

    # initializations
    initz = ''
    initcenter = ''

    # check user arguments
    if not args:
        args = sys.argv[1:]

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(sys.argv[1:])
    fname_in = arguments["-i"]
    fname_seg = arguments['-seg']
    contrast = arguments['-t']
    if '-o' in arguments:
        fname_out = arguments["-o"]
    else:
        fname_out = ''
    if '-initz' in arguments:
        initz = arguments['-initz']
    if '-initcenter' in arguments:
        initcenter = arguments['-initcenter']
    param.verbose = int(arguments['-v'])
    param.remove_tmp_files = int(arguments['-r'])

    # create temporary folder
    printv('\nCreate temporary folder...', param.verbose)
    path_tmp = slash_at_the_end('tmp.'+strftime("%y%m%d%H%M%S"), 1)
    run('mkdir '+path_tmp, param.verbose)

    # Copying input data to tmp folder
    printv('\nCopying input data to tmp folder...', param.verbose)
    run('sct_convert -i '+fname_in+' -o '+path_tmp+'data.nii')
    run('sct_convert -i '+fname_seg+' -o '+path_tmp+'segmentation.nii.gz')

    # Go go temp folder
    path_tmp = '/Users/julien/data/sct_debug/tmp.150826170018/'
    chdir(path_tmp)

    # create label to identify disc
    printv('\nCreate label to identify disc...', param.verbose)
    if initz:
        create_label_z('segmentation.nii.gz', initz[0], initz[1])  # create label located at z_center
    elif initcenter:
        # find z centered in FOV
        nii = Image(fname_seg)
        nii.change_orientation('RPI')  # reorient to RPI
        nx, ny, nz, nt, px, py, pz, pt = nii.dim  # Get dimensions
        z_center = int(round(nz/2))  # get z_center
        create_label_z('segmentation.nii.gz', z_center, initcenter)  # create label located at z_center

    # # Straighten spinal cord
    # printv('\nStraighten spinal cord...', param.verbose)
    # run('sct_straighten_spinalcord -i data.nii -c segmentation.nii.gz')

    # Apply straightening to segmentation
    # N.B. Output is RPI
    printv('\nApply straightening to segmentation...', param.verbose)
    run('sct_apply_transfo -i segmentation.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear')
    # Threshold segmentation to 0.5
    run('sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz')

    # Apply straightening to z-label
    printv('\nDilate z-label and apply straightening...', param.verbose)
    run('sct_apply_transfo -i labelz.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn')

    # get z value and disk value to initialize labeling
    printv('\nGet z and disc values from straight label...', param.verbose)
    init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz')
    printv('.. '+str(init_disc), param.verbose)

    # detect vertebral levels on straight spinal cord
    printv('\nDetect inter-vertebral discs and label vertebral levels...', param.verbose)
    vertebral_detection('data_straight.nii', 'segmentation_straight.nii.gz', contrast, init_disc)

    # un-straighten spinal cord
    printv('\nUn-straighten labeling...', param.verbose)
    run('sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn')

    # clean labeled segmentation
    # TODO: (i) find missing voxels wrt. original segmentation, and attribute value closest to neighboring label and (ii) remove additional voxels.

    # Build fname_out
    if fname_out == '':
        path_seg, file_seg, ext_seg = extract_fname(fname_seg)
        fname_out = path_seg+file_seg+'_labeled'+ext_seg

    # come back to parent folder
    chdir('..')

    # Generate output files
    printv('\nGenerate output files...', param.verbose)
    generate_output_file(path_tmp+'segmentation_labeled.nii.gz', fname_out)

    # Remove temporary files
    if param.remove_tmp_files == 1:
        printv('\nRemove temporary files...', param.verbose)
        run('rm -rf '+path_tmp)

    # to view results
    printv('\nDone! To view results, type:', param.verbose)
    printv('fslview '+fname_in+' '+fname_out+' &\n', param.verbose, 'info')
    def validation(self):
        ext = '.nii.gz'
        validation_dir = 'validation'
        sct.run('mkdir ' + validation_dir)

        # loading the images
        im_ref_gm_seg = Image('../' + self.ref_gm_seg_fname)
        im_ref_wm_seg = inverse_gmseg_to_wmseg(im_ref_gm_seg, Image('../' + self.sc_seg_fname), im_ref_gm_seg.path + im_ref_gm_seg.file_name, save=False)

        res_gm_seg_bin = self.gm_seg.res_gm_seg.copy()
        res_wm_seg_bin = self.gm_seg.res_wm_seg.copy()

        if self.param.res_type == 'prob':
            res_gm_seg_bin.data = np.asarray((res_gm_seg_bin.data >= 0.5).astype(int))
            res_wm_seg_bin.data = np.asarray((res_wm_seg_bin.data >= 0.50001).astype(int))

        mask = Image(self.preprocessed.square_mask)

        # doing the validation
        os.chdir(validation_dir)

        im_ref_gm_seg.path = './'
        im_ref_gm_seg.file_name = 'ref_gm_seg'
        im_ref_gm_seg.ext = ext
        im_ref_gm_seg.save()
        ref_gm_seg_new_name = resample_image(im_ref_gm_seg.file_name + ext, npx=self.preprocessed.resample_to, npy=self.preprocessed.resample_to, binary=True)
        im_ref_gm_seg = Image(ref_gm_seg_new_name)
        sct.run('rm ' + ref_gm_seg_new_name)

        im_ref_wm_seg.path = './'
        im_ref_wm_seg.file_name = 'ref_wm_seg'
        im_ref_wm_seg.ext = ext
        im_ref_wm_seg.save()
        ref_wm_seg_new_name = resample_image(im_ref_wm_seg.file_name + ext, npx=self.preprocessed.resample_to, npy=self.preprocessed.resample_to, binary=True)
        im_ref_wm_seg = Image(ref_wm_seg_new_name)
        sct.run('rm ' + ref_wm_seg_new_name)

        ref_orientation = im_ref_gm_seg.orientation
        im_ref_gm_seg.change_orientation('IRP')
        im_ref_wm_seg.change_orientation('IRP')

        im_ref_gm_seg.crop_and_stack(mask, save=False)
        im_ref_wm_seg.crop_and_stack(mask, save=False)

        im_ref_gm_seg.change_orientation('RPI')
        im_ref_wm_seg.change_orientation('RPI')

        # saving the images to call the validation functions
        res_gm_seg_bin.path = './'
        res_gm_seg_bin.file_name = 'res_gm_seg_bin'
        res_gm_seg_bin.ext = ext
        res_gm_seg_bin.save()

        res_wm_seg_bin.path = './'
        res_wm_seg_bin.file_name = 'res_wm_seg_bin'
        res_wm_seg_bin.ext = ext
        res_wm_seg_bin.save()

        im_ref_gm_seg.path = './'
        im_ref_gm_seg.file_name = 'ref_gm_seg'
        im_ref_gm_seg.ext = ext
        im_ref_gm_seg.save()

        im_ref_wm_seg.path = './'
        im_ref_wm_seg.file_name = 'ref_wm_seg'
        im_ref_wm_seg.ext = ext
        im_ref_wm_seg.save()

        sct.run('sct_orientation -i ' + res_gm_seg_bin.file_name + ext + ' -s RPI')
        res_gm_seg_bin.file_name += '_RPI'
        sct.run('sct_orientation -i ' + res_wm_seg_bin.file_name + ext + ' -s RPI')
        res_wm_seg_bin.file_name += '_RPI'

        res_gm_seg_bin = Image(res_gm_seg_bin.file_name + ext)
        im_ref_gm_seg.hdr.set_zooms(res_gm_seg_bin.hdr.get_zooms())  # correcting the pix dimension
        im_ref_gm_seg.save()

        res_wm_seg_bin = Image(res_wm_seg_bin.file_name + ext)
        im_ref_wm_seg.hdr.set_zooms(res_wm_seg_bin.hdr.get_zooms())  # correcting the pix dimension
        im_ref_wm_seg.save()

        # Dice
        try:
            status_gm, output_gm = sct.run('sct_dice_coefficient ' + im_ref_gm_seg.file_name + ext + ' ' + res_gm_seg_bin.file_name + ext + '  -2d-slices 2', error_exit='warning', raise_exception=True)
        except Exception:
            sct.run('c3d ' + res_gm_seg_bin.file_name + ext + ' ' + im_ref_gm_seg.file_name + ext + ' -reslice-identity -o ' + im_ref_gm_seg.file_name + '_in_res_space' + ext)
            sct.run('fslmaths ' + im_ref_gm_seg.file_name + '_in_res_space' + ext + ' -thr 0.1 ' + im_ref_gm_seg.file_name + '_in_res_space' + ext )
            sct.run('fslmaths ' + im_ref_gm_seg.file_name + '_in_res_space' + ext + ' -bin ' + im_ref_gm_seg.file_name + '_in_res_space' + ext )
            status_gm, output_gm = sct.run('sct_dice_coefficient ' + im_ref_gm_seg.file_name + '_in_res_space' + ext + ' ' + res_gm_seg_bin.file_name + ext + '  -2d-slices 2', error_exit='warning')
        try:
            status_wm, output_wm = sct.run('sct_dice_coefficient ' + im_ref_wm_seg.file_name + ext + ' ' + res_wm_seg_bin.file_name + ext + '  -2d-slices 2', error_exit='warning', raise_exception=True)
        except Exception:
            sct.run('c3d ' + res_wm_seg_bin.file_name + ext + ' ' + im_ref_wm_seg.file_name + ext + ' -reslice-identity -o ' + im_ref_wm_seg.file_name + '_in_res_space' + ext)
            sct.run('fslmaths ' + im_ref_wm_seg.file_name + '_in_res_space' + ext + ' -thr 0.1 ' + im_ref_wm_seg.file_name + '_in_res_space' + ext)
            sct.run('fslmaths ' + im_ref_wm_seg.file_name + '_in_res_space' + ext + ' -bin ' + im_ref_wm_seg.file_name + '_in_res_space' + ext)
            status_wm, output_wm = sct.run('sct_dice_coefficient ' + im_ref_wm_seg.file_name + '_in_res_space' + ext + ' ' + res_wm_seg_bin.file_name + ext + '  -2d-slices 2', error_exit='warning')

        dice_name = 'dice_' + sct.extract_fname(self.target_fname)[1] + '_' + self.param.res_type + '.txt'
        dice_fic = open('../../' + dice_name, 'w')
        if self.param.res_type == 'prob':
            dice_fic.write('WARNING : the probabilistic segmentations were binarized with a threshold at 0.5 to compute the dice coefficient \n')
        dice_fic.write('\n--------------------------------------------------------------\n'
                       'Dice coefficient on the Gray Matter segmentation:\n')
        dice_fic.write(output_gm)
        dice_fic.write('\n\n--------------------------------------------------------------\n'
                       'Dice coefficient on the White Matter segmentation:\n')
        dice_fic.write(output_wm)
        dice_fic.close()
        # sct.run(' mv ./' + dice_name + ' ../')

        hd_name = 'hd_' + sct.extract_fname(self.target_fname)[1] + '_' + self.param.res_type + '.txt'
        sct.run('sct_compute_hausdorff_distance.py -i ' + res_gm_seg_bin.file_name + ext + ' -r ' + im_ref_gm_seg.file_name + ext + ' -t 1 -o ' + hd_name + ' -v ' + str(self.param.verbose))
        sct.run('mv ./' + hd_name + ' ../../')

        os.chdir('..')
        return dice_name, hd_name
Exemplo n.º 29
0
def load_level(list_slices_target, fname_level):
    verbose = 1
    path_level, file_level, ext_level = extract_fname(fname_level)

    #  ####### Check if the level file is an image or a text file
    # Level file is an image
    if ext_level in ['.nii', '.nii.gz']:
        im_level = Image(fname_level)
        im_level.change_orientation('IRP')

        list_level = []
        list_med_level = []
        for slice_level in im_level.data:
            try:
                # vertebral level of the slice
                l = np.mean(slice_level[slice_level > 0])
                # median of the vertebral level of the slice: if all voxels are int, med will be an int.
                med = np.median(slice_level[slice_level > 0])
                # change med in int if it is an int
                med = int(med) if int(med) == med else med
            except Exception as e:
                printv(
                    'WARNING: ' + str(e) +
                    '\nNo level label found. Level will be set to 0 for this slice',
                    verbose, 'warning')
                l = 0
                med = 0
            list_level.append(l)
            list_med_level.append(med)

        # if all median of level are int for all slices : consider level as int
        if all([isinstance(med, int) for med in list_med_level]):
            # level as int are placed in the middle of each vertebra (that's why there is a "+0.5")
            list_level = [int(round(l)) + 0.5 for l in list_level]

    # Level file is a text file
    elif ext_level == '.txt':
        file_level = open(fname_level, 'r')
        lines_level = file_level.readlines()
        file_level.close()

        list_level_by_slice = []
        list_type_level = []  # True or int, False for float
        for line in lines_level[1:]:
            i_slice, level = line.split(',')

            # correct level value
            for c in [' ', '\n', '\r', '\t']:
                level = level.replace(c, '')

            try:
                level = float(level)
            except Exception as e:
                # adapt if level value is not unique
                if len(level) > 2:
                    l1 = l2 = 0
                    if '-' in level:
                        l1, l2 = level.split('-')
                    elif '/' in level:
                        l1, l2 = level.split('/')
                    # convention = the vertebral disk between two levels belong to the lower level (C2-C3 = C3)
                    level = max(float(l1), float(l2))
                else:
                    # level unrecognized
                    level = 0

            i_slice = int(i_slice)

            list_type_level.append(int(level) == level)
            list_level_by_slice.append((i_slice, level))

        # sort list by number of slice
        list_level_by_slice.sort()

        if all(list_type_level):  # levels are int
            # add 0.5 to the int level to place in the middle of the vertebra
            to_add = 0.5
        else:
            # levels are float: keep them untouched
            to_add = 0

        list_level = [l[1] + to_add for l in list_level_by_slice]

    # Level file is not recognized
    else:
        list_level = None
        printv('ERROR: the level file is nor an image nor a text file ...',
               verbose, 'error')

    #  ####### Set level number for each slice of list_slices_target:
    for target_slice, level in zip(list_slices_target, list_level):
        target_slice.set(level=level)

    return list_slices_target
    def execute(self):
        print 'Execution of the SCAD algorithm in ' + str(os.getcwd())

        original_name = self.input_image.file_name
        vesselness_file_name = "imageVesselNessFilter.nii.gz"
        raw_file_name = "raw.nii"

        self.setup_debug_folder()

        if self.debug:
            import matplotlib.pyplot as plt  # import for debug purposes

        # create tmp and copy input
        path_tmp = self.create_temporary_path()
        conv.convert(self.input_image.absolutepath, path_tmp + raw_file_name)

        if self.vesselness_provided:
            sct.run('cp ' + vesselness_file_name + ' ' + path_tmp +
                    vesselness_file_name)
        os.chdir(path_tmp)

        # get input image information
        img = Image(raw_file_name)

        # save original orientation and change image to RPI
        self.raw_orientation = img.change_orientation()

        # get body symmetry
        if self.enable_symmetry:
            from msct_image import change_data_orientation
            sym = SymmetryDetector(raw_file_name, self.contrast, crop_xy=0)
            self.raw_symmetry = sym.execute()
            img.change_orientation(self.raw_orientation)
            self.output_debug_file(img, self.raw_symmetry, "body_symmetry")
            img.change_orientation()

        # vesselness filter
        if not self.vesselness_provided:
            sct.run('isct_vesselness -i ' + raw_file_name + ' -t ' +
                    self._contrast + " -radius " + str(self.spinalcord_radius))

        # load vesselness filter data and perform minimum path on it
        img = Image(vesselness_file_name)
        self.output_debug_file(img, img.data, "Vesselness_Filter")
        img.change_orientation()
        self.minimum_path_data, self.J1_min_path, self.J2_min_path = get_minimum_path(
            img.data, invert=1, debug=1)
        self.output_debug_file(img, self.minimum_path_data, "minimal_path")
        self.output_debug_file(img, self.J1_min_path, "J1_minimal_path")
        self.output_debug_file(img, self.J2_min_path, "J2_minimal_path")

        # Apply an exponent to the minimum path
        self.minimum_path_powered = np.power(self.minimum_path_data,
                                             self.minimum_path_exponent)
        self.output_debug_file(
            img, self.minimum_path_powered,
            "minimal_path_power_" + str(self.minimum_path_exponent))

        # Saving in Image since smooth_minimal_path needs pixel dimensions
        img.data = self.minimum_path_powered

        # smooth resulting minimal path
        self.smoothed_min_path = smooth_minimal_path(img)
        self.output_debug_file(img, self.smoothed_min_path.data,
                               "minimal_path_smooth")

        # normalise symmetry values between 0 and 1
        if self.enable_symmetry:
            normalised_symmetry = normalize_array_histogram(self.raw_symmetry)
            self.output_debug_file(img, self.smoothed_min_path.data,
                                   "minimal_path_smooth")

            # multiply normalised symmetry data with the minimum path result
            from msct_image import change_data_orientation
            self.spine_detect_data = np.multiply(
                self.smoothed_min_path.data,
                change_data_orientation(
                    np.power(normalised_symmetry, self.symmetry_exponent),
                    self.raw_orientation, "RPI"))
            self.output_debug_file(img, self.spine_detect_data,
                                   "symmetry_x_min_path")
            # extract the centerline from the minimal path image
            self.centerline_with_outliers = get_centerline(
                self.spine_detect_data, self.spine_detect_data.shape)
        else:
            # extract the centerline from the minimal path image
            self.centerline_with_outliers = get_centerline(
                self.smoothed_min_path.data, self.smoothed_min_path.data.shape)
        self.output_debug_file(img, self.centerline_with_outliers,
                               "centerline_with_outliers")

        # saving centerline with outliers to have
        img.data = self.centerline_with_outliers
        img.change_orientation()
        img.file_name = "centerline_with_outliers"
        img.save()

        # use a b-spline to smooth out the centerline
        x, y, z, dx, dy, dz = smooth_centerline(
            "centerline_with_outliers.nii.gz")

        # save the centerline
        nx, ny, nz, nt, px, py, pz, pt = img.dim
        img.data = np.zeros((nx, ny, nz))
        for i in range(0, np.size(x) - 1):
            img.data[int(x[i]), int(y[i]), int(z[i])] = 1

        self.output_debug_file(img, img.data, "centerline")
        img.change_orientation(self.raw_orientation)
        img.file_name = "centerline"
        img.save()

        # copy back centerline
        os.chdir('../')
        conv.convert(path_tmp + img.file_name + img.ext, self.output_filename)
        if self.rm_tmp_file == 1:
            import shutil
            shutil.rmtree(path_tmp)

        print "To view the output with FSL :"
        sct.printv(
            "fslview " + self.input_image.absolutepath + " " +
            self.output_filename + " -l Red", self.verbose, "info")
Exemplo n.º 31
0
    def execute(self):
        print 'Execution of the SCAD algorithm'

        vesselness_file_name = "imageVesselNessFilter.nii.gz"
        raw_file_name = "raw.nii"

        if self.debug:
            import matplotlib.pyplot as plt # import for debug purposes

        # create tmp and copy input
        path_tmp = sct.tmp_create()
        sct.tmp_copy_nifti(self.input_image.absolutepath, path_tmp, raw_file_name)

        if self.vesselness_provided:
            sct.run('cp '+vesselness_file_name+' '+path_tmp+vesselness_file_name)
        os.chdir(path_tmp)

        # get input image information
        img = Image(raw_file_name)

        # save original orientation and change image to RPI
        self.raw_orientation = img.change_orientation()

        # get body symmetry
        sym = SymmetryDetector(raw_file_name, self.contrast, crop_xy=1)
        self.raw_symmetry = sym.execute()

        # vesselness filter
        if not self.vesselness_provided:
            sct.run('sct_vesselness -i '+raw_file_name+' -t ' + self._contrast)

        # load vesselness filter data and perform minimum path on it
        img = Image(vesselness_file_name)
        raw_orientation = img.change_orientation()
        self.minimum_path_data, self.J1_min_path, self.J2_min_path = get_minimum_path(img.data, invert=1, debug=1, smooth_factor=1)

        # Apply an exponent to the minimum path
        self.minimum_path_powered = np.power(self.minimum_path_data, self.minimum_path_exponent)

        # Saving in Image since smooth_minimal_path needs pixel dimensions
        img.data = self.minimum_path_powered

        # smooth resulting minimal path
        self.smoothed_min_path = smooth_minimal_path(img)

        # normalise symmetry values between 0 and 1
        normalised_symmetry = equalize_array_histogram(self.raw_symmetry)

        # multiply normalised symmetry data with the minimum path result
        self.spine_detect_data = np.multiply(self.smoothed_min_path.data, normalised_symmetry)

        # extract the centerline from the minimal path image
        centerline_with_outliers = get_centerline(self.spine_detect_data, self.spine_detect_data.shape)
        img.data = centerline_with_outliers
        img.change_orientation()
        img.file_name = "centerline_with_outliers"
        img.save()

        # use a b-spline to smooth out the centerline
        x, y, z, dx, dy, dz = smooth_centerline("centerline_with_outliers.nii.gz")

        # save the centerline
        centerline_dim = img.dim
        img.data = np.zeros(centerline_dim)
        for i in range(0, np.size(x)-1):
            img.data[int(x[i]), int(y[i]), int(z[i])] = 1

        img.change_orientation(raw_orientation)
        img.file_name = "centerline"
        img.save()

        # copy back centerline
        os.chdir('../')
        sct.tmp_copy_nifti(path_tmp + 'centerline.nii.gz',self.input_image.path,self.input_image.file_name+'_centerline'+self.input_image.ext)

        if self.rm_tmp_file == 1:
            import shutil
            shutil.rmtree(path_tmp)

        if self.produce_output:
            self.produce_output_files()
    # path_tmp = tmp_create()
    # tmp_copy_nifti(input_file, path_tmp, 'raw.nii')
    # run('cp '+warping_fields_filename[0]+' '+path_tmp)
    # chdir(path_tmp)
    run('mkdir images')
    run('mkdir niftis')
    while True:
        try:
            warping_fields[0].num_of_frames = number_of_frames
            image_output_iter, iteration = warping_fields[0].next()
            image_output_iter.save()
            filename_warp = image_output_iter.path + image_output_iter.file_name + image_output_iter.ext
            filename_output = "niftis/tmp.warped_image_" + str(
                iteration - 1) + image_output_iter.ext
            run("sct_apply_transfo -i " + input_file + " -d " +
                reference_image + " -w " + filename_warp + " -o " +
                filename_output)
            result = Image(filename_output)
            result.change_orientation()

            toimage(result.data[int(result.data.shape[0] / 2)].squeeze(),
                    cmin=0.0).save('images/' +
                                   extract_fname(filename_output)[1] + '.jpg')
            filenames_output.append(filename_output)
        except ValueError:
            printv('\nError during warping field generation...', 1, 'error')
        except StopIteration:
            printv('\nFinished iterations.')
            break
    def validation(self):
        ext = '.nii.gz'
        validation_dir = 'validation'
        sct.run('mkdir ' + validation_dir)

        # loading the images
        im_ref_gm_seg = Image('../' + self.ref_gm_seg_fname)
        im_ref_wm_seg = inverse_gmseg_to_wmseg(
            im_ref_gm_seg,
            Image('../' + self.sc_seg_fname),
            im_ref_gm_seg.path + im_ref_gm_seg.file_name,
            save=False)

        res_gm_seg_bin = self.gm_seg.res_gm_seg.copy()
        res_wm_seg_bin = self.gm_seg.res_wm_seg.copy()

        if self.param.res_type == 'prob':
            res_gm_seg_bin.data = np.asarray(
                (res_gm_seg_bin.data >= 0.5).astype(int))
            res_wm_seg_bin.data = np.asarray(
                (res_wm_seg_bin.data >= 0.50001).astype(int))

        mask = Image(self.preprocessed.square_mask)

        # doing the validation
        os.chdir(validation_dir)

        im_ref_gm_seg.path = './'
        im_ref_gm_seg.file_name = 'ref_gm_seg'
        im_ref_gm_seg.ext = ext
        im_ref_gm_seg.save()
        ref_gm_seg_new_name = resample_image(im_ref_gm_seg.file_name + ext,
                                             npx=self.preprocessed.resample_to,
                                             npy=self.preprocessed.resample_to,
                                             binary=True)
        im_ref_gm_seg = Image(ref_gm_seg_new_name)
        sct.run('rm ' + ref_gm_seg_new_name)

        im_ref_wm_seg.path = './'
        im_ref_wm_seg.file_name = 'ref_wm_seg'
        im_ref_wm_seg.ext = ext
        im_ref_wm_seg.save()
        ref_wm_seg_new_name = resample_image(im_ref_wm_seg.file_name + ext,
                                             npx=self.preprocessed.resample_to,
                                             npy=self.preprocessed.resample_to,
                                             binary=True)
        im_ref_wm_seg = Image(ref_wm_seg_new_name)
        sct.run('rm ' + ref_wm_seg_new_name)

        ref_orientation = im_ref_gm_seg.orientation
        im_ref_gm_seg.change_orientation('IRP')
        im_ref_wm_seg.change_orientation('IRP')

        im_ref_gm_seg.crop_and_stack(mask, save=False)
        im_ref_wm_seg.crop_and_stack(mask, save=False)

        im_ref_gm_seg.change_orientation('RPI')
        im_ref_wm_seg.change_orientation('RPI')

        # saving the images to call the validation functions
        res_gm_seg_bin.path = './'
        res_gm_seg_bin.file_name = 'res_gm_seg_bin'
        res_gm_seg_bin.ext = ext
        res_gm_seg_bin.save()

        res_wm_seg_bin.path = './'
        res_wm_seg_bin.file_name = 'res_wm_seg_bin'
        res_wm_seg_bin.ext = ext
        res_wm_seg_bin.save()

        im_ref_gm_seg.path = './'
        im_ref_gm_seg.file_name = 'ref_gm_seg'
        im_ref_gm_seg.ext = ext
        im_ref_gm_seg.save()

        im_ref_wm_seg.path = './'
        im_ref_wm_seg.file_name = 'ref_wm_seg'
        im_ref_wm_seg.ext = ext
        im_ref_wm_seg.save()

        sct.run('sct_orientation -i ' + res_gm_seg_bin.file_name + ext +
                ' -s RPI')
        res_gm_seg_bin.file_name += '_RPI'
        sct.run('sct_orientation -i ' + res_wm_seg_bin.file_name + ext +
                ' -s RPI')
        res_wm_seg_bin.file_name += '_RPI'

        res_gm_seg_bin = Image(res_gm_seg_bin.file_name + ext)
        im_ref_gm_seg.hdr.set_zooms(
            res_gm_seg_bin.hdr.get_zooms())  # correcting the pix dimension
        im_ref_gm_seg.save()

        res_wm_seg_bin = Image(res_wm_seg_bin.file_name + ext)
        im_ref_wm_seg.hdr.set_zooms(
            res_wm_seg_bin.hdr.get_zooms())  # correcting the pix dimension
        im_ref_wm_seg.save()

        # Dice
        try:
            status_gm, output_gm = sct.run(
                'sct_dice_coefficient ' + im_ref_gm_seg.file_name + ext + ' ' +
                res_gm_seg_bin.file_name + ext + '  -2d-slices 2',
                error_exit='warning',
                raise_exception=True)
        except Exception:
            sct.run('c3d ' + res_gm_seg_bin.file_name + ext + ' ' +
                    im_ref_gm_seg.file_name + ext + ' -reslice-identity -o ' +
                    im_ref_gm_seg.file_name + '_in_res_space' + ext)
            sct.run('fslmaths ' + im_ref_gm_seg.file_name + '_in_res_space' +
                    ext + ' -thr 0.1 ' + im_ref_gm_seg.file_name +
                    '_in_res_space' + ext)
            sct.run('fslmaths ' + im_ref_gm_seg.file_name + '_in_res_space' +
                    ext + ' -bin ' + im_ref_gm_seg.file_name +
                    '_in_res_space' + ext)
            status_gm, output_gm = sct.run(
                'sct_dice_coefficient ' + im_ref_gm_seg.file_name +
                '_in_res_space' + ext + ' ' + res_gm_seg_bin.file_name + ext +
                '  -2d-slices 2',
                error_exit='warning')
        try:
            status_wm, output_wm = sct.run(
                'sct_dice_coefficient ' + im_ref_wm_seg.file_name + ext + ' ' +
                res_wm_seg_bin.file_name + ext + '  -2d-slices 2',
                error_exit='warning',
                raise_exception=True)
        except Exception:
            sct.run('c3d ' + res_wm_seg_bin.file_name + ext + ' ' +
                    im_ref_wm_seg.file_name + ext + ' -reslice-identity -o ' +
                    im_ref_wm_seg.file_name + '_in_res_space' + ext)
            sct.run('fslmaths ' + im_ref_wm_seg.file_name + '_in_res_space' +
                    ext + ' -thr 0.1 ' + im_ref_wm_seg.file_name +
                    '_in_res_space' + ext)
            sct.run('fslmaths ' + im_ref_wm_seg.file_name + '_in_res_space' +
                    ext + ' -bin ' + im_ref_wm_seg.file_name +
                    '_in_res_space' + ext)
            status_wm, output_wm = sct.run(
                'sct_dice_coefficient ' + im_ref_wm_seg.file_name +
                '_in_res_space' + ext + ' ' + res_wm_seg_bin.file_name + ext +
                '  -2d-slices 2',
                error_exit='warning')

        dice_name = 'dice_' + sct.extract_fname(
            self.target_fname)[1] + '_' + self.param.res_type + '.txt'
        dice_fic = open('../../' + dice_name, 'w')
        if self.param.res_type == 'prob':
            dice_fic.write(
                'WARNING : the probabilistic segmentations were binarized with a threshold at 0.5 to compute the dice coefficient \n'
            )
        dice_fic.write(
            '\n--------------------------------------------------------------\n'
            'Dice coefficient on the Gray Matter segmentation:\n')
        dice_fic.write(output_gm)
        dice_fic.write(
            '\n\n--------------------------------------------------------------\n'
            'Dice coefficient on the White Matter segmentation:\n')
        dice_fic.write(output_wm)
        dice_fic.close()
        # sct.run(' mv ./' + dice_name + ' ../')

        hd_name = 'hd_' + sct.extract_fname(
            self.target_fname)[1] + '_' + self.param.res_type + '.txt'
        sct.run('sct_compute_hausdorff_distance.py -i ' +
                res_gm_seg_bin.file_name + ext + ' -r ' +
                im_ref_gm_seg.file_name + ext + ' -t 1 -o ' + hd_name +
                ' -v ' + str(self.param.verbose))
        sct.run('mv ./' + hd_name + ' ../../')

        os.chdir('..')
        return dice_name, hd_name
Exemplo n.º 34
0
def main(args=None):

    # initializations
    initz = ''
    initcenter = ''
    initc2 = 'auto'
    param = Param()

    # check user arguments
    if not args:
        args = sys.argv[1:]

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(sys.argv[1:])
    fname_in = arguments["-i"]
    fname_seg = arguments['-s']
    contrast = arguments['-c']
    path_template = sct.slash_at_the_end(arguments['-t'], 1)
    # if '-o' in arguments:
    #     file_out = arguments["-o"]
    # else:
    #     file_out = ''
    if '-ofolder' in arguments:
        path_output = sct.slash_at_the_end(os.path.abspath(arguments['-ofolder']), slash=1)
    else:
        path_output = sct.slash_at_the_end(os.path.abspath(os.curdir), slash=1)
    if '-initz' in arguments:
        initz = arguments['-initz']
    if '-initcenter' in arguments:
        initcenter = arguments['-initcenter']
    # if user provided text file, parse and overwrite arguments
    if '-initfile' in arguments:
        # open file
        file = open(arguments['-initfile'], 'r')
        initfile = ' '+file.read().replace('\n', '')
        arg_initfile = initfile.split(' ')
        for i in xrange(len(arg_initfile)):
            if arg_initfile[i] == '-initz':
                initz = [int(x) for x in arg_initfile[i+1].split(',')]
            if arg_initfile[i] == '-initcenter':
                initcenter = int(arg_initfile[i+1])
    if '-initc2' in arguments:
        initc2 = 'manual'
    if '-param' in arguments:
        param.update(arguments['-param'][0])
    verbose = int(arguments['-v'])
    remove_tmp_files = int(arguments['-r'])
    denoise = int(arguments['-denoise'])
    laplacian = int(arguments['-laplacian'])

    # if verbose, import matplotlib
    # if verbose == 2:
        # import matplotlib.pyplot as plt

    # create temporary folder
    printv('\nCreate temporary folder...', verbose)
    path_tmp = tmp_create(verbose=verbose)
    # path_tmp = '/Users/julien/Dropbox/documents/processing/20160813_wang/t12/tmp.160814213032_725693/'

    # Copying input data to tmp folder
    printv('\nCopying input data to tmp folder...', verbose)
    run('sct_convert -i '+fname_in+' -o '+path_tmp+'data.nii')
    run('sct_convert -i '+fname_seg+' -o '+path_tmp+'segmentation.nii.gz')

    # Go go temp folder
    os.chdir(path_tmp)

    # create label to identify disc
    printv('\nCreate label to identify disc...', verbose)
    initauto = False
    if initz:
        create_label_z('segmentation.nii.gz', initz[0], initz[1])  # create label located at z_center
    elif initcenter:
        # find z centered in FOV
        nii = Image('segmentation.nii.gz')
        nii.change_orientation('RPI')  # reorient to RPI
        nx, ny, nz, nt, px, py, pz, pt = nii.dim  # Get dimensions
        z_center = int(round(nz/2))  # get z_center
        create_label_z('segmentation.nii.gz', z_center, initcenter)  # create label located at z_center
    else:
        initauto = True
        # printv('\nERROR: You need to initialize the disc detection algorithm using one of these two options: -initz, -initcenter\n', 1, 'error')

    # Straighten spinal cord
    printv('\nStraighten spinal cord...', verbose)
    # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time)
    if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'):
        # if they exist, copy them into current folder
        sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning')
        shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz')
        shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz')
        shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz')
        # apply straightening
        sct.run('sct_apply_transfo -i data.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o data_straight.nii')
    else:
        run('sct_straighten_spinalcord -i data.nii -s segmentation.nii.gz -r 0 -qc 0')

    # resample to 0.5mm isotropic to match template resolution
    printv('\nResample to 0.5mm isotropic...', verbose)
    run('sct_resample -i data_straight.nii -mm 0.5x0.5x0.5 -x linear -o data_straightr.nii', verbose)
    # run('sct_resample -i segmentation.nii.gz -mm 0.5x0.5x0.5 -x linear -o segmentationr.nii.gz', verbose)
    # run('sct_resample -i labelz.nii.gz -mm 0.5x0.5x0.5 -x linear -o labelzr.nii', verbose)

    # Apply straightening to segmentation
    # N.B. Output is RPI
    printv('\nApply straightening to segmentation...', verbose)
    run('sct_apply_transfo -i segmentation.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear', verbose)
    # Threshold segmentation at 0.5
    run('sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz', verbose)

    if initauto:
        init_disc = []
    else:
        # Apply straightening to z-label
        printv('\nDilate z-label and apply straightening...', verbose)
        run('sct_apply_transfo -i labelz.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn', verbose)
        # get z value and disk value to initialize labeling
        printv('\nGet z and disc values from straight label...', verbose)
        init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz')
        printv('.. '+str(init_disc), verbose)

    # denoise data
    if denoise:
        printv('\nDenoise data...', verbose)
        run('sct_maths -i data_straightr.nii -denoise h=0.05 -o data_straightr.nii', verbose)

    # apply laplacian filtering
    if laplacian:
        printv('\nApply Laplacian filter...', verbose)
        run('sct_maths -i data_straightr.nii -laplacian 1 -o data_straightr.nii', verbose)

    # detect vertebral levels on straight spinal cord
    vertebral_detection('data_straightr.nii', 'segmentation_straight.nii.gz', contrast, param, init_disc=init_disc, verbose=verbose, path_template=path_template, initc2=initc2, path_output=path_output)

    # un-straighten labeled spinal cord
    printv('\nUn-straighten labeling...', verbose)
    run('sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn', verbose)

    # Clean labeled segmentation
    printv('\nClean labeled segmentation (correct interpolation errors)...', verbose)
    clean_labeled_segmentation('segmentation_labeled.nii.gz', 'segmentation.nii.gz', 'segmentation_labeled.nii.gz')

    # label discs
    printv('\nLabel discs...', verbose)
    label_discs('segmentation_labeled.nii.gz', verbose=verbose)

    # come back to parent folder
    os.chdir('..')

    # Generate output files
    path_seg, file_seg, ext_seg = extract_fname(fname_seg)
    printv('\nGenerate output files...', verbose)
    generate_output_file(path_tmp+'segmentation_labeled.nii.gz', path_output+file_seg+'_labeled'+ext_seg)
    generate_output_file(path_tmp+'segmentation_labeled_disc.nii.gz', path_output+file_seg+'_labeled_discs'+ext_seg)
    # copy straightening files in case subsequent SCT functions need them
    generate_output_file(path_tmp+'warp_curve2straight.nii.gz', path_output+'warp_curve2straight.nii.gz', verbose)
    generate_output_file(path_tmp+'warp_straight2curve.nii.gz', path_output+'warp_straight2curve.nii.gz', verbose)
    generate_output_file(path_tmp+'straight_ref.nii.gz', path_output+'straight_ref.nii.gz', verbose)

    # Remove temporary files
    if remove_tmp_files == 1:
        printv('\nRemove temporary files...', verbose)
        run('rm -rf '+path_tmp)

    # to view results
    printv('\nDone! To view results, type:', verbose)
    printv('fslview '+fname_in+' '+path_output+file_seg+'_labeled'+' -l Random-Rainbow -t 0.5 &\n', verbose, 'info')
Exemplo n.º 35
0
def scadMRValidation(algorithm, isPython=False, verbose=True):
    if not isinstance(algorithm, str) or not algorithm:
        print 'ERROR: You must provide the name of your algorithm as a string.'
        usage()

    import time
    import sct_utils as sct

    # creating a new folder with the experiment
    path_experiment = 'scad-experiment.'+algorithm+'.'+time.strftime("%y%m%d%H%M%S")
    #status, output = sct.run('mkdir '+path_experiment, verbose)

    # copying images from "data" folder into experiment folder
    sct.copyDirectory('data', path_experiment)

    # Starting validation
    os.chdir(path_experiment)
    # t1
    os.chdir('t1/')
    for subject_dir in os.listdir('./'):
        if os.path.isdir(subject_dir):
            os.chdir(subject_dir)

            # creating list of images and corresponding manual segmentation
            list_images = dict()
            for file_name in os.listdir('./'):
                if not 'manual_segmentation' in file_name:
                    for file_name_corr in os.listdir('./'):
                        if 'manual_segmentation' in file_name_corr and sct.extract_fname(file_name)[1] in file_name_corr:
                            list_images[file_name] = file_name_corr

            # running the proposed algorithm on images in the folder and analyzing the results
            for image, image_manual_seg in list_images.items():
                print image
                path_in, file_in, ext_in = sct.extract_fname(image)
                image_output = file_in+'_centerline'+ext_in
                if ispython:
                    try:
                        eval(algorithm+'('+image+', t1, verbose='+str(verbose)+')')
                    except Exception as e:
                        print 'Error during spinal cord detection on line {}:'.format(sys.exc_info()[-1].tb_lineno)
                        print 'Subject: t1/'+subject_dir+'/'+image
                        print e
                        sys.exit(2)
                else:
                    cmd = algorithm+' -i '+image+' -t t1'
                    if verbose:
                        cmd += ' -v'
                    status, output = sct.run(cmd, verbose=verbose)
                    if status != 0:
                        print 'Error during spinal cord detection on Subject: t1/'+subject_dir+'/'+image
                        print output
                        sys.exit(2)

                # analyzing the resulting centerline
                from msct_image import Image
                manual_segmentation_image = Image(image_manual_seg)
                manual_segmentation_image.change_orientation()
                centerline_image = Image(image_output)
                centerline_image.change_orientation()

                from msct_types import Coordinate
                # coord_manseg = manual_segmentation_image.getNonZeroCoordinates()
                coord_centerline = centerline_image.getNonZeroCoordinates()

                # check if centerline is in manual segmentation
                result_centerline_in = True
                for coord in coord_centerline:
                    if manual_segmentation_image.data[coord.x, coord.y, coord.z] == 0:
                        result_centerline_in = False
                        print 'failed on slice #' + str(coord.z)
                        break
                if result_centerline_in:
                    print 'OK: Centerline is inside manual segmentation.'
                else:
                    print 'FAIL: Centerline is outside manual segmentation.'


                # check the length of centerline compared to manual segmentation
                # import sct_process_segmentation as sct_seg
                # length_manseg = sct_seg.compute_length(image_manual_seg)
                # length_centerline = sct_seg.compute_length(image_output)
                # if length_manseg*0.9 <= length_centerline <= length_manseg*1.1:
                #     print 'OK: Length of centerline correspond to length of manual segmentation.'
                # else:
                #     print 'FAIL: Length of centerline does not correspond to length of manual segmentation.'
            os.chdir('..')
    warping_fields = [WarpingField(filename) for filename in warping_fields_filename]

    filenames_output = []

    # path_tmp = tmp_create()
    # tmp_copy_nifti(input_file, path_tmp, 'raw.nii')
    # run('cp '+warping_fields_filename[0]+' '+path_tmp)
    # chdir(path_tmp)
    run('mkdir images')
    run('mkdir niftis')
    while True:
        try:
            warping_fields[0].num_of_frames = number_of_frames
            image_output_iter, iteration = warping_fields[0].next()
            image_output_iter.save()
            filename_warp = image_output_iter.path + image_output_iter.file_name + image_output_iter.ext
            filename_output = "niftis/tmp.warped_image_" + str(iteration - 1) + image_output_iter.ext
            run("sct_apply_transfo -i " + input_file + " -d " + reference_image + " -w " + filename_warp +
                " -o " + filename_output)
            result=Image(filename_output)
            result.change_orientation()

            toimage(result.data[int(result.data.shape[0]/2)].squeeze(), cmin=0.0).save('images/'+extract_fname(filename_output)[1]+'.jpg')
            filenames_output.append(filename_output)
        except ValueError:
            printv('\nError during warping field generation...', 1, 'error')
        except StopIteration:
            printv('\nFinished iterations.')
            break

def main(args=None):

    # initializations
    initz = ''
    initcenter = ''
    initc2 = 'auto'
    param = Param()

    # check user arguments
    if not args:
        args = sys.argv[1:]

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(args)
    fname_in = arguments["-i"]
    fname_seg = arguments['-s']
    contrast = arguments['-c']
    path_template = sct.slash_at_the_end(arguments['-t'], 1)
    # if '-o' in arguments:
    #     file_out = arguments["-o"]
    # else:
    #     file_out = ''
    if '-ofolder' in arguments:
        path_output = sct.slash_at_the_end(os.path.abspath(
            arguments['-ofolder']),
                                           slash=1)
    else:
        path_output = sct.slash_at_the_end(os.path.abspath(os.curdir), slash=1)
    if '-initz' in arguments:
        initz = arguments['-initz']
    if '-initcenter' in arguments:
        initcenter = arguments['-initcenter']
    # if user provided text file, parse and overwrite arguments
    if '-initfile' in arguments:
        # open file
        file = open(arguments['-initfile'], 'r')
        initfile = ' ' + file.read().replace('\n', '')
        arg_initfile = initfile.split(' ')
        for i in xrange(len(arg_initfile)):
            if arg_initfile[i] == '-initz':
                initz = [int(x) for x in arg_initfile[i + 1].split(',')]
            if arg_initfile[i] == '-initcenter':
                initcenter = int(arg_initfile[i + 1])
    if '-initc2' in arguments:
        initc2 = 'manual'
    if '-param' in arguments:
        param.update(arguments['-param'][0])
    verbose = int(arguments['-v'])
    remove_tmp_files = int(arguments['-r'])
    denoise = int(arguments['-denoise'])
    laplacian = int(arguments['-laplacian'])

    # create temporary folder
    sct.printv('\nCreate temporary folder...', verbose)
    path_tmp = sct.tmp_create(verbose=verbose)

    # Copying input data to tmp folder
    sct.printv('\nCopying input data to tmp folder...', verbose)
    sct.run('sct_convert -i ' + fname_in + ' -o ' + path_tmp + 'data.nii')
    sct.run('sct_convert -i ' + fname_seg + ' -o ' + path_tmp +
            'segmentation.nii.gz')

    # Go go temp folder
    os.chdir(path_tmp)

    # create label to identify disc
    sct.printv('\nCreate label to identify disc...', verbose)
    initauto = False
    if initz:
        create_label_z('segmentation.nii.gz', initz[0],
                       initz[1])  # create label located at z_center
    elif initcenter:
        # find z centered in FOV
        nii = Image('segmentation.nii.gz')
        nii.change_orientation('RPI')  # reorient to RPI
        nx, ny, nz, nt, px, py, pz, pt = nii.dim  # Get dimensions
        z_center = int(round(nz / 2))  # get z_center
        create_label_z('segmentation.nii.gz', z_center,
                       initcenter)  # create label located at z_center
    else:
        initauto = True
        # printv('\nERROR: You need to initialize the disc detection algorithm using one of these two options: -initz, -initcenter\n', 1, 'error')

    # Straighten spinal cord
    sct.printv('\nStraighten spinal cord...', verbose)
    # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time)
    if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile(
            '../warp_straight2curve.nii.gz') and os.path.isfile(
                '../straight_ref.nii.gz'):
        # if they exist, copy them into current folder
        sct.printv(
            'WARNING: Straightening was already run previously. Copying warping fields...',
            verbose, 'warning')
        shutil.copy('../warp_curve2straight.nii.gz',
                    'warp_curve2straight.nii.gz')
        shutil.copy('../warp_straight2curve.nii.gz',
                    'warp_straight2curve.nii.gz')
        shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz')
        # apply straightening
        sct.run(
            'sct_apply_transfo -i data.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o data_straight.nii'
        )
    else:
        sct.run(
            'sct_straighten_spinalcord -i data.nii -s segmentation.nii.gz -r 0 -qc 0'
        )

    # resample to 0.5mm isotropic to match template resolution
    sct.printv('\nResample to 0.5mm isotropic...', verbose)
    sct.run(
        'sct_resample -i data_straight.nii -mm 0.5x0.5x0.5 -x linear -o data_straightr.nii',
        verbose)
    # sct.run('sct_resample -i segmentation.nii.gz -mm 0.5x0.5x0.5 -x linear -o segmentationr.nii.gz', verbose)
    # sct.run('sct_resample -i labelz.nii.gz -mm 0.5x0.5x0.5 -x linear -o labelzr.nii', verbose)

    # Apply straightening to segmentation
    # N.B. Output is RPI
    sct.printv('\nApply straightening to segmentation...', verbose)
    sct.run(
        'sct_apply_transfo -i segmentation.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear',
        verbose)
    # Threshold segmentation at 0.5
    sct.run(
        'sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz',
        verbose)

    if initauto:
        init_disc = []
    else:
        # Apply straightening to z-label
        sct.printv('\nDilate z-label and apply straightening...', verbose)
        sct.run(
            'sct_apply_transfo -i labelz.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn',
            verbose)
        # get z value and disk value to initialize labeling
        sct.printv('\nGet z and disc values from straight label...', verbose)
        init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz')
        sct.printv('.. ' + str(init_disc), verbose)

    # denoise data
    if denoise:
        sct.printv('\nDenoise data...', verbose)
        sct.run(
            'sct_maths -i data_straightr.nii -denoise h=0.05 -o data_straightr.nii',
            verbose)

    # apply laplacian filtering
    if laplacian:
        sct.printv('\nApply Laplacian filter...', verbose)
        sct.run(
            'sct_maths -i data_straightr.nii -laplacian 1 -o data_straightr.nii',
            verbose)

    # detect vertebral levels on straight spinal cord
    vertebral_detection('data_straightr.nii',
                        'segmentation_straight.nii.gz',
                        contrast,
                        param,
                        init_disc=init_disc,
                        verbose=verbose,
                        path_template=path_template,
                        initc2=initc2,
                        path_output=path_output)

    # un-straighten labeled spinal cord
    sct.printv('\nUn-straighten labeling...', verbose)
    sct.run(
        'sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn',
        verbose)

    # Clean labeled segmentation
    sct.printv(
        '\nClean labeled segmentation (correct interpolation errors)...',
        verbose)
    clean_labeled_segmentation('segmentation_labeled.nii.gz',
                               'segmentation.nii.gz',
                               'segmentation_labeled.nii.gz')

    # label discs
    sct.printv('\nLabel discs...', verbose)
    label_discs('segmentation_labeled.nii.gz', verbose=verbose)

    # come back to parent folder
    os.chdir('..')

    # Generate output files
    path_seg, file_seg, ext_seg = sct.extract_fname(fname_seg)
    sct.printv('\nGenerate output files...', verbose)
    sct.generate_output_file(path_tmp + 'segmentation_labeled.nii.gz',
                             path_output + file_seg + '_labeled' + ext_seg)
    sct.generate_output_file(
        path_tmp + 'segmentation_labeled_disc.nii.gz',
        path_output + file_seg + '_labeled_discs' + ext_seg)
    # copy straightening files in case subsequent SCT functions need them
    sct.generate_output_file(path_tmp + 'warp_curve2straight.nii.gz',
                             path_output + 'warp_curve2straight.nii.gz',
                             verbose)
    sct.generate_output_file(path_tmp + 'warp_straight2curve.nii.gz',
                             path_output + 'warp_straight2curve.nii.gz',
                             verbose)
    sct.generate_output_file(path_tmp + 'straight_ref.nii.gz',
                             path_output + 'straight_ref.nii.gz', verbose)

    # Remove temporary files
    if remove_tmp_files == 1:
        sct.printv('\nRemove temporary files...', verbose)
        shutil.rmtree(path_tmp, ignore_errors=True)

    # Generate QC report
    try:
        if '-qc' in arguments and not arguments.get('-noqc', False):
            qc_path = arguments['-qc']

            import spinalcordtoolbox.reports.qc as qc
            import spinalcordtoolbox.reports.slice as qcslice

            qc_param = qc.Params(fname_in, 'sct_label_vertebrae', args,
                                 'Sagittal', qc_path)
            report = qc.QcReport(qc_param, '')

            @qc.QcImage(report, 'none', [
                qc.QcImage.label_vertebrae,
            ])
            def test(qslice):
                return qslice.single()

            labeled_seg_file = path_output + file_seg + '_labeled' + ext_seg
            test(qcslice.Sagittal(Image(fname_in), Image(labeled_seg_file)))
            sct.printv('Sucessfully generated the QC results in %s' %
                       qc_param.qc_results)
            sct.printv(
                'Use the following command to see the results in a browser:')
            sct.printv('sct_qc -folder %s' % qc_path, type='info')
    except Exception as err:
        sct.printv(err, verbose, 'warning')
        sct.printv('WARNING: Cannot generate report.', verbose, 'warning')

    # to view results
    sct.printv('\nDone! To view results, type:', verbose)
    sct.printv(
        'fslview ' + fname_in + ' ' + path_output + file_seg + '_labeled' +
        ' -l Random-Rainbow -t 0.5 &\n', verbose, 'info')
def vertebral_detection(fname, fname_seg, contrast):

    shift_AP = 14  # shift the centerline on the spine in mm default : 17 mm
    size_AP = 3  # mean around the centerline in the anterior-posterior direction in mm
    size_RL = 3  # mean around the centerline in the right-left direction in mm
    verbose = param.verbose

    if verbose:
        import matplotlib.pyplot as plt

    # open anatomical volume
    img = Image(fname)
    # orient to RPI
    img.change_orientation()
    # get dimension
    nx, ny, nz, nt, px, py, pz, pt = img.dim


    #==================================================
    # Compute intensity profile across vertebrae
    #==================================================

    shift_AP = shift_AP * py
    size_AP = size_AP * py
    size_RL = size_RL * px

    # orient segmentation to RPI
    run('sct_orientation -i ' + fname_seg + ' -s RPI')
    # smooth segmentation/centerline
    path_centerline, file_centerline, ext_centerline = extract_fname(fname_seg)
    x, y, z, Tx, Ty, Tz = smooth_centerline(path_centerline + file_centerline + '_RPI' + ext_centerline)

    # build intensity profile along the centerline
    I = np.zeros((len(y), 1))

    #  mask where intensity profile will be taken
    if verbose == 2:
        mat = img.copy()
        mat.data = np.zeros(mat.dim)

    for iz in range(len(z)):
        # define vector orthogonal to the cord in RL direction
        P1 = np.array([1, 0, -Tx[iz]/Tz[iz]])
        P1 = P1/np.linalg.norm(P1)
        # define vector orthogonal to the cord in AP direction
        P2 = np.array([0, 1, -Ty[iz]/Tz[iz]])
        P2 = P2/np.linalg.norm(P2)
        # define X and Y coordinates of the voxels to extract intensity profile from
        indexRL = range(-np.int(round(size_RL)), np.int(round(size_RL)))
        indexAP = range(0, np.int(round(size_AP)))+np.array(shift_AP)
        # loop over coordinates of perpendicular plane
        for i_RL in indexRL:
            for i_AP in indexAP:
                i_vect = np.round(np.array([x[iz], y[iz], z[iz]])+P1*i_RL+P2*i_AP)
                i_vect = np.minimum(np.maximum(i_vect, 0), np.array([nx, ny, nz])-1)  # check if index stays in image dimension
                I[iz] = I[iz] + img.data[i_vect[0], i_vect[1], i_vect[2]]

                # create a mask with this perpendicular plane
                if verbose == 2:
                    mat.data[i_vect[0], i_vect[1], i_vect[2]] = 1

    if verbose == 2:
        mat.file_name = 'mask'
        mat.save()

    # Detrending Intensity
    start_centerline_y = y[0]
    X = np.where(I == 0)
    mask2 = np.ones((len(y), 1), dtype=bool)
    mask2[X, 0] = False

    # low pass filtering
    import scipy.signal
    frequency = 2/pz
    Wn = 0.1/frequency
    N = 2              #Order of the filter
    #    b, a = scipy.signal.butter(N, Wn, btype='low', analog=False, output='ba')
    b, a = scipy.signal.iirfilter(N, Wn, rp=None, rs=None, btype='high', analog=False, ftype='bessel', output='ba')
    I_detrend = scipy.signal.filtfilt(b, a, I[:, 0], axis=-1, padtype='constant', padlen=None)
    I_detrend = I_detrend/(np.amax(I_detrend))


    #==================================================
    # step 1 : Find the First Peak
    #==================================================
    if contrast == 't1':
        I_detrend2 = np.diff(I_detrend)
    elif contrast == 't2':
        space = np.linspace(-10/pz, 10/pz, round(21/pz), endpoint=True)
        pattern = (np.sinc((space*pz)/20)) ** 20
        I_corr = scipy.signal.correlate(-I_detrend.squeeze().squeeze()+1,pattern,'same')
        b, a = scipy.signal.iirfilter(N, Wn, rp=None, rs=None, btype='high', analog=False, ftype='bessel', output='ba')
        I_detrend2 = scipy.signal.filtfilt(b, a, I_corr, axis=-1, padtype='constant', padlen=None)

    I_detrend2[I_detrend2 < 0.2] = 0
    ind_locs = np.squeeze(scipy.signal.argrelextrema(I_detrend2, np.greater))

    # remove peaks that are too closed
    locsdiff = np.diff(z[ind_locs])
    ind = locsdiff > 10
    ind_locs = np.hstack((ind_locs[ind], ind_locs[-1]))
    locs = z[ind_locs]

    if verbose == 2:
        # x=0: most caudal, x=max: most rostral
        plt.figure()
        plt.plot(I_detrend2)
        plt.plot(ind_locs, I_detrend2[ind_locs], '+')
        plt.show()


    #=====================================================================================
    # step 2 : Cross correlation between the adjusted template and the intensity profile.
    #          Local moving of template's peak from the first peak already found
    #=====================================================================================

    #For each loop, a peak is located at the most likely position and then local adjustment is done.
    #The position of the next peak is calculated from previous positions

    # TODO: use mean distance
    mean_distance = [12.1600, 20.8300, 18.0000, 16.0000, 15.1667, 15.3333, 15.8333,   18.1667,   18.6667,   18.6667,
    19.8333,   20.6667,   21.6667,   22.3333,   23.8333,   24.1667,   26.0000,   28.6667,   30.5000,   33.5000,
    33.0000,   31.3330]
    #
    # #Creating pattern
    printv('\nFinding Cross correlation between the adjusted template and the intensity profile...', verbose)
    space = np.linspace(-10/pz, 10/pz, round(21/pz), endpoint=True)
    pattern = (np.sinc((space*pz)/20))**20
    I_corr = scipy.signal.correlate(I_detrend2.squeeze().squeeze()+1, pattern, 'same')
    #
    # level_start=1
    # if contrast == 'T1':
    #     mean_distance = mean_distance[level_start-1:len(mean_distance)]
    #     xmax_pattern = np.argmax(pattern)
    # else:
    #     mean_distance = mean_distance[level_start+1:len(mean_distance)]
    #     xmax_pattern = np.argmin(pattern)          # position of the peak in the pattern
    # pixend = len(pattern) - xmax_pattern       #number of pixel after the peaks in the pattern
    #
    #
    # mean_distance_new = mean_distance
    # mean_ratio = np.zeros(len(mean_distance))
    #
    # L = np.round(1.2*max(mean_distance)) - np.round(0.8*min(mean_distance))
    # corr_peak  = np.zeros((L,len(mean_distance)))          # corr_peak  = np.nan #for T2
    #
    # #loop on each peak
    # for i_peak in range(len(mean_distance)):
    #     scale_min = np.round(0.80*mean_distance_new[i_peak]) - xmax_pattern - pixend
    #     if scale_min<0:
    #         scale_min = 0
    #
    #     scale_max = np.round(1.2*mean_distance_new[i_peak]) - xmax_pattern - pixend
    #     scale_peak = np.arange(scale_min,scale_max+1)
    #
    #     for i_scale in range(len(scale_peak)):
    #         template_resize_peak = np.concatenate([template_truncated,np.zeros(scale_peak[i_scale]),pattern])
    #         if len(I_detrend[:,0])>len(template_resize_peak):
    #             template_resize_peak1 = np.concatenate((template_resize_peak,np.zeros(len(I_detrend[:,0])-len(template_resize_peak))))
    #
    #         #cross correlation
    #         corr_template = scipy.signal.correlate(I_detrend[:,0],template_resize_peak)
    #
    #         if len(I_detrend[:,0])>len(template_resize_peak):
    #             val = np.dot(I_detrend[:,0],template_resize_peak1.T)
    #         else:
    #             I_detrend_2 = np.concatenate((I_detrend[:,0],np.zeros(len(template_resize_peak)-len(I_detrend[:,0]))))
    #             val = np.dot(I_detrend_2,template_resize_peak.T)
    #         corr_peak[i_scale,i_peak] = val
    #
    #         if verbose:
    #             plt.xlim(0,len(I_detrend[:,0]))
    #             plt.plot(I_detrend[:,0])
    #             plt.plot(template_resize_peak)
    #             plt.show(block=False)
    #
    #             plt.plot(corr_peak[:,i_peak],marker='+',linestyle='None',color='r')
    #             plt.title('correlation value against the displacement of the peak (px)')
    #             plt.show(block=False)
    #
    #     max_peak = np.amax(corr_peak[:,i_peak])
    #     index_scale_peak = np.where(corr_peak[:,i_peak]==max_peak)
    #     good_scale_peak = scale_peak[index_scale_peak][0]
    #     Mcorr = Mcorr1
    #     Mcorr = np.resize(Mcorr,i_peak+2)
    #     Mcorr[i_peak+1] = np.amax(corr_peak[:,0:(i_peak+1)])
    #     flag = 0
    #
    #     #If the correlation coefficient is too low, put the peak at the mean position
    #     if i_peak>0:
    #         if (Mcorr[i_peak+1]-Mcorr[i_peak])<0.4*np.mean(Mcorr[1:i_peak+2]-Mcorr[0:i_peak+1]):
    #             test = i_peak
    #             template_resize_peak = np.concatenate((template_truncated,np.zeros(round(mean_distance[i_peak])-xmax_pattern-pixend),pattern))
    #             good_scale_peak = np.round(mean_distance[i_peak]) - xmax_pattern - pixend
    #             flag = 1
    #     if i_peak==0:
    #         if (Mcorr[i_peak+1] - Mcorr[i_peak])<0.4*Mcorr[0]:
    #             template_resize_peak = np.concatenate((template_truncated,np.zeros(round(mean_distance[i_peak])-xmax_pattern-pixend),pattern))
    #             good_scale_peak = round(mean_distance[i_peak]) - xmax_pattern - pixend
    #             flag = 1
    #     if flag==0:
    #         template_resize_peak=np.concatenate((template_truncated,np.zeros(good_scale_peak),pattern))
    #
    #     #update mean-distance by a adjustement ratio
    #     mean_distance_new[i_peak] = good_scale_peak + xmax_pattern + pixend
    #     mean_ratio[i_peak] = np.mean(mean_distance_new[:,0:i_peak]/mean_distance[:,0:i_peak])
    #
    #     template_truncated = template_resize_peak
    #
    #     if verbose:
    #         plt.plot(I_detrend[:,0])
    #         plt.plot(template_truncated)
    #         plt.xlim(0,(len(I_detrend[:,0])-1))
    #         plt.show()
    #
    # #finding the maxima of the adjusted template
    # minpeakvalue = 0.5
    # loc_disk = np.arange(len(template_truncated))
    # index_disk = []
    # for i in range(len(template_truncated)):
    #     if template_truncated[i]>=minpeakvalue:
    #         if i==0:
    #             if template_truncated[i]<template_truncated[i+1]:
    #                 index_disk.append(i)
    #         elif i==(len(template_truncated)-1):
    #             if template_truncated[i]<template_truncated[i-1]:
    #                 index_disk.append(i)
    #         else:
    #             if template_truncated[i]<template_truncated[i+1]:
    #                 index_disk.append(i)
    #             elif template_truncated[i]<template_truncated[i-1]:
    #                 index_disk.append(i)
    #     else:
    #         index_disk.append(i)
    #
    # mask_disk = np.ones(len(template_truncated), dtype=bool)
    # mask_disk[index_disk] = False
    # loc_disk = loc_disk[mask_disk]
    # X1 = np.where(loc_disk > I_detrend.shape[0])
    # mask_disk1 = np.ones(len(loc_disk), dtype=bool)
    # mask_disk1[X1] = False
    # loc_disk = loc_disk[mask_disk1]
    # loc_disk = loc_disk + start_centerline_y - 1


    #=====================================================================
    # Step 3: Label segmentation
    #=====================================================================

    # # Project vertebral levels back to the centerline
    # centerline = Image(fname_seg)
    # raw_orientation = centerline.change_orientation()
    # centerline.data[:, :, :] = 0
    # for iz in range(locs[0]):
    #         centerline.data[np.round(x[iz]), np.round(y[iz]), iz] = 1
    # for i in range(len(locs)-1):
    #     for iz in range(locs[i], min(locs[i+1], len(z))):
    #         centerline.data[np.round(x[iz]), np.round(y[iz]), iz] = i+2
    # for iz in range(locs[-1], len(z)):
    #         centerline.data[np.round(x[iz]), np.round(y[iz]), iz] = i+3
    #
    # #centerline.change_orientation(raw_orientation)
    # centerline.file_name += '_labeled'
    # centerline.save()

    # Label segmentation with vertebral number
    # Method: loop across all voxels of the segmentation, project each voxel to the line passing through the vertebrae
    # (using minimum distance) and assign vertebral level.
    printv('\nLabel segmentation...', verbose)
    seg = Image(fname_seg)
    seg_raw_orientation = seg.change_orientation()
    # find all voxels belonging to segmentation
    x_seg, y_seg, z_seg = np.where(seg.data)
    # loop across voxels in segmentation
    for ivox in range(len(x_seg)):
        # get voxel coordinate
        vox_coord = np.array([x_seg[ivox], y_seg[ivox], z_seg[ivox]])
        # find closest point to the curved line passing through the vertebrae

        for iplane in range(len(locs)):
            ind = np.where(z == locs[iplane])
            vox_vector = vox_coord - np.hstack((x[ind], y[ind], z[ind]))
            normal2plane_vector = np.hstack((Tx[ind], Ty[ind], Tz[ind]))  # Tx, Ty and Tz are the derivatives of the centerline

            # if voxel is above the plane --> give the number of the plane
            if np.dot(vox_vector, normal2plane_vector) > 0:
                seg.data[vox_coord[0], vox_coord[1], vox_coord[2]] = iplane+2
            else:  # if the voxel gets below the plane --> next voxel
                break
    seg.change_orientation(seg_raw_orientation)
    seg.file_name += '_labeled'
    seg.save()

    # # color the segmentation with vertebral number
    # printv('\nLabel input segmentation...', verbose)
    # # if fname_segmentation:
    # seg = Image(fname_seg)
    # seg_raw_orientation = seg.change_orientation()
    # x_seg, y_seg, z_seg = np.where(seg.data)  # find all voxels belonging to segmentation
    # for ivox in range(len(x_seg)):  # loop across voxels in segmentation
    #     vox_coord = np.array([x_seg[ivox], y_seg[ivox], z_seg[ivox]])  # get voxel coordinate
    #     for iplane in range(len(locs)):
    #         ind = np.where(z == locs[iplane])
    #         vox_vector = vox_coord - np.hstack((x[ind], y[ind], z[ind]))
    #         normal2plane_vector = np.hstack((Tx[ind], Ty[ind], Tz[ind]))  # Tx, Ty and Tz are the derivatives of the centerline
    #
    #         # if voxel is above the plane --> give the number of the plane
    #         if np.dot(vox_vector, normal2plane_vector) > 0:
    #             seg.data[vox_coord[0], vox_coord[1], vox_coord[2]] = iplane+2
    #         else:  # if the voxel gets below the plane --> next voxel
    #             break
    # seg.change_orientation(seg_raw_orientation)
    # seg.file_name += '_labeled'
    # seg.save()

    return locs
def test(path_data='', parameters=''):

    if not parameters:
        parameters = '-i t2/t2.nii.gz -c t2 -p auto'

    # parameters
    folder_data = 't2/'
    file_data = ['t2.nii.gz', 't2_centerline_init.nii.gz', 't2_centerline_labels.nii.gz', 't2_seg_manual.nii.gz']

    parser = sct_get_centerline.get_parser()
    dict_param = parser.parse(parameters.split(), check_file_exist=False)
    contrast = dict_param['-c']
    dict_param_with_path = parser.add_path_to_file(dict_param, path_data, input_file=True)
    param_with_path = parser.dictionary_to_string(dict_param_with_path)

    # Check if input files exist
    if not (os.path.isfile(dict_param_with_path['-i'])):
        status = 200
        output = 'ERROR: the file(s) provided to test function do not exist in folder: ' + path_data
        return status, output, DataFrame(data={'status': status, 'output': output, 'mse': float('nan'), 'dist_max': float('nan')}, index=[path_data])

    cmd = 'sct_get_centerline '+param_with_path
    status, output = sct.run(cmd, 0)
    scad_centerline = Image(contrast+"_centerline.nii.gz")
    manual_seg = Image(path_data + folder_data + contrast +'_seg_manual.nii.gz')

    max_distance = 0
    standard_deviation = 0
    average = 0
    root_mean_square = 0
    overall_distance = 0
    max_distance = 0
    overall_std = 0
    rmse = 0

    try:
        if status == 0:
            manual_seg.change_orientation()
            scad_centerline.change_orientation()
            from scipy.ndimage.measurements import center_of_mass
            # find COM
            iterator = range(manual_seg.data.shape[2])
            com_x = [0 for ix in iterator]
            com_y = [0 for iy in iterator]

            for iz in iterator:
                com_x[iz], com_y[iz] = center_of_mass(manual_seg.data[:, :, iz])
            max_distance = {}
            distance = {}
            for iz in range(1, scad_centerline.data.shape[2]-1):
                ind1 = np.argmax(scad_centerline.data[:, :, iz])
                X,Y = ind2sub(scad_centerline.data[:, :, iz].shape,ind1)
                com_phys = np.array(manual_seg.transfo_pix2phys([[com_x[iz], com_y[iz], iz]]))
                scad_phys = np.array(scad_centerline.transfo_pix2phys([[X, Y, iz]]))
                distance_magnitude = np.linalg.norm([com_phys[0][0]-scad_phys[0][0], com_phys[0][1]-scad_phys[0][1], 0])
                if math.isnan(distance_magnitude):
                    print "Value is nan"
                else:
                    distance[iz] = distance_magnitude

            max_distance = max(distance.values())
            standard_deviation = np.std(np.array(distance.values()))
            average = sum(distance.values())/len(distance)
            root_mean_square = np.sqrt(np.mean(np.square(distance.values())))
            overall_distance = average
            max_distance = max(distance.values())
            overall_std = standard_deviation
            rmse = root_mean_square

    except Exception, e:
        sct.printv("Exception found while testing scad integrity")
        output = e.message