def concat_data(fname_in, fname_out, dim):
    """
    Concatenate data
    :param fname_in: list of file names.
    :param fname_out:
    :param dim: dimension: 0, 1, 2, 3.
    :return: none
    """
    # create empty list
    list_data = []

    # loop across files
    for i in range(len(fname_in)):
        # append data to list
        list_data.append(Image(fname_in[i]).data)

    # expand dimension of all elements in the list if necessary
    if dim > list_data[0].ndim-1:
        list_data = [expand_dims(i, dim) for i in list_data]
    # concatenate
    try:
        data_concat = concatenate(list_data, axis=dim)
    except Exception as e:
        sct.printv('\nERROR: Concatenation on line {}'.format(sys.exc_info()[-1].tb_lineno)+'\n'+str(e)+'\n', 1, 'error')

    # write file
    im = Image(fname_in[0])
    im.data = data_concat
    im.setFileName(fname_out)
    im.save()
 def update(self, param_user):
     # list_objects = param_user.split(',')
     for object in param_user:
         if len(object) < 2:
             sct.printv('ERROR: Wrong usage.', 1, type='error')
         obj = object.split('=')
         setattr(self, obj[0], obj[1])
Example #3
0
def generate_warping_field(fname_dest, warp_x, warp_y, fname_warp='warping_field.nii.gz', verbose=1):
    """
    Generate an ITK warping field
    :param fname_dest:
    :param warp_x:
    :param warp_y:
    :param fname_warp:
    :param verbose:
    :return:
    """
    sct.printv('\nGenerate warping field...', verbose)

    # Get image dimensions
    # sct.printv('Get destination dimension', verbose)
    nx, ny, nz, nt, px, py, pz, pt = Image(fname_dest).dim
    # sct.printv('  matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz), verbose)
    # sct.printv('  voxel size:  '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm', verbose)

    # initialize
    data_warp = np.zeros((nx, ny, nz, 1, 3))

    # fill matrix
    data_warp[:, :, :, 0, 0] = -warp_x  # need to invert due to ITK conventions
    data_warp[:, :, :, 0, 1] = -warp_y  # need to invert due to ITK conventions

    # save warping field
    im_dest = load(fname_dest)
    hdr_dest = im_dest.get_header()
    hdr_warp = hdr_dest.copy()
    hdr_warp.set_intent('vector', (), '')
    hdr_warp.set_data_dtype('float32')
    img = Nifti1Image(data_warp, None, hdr_warp)
    save(img, fname_warp)
    sct.printv(' --> '+fname_warp, verbose)
    def getNonZeroCoordinates(self, sorting=None, reverse_coord=False, coordValue=False):
        """
        This function return all the non-zero coordinates that the image contains.
        Coordinate list can also be sorted by x, y, z, or the value with the parameter sorting='x', sorting='y', sorting='z' or sorting='value'
        If reverse_coord is True, coordinate are sorted from larger to smaller.
        """
        from msct_types import Coordinate
        from sct_utils import printv
        n_dim = 1
        if self.dim[3] == 1:
            n_dim = 3
        else:
            n_dim = 4
        if self.dim[2] == 1:
            n_dim = 2

        try:
            if n_dim == 3:
                X, Y, Z = (self.data > 0).nonzero()
                list_coordinates = [Coordinate([X[i], Y[i], Z[i], self.data[X[i], Y[i], Z[i]]]) for i in range(0, len(X))]
            elif n_dim == 2:
                X, Y = (self.data > 0).nonzero()
                list_coordinates = [Coordinate([X[i], Y[i], self.data[X[i], Y[i]]]) for i in range(0, len(X))]
        except Exception, e:
            print 'ERROR', e
            printv('ERROR: Exception ' + str(e) + ' caught while geting non Zeros coordinates', 1, 'error')
 def printTotalTime(self):
     hours, rem = divmod(self.time_list[-1], 3600)
     minutes, seconds = divmod(rem, 60)
     if self.is_started:
         sct.printv('Remaining time: {:0>2}:{:0>2}:{:05.2f}'.format(int(hours), int(minutes), seconds))
     else:
         sct.printv('Total time: {:0>2}:{:0>2}:{:05.2f}'.format(int(hours), int(minutes), seconds))
    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 = msct_image.zeros_like(self.image_input)

        # 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(np.round(center_of_mass.x)) + ", " + str(np.round(center_of_mass.y)) + ", " + str(np.round(center_of_mass.z)) + ")", verbose=self.verbose)
            output_image.data[int(np.round(center_of_mass.x)), int(np.round(center_of_mass.y)), int(np.round(center_of_mass.z))] = center_of_mass.value

        return output_image
    def remove_or_keep_labels(self, labels, action):
        """
        Create or remove labels from self.image_input
        :param list(int): Labels to keep or remove
        :param str: 'remove': remove specified labels (i.e. set to zero), 'keep': keep specified labels and remove the others
        """
        if action == 'keep':
            image_output = msct_image.zeros_like(self.image_input)
        elif action == 'remove':
            image_output = self.image_input.copy()
        coordinates_input = self.image_input.getNonZeroCoordinates()

        for labelNumber in labels:
            isInLabels = False
            for coord in coordinates_input:
                if labelNumber == coord.value:
                    new_coord = coord
                    isInLabels = True
            if isInLabels:
                if action == 'keep':
                    image_output.data[int(new_coord.x), int(new_coord.y), int(new_coord.z)] = new_coord.value
                elif action == 'remove':
                    image_output.data[int(new_coord.x), int(new_coord.y), int(new_coord.z)] = 0.0
            else:
                sct.printv("WARNING: Label " + str(float(labelNumber)) + " not found in input image.", type='warning')

        return image_output
Example #8
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 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 add else msct_image.zeros_like(self.image_input)

        # 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
Example #10
0
def combine_matrix(param):

    # required fields
    # param.mat_2_combine
    # param.mat_final
    # param.verbose

    sct.printv("\nCombine matrices...", param.verbose)
    # list all mat files in source mat folder
    m2c_fnames = [
        fname for fname in os.listdir(param.mat_2_combine) if os.path.isfile(os.path.join(param.mat_2_combine, fname))
    ]
    # loop across files
    for fname in m2c_fnames:
        if os.path.isfile(os.path.join(param.mat_final, fname)):
            # read source matrix
            file = open(os.path.join(param.mat_2_combine, fname))
            Matrix_m2c = np.loadtxt(file)
            file.close()
            # read destination matrix
            file = open(os.path.join(param.mat_final, fname))
            Matrix_f = np.loadtxt(file)
            file.close()
            # initialize final matrix
            Matrix_final = np.identity(4)
            # multiplies rotation matrix (3x3)
            Matrix_final[0:3, 0:3] = Matrix_f[0:3, 0:3] * Matrix_m2c[0:3, 0:3]
            # add translations matrix (3x1)
            Matrix_final[0, 3] = Matrix_f[0, 3] + Matrix_m2c[0, 3]
            Matrix_final[1, 3] = Matrix_f[1, 3] + Matrix_m2c[1, 3]
            Matrix_final[2, 3] = Matrix_f[2, 3] + Matrix_m2c[2, 3]
            # write final matrix (overwrite destination)
            file = open(os.path.join(param.mat_final, fname), "w")
            np.savetxt(os.path.join(param.mat_final, fname), Matrix_final, fmt="%s", delimiter="  ", newline="\n")
            file.close()
Example #11
0
    def select_kept_modes(self, modes_to_ignore=0):
        """
        select the modes to keep according the percentage of variability to keep (self.k)

        :param modes_to_ignore: if not 0 the specified number of first modes will be ignored

        :return kept_modes, kept_eigenvalues: list of the kept modes vectors and list of the kept eigenvalues
        """
        kept_eigenvalues = []
        s = sum([eig[0] for eig in self.eig_pairs])
        sct.printv('\n ---> sum of eigenvalues : ' + str(s), self.verbose, 'normal')
        first = 1
        start = modes_to_ignore
        kept_modes = []
        for eig in self.eig_pairs[start:]:
            if first:
                kept_modes = np.asarray(eig[1]).reshape(self.N, 1)
                kept_eigenvalues.append(eig[0])
                first = 0
            else:
                if (sum(kept_eigenvalues) + eig[0])/s <= self.k:
                    kept_eigenvalues.append(eig[0])
                    kept_modes = np.hstack((kept_modes, np.asarray(eig[1]).reshape(self.N, 1)))
                else:
                    break

        sct.printv('kept eigenvalues (PCA space dimension)  : ' + str(len(kept_eigenvalues)), self.verbose, 'normal')
        return kept_modes, kept_eigenvalues
    def segmentation_pipeline(self):
        sct.printv('\nDoing target pre-processing ...', verbose=self.seg_param.verbose, type='normal')
        self.preprocessed = Preprocessing(self.target_fname, self.sc_seg_fname, tmp_dir=self.tmp_dir, t2_data=self.t2_data, level_fname=self.level_fname, denoising=self.seg_param.target_denoising)
        self.preprocessed.process()

        os.chdir(self.tmp_dir)

        if self.preprocessed.fname_level is not None:
            self.level_to_use = self.preprocessed.fname_level
        else:
            self.level_to_use = None

        sct.printv('\nDoing target gray matter segmentation ...', verbose=self.seg_param.verbose, type='normal')
        self.gm_seg = SupervisedSegmentationMethod(self.preprocessed.processed_target, self.level_to_use, self.model, gm_seg_param=self.seg_param)

        sct.printv('\nDoing result post-processing ...', verbose=self.seg_param.verbose, type='normal')
        self.post_processing()

        if self.ref_gm_seg_fname is not None:
            os.chdir('..')
            ref_gmseg = 'ref_gmseg.nii.gz'
            sct.run('cp ' + self.ref_gm_seg_fname + ' ' + self.tmp_dir + '/' + ref_gmseg)
            os.chdir(self.tmp_dir)
            sct.printv('Computing Dice coefficient and Hausdorff distance ...', verbose=self.seg_param.verbose, type='normal')
            self.dice_name, self.hausdorff_name = self.validation(ref_gmseg)

        if compute_ratio:
            sct.printv('\nComputing ratio GM/WM ...', verbose=self.seg_param.verbose, type='normal')
            self.ratio_name = self.compute_ratio(type=compute_ratio)

        os.chdir('..')
Example #13
0
def concat_data(im_in_list, dim):
    """
    Concatenate data
    :param im_in_list: list of images.
    :param dim: dimension: 0, 1, 2, 3.
    :return im_out: concatenated image
    """
    # WARNING: calling concat_data in python instead of in command line causes a non understood issue (results are different with both options)
    from numpy import concatenate, expand_dims

    data_list = [im.data for im in im_in_list]
    # expand dimension of all elements in the list if necessary
    if dim > im_in_list[0].data.ndim-1:
        data_list = [expand_dims(dat, dim) for dat in data_list]
    # concatenate
    try:
        data_concat = concatenate(data_list, axis=dim)
    except Exception as e:
        printv('\nERROR: Concatenation on line {}'.format(sys.exc_info()[-1].tb_lineno)+'\n'+str(e)+'\n', 1, 'error')
        data_concat = None

    # write file
    im_out = im_in_list[0].copy()
    im_out.data = data_concat
    im_out.setFileName(im_out.file_name+'_concat'+im_out.ext)

    return im_out
    def checkIfNifti(self, param):
        import os
        sct.printv("Check file existence...", 0)
        nii = False
        niigz = False
        param_tmp = str()
        if param.lower().endswith('.nii'):
            nii = os.path.isfile(param)
            niigz = os.path.isfile(param+'.gz')
            param_tmp = param[:-4]
            pass
        elif param.lower().endswith('.nii.gz'):
            niigz = os.path.isfile(param)
            nii = os.path.isfile(param[:-3])
            param_tmp = param[:-7]
            pass
        else:
            sct.printv("ERROR : File is not a NIFTI image file. Exiting", type='error')

        if nii:
            return param_tmp+'.nii'
        elif niigz:
            return param_tmp+'.nii.gz'
        if nii and niigz:
            return param_tmp+'.nii.gz'
Example #15
0
 def update(self, paramreg_user):
     list_objects = paramreg_user.split(',')
     for object in list_objects:
         if len(object) < 2:
             sct.printv('Please check parameter -param (usage changed from previous version)', 1, type='error')
         obj = object.split('=')
         setattr(self, obj[0], obj[1])
 def error(self, error=None):
     if error:
         self.generate(error)
     else:
         sct.printv(self.generate())
         from sys import exit
         exit(0)
def main(args=None):

    if not args:
        args = sys.argv[1:]

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(sys.argv[1:])
    fname_in = arguments['-bvec']
    if '-o' in arguments:
        fname_out = arguments['-o']
    else:
        fname_out = ''
    verbose = int(arguments['-v'])

    # get bvecs in proper orientation
    from dipy.io import read_bvals_bvecs
    bvals, bvecs = read_bvals_bvecs(None, fname_in)

    # # Transpose bvecs
    # printv('Transpose bvecs...', verbose)
    # # from numpy import transpose
    # bvecs = bvecs.transpose()

    # Write new file
    if fname_out == '':
        path_in, file_in, ext_in = extract_fname(fname_in)
        fname_out = path_in+file_in+ext_in
    fid = open(fname_out, 'w')
    for iLine in range(bvecs.shape[0]):
        fid.write(' '.join(str(i) for i in bvecs[iLine, :])+'\n')
    fid.close()

    # display message
    printv('Created file:\n--> '+fname_out+'\n', verbose, 'info')
def main(args=None):
    if args is None:
        args = sys.argv[1:]

    # Get parser
    parser = get_parser()
    arguments = parser.parse(args)

    # Set param arguments ad inputted by user
    fname_in = arguments["-i"]
    contrast = arguments["-c"]

    # Segmentation or Centerline line
    if '-s' in arguments:
        fname_seg = arguments['-s']
        if not os.path.isfile(fname_seg):
            fname_seg = None
            sct.printv('WARNING: -s input file: "' + arguments['-s'] + '" does not exist.\nDetecting PMJ without using segmentation information', 1, 'warning')
    else:
        fname_seg = None

    # Output Folder
    if '-ofolder' in arguments:
        path_results = arguments["-ofolder"]
        if not os.path.isdir(path_results) and os.path.exists(path_results):
            sct.printv("ERROR output directory %s is not a valid directory" % path_results, 1, 'error')
        if not os.path.exists(path_results):
            os.makedirs(path_results)
    else:
        path_results = '.'

    path_qc = arguments.get("-qc", None)

    # Remove temp folder
    rm_tmp = bool(int(arguments.get("-r", 1)))

    verbose = int(arguments.get('-v'))
    sct.init_sct(log_level=verbose, update=True)  # Update log level

    # Initialize DetectPMJ
    detector = DetectPMJ(fname_im=fname_in,
                            contrast=contrast,
                            fname_seg=fname_seg,
                            path_out=path_results,
                            verbose=verbose)

    # run the extraction
    fname_out, tmp_dir = detector.apply()

    # Remove tmp_dir
    if rm_tmp:
        sct.rmtree(tmp_dir)

    # View results
    if fname_out is not None:
        if path_qc is not None:
            generate_qc(fname_in, fname_seg=fname_out, args=args, path_qc=os.path.abspath(path_qc),
                        process='sct_detect_pmj')

        sct.display_viewer_syntax([fname_in, fname_out], colormaps=['gray', 'red'])
 def checkIfNifti(self, param):
     import os
     sct.printv("Check file existence...", 0)
     nii = False
     niigz = False
     no_image = False
     param_tmp = str()
     if param.lower().endswith('.nii'):
         if self.check_file_exist and self.parser.check_file_exist:
             nii = os.path.isfile(param)
             niigz = os.path.isfile(param + '.gz')
         else:
             nii, niigz = True, False
         param_tmp = param[:-4]
     elif param.lower().endswith('.nii.gz'):
         if self.check_file_exist and self.parser.check_file_exist:
             niigz = os.path.isfile(param)
             nii = os.path.isfile(param[:-3])
         else:
             nii, niigz = False, True
         param_tmp = param[:-7]
     elif param.lower() in self.list_no_image:
         no_image = True
     else:
         self.parser.usage.error("Option " + self.name + " file " + param + " is not a NIFTI image file. Exiting")
     if nii:
         return param_tmp + '.nii'
     elif niigz:
         return param_tmp + '.nii.gz'
     elif no_image:
         return param
     else:
         logger.debug('executed in {}'.format(os.getcwd()))
         self.parser.usage.error("Option " + self.name + " file " + param + " does not exist.")
Example #20
0
    def add_path_to_file(self, dictionary, path_to_add, input_file=True, output_file=False, do_not_add_path=[]):
        """
        This function add a path in front of each value in a dictionary (provided by the parser) for option that are files or folders.
        This function can affect option files that represent input and/or output with "input_file" and output_file" parameters.
        The parameter path_to_add must contain the character "/" at its end.
        Output is the same dictionary as provided but modified with added path.
        :param dictionary:
        :param path_to_add:
        :param input_file:
        :param output_file:
        :param do_not_add_path: list of keys for which path should NOT be added.
        :return:
        """
        for key, option in dictionary.iteritems():
            # Check if option is present in this parser
            if key in self.options:
                # if key is listed in the do_not_add_path variable, do nothing
                if not key in do_not_add_path:
                    # If input file is a list, we need to check what type of list it is.
                    # If it contains files, it must be updated.
                    if (input_file and self.options[key].type_value in Option.OPTION_PATH_INPUT) or (output_file and self.options[key].type_value in Option.OPTION_PATH_OUTPUT):
                        if isinstance(self.options[key].type_value, list):
                            for i, value in enumerate(option):
                                option[i] = path_to_add + value
                            dictionary[key] = option
                        else:
                            dictionary[key] = str(path_to_add) + str(option)
            else:
                sct.printv("ERROR: the option you provided is not contained in this parser. Please check the dictionary", verbose=1, type='error')

        return dictionary
Example #21
0
def get_dimension(im_file, verbose=1):
    """
    Get dimension from nibabel object. Manages 2D, 3D or 4D images.
    :return: nx, ny, nz, nt, px, py, pz, pt
    """
    import nibabel.nifti1
    # initialization
    nx, ny, nz, nt, px, py, pz, pt = 1, 1, 1, 1, 1, 1, 1, 1
    if type(im_file) is nibabel.nifti1.Nifti1Image:
        header = im_file.header
    elif type(im_file) is Image:
        header = im_file.hdr
    else:
        header = None
        sct.printv('WARNING: the provided image file isn\'t a nibabel.nifti1.Nifti1Image instance nor a msct_image.Image instance', verbose, 'warning')

    nb_dims = len(header.get_data_shape())
    if nb_dims == 2:
        nx, ny = header.get_data_shape()
        px, py = header.get_zooms()
    if nb_dims == 3:
        nx, ny, nz = header.get_data_shape()
        px, py, pz = header.get_zooms()
    if nb_dims == 4:
        nx, ny, nz, nt = header.get_data_shape()
        px, py, pz, pt = header.get_zooms()

    return nx, ny, nz, nt, px, py, pz, pt
Example #22
0
    def save(self, type=""):
        """
        Write an image in a nifti file
        :param type:    if not set, the image is saved in standard type
                        if 'minimize', image space is minimize
                        (2, 'uint8', np.uint8, "NIFTI_TYPE_UINT8"),
                        (4, 'int16', np.int16, "NIFTI_TYPE_INT16"),
                        (8, 'int32', np.int32, "NIFTI_TYPE_INT32"),
                        (16, 'float32', np.float32, "NIFTI_TYPE_FLOAT32"),
                        (32, 'complex64', np.complex64, "NIFTI_TYPE_COMPLEX64"),
                        (64, 'float64', np.float64, "NIFTI_TYPE_FLOAT64"),
                        (256, 'int8', np.int8, "NIFTI_TYPE_INT8"),
                        (512, 'uint16', np.uint16, "NIFTI_TYPE_UINT16"),
                        (768, 'uint32', np.uint32, "NIFTI_TYPE_UINT32"),
                        (1024,'int64', np.int64, "NIFTI_TYPE_INT64"),
                        (1280, 'uint64', np.uint64, "NIFTI_TYPE_UINT64"),
                        (1536, 'float128', _float128t, "NIFTI_TYPE_FLOAT128"),
                        (1792, 'complex128', np.complex128, "NIFTI_TYPE_COMPLEX128"),
                        (2048, 'complex256', _complex256t, "NIFTI_TYPE_COMPLEX256"),
        """
        from nibabel import Nifti1Image, save
        from sct_utils import printv

        if type != "":
            self.changeType(type)

        self.hdr.set_data_shape(self.data.shape)
        img = Nifti1Image(self.data, None, self.hdr)
        printv("saving " + self.path + self.file_name + self.ext + "\n", verbose=self.verbose, type="normal")
        save(img, self.path + self.file_name + self.ext)
Example #23
0
def warp_label(path_label, folder_label, file_label, fname_src, fname_transfo, path_out):
    """
    Warp label files according to info_label.txt file
    :param path_label:
    :param folder_label:
    :param file_label:
    :param fname_src:
    :param fname_transfo:
    :param path_out:
    :return:
    """
    # read label file and check if file exists
    sct.printv('\nRead label file...', param.verbose)
    try:
        template_label_ids, template_label_names, template_label_file, combined_labels_ids, combined_labels_names, combined_labels_id_groups, clusters_apriori = read_label_file(path_label + folder_label, file_label)
    except Exception as error:
        sct.printv('\nWARNING: Cannot warp label '+folder_label+': '+str(error), 1, 'warning')
        # raise
    # try:
    #     template_label_ids, template_label_names, template_label_file, combined_labels_ids, combined_labels_names, combined_labels_id_groups = read_label_file(path_label+folder_label, file_label)
    # except Exception:
    #     import traceback
    #     sct.printv('\nERROR: ' + traceback.format_exc(), 1, 'error')
    else:
        # create output folder
        sct.run('mkdir '+path_out+folder_label, param.verbose)
        # Warp label
        for i in xrange(0, len(template_label_file)):
            fname_label = path_label+folder_label+template_label_file[i]
            # check if file exists
            # sct.check_file_exist(fname_label)
            # apply transfo
            sct.run('sct_apply_transfo -i '+fname_label+' -o '+path_out+folder_label+template_label_file[i] +' -d '+fname_src+' -w '+fname_transfo+' -x '+get_interp(template_label_file[i]), param.verbose)
        # Copy list.txt
        sct.run('cp '+path_label+folder_label+param.file_info_label+' '+path_out+folder_label, 0)
def do_loocv(directory):
    original_path = os.path.abspath('.')
    os.chdir(directory)
    dic_3d = None
    dic_by_slice = None
    for dir_name in os.listdir('.'):
        if os.path.isdir(dir_name):
            if '3d' in dir_name.lower():
                dic_3d = dir_name
            if 'by_slice' in dir_name.lower():
                dic_by_slice = dir_name
    if dic_3d is None or dic_by_slice is None:
        sct.printv('WARNING: dictionaries not in the loocv folder ...', 1, 'warning')
    else:
        denoising = factors_levels['denoising'][exp_plan[directory][factors['denoising']]]
        reg = factors_levels['reg'][exp_plan[directory][factors['reg']]]
        metric = factors_levels['metric'][exp_plan[directory][factors['metric']]]
        gamma = factors_levels['gamma'][exp_plan[directory][factors['gamma']]]
        eq = factors_levels['eq'][exp_plan[directory][factors['eq']]]
        mode_weight = factors_levels['mode_weight'][exp_plan[directory][factors['mode_weight']]]
        w_label_fus = factors_levels['weighted_label_fusion'][exp_plan[directory][factors['weighted_label_fusion']]]

        leave_one_out_by_subject(dic_by_slice, dic_3d, denoising=denoising, reg=reg, metric=metric, use_levels=bool(gamma), weight=gamma, eq=eq, mode_weighted_sim=mode_weight, weighted_label_fusion=w_label_fus)

    os.chdir(original_path)
def Metric_Images(imageA, imageB, type=''):

    data_A_list = load(imageA).get_data().tolist()
    data_B_list = load(imageB).get_data().tolist()

    # Define both list of intensity
    list_A = []
    list_B = []
    for i in range(len(data_A_list)):
        list_A = list_A + data_A_list[i]
        list_B = list_B + data_B_list[i]
    # Calculate metric depending on the type
    if type == 'MeanSquares':
        result_metric = 1.0 / (len(list_A)) * np.sum(np.array([list_A[i][0] - list_B[i][0] for i in range(len(list_A))])**2)
        #result_metric = 1/(len(list_A)) * np.sum(np.array(list_A - list_B)**2)

    if type == 'Correlation':
        result_metric = 1.0 / (len(list_A)) * np.sum(np.absolute(np.array([list_A[i][0] - list_B[i][0] for i in range(len(list_A))])))

    if type == 'MI':
        sct.printv('\nto do: MI')

    # Return results
    sct.printv('\nResult of metric is: ' + str(result_metric))
    return result_metric
    def _measure_volume(self, im_data, p_lst, idx):
        for zz in range(im_data.shape[2]):
            self.volumes[zz, idx - 1] = np.sum(im_data[:, :, zz]) * p_lst[0] * p_lst[1] * p_lst[2]

        vol_tot_cur = np.sum(self.volumes[:, idx - 1])
        self.measure_pd.loc[idx, 'volume [mm3]'] = vol_tot_cur
        printv('  Volume : ' + str(np.round(vol_tot_cur, 2)) + ' mm^3', self.verbose, type='info')
    def ifolder2tmp(self):
        # copy input image
        if self.fname_mask is not None:
            sct.copy(self.fname_mask, self.tmp_dir)
            self.fname_mask = ''.join(extract_fname(self.fname_mask)[1:])
        else:
            printv('ERROR: No input image', self.verbose, 'error')

        # copy seg image
        if self.fname_sc is not None:
            sct.copy(self.fname_sc, self.tmp_dir)
            self.fname_sc = ''.join(extract_fname(self.fname_sc)[1:])

        # copy ref image
        if self.fname_ref is not None:
            sct.copy(self.fname_ref, self.tmp_dir)
            self.fname_ref = ''.join(extract_fname(self.fname_ref)[1:])

        # copy registered template
        if self.path_template is not None:
            sct.copy(self.path_levels, self.tmp_dir)
            self.path_levels = ''.join(extract_fname(self.path_levels)[1:])

            self.atlas_roi_lst = []
            for fname_atlas_roi in os.listdir(self.path_atlas):
                if fname_atlas_roi.endswith('.nii.gz'):
                    tract_id = int(fname_atlas_roi.split('_')[-1].split('.nii.gz')[0])
                    if tract_id < 36:  # Not interested in CSF
                        sct.copy(os.path.join(self.path_atlas, fname_atlas_roi), self.tmp_dir)
                        self.atlas_roi_lst.append(fname_atlas_roi)

        os.chdir(self.tmp_dir)  # go to tmp directory
def main(args = None):

    dim_list = ['x', 'y', 'z', 't']

    if not args:
        args = sys.argv[1:]

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(sys.argv[1:])
    fname_in = arguments["-i"]
    fname_out = arguments["-o"]
    verbose = int(arguments['-v'])

    # Build fname_out
    if fname_out == '':
        path_in, file_in, ext_in = extract_fname(fname_in)
        fname_out = path_in+file_in+'_mean'+ext_in

    # Open file.
    nii = Image(fname_in)
    data = nii.data

    # run command
    if '-otsu' in arguments:
        param = arguments['-otsu']
        data_out = otsu(data, param)
    elif '-otsu_adap' in arguments:
        param = arguments['-otsu_adap']
        data_out = otsu_adap(data, param[0], param[1])
    elif '-otsu_median' in arguments:
        param = arguments['-otsu_median']
        data_out = otsu_median(data, param[0], param[1])
    elif '-thr' in arguments:
        param = arguments['-thr']
        data_out = threshold(data, param)
    elif '-percent' in arguments:
        param = arguments['-percent']
        data_out = perc(data, param)
    elif '-mean' in arguments:
        dim = dim_list.index(arguments['-mean'])
        data_out = compute_mean(data, dim)
    elif '-std' in arguments:
        dim = dim_list.index(arguments['-std'])
        data_out = compute_std(data, dim)
    elif '-dilate' in arguments:
        data_out = dilate(data, arguments['-dilate'])
    elif '-erode' in arguments:
        data_out = erode(data, arguments['-dilate'])
    else:
        printv('No process applied.', 1, 'warning')
        return

    # Write output
    nii.data = data_out
    nii.setFileName(fname_out)
    nii.save()

    # display message
    printv('Created file:\n--> '+fname_out+'\n', verbose, 'info')
Example #29
0
def orientation(im, ori=None, set=False, get=False, set_data=False, verbose=1, fname_out=''):
    verbose = 0 if get else verbose
    printv('\nGet dimensions of data...', verbose)
    nx, ny, nz, nt, px, py, pz, pt = get_dimension(im)

    printv(str(nx) + ' x ' + str(ny) + ' x ' + str(nz)+ ' x ' + str(nt), verbose)

    # if data are 2d or 3d, get orientation from header using fslhd

    if (nz == 1 or nt==1) and len(im.data.shape)<5:
        if get:
            try:
                printv('\nGet orientation...', verbose)
                im_out = None
                ori = get_orientation(im)
            except Exception, e:
                printv('ERROR: an error occurred: \n'+str(e), verbose,'error')
            return ori
        elif set:
            # set orientation
            printv('\nChange orientation...', verbose)
            im_out = set_orientation(im, ori)
        elif set_data:
            im_out = set_orientation(im, ori, True)
        else:
            im_out = None
def main(args=None):

    if args is None:
        args = sys.argv[1:]
    param = Param()

    # Check input parameters
    parser = get_parser()
    arguments = parser.parse(args)

    param.fname_data = os.path.abspath(arguments['-i'])

    if '-p' in arguments:
        param.process = arguments['-p']
        if param.process[0] not in param.process_list:
            sct.printv(parser.usage.generate(error='ERROR: Process ' + param.process[0] + ' is not recognized.'))
    if '-size' in arguments:
        param.size = arguments['-size']
    if '-f' in arguments:
        param.shape = arguments['-f']
    if '-o' in arguments:
        param.fname_out = os.path.abspath(arguments['-o'])
    if '-r' in arguments:
        param.remove_temp_files = int(arguments['-r'])

    param.verbose = int(arguments.get('-v'))
    sct.init_sct(log_level=param.verbose, update=True)  # Update log level

    # run main program
    create_mask(param)
Example #31
0
        choices=(0, 1, 2),
        default = 1)

    return parser


########################################################################################################################
# ------------------------------------------------------  MAIN ------------------------------------------------------- #
########################################################################################################################

if __name__ == "__main__":
    init_sct()
    param = Param()
    input_fname = None
    if param.debug:
        sct.printv('\n*** WARNING: DEBUG MODE ON ***\n')
    else:
        param_default = Param()
        parser = get_parser()
        arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help'])
        input_fname = arguments.i
        input_second_fname = ''
        output_fname = 'hausdorff_distance.txt'
        resample_to = 0.1

        if arguments.d is not None:
            input_second_fname = arguments.d
        if arguments.thinning is not None:
            param.thinning = bool(arguments.thinning)
        if arguments.resampling is not None:
            resample_to = arguments.resampling
Example #32
0
def vertebral_detection(fname,
                        fname_seg,
                        contrast,
                        param,
                        init_disc,
                        verbose=1,
                        path_template='',
                        path_output='../',
                        scale_dist=1.):
    """
    Find intervertebral discs in straightened image using template matching

    :param fname: file name of straigthened spinal cord
    :param fname_seg: file name of straigthened spinal cord segmentation
    :param contrast: t1 or t2
    :param param:  advanced parameters
    :param init_disc:
    :param verbose:
    :param path_template:
    :param path_output: output path for verbose=2 pictures
    :param scale_dist: float: Scaling factor to adjust average distance between two adjacent intervertebral discs
    :return:
    """
    sct.printv('\nLook for template...', verbose)
    sct.printv('Path template: ' + path_template, verbose)

    # adjust file names if MNI-Poly-AMU template is used (by default: PAM50)
    fname_level = get_file_label(
        os.path.join(path_template, 'template'),
        id_label=7,
        output='filewithpath'
    )  # label = spinal cord mask with discrete vertebral levels
    id_label_dct = {'T1': 0, 'T2': 1, 'T2S': 2}
    fname_template = get_file_label(
        os.path.join(path_template, 'template'),
        id_label=id_label_dct[contrast.upper()],
        output='filewithpath')  # label = *-weighted template

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

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

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

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

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

    # display init disc
    if verbose == 2:
        from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
        from matplotlib.figure import Figure
        fig_disc = Figure()
        FigureCanvas(fig_disc)
        ax_disc = fig_disc.add_subplot(111)
        # ax_disc = fig_disc.add_axes((0, 0, 1, 1))
        # get percentile for automatic contrast adjustment
        data_display = np.mean(data[xc - param.size_RL:xc +
                                    param.size_RL, :, :],
                               axis=0).transpose()
        percmin = np.percentile(data_display, 10)
        percmax = np.percentile(data_display, 90)
        # display image
        ax_disc.matshow(data_display,
                        cmap='gray',
                        clim=[percmin, percmax],
                        origin='lower')
        ax_disc.set_title('Anatomical image')
        # ax.autoscale(enable=False)  # to prevent autoscale of axis when displaying plot
        ax_disc.scatter(yc + param.shift_AP_visu,
                        init_disc[0],
                        c='yellow',
                        s=10)
        ax_disc.text(yc + param.shift_AP_visu + 4,
                     init_disc[0],
                     str(init_disc[1]) + '/' + str(init_disc[1] + 1),
                     verticalalignment='center',
                     horizontalalignment='left',
                     color='pink',
                     fontsize=7)

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

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

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

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

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

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

    if verbose == 2:
        fig_disc.savefig('fig_label_discs.png')

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

    # Label segmentation
    label_segmentation(fname_seg,
                       list_disc_z,
                       list_disc_value,
                       verbose=verbose)
Example #33
0
def compute_corr_3d(src, target, x, xshift, xsize, y, yshift, ysize, z, zshift,
                    zsize, xtarget, ytarget, ztarget, zrange, verbose,
                    save_suffix, gaussian_std, path_output):
    """
    FIXME doc
    Find z that maximizes correlation between src and target 3d data.

    :param src: 3d source data
    :param target: 3d target data
    :param x:
    :param xshift:
    :param xsize:
    :param y:
    :param yshift:
    :param ysize:
    :param z:
    :param zshift:
    :param zsize:
    :param xtarget:
    :param ytarget:
    :param ztarget:
    :param zrange:
    :param verbose:
    :param save_suffix:
    :param gaussian_std:
    :return:
    """
    # parameters
    thr_corr = 0.2  # disc correlation threshold. Below this value, use template distance.
    # get dimensions from src
    nx, ny, nz = src.shape
    # Get pattern from template
    pattern = target[xtarget - xsize:xtarget + xsize + 1,
                     ytarget + yshift - ysize:ytarget + yshift + ysize + 1,
                     ztarget + zshift - zsize:ztarget + zshift + zsize + 1]
    pattern1d = pattern.ravel()
    # initializations
    I_corr = np.zeros(len(zrange))
    allzeros = 0
    # current_z = 0
    ind_I = 0
    # loop across range of z defined by src
    for iz in zrange:
        # if pattern extends towards the top part of the image, then crop and pad with zeros
        if z + iz + zsize + 1 > nz:
            # sct.printv('iz='+str(iz)+': padding on top')
            padding_size = z + iz + zsize + 1 - nz
            data_chunk3d = src[x - xsize:x + xsize + 1,
                               y + yshift - ysize:y + yshift + ysize + 1, z +
                               iz - zsize:z + iz + zsize + 1 - padding_size]
            data_chunk3d = np.pad(data_chunk3d,
                                  ((0, 0), (0, 0), (0, padding_size)),
                                  'constant',
                                  constant_values=0)
        # if pattern extends towards bottom part of the image, then crop and pad with zeros
        elif z + iz - zsize < 0:
            # sct.printv('iz='+str(iz)+': padding at bottom')
            padding_size = abs(iz - zsize)
            data_chunk3d = src[x - xsize:x + xsize + 1,
                               y + yshift - ysize:y + yshift + ysize + 1, z +
                               iz - zsize + padding_size:z + iz + zsize + 1]
            data_chunk3d = np.pad(data_chunk3d,
                                  ((0, 0), (0, 0), (padding_size, 0)),
                                  'constant',
                                  constant_values=0)
        else:
            data_chunk3d = src[x - xsize:x + xsize + 1,
                               y + yshift - ysize:y + yshift + ysize + 1,
                               z + iz - zsize:z + iz + zsize + 1]

        # convert subject pattern to 1d
        data_chunk1d = data_chunk3d.ravel()
        # check if data_chunk1d contains at least one non-zero value
        if (data_chunk1d.size == pattern1d.size) and np.any(data_chunk1d):
            I_corr[ind_I] = mutual_information(data_chunk1d,
                                               pattern1d,
                                               nbins=16,
                                               normalized=False)
        else:
            allzeros = 1
        ind_I = ind_I + 1
    # ind_y = ind_y + 1
    if allzeros:
        sct.printv(
            '.. WARNING: Data contained zero. We probably hit the edge of the image.',
            verbose)

    # adjust correlation with Gaussian function centered at the right edge of the curve (most rostral point of FOV)
    from scipy.signal import gaussian
    gaussian_window = gaussian(len(I_corr) * 2, std=len(I_corr) * gaussian_std)
    I_corr_gauss = np.multiply(I_corr, gaussian_window[0:len(I_corr)])

    # Find global maximum
    if np.any(I_corr_gauss):
        # if I_corr contains at least a non-zero value
        ind_peak = [
            i for i in range(len(I_corr_gauss))
            if I_corr_gauss[i] == max(I_corr_gauss)
        ][0]  # index of max along z
        sct.printv(
            '.. Peak found: z=' + str(zrange[ind_peak]) + ' (correlation = ' +
            str(I_corr_gauss[ind_peak]) + ')', verbose)
        # check if correlation is high enough
        if I_corr_gauss[ind_peak] < thr_corr:
            sct.printv(
                '.. WARNING: Correlation is too low. Using adjusted template distance.',
                verbose)
            ind_peak = zrange.index(0)  # approx_distance_to_next_disc
    else:
        # if I_corr contains only zeros
        sct.printv(
            '.. WARNING: Correlation vector only contains zeros. Using adjusted template distance.',
            verbose)
        ind_peak = zrange.index(0)  # approx_distance_to_next_disc

    # display patterns and correlation
    if verbose == 2:
        from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
        from matplotlib.figure import Figure
        fig = Figure(figsize=(15, 7))
        FigureCanvas(fig)
        # display template pattern
        ax = fig.add_subplot(131)
        ax.imshow(np.flipud(np.mean(pattern[:, :, :], axis=0).transpose()),
                  origin='upper',
                  cmap='gray',
                  interpolation='none')
        ax.set_title('Template pattern')
        # display subject pattern at best z
        ax = fig.add_subplot(132)
        iz = zrange[ind_peak]
        data_chunk3d = src[x - xsize:x + xsize + 1,
                           y + yshift - ysize:y + yshift + ysize + 1,
                           z + iz - zsize:z + iz + zsize + 1]
        ax.imshow(np.flipud(
            np.mean(data_chunk3d[:, :, :], axis=0).transpose()),
                  origin='upper',
                  cmap='gray',
                  clim=[0, 800],
                  interpolation='none')
        ax.set_title('Subject at iz=' + str(iz))
        # display correlation curve
        ax = fig.add_subplot(133)
        ax.plot(zrange, I_corr)
        ax.plot(zrange, I_corr_gauss, 'black', linestyle='dashed')
        ax.legend(['I_corr', 'I_corr_gauss'])
        ax.set_title('Mutual Info, gaussian_std=' + str(gaussian_std))
        ax.plot(zrange[ind_peak], I_corr_gauss[ind_peak], 'ro')
        ax.axvline(x=zrange.index(0),
                   linewidth=1,
                   color='black',
                   linestyle='dashed')
        ax.axhline(y=thr_corr, linewidth=1, color='r', linestyle='dashed')
        ax.grid()
        # save figure
        fig.savefig('fig_pattern' + save_suffix + '.png')

    # return z-origin (z) + z-displacement minus zshift (to account for non-centered disc)
    return z + zrange[ind_peak] - zshift
Example #34
0
def main(args=None):

    # initializations
    initz = ''
    initcenter = ''
    fname_initlabel = ''
    file_labelz = 'labelz.nii.gz'
    param = Param()

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

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(args)
    fname_in = os.path.abspath(arguments["-i"])
    fname_seg = os.path.abspath(arguments['-s'])
    contrast = arguments['-c']
    path_template = os.path.abspath(arguments['-t'])
    scale_dist = arguments['-scale-dist']
    if '-ofolder' in arguments:
        path_output = arguments['-ofolder']
    else:
        path_output = os.curdir
    param.path_qc = arguments.get("-qc", None)
    if '-discfile' in arguments:
        fname_disc = os.path.abspath(arguments['-discfile'])
    else:
        fname_disc = None
    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:
        file = open(arguments['-initfile'], 'r')
        initfile = ' ' + file.read().replace('\n', '')
        arg_initfile = initfile.split(' ')
        for idx_arg, arg in enumerate(arg_initfile):
            if arg == '-initz':
                initz = [int(x) for x in arg_initfile[idx_arg + 1].split(',')]
            if arg == '-initcenter':
                initcenter = int(arg_initfile[idx_arg + 1])
    if '-initlabel' in arguments:
        # get absolute path of label
        fname_initlabel = os.path.abspath(arguments['-initlabel'])
    if '-param' in arguments:
        param.update(arguments['-param'][0])
    verbose = int(arguments.get('-v'))
    sct.init_sct(log_level=verbose, update=True)  # Update log level
    remove_temp_files = int(arguments['-r'])
    denoise = int(arguments['-denoise'])
    laplacian = int(arguments['-laplacian'])

    path_tmp = sct.tmp_create(basename="label_vertebrae", verbose=verbose)

    # Copying input data to tmp folder
    sct.printv('\nCopying input data to tmp folder...', verbose)
    Image(fname_in).save(os.path.join(path_tmp, "data.nii"))
    Image(fname_seg).save(os.path.join(path_tmp, "segmentation.nii"))

    # Go go temp folder
    curdir = os.getcwd()
    os.chdir(path_tmp)

    # 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)
    cache_sig = sct.cache_signature(input_files=[fname_in, fname_seg], )
    cachefile = os.path.join(curdir, "straightening.cache")
    if sct.cache_valid(cachefile, cache_sig) and os.path.isfile(
            os.path.join(
                curdir, "warp_curve2straight.nii.gz")) and os.path.isfile(
                    os.path.join(
                        curdir,
                        "warp_straight2curve.nii.gz")) and os.path.isfile(
                            os.path.join(curdir, "straight_ref.nii.gz")):
        # if they exist, copy them into current folder
        sct.printv('Reusing existing warping field which seems to be valid',
                   verbose, 'warning')
        sct.copy(os.path.join(curdir, "warp_curve2straight.nii.gz"),
                 'warp_curve2straight.nii.gz')
        sct.copy(os.path.join(curdir, "warp_straight2curve.nii.gz"),
                 'warp_straight2curve.nii.gz')
        sct.copy(os.path.join(curdir, "straight_ref.nii.gz"),
                 'straight_ref.nii.gz')
        # apply straightening
        s, o = sct.run([
            'sct_apply_transfo', '-i', 'data.nii', '-w',
            'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o',
            'data_straight.nii'
        ])
    else:
        sct_straighten_spinalcord.main(args=[
            '-i',
            'data.nii',
            '-s',
            'segmentation.nii',
            '-r',
            str(remove_temp_files),
            '-v',
            str(verbose),
        ])
        sct.cache_save(cachefile, cache_sig)

    # resample to 0.5mm isotropic to match template resolution
    sct.printv('\nResample to 0.5mm isotropic...', verbose)
    s, o = sct.run([
        'sct_resample', '-i', 'data_straight.nii', '-mm', '0.5x0.5x0.5', '-x',
        'linear', '-o', 'data_straightr.nii'
    ],
                   verbose=verbose)

    # Apply straightening to segmentation
    # N.B. Output is RPI
    sct.printv('\nApply straightening to segmentation...', verbose)
    sct.run(
        'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' %
        ('segmentation.nii', 'data_straightr.nii',
         'warp_curve2straight.nii.gz', 'segmentation_straight.nii', 'Linear'),
        verbose=verbose,
        is_sct_binary=True,
    )
    # Threshold segmentation at 0.5
    sct.run([
        'sct_maths', '-i', 'segmentation_straight.nii', '-thr', '0.5', '-o',
        'segmentation_straight.nii'
    ], verbose)

    # If disc label file is provided, label vertebrae using that file instead of automatically
    if fname_disc:
        # Apply straightening to disc-label
        sct.printv('\nApply straightening to disc labels...', verbose)
        sct.run(
            'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' %
            (fname_disc, 'data_straightr.nii', 'warp_curve2straight.nii.gz',
             'labeldisc_straight.nii.gz', 'NearestNeighbor'),
            verbose=verbose,
            is_sct_binary=True,
        )
        label_vert('segmentation_straight.nii',
                   'labeldisc_straight.nii.gz',
                   verbose=1)

    else:
        # create label to identify disc
        sct.printv('\nCreate label to identify disc...', verbose)
        fname_labelz = os.path.join(path_tmp, file_labelz)
        if initz or initcenter:
            if initcenter:
                # find z centered in FOV
                nii = Image('segmentation.nii').change_orientation("RPI")
                nx, ny, nz, nt, px, py, pz, pt = nii.dim  # Get dimensions
                z_center = int(np.round(nz / 2))  # get z_center
                initz = [z_center, initcenter]
            # create single label and output as labels.nii.gz
            label = ProcessLabels(
                'segmentation.nii',
                fname_output='tmp.labelz.nii.gz',
                coordinates=['{},{}'.format(initz[0], initz[1])])
            im_label = label.process('create-seg')
            im_label.data = sct_maths.dilate(
                im_label.data,
                [3])  # TODO: create a dilation method specific to labels,
            # which does not apply a convolution across all voxels (highly inneficient)
            im_label.save(fname_labelz)
        elif fname_initlabel:
            import sct_label_utils
            # subtract "1" to label value because due to legacy, in this code the disc C2-C3 has value "2", whereas in the
            # recent version of SCT it is defined as "3". Therefore, when asking the user to define a label, we point to the
            # new definition of labels (i.e., C2-C3 = 3).
            sct_label_utils.main(
                ['-i', fname_initlabel, '-add', '-1', '-o', fname_labelz])
        else:
            # automatically finds C2-C3 disc
            im_data = Image('data.nii')
            im_seg = Image('segmentation.nii')
            if not remove_temp_files:  # because verbose is here also used for keeping temp files
                verbose_detect_c2c3 = 2
            else:
                verbose_detect_c2c3 = 0
            im_label_c2c3 = detect_c2c3(im_data,
                                        im_seg,
                                        contrast,
                                        verbose=verbose_detect_c2c3)
            ind_label = np.where(im_label_c2c3.data)
            if not np.size(ind_label) == 0:
                # subtract "1" to label value because due to legacy, in this code the disc C2-C3 has value "2", whereas in the
                # recent version of SCT it is defined as "3".
                im_label_c2c3.data[ind_label] = 2
            else:
                sct.printv(
                    'Automatic C2-C3 detection failed. Please provide manual label with sct_label_utils',
                    1, 'error')
                sys.exit()
            im_label_c2c3.save(fname_labelz)

        # dilate label so it is not lost when applying warping
        sct_maths.main(
            ['-i', fname_labelz, '-dilate', '3', '-o', fname_labelz])

        # Apply straightening to z-label
        sct.printv('\nAnd apply straightening to label...', verbose)
        sct.run(
            'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' %
            (file_labelz, 'data_straightr.nii', 'warp_curve2straight.nii.gz',
             'labelz_straight.nii.gz', 'NearestNeighbor'),
            verbose=verbose,
            is_sct_binary=True,
        )
        # 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',
                            contrast,
                            param,
                            init_disc=init_disc,
                            verbose=verbose,
                            path_template=path_template,
                            path_output=path_output,
                            scale_dist=scale_dist)

    # un-straighten labeled spinal cord
    sct.printv('\nUn-straighten labeling...', verbose)
    sct.run(
        'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' %
        ('segmentation_straight_labeled.nii', 'segmentation.nii',
         'warp_straight2curve.nii.gz', 'segmentation_labeled.nii',
         'NearestNeighbor'),
        verbose=verbose,
        is_sct_binary=True,
    )
    # Clean labeled segmentation
    sct.printv(
        '\nClean labeled segmentation (correct interpolation errors)...',
        verbose)
    clean_labeled_segmentation('segmentation_labeled.nii', 'segmentation.nii',
                               'segmentation_labeled.nii')

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

    # come back
    os.chdir(curdir)

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

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

    # Generate QC report
    if param.path_qc is not None:
        path_qc = os.path.abspath(param.path_qc)
        qc_dataset = arguments.get("-qc-dataset", None)
        qc_subject = arguments.get("-qc-subject", None)
        labeled_seg_file = os.path.join(path_output,
                                        file_seg + '_labeled' + ext_seg)
        generate_qc(fname_in,
                    fname_seg=labeled_seg_file,
                    args=args,
                    path_qc=os.path.abspath(path_qc),
                    dataset=qc_dataset,
                    subject=qc_subject,
                    process='sct_label_vertebrae')

    sct.display_viewer_syntax([fname_in, fname_seg_labeled],
                              colormaps=['', 'subcortical'],
                              opacities=['1', '0.5'])
def main():

    # Initialization
    fname_warp_list = ''  # list of warping fields
    fname_dest = ''  # destination image (fix)
    fname_warp_final = ''  # concatenated transformations
    verbose = 1

    # Parameters for debug mode
    if param.debug:
        sct.printv('\n*** WARNING: DEBUG MODE ON ***\n')
        path_sct_data = os.environ.get(
            "SCT_TESTING_DATA_DIR",
            os.path.join(os.path.dirname(os.path.dirname(__file__))),
            "testing_data")
        fname_warp_list = os.path.join(
            path_sct_data, 't2',
            'warp_template2anat.nii.gz') + '-' + os.path.join(
                path_sct_data, 'mt', 'warp_template2mt.nii.gz')
        fname_dest = os.path.join(path_sct_data, 'mt', 'mtr.nii.gz')
        verbose = 1
    else:
        # Check input parameters
        parser = get_parser()
        arguments = parser.parse(sys.argv[1:])

        fname_dest = arguments['-d']
        fname_warp_list = arguments['-w']

        if '-o' in arguments:
            fname_warp_final = arguments['-o']
        verbose = int(arguments['-v'])

    # Parse list of warping fields
    sct.printv('\nParse list of transformations...', verbose)
    use_inverse = []
    fname_warp_list_invert = []
    for i in range(len(fname_warp_list)):
        # Check if inverse matrix is specified with '-' at the beginning of file name
        if fname_warp_list[i].find('-') == 0:
            use_inverse.append('-i')
            fname_warp_list[i] = fname_warp_list[i][1:]  # remove '-'
            fname_warp_list_invert += [[use_inverse[i], fname_warp_list[i]]]
        else:
            use_inverse.append('')
            fname_warp_list_invert += [[fname_warp_list[i]]]
        sct.printv(
            '  Transfo #' + str(i) + ': ' + use_inverse[i] +
            fname_warp_list[i], verbose)

    # Check file existence
    sct.printv('\nCheck file existence...', verbose)
    sct.check_file_exist(fname_dest, verbose)
    for i in range(len(fname_warp_list)):
        sct.check_file_exist(fname_warp_list[i], verbose)

    # Get output folder and file name
    if fname_warp_final == '':
        path_out, file_out, ext_out = sct.extract_fname(param.fname_warp_final)
    else:
        path_out, file_out, ext_out = sct.extract_fname(fname_warp_final)

    # Check dimension of destination data (cf. issue #1419, #1429)
    im_dest = Image(fname_dest)
    if im_dest.dim[2] == 1:
        dimensionality = '2'
    else:
        dimensionality = '3'

    # Concatenate warping fields
    sct.printv('\nConcatenate warping fields...', verbose)
    # N.B. Here we take the inverse of the warp list
    fname_warp_list_invert.reverse()
    fname_warp_list_invert = functools.reduce(lambda x, y: x + y,
                                              fname_warp_list_invert)

    cmd = [
        'isct_ComposeMultiTransform', dimensionality, 'warp_final' + ext_out,
        '-R', fname_dest
    ] + fname_warp_list_invert
    status, output = sct.run(cmd, verbose=verbose)

    # check if output was generated
    if not os.path.isfile('warp_final' + ext_out):
        sct.printv('ERROR: Warping field was not generated.\n' + output, 1,
                   'error')

    # Generate output files
    sct.printv('\nGenerate output files...', verbose)
    sct.generate_output_file('warp_final' + ext_out,
                             os.path.join(path_out, file_out + ext_out))
Example #36
0
def compute_properties_along_centerline(fname_seg_image, property_list, fname_disks_image=None, smooth_factor=5.0, interpolation_mode=0, remove_temp_files=1, verbose=1):

    # Check list of properties
    # If diameters is in the list, compute major and minor axis length and check orientation
    compute_diameters = False
    property_list_local = list(property_list)
    if 'diameters' in property_list_local:
        compute_diameters = True
        property_list_local.remove('diameters')
        property_list_local.append('major_axis_length')
        property_list_local.append('minor_axis_length')
        property_list_local.append('orientation')

    # TODO: make sure fname_segmentation and fname_disks are in the same space
    # create temporary folder and copying data
    sct.printv('\nCreate temporary folder...', verbose)
    path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S") + '_' + str(randint(1, 1000000)), 1)
    sct.run('mkdir ' + path_tmp, verbose)

    sct.run('cp ' + fname_seg_image + ' ' + path_tmp)
    if fname_disks_image is not None:
        sct.run('cp ' + fname_disks_image + ' ' + path_tmp)

    # go to tmp folder
    os.chdir(path_tmp)

    fname_segmentation = os.path.abspath(fname_seg_image)
    path_data, file_data, ext_data = sct.extract_fname(fname_segmentation)

    # Change orientation of the input centerline into RPI
    sct.printv('\nOrient centerline to RPI orientation...', verbose)
    im_seg = Image(file_data + ext_data)
    fname_segmentation_orient = 'segmentation_rpi' + ext_data
    image = set_orientation(im_seg, 'RPI')
    image.setFileName(fname_segmentation_orient)
    image.save()

    # Initiating some variables
    nx, ny, nz, nt, px, py, pz, pt = image.dim
    resolution = 0.5
    properties = {key: [] for key in property_list_local}
    properties['incremental_length'] = []
    properties['distance_from_C1'] = []
    properties['vertebral_level'] = []
    properties['z_slice'] = []

    # compute the spinal cord centerline based on the spinal cord segmentation
    number_of_points = 5 * nz
    x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline(fname_segmentation_orient, algo_fitting='nurbs', verbose=verbose, nurbs_pts_number=number_of_points, all_slices=False, phys_coordinates=True, remove_outliers=True)
    centerline = Centerline(x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv)

    # Compute vertebral distribution along centerline based on position of intervertebral disks
    if fname_disks_image is not None:
        fname_disks = os.path.abspath(fname_disks_image)
        path_data, file_data, ext_data = sct.extract_fname(fname_disks)
        im_disks = Image(file_data + ext_data)
        fname_disks_orient = 'disks_rpi' + ext_data
        image_disks = set_orientation(im_disks, 'RPI')
        image_disks.setFileName(fname_disks_orient)
        image_disks.save()

        image_disks = Image(fname_disks_orient)
        coord = image_disks.getNonZeroCoordinates(sorting='z', reverse_coord=True)
        coord_physical = []
        for c in coord:
            c_p = image_disks.transfo_pix2phys([[c.x, c.y, c.z]])[0]
            c_p.append(c.value)
            coord_physical.append(c_p)
        centerline.compute_vertebral_distribution(coord_physical)

    sct.printv('Computing spinal cord shape along the spinal cord...')
    timer_properties = sct.Timer(number_of_iteration=centerline.number_of_points)
    timer_properties.start()
    # Extracting patches perpendicular to the spinal cord and computing spinal cord shape
    for index in range(centerline.number_of_points):
        value_out = -5.0
        current_patch = centerline.extract_perpendicular_square(image, index, resolution=resolution, interpolation_mode=interpolation_mode, border='constant', cval=value_out)

        # check for pixels close to the spinal cord segmentation that are out of the image
        from skimage.morphology import dilation
        patch_zero = np.copy(current_patch)
        patch_zero[patch_zero == value_out] = 0.0
        patch_borders = dilation(patch_zero) - patch_zero

        if np.count_nonzero(patch_borders + current_patch == value_out + 1.0) != 0:
            continue

        sc_properties = properties2d(patch_zero, [resolution, resolution])
        if sc_properties is not None:
            properties['incremental_length'].append(centerline.incremental_length[index])
            if fname_disks_image is not None:
                properties['distance_from_C1'].append(centerline.dist_points[index])
                properties['vertebral_level'].append(centerline.l_points[index])
            properties['z_slice'].append(image.transfo_phys2pix([centerline.points[index]])[0][2])
            for property_name in property_list_local:
                properties[property_name].append(sc_properties[property_name])

        timer_properties.add_iteration()
    timer_properties.stop()

    # Adding centerline to the properties for later use
    properties['centerline'] = centerline

    # We assume that the major axis is in the right-left direction
    # this script checks the orientation of the spinal cord and invert axis if necessary to make sure the major axis is right-left
    if compute_diameters:
        diameter_major = properties['major_axis_length']
        diameter_minor = properties['minor_axis_length']
        orientation = properties['orientation']
        for i, orientation_item in enumerate(orientation):
            if -45.0 < orientation_item < 45.0:
                continue
            else:
                temp = diameter_minor[i]
                properties['minor_axis_length'][i] = diameter_major[i]
                properties['major_axis_length'][i] = temp

        properties['RL_diameter'] = properties['major_axis_length']
        properties['AP_diameter'] = properties['minor_axis_length']
        del properties['major_axis_length']
        del properties['minor_axis_length']

    # smooth the spinal cord shape with a gaussian kernel if required
    # TODO: not all properties can be smoothed
    if smooth_factor != 0.0:  # smooth_factor is in mm
        import scipy
        window = scipy.signal.hann(smooth_factor / np.mean(centerline.progressive_length))
        for property_name in property_list_local:
            properties[property_name] = scipy.signal.convolve(properties[property_name], window, mode='same') / np.sum(window)

    if compute_diameters:
        property_list_local.remove('major_axis_length')
        property_list_local.remove('minor_axis_length')
        property_list_local.append('RL_diameter')
        property_list_local.append('AP_diameter')
        property_list = property_list_local

    # Display properties on the referential space. Requires intervertebral disks
    if verbose == 2:
        x_increment = 'distance_from_C1'
        if fname_disks_image is None:
            x_increment = 'incremental_length'

        # Display the image and plot all contours found
        fig, axes = plt.subplots(len(property_list_local), sharex=True, sharey=False)
        for k, property_name in enumerate(property_list_local):
            axes[k].plot(properties[x_increment], properties[property_name])
            axes[k].set_ylabel(property_name)

        if fname_disks_image is not None:
            properties['distance_disk_from_C1'] = centerline.distance_from_C1label  # distance between each disk and C1 (or first disk)
            xlabel_disks = [centerline.convert_vertlabel2disklabel[label] for label in properties['distance_disk_from_C1']]
            xtick_disks = [properties['distance_disk_from_C1'][label] for label in properties['distance_disk_from_C1']]
            plt.xticks(xtick_disks, xlabel_disks, rotation=30)
        else:
            axes[-1].set_xlabel('Position along the spinal cord (in mm)')

        plt.show()

    # Removing temporary folder
    os.chdir('..')
    shutil.rmtree(path_tmp, ignore_errors=True)

    return property_list, properties
Example #37
0
    def __init__(self, im1, im2=None, param=None):
        self.im1 = im1
        self.im2 = im2
        self.dim_im = len(self.im1.data.shape)
        self.dim_pix = 0
        self.distances = None
        self.res = ''
        self.param = param
        self.dist1_distribution = None
        self.dist2_distribution = None

        if self.dim_im == 3:
            self.orientation1 = self.im1.orientation
            if self.orientation1 != 'IRP':
                self.im1.change_orientation('IRP', generate_path=True)

            if self.im2 is not None:
                self.orientation2 = self.im2.orientation
                if self.orientation2 != 'IRP':
                    self.im2.change_orientation('IRP', generate_path=True)

        if self.param.thinning:
            self.thinning1 = Thinning(self.im1, self.param.verbose)
            self.thinning1.thinned_image.save()

            if self.im2 is not None:
                self.thinning2 = Thinning(self.im2, self.param.verbose)
                self.thinning2.thinned_image.save()

        if self.dim_im == 2 and self.im2 is not None:
            self.compute_dist_2im_2d()

        if self.dim_im == 3:
            if self.im2 is None:
                self.compute_dist_1im_3d()
            else:
                self.compute_dist_2im_3d()

        if self.dim_im == 2 and self.distances is not None:
            self.dist1_distribution = self.distances.min_distances_1[np.nonzero(self.distances.min_distances_1)]
            self.dist2_distribution = self.distances.min_distances_2[np.nonzero(self.distances.min_distances_2)]
        if self.dim_im == 3:
            self.dist1_distribution = []
            self.dist2_distribution = []

            for d in self.distances:
                if np.nonzero(d.min_distances_1)[0].size:  # Exist non zero values
                    self.dist1_distribution.append(d.min_distances_1[np.nonzero(d.min_distances_1)])
                else:  # all values are zero
                    self.dist1_distribution.append(0)
                if np.nonzero(d.min_distances_2)[0].size:  # Exist non zero values
                    self.dist2_distribution.append(d.min_distances_2[np.nonzero(d.min_distances_2)])
                else:  # all values are zero
                    self.dist2_distribution.append(0)

            self.res = 'Hausdorff\'s distance  -  First relative Hausdorff\'s distance median - Second relative Hausdorff\'s distance median(all in mm)\n'
            for i, d in enumerate(self.distances):
                med1 = np.median(self.dist1_distribution[i])
                med2 = np.median(self.dist2_distribution[i])
                if self.im2 is None:
                    self.res += 'Slice ' + str(i) + ' - slice ' + str(i + 1) + ': ' + str(d.H * self.dim_pix) + '  -  ' + str(med1 * self.dim_pix) + '  -  ' + str(med2 * self.dim_pix) + ' \n'
                else:
                    self.res += 'Slice ' + str(i) + ': ' + str(d.H * self.dim_pix) + '  -  ' + str(med1 * self.dim_pix) + '  -  ' + str(med2 * self.dim_pix) + ' \n'

        sct.printv('-----------------------------------------------------------------------------\n' +
                   self.res, self.param.verbose, 'normal')

        if self.param.verbose == 2:
            self.show_results()
def main(args = None):

    dim_list = ['x', 'y', 'z', 't']

    if not args:
        args = sys.argv[1:]

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(args)
    fname_in = arguments["-i"]
    fname_out = arguments["-o"]
    verbose = int(arguments['-v'])
    if '-type' in arguments:
        output_type = arguments['-type']
    else:
        output_type = None

    # Open file(s)
    im = Image(fname_in)
    data = im.data  # 3d or 4d numpy array
    dim = im.dim

    # run command
    if '-otsu' in arguments:
        param = arguments['-otsu']
        data_out = otsu(data, param)

    elif '-otsu_adap' in arguments:
        param = arguments['-otsu_adap']
        data_out = otsu_adap(data, param[0], param[1])

    elif '-otsu_median' in arguments:
        param = arguments['-otsu_median']
        data_out = otsu_median(data, param[0], param[1])

    elif '-thr' in arguments:
        param = arguments['-thr']
        data_out = threshold(data, param)

    elif '-percent' in arguments:
        param = arguments['-percent']
        data_out = perc(data, param)

    elif '-bin' in arguments:
        bin_thr = arguments['-bin']
        data_out = binarise(data, bin_thr=bin_thr)

    elif '-add' in arguments:
        from numpy import sum
        data2 = get_data_or_scalar(arguments["-add"], data)
        data_concat = concatenate_along_4th_dimension(data, data2)
        data_out = sum(data_concat, axis=3)

    elif '-sub' in arguments:
        data2 = get_data_or_scalar(arguments['-sub'], data)
        data_out = data - data2

    elif "-laplacian" in arguments:
        sigmas = arguments["-laplacian"]
        if len(sigmas) == 1:
            sigmas = [sigmas for i in range(len(data.shape))]
        elif len(sigmas) != len(data.shape):
            printv(parser.usage.generate(error='ERROR: -laplacian need the same number of inputs as the number of image dimension OR only one input'))
        # adjust sigma based on voxel size
        sigmas = [sigmas[i] / dim[i + 4] for i in range(3)]
        # smooth data
        data_out = laplacian(data, sigmas)

    elif '-mul' in arguments:
        from numpy import prod
        data2 = get_data_or_scalar(arguments["-mul"], data)
        data_concat = concatenate_along_4th_dimension(data, data2)
        data_out = prod(data_concat, axis=3)

    elif '-div' in arguments:
        from numpy import divide
        data2 = get_data_or_scalar(arguments["-div"], data)
        data_out = divide(data, data2)

    elif '-mean' in arguments:
        from numpy import mean
        dim = dim_list.index(arguments['-mean'])
        if dim + 1 > len(np.shape(data)):  # in case input volume is 3d and dim=t
            data = data[..., np.newaxis]
        data_out = mean(data, dim)

    elif '-rms' in arguments:
        from numpy import mean, sqrt, square
        dim = dim_list.index(arguments['-rms'])
        if dim + 1 > len(np.shape(data)):  # in case input volume is 3d and dim=t
            data = data[..., np.newaxis]
        data_out = sqrt(mean(square(data.astype(float)), dim))

    elif '-std' in arguments:
        from numpy import std
        dim = dim_list.index(arguments['-std'])
        if dim + 1 > len(np.shape(data)):  # in case input volume is 3d and dim=t
            data = data[..., np.newaxis]
        data_out = std(data, dim, ddof=1)

    elif "-smooth" in arguments:
        sigmas = arguments["-smooth"]
        if len(sigmas) == 1:
            sigmas = [sigmas[0] for i in range(len(data.shape))]
        elif len(sigmas) != len(data.shape):
            printv(parser.usage.generate(error='ERROR: -smooth need the same number of inputs as the number of image dimension OR only one input'))
        # adjust sigma based on voxel size
        sigmas = [sigmas[i] / dim[i + 4] for i in range(3)]
        # smooth data
        data_out = smooth(data, sigmas)

    elif '-dilate' in arguments:
        data_out = dilate(data, arguments['-dilate'])

    elif '-erode' in arguments:
        data_out = erode(data, arguments['-erode'])

    elif '-denoise' in arguments:
        # parse denoising arguments
        p, b = 1, 5  # default arguments
        list_denoise = arguments['-denoise']
        for i in list_denoise:
            if 'p' in i:
                p = int(i.split('=')[1])
            if 'b' in i:
                b = int(i.split('=')[1])
        data_out = denoise_nlmeans(data, patch_radius=p, block_radius=b)

    elif '-symmetrize' in arguments:
        data_out = (data + data[list(range(data.shape[0] - 1, -1, -1)), :, :]) / float(2)

    elif '-mi' in arguments:
        # input 1 = from flag -i --> im
        # input 2 = from flag -mi
        im_2 = Image(arguments['-mi'])
        compute_similarity(im.data, im_2.data, fname_out, metric='mi', verbose=verbose)
        data_out = None

    elif '-minorm' in arguments:
        im_2 = Image(arguments['-minorm'])
        compute_similarity(im.data, im_2.data, fname_out, metric='minorm', verbose=verbose)
        data_out = None

    elif '-corr' in arguments:
        # input 1 = from flag -i --> im
        # input 2 = from flag -mi
        im_2 = Image(arguments['-corr'])
        compute_similarity(im.data, im_2.data, fname_out, metric='corr', verbose=verbose)
        data_out = None

    # if no flag is set
    else:
        data_out = None
        printv(parser.usage.generate(error='ERROR: you need to specify an operation to do on the input image'))

    if data_out is not None:
        # Write output
        nii_out = Image(fname_in)  # use header of input file
        nii_out.data = data_out
        nii_out.save(fname_out, dtype=output_type)
    # TODO: case of multiple outputs
    # assert len(data_out) == n_out
    # if n_in == n_out:
    #     for im_in, d_out, fn_out in zip(nii, data_out, fname_out):
    #         im_in.data = d_out
    #         im_in.absolutepath = fn_out
    #         if "-w" in arguments:
    #             im_in.hdr.set_intent('vector', (), '')
    #         im_in.save()
    # elif n_out == 1:
    #     nii[0].data = data_out[0]
    #     nii[0].absolutepath = fname_out[0]
    #     if "-w" in arguments:
    #             nii[0].hdr.set_intent('vector', (), '')
    #     nii[0].save()
    # elif n_out > n_in:
    #     for dat_out, name_out in zip(data_out, fname_out):
    #         im_out = nii[0].copy()
    #         im_out.data = dat_out
    #         im_out.absolutepath = name_out
    #         if "-w" in arguments:
    #             im_out.hdr.set_intent('vector', (), '')
    #         im_out.save()
    # else:
    #     printv(parser.usage.generate(error='ERROR: not the correct numbers of inputs and outputs'))

    # display message
    if data_out is not None:
        sct.display_viewer_syntax([fname_out], verbose=verbose)
    else:
        printv('\nDone! File created: ' + fname_out, verbose, 'info')
Example #39
0
def heatmap(filename_in,
            filename_out,
            model,
            patch_shape,
            mean_train,
            std_train,
            brain_bool=True):
    """Compute the heatmap with CNN_1 representing the SC localization."""
    im = Image(filename_in)
    data_im = im.data.astype(np.float32)
    im_out = change_type(im, "uint8")
    del im
    data = np.zeros(im_out.data.shape)

    x_shape, y_shape = data_im.shape[:2]
    x_shape_block, y_shape_block = np.ceil(
        x_shape * 1.0 / patch_shape[0]).astype(np.int), np.int(y_shape * 1.0 /
                                                               patch_shape[1])
    x_pad = int(x_shape_block * patch_shape[0] - x_shape)
    if y_shape > patch_shape[1]:
        y_crop = y_shape - y_shape_block * patch_shape[1]
        # slightly crop the input data in the P-A direction so that data_im.shape[1] % patch_shape[1] == 0
        data_im = data_im[:, :y_shape - y_crop, :]
        # coordinates of the blocks to scan during the detection, in the cross-sectional plane
        coord_lst = [[
            x_dim * patch_shape[0], y_dim * patch_shape[1],
            (x_dim + 1) * patch_shape[0], (y_dim + 1) * patch_shape[1]
        ] for y_dim in range(y_shape_block) for x_dim in range(x_shape_block)]
    else:
        data_im = np.pad(data_im,
                         ((0, 0), (0, patch_shape[1] - y_shape), (0, 0)),
                         'constant')
        coord_lst = [[
            x_dim * patch_shape[0], 0, (x_dim + 1) * patch_shape[0],
            patch_shape[1]
        ] for x_dim in range(x_shape_block)]
    # pad the input data in the R-L direction
    data_im = np.pad(data_im, ((0, x_pad), (0, 0), (0, 0)), 'constant')
    # scale intensities between 0 and 255
    data_im = scale_intensity(data_im)

    x_CoM, y_CoM = None, None
    z_sc_notDetected_cmpt = 0
    for zz in range(data_im.shape[2]):
        # if SC was detected at zz-1, we will start doing the detection on the block centered around the previously conputed center of mass (CoM)
        if x_CoM is not None:
            z_sc_notDetected_cmpt = 0  # SC detected, cmpt set to zero
            x_0, x_1 = _find_crop_start_end(x_CoM, patch_shape[0],
                                            data_im.shape[0])
            y_0, y_1 = _find_crop_start_end(y_CoM, patch_shape[1],
                                            data_im.shape[1])
            block = data_im[x_0:x_1, y_0:y_1, zz]
            block_nn = np.expand_dims(np.expand_dims(block, 0), -1)
            block_nn_norm = _normalize_data(block_nn, mean_train, std_train)
            block_pred = model.predict(block_nn_norm, batch_size=BATCH_SIZE)

            # coordinates manipulation due to the above padding and cropping
            if x_1 > data.shape[0]:
                x_end = data.shape[0]
                x_1 = data.shape[0]
                x_0 = data.shape[0] - patch_shape[0] if data.shape[
                    0] > patch_shape[0] else 0
            else:
                x_end = patch_shape[0]
            if y_1 > data.shape[1]:
                y_end = data.shape[1]
                y_1 = data.shape[1]
                y_0 = data.shape[1] - patch_shape[1] if data.shape[
                    1] > patch_shape[1] else 0
            else:
                y_end = patch_shape[1]

            data[x_0:x_1, y_0:y_1, zz] = block_pred[0, :x_end, :y_end, 0]

            # computation of the new center of mass
            if np.max(data[:, :, zz]) > 0.5:
                z_slice_out_bin = data[:, :,
                                       zz] > 0.5  # if the SC was detection
                x_CoM, y_CoM = center_of_mass(z_slice_out_bin)
                x_CoM, y_CoM = int(x_CoM), int(y_CoM)
            else:
                x_CoM, y_CoM = None, None

        # if the SC was not detected at zz-1 or on the patch centered around CoM in slice zz, the entire cross-sectional slice is scaned
        if x_CoM is None:
            z_slice, x_CoM, y_CoM, coord_lst = scan_slice(
                data_im[:, :, zz], model, mean_train, std_train, coord_lst,
                patch_shape, data.shape[:2])
            data[:, :, zz] = z_slice

            z_sc_notDetected_cmpt += 1
            # if the SC has not been detected on 10 consecutive z_slices, we stop the SC investigation
            if z_sc_notDetected_cmpt > 10 and brain_bool:
                sct.printv('Brain section detected.')
                break

        # distance transform to deal with the harsh edges of the prediction boundaries (Dice)
        data[:, :, zz][np.where(data[:, :, zz] < 0.5)] = 0
        data[:, :, zz] = distance_transform_edt(data[:, :, zz])

    if not np.any(data):
        sct.log.error(
            '\nSpinal cord was not detected using "-centerline cnn". Please try another "-centerline" method.\n'
        )
        sys.exit(1)

    im_out.data = data
    im_out.save(filename_out)
    del im_out

    # z_max is used to reject brain sections
    z_max = np.max(list(set(np.where(data)[2])))
    if z_max == data.shape[2] - 1:
        return None
    else:
        return z_max
def main(args = None):

    orientation = ''
    change_header = ''
    fname_out = ''

    if not args:
        args = sys.argv[1:]

    # Building the command, do sanity checks
    parser = get_parser()
    arguments = parser.parse(sys.argv[1:])
    fname_in = arguments['-i']
    if '-o' in arguments:
        fname_out = arguments['-o']
    if '-s' in arguments:
        orientation = arguments['-s']
    if '-a' in arguments:
        change_header = arguments['-a']
    remove_tmp_files = int(arguments['-r'])
    verbose = int(arguments['-v'])
    inversion = False  # change orientation

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

    # copy file in temp folder
    sct.printv('\nCopy files to tmp folder...', verbose)
    convert(fname_in, path_tmp+'data.nii')

    # go to temp folder
    os.chdir(path_tmp)

    # Get dimensions of data
    sct.printv('\nGet dimensions of data...', verbose)
    nx, ny, nz, nt, px, py, pz, pt = Image('data.nii').dim
    sct.printv(str(nx) + ' x ' + str(ny) + ' x ' + str(nz)+ ' x ' + str(nt), verbose)

    # if data are 3d, directly set or get orientation
    if nt == 1:
        if orientation != '':
            # set orientation
            sct.printv('\nChange orientation...', verbose)
            if change_header == '':
                set_orientation('data.nii', orientation, 'data_orient.nii')
            else:
                set_orientation('data.nii', change_header, 'data_orient.nii', True)
        else:
            # get orientation
            sct.printv('\nGet orientation...', verbose)
            print get_orientation('data.nii')
    else:
        # split along T dimension
        sct.printv('\nSplit along T dimension...', verbose)
        from sct_split_data import split_data
        split_data('data.nii', 3, '_T')
        if orientation != '':
            # set orientation
            sct.printv('\nChange orientation...', verbose)
            for it in range(nt):
                file_data_split = 'data_T'+str(it).zfill(4)+'.nii'
                file_data_split_orient = 'data_orient_T'+str(it).zfill(4)+'.nii'
                set_orientation(file_data_split, orientation, file_data_split_orient)
            # Merge files back
            sct.printv('\nMerge file back...', verbose)
            from sct_concat_data import concat_data
            from glob import glob
            concat_data(glob('data_orient_T*.nii'), 'data_orient.nii', dim=3)

        else:
            sct.printv('\nGet orientation...', verbose)
            print get_orientation('data_T0000.nii')

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

    # Generate output files
    if orientation != '':
        # Build fname_out
        if fname_out == '':
            path_data, file_data, ext_data = sct.extract_fname(fname_in)
            fname_out = path_data+file_data+'_'+orientation+ext_data
        sct.printv('\nGenerate output files...', verbose)
        sct.generate_output_file(path_tmp+'data_orient.nii', fname_out)

    # Remove temporary files
    if remove_tmp_files == 1:
        sct.printv('\nRemove temporary files...', verbose)
        sct.run('rm -rf '+path_tmp, verbose)
Example #41
0
def spline(folder_mat, nt, nz, verbose, index_b0=[], graph=0):
    # get path of the toolbox
    status, path_sct = commands.getstatusoutput('echo $SCT_DIR')
    # append path that contains scripts, to be able to load modules
    sys.path.append(path_sct + '/scripts')
    import sct_utils as sct

    sct.printv(
        '\n\n\n------------------------------------------------------------------------------',
        verbose)
    sct.printv('Spline Regularization along T: Smoothing Patient Motion...',
               verbose)

    file_mat = [[[] for i in range(nz)] for i in range(nt)]
    for it in range(nt):
        for iz in range(nz):
            file_mat[it][iz] = folder_mat + 'mat.T' + str(it) + '_Z' + str(
                iz) + '.txt'

    # Copying the existing Matrices to another folder
    old_mat = folder_mat + 'old/'
    if not os.path.exists(old_mat):
        os.makedirs(old_mat)
    cmd = 'cp ' + folder_mat + '*.txt ' + old_mat
    status, output = sct.run(cmd, verbose)

    sct.printv('\nloading matrices...', verbose)
    X = [[[] for i in range(nt)] for i in range(nz)]
    Y = [[[] for i in range(nt)] for i in range(nz)]
    X_smooth = [[[] for i in range(nt)] for i in range(nz)]
    Y_smooth = [[[] for i in range(nt)] for i in range(nz)]
    for iz in range(nz):
        for it in range(nt):
            file = open(file_mat[it][iz])
            Matrix = np.loadtxt(file)
            file.close()

            X[iz][it] = Matrix[0, 3]
            Y[iz][it] = Matrix[1, 3]

    # Generate motion splines
    sct.printv('\nGenerate motion splines...', verbose)
    T = np.arange(nt)
    if graph:
        import pylab as pl

    for iz in range(nz):

        #        frequency = scipy.fftpack.fftfreq(len(X[iz][:]), d=1)
        #        spectrum = np.abs(scipy.fftpack.fft(X[iz][:], n=None, axis=-1, overwrite_x=False))
        #        Wn = np.amax(frequency)/10
        #        N = 5              #Order of the filter
        #        b, a = scipy.signal.iirfilter(N, Wn, rp=None, rs=None, btype='low', analog=False, ftype='butter', output='ba')
        #        X_smooth[iz][:] = scipy.signal.filtfilt(b, a, X[iz][:], axis=-1, padtype=None)

        spline = scipy.interpolate.UnivariateSpline(T,
                                                    X[iz][:],
                                                    w=None,
                                                    bbox=[None, None],
                                                    k=3,
                                                    s=None)
        X_smooth[iz][:] = spline(T)

        if graph:
            pl.plot(T, X_smooth[iz][:], label='spline_smoothing')
            pl.plot(T,
                    X[iz][:],
                    marker='*',
                    linestyle='None',
                    label='original_val')
            if len(index_b0) != 0:
                T_b0 = [T[i_b0] for i_b0 in index_b0]
                X_b0 = [X[iz][i_b0] for i_b0 in index_b0]
                pl.plot(T_b0,
                        X_b0,
                        marker='D',
                        linestyle='None',
                        color='k',
                        label='b=0')
            pl.title('X')
            pl.grid()
            pl.legend()
            pl.show()

#        frequency = scipy.fftpack.fftfreq(len(Y[iz][:]), d=1)
#        spectrum = np.abs(scipy.fftpack.fft(Y[iz][:], n=None, axis=-1, overwrite_x=False))
#        Wn = np.amax(frequency)/10
#        N = 5              #Order of the filter
#        b, a = scipy.signal.iirfilter(N, Wn, rp=None, rs=None, btype='low', analog=False, ftype='butter', output='ba')
#        Y_smooth[iz][:] = scipy.signal.filtfilt(b, a, Y[iz][:], axis=-1, padtype=None)

        spline = scipy.interpolate.UnivariateSpline(T,
                                                    Y[iz][:],
                                                    w=None,
                                                    bbox=[None, None],
                                                    k=3,
                                                    s=None)
        Y_smooth[iz][:] = spline(T)

        if graph:
            pl.plot(T, Y_smooth[iz][:], label='spline_smoothing')
            pl.plot(T,
                    Y[iz][:],
                    marker='*',
                    linestyle='None',
                    label='original_val')
            if len(index_b0) != 0:
                T_b0 = [T[i_b0] for i_b0 in index_b0]
                Y_b0 = [Y[iz][i_b0] for i_b0 in index_b0]
                pl.plot(T_b0,
                        Y_b0,
                        marker='D',
                        linestyle='None',
                        color='k',
                        label='b=0')
            pl.title('Y')
            pl.grid()
            pl.legend()
            pl.show()

    # Storing the final Matrices
    sct.printv('\nStoring the final Matrices...', verbose)
    for iz in range(nz):
        for it in range(nt):
            file = open(file_mat[it][iz])
            Matrix = np.loadtxt(file)
            file.close()

            Matrix[0, 3] = X_smooth[iz][it]
            Matrix[1, 3] = Y_smooth[iz][it]

            file = open(file_mat[it][iz], 'w')
            np.savetxt(file_mat[it][iz],
                       Matrix,
                       fmt="%s",
                       delimiter='  ',
                       newline='\n')
            file.close()

    sct.printv('\n...Done. Patient motion has been smoothed', verbose)
    sct.printv(
        '------------------------------------------------------------------------------\n',
        verbose)
Example #42
0
def main():
    """Main function."""
    parser = get_parser()
    args = parser.parse_args(args=None if sys.argv[1:] else ['--help'])

    fname_image = args.i
    contrast_type = args.c

    ctr_algo = args.centerline

    brain_bool = bool(args.brain)
    if args.brain is None and contrast_type in ['t2s', 't2_ax']:
        brain_bool = False

    output_folder = args.ofolder

    if ctr_algo == 'file' and args.file_centerline is None:
        sct.printv(
            'Please use the flag -file_centerline to indicate the centerline filename.',
            1, 'error')
        sys.exit(1)

    if args.file_centerline is not None:
        manual_centerline_fname = args.file_centerline
        ctr_algo = 'file'
    else:
        manual_centerline_fname = None

    remove_temp_files = args.r
    verbose = args.v
    sct.init_sct(log_level=verbose, update=True)  # Update log level

    algo_config_stg = '\nMethod:'
    algo_config_stg += '\n\tCenterline algorithm: ' + str(ctr_algo)
    algo_config_stg += '\n\tAssumes brain section included in the image: ' + str(
        brain_bool) + '\n'
    sct.printv(algo_config_stg)

    # Segment image
    from spinalcordtoolbox.image import Image
    from spinalcordtoolbox.deepseg_lesion.core import deep_segmentation_MSlesion
    im_image = Image(fname_image)
    im_seg, im_labels_viewer, im_ctr = deep_segmentation_MSlesion(
        im_image,
        contrast_type,
        ctr_algo=ctr_algo,
        ctr_file=manual_centerline_fname,
        brain_bool=brain_bool,
        remove_temp_files=remove_temp_files,
        verbose=verbose)

    # Save segmentation
    fname_seg = os.path.abspath(
        os.path.join(
            output_folder,
            sct.extract_fname(fname_image)[1] + '_lesionseg' +
            sct.extract_fname(fname_image)[2]))
    im_seg.save(fname_seg)

    if ctr_algo == 'viewer':
        # Save labels
        fname_labels = os.path.abspath(
            os.path.join(
                output_folder,
                sct.extract_fname(fname_image)[1] + '_labels-centerline' +
                sct.extract_fname(fname_image)[2]))
        im_labels_viewer.save(fname_labels)

    if verbose == 2:
        # Save ctr
        fname_ctr = os.path.abspath(
            os.path.join(
                output_folder,
                sct.extract_fname(fname_image)[1] + '_centerline' +
                sct.extract_fname(fname_image)[2]))
        im_ctr.save(fname_ctr)

    sct.display_viewer_syntax([fname_image, fname_seg],
                              colormaps=['gray', 'red'],
                              opacities=['', '0.7'])
Example #43
0
def resample():

    fsloutput = 'export FSLOUTPUTTYPE=NIFTI; '  # for faster processing, all outputs are in NIFTI
    ext = '.nii'

    # display usage if a mandatory argument is not provided
    if param.fname_data == '' or param.factor == '':
        sct.printv('\nERROR: All mandatory arguments are not provided. See usage (add -h).\n', 1, 'error')

    # check existence of input files
    sct.printv('\nCheck existence of input files...', param.verbose)
    sct.check_file_exist(param.fname_data, param.verbose)

    # extract resampling factor
    sct.printv('\nParse resampling factor...', param.verbose)
    factor_split = param.factor.split('x')
    factor = [float(factor_split[i]) for i in range(len(factor_split))]
    # check if it has three values
    if not len(factor) == 3:
        sct.printv('\nERROR: factor should have three dimensions. E.g., 2x2x1.\n', 1, 'error')
    else:
        fx, fy, fz = [float(factor_split[i]) for i in range(len(factor_split))]

    # check interpolation
    if param.interpolation not in ['NearestNeighbor','Linear','Cubic','Sinc','Gaussian']:
        sct.printv('\nERROR: interpolation should be one of those:NearestNeighbor|Linear|Cubic|Sinc|Gaussian.\n', 1, 'error')

    # display input parameters
    sct.printv('\nInput parameters:', param.verbose)
    sct.printv('  data ..................'+param.fname_data, param.verbose)
    sct.printv('  resampling factor .....'+param.factor, param.verbose)

    # Extract path/file/extension
    path_data, file_data, ext_data = sct.extract_fname(param.fname_data)
    path_out, file_out, ext_out = '', file_data, ext_data

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

    # Copying input data to tmp folder and convert to nii
    # NB: cannot use c3d here because c3d cannot convert 4D data.
    sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose)
    sct.run('cp '+param.fname_data+' '+path_tmp+'data'+ext_data, param.verbose)

    # go to tmp folder
    os.chdir(path_tmp)

    # convert to nii format
    convert('data'+ext_data, 'data.nii')

    # Get dimensions of data
    sct.printv('\nGet dimensions of data...', param.verbose)
    nx, ny, nz, nt, px, py, pz, pt = Image('data.nii').dim
    sct.printv('  ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz)+ ' x ' + str(nt), param.verbose)
    dim = 4  # by default, will be adjusted later
    if nt == 1:
        dim = 3
    if nz == 1:
        dim = 2
        sct.run('ERROR (sct_resample): Dimension of input data is different from 3 or 4. Exit program', param.verbose, 'error')

    # Calculate new dimensions
    sct.printv('\nCalculate new dimensions...', param.verbose)
    nx_new = int(round(nx*fx))
    ny_new = int(round(ny*fy))
    nz_new = int(round(nz*fz))
    sct.printv('  ' + str(nx_new) + ' x ' + str(ny_new) + ' x ' + str(nz_new)+ ' x ' + str(nt), param.verbose)

    # if dim=4, split data
    if dim == 4:
        # Split into T dimension
        sct.printv('\nSplit along T dimension...', param.verbose)
        from sct_split_data import split_data
        split_data('data.nii', 3, '_T')
    elif dim == 3:
        # rename file to have compatible code with 4d
        status, output = sct.run('cp data.nii data_T0000.nii', param.verbose)

    for it in range(nt):
        # identify current volume
        file_data_splitT = 'data_T'+str(it).zfill(4)
        file_data_splitT_resample = file_data_splitT+'r'

        # resample volume
        sct.printv(('\nResample volume '+str((it+1))+'/'+str(nt)+':'), param.verbose)
        sct.run('isct_c3d '+file_data_splitT+ext+' -interpolation '+param.interpolation+' -resample '+str(nx_new)+'x'+str(ny_new)+'x'+str(nz_new)+'vox -o '+file_data_splitT_resample+ext)

        # pad data (for ANTs)
        # # TODO: check if need to pad also for the estimate_and_apply
        # if program == 'ants' and todo == 'estimate' and slicewise == 0:
        #     sct.run('isct_c3d '+file_data_splitT_num[it]+' -pad 0x0x3vox 0x0x3vox 0 -o '+file_data_splitT_num[it]+'_pad.nii')
        #     file_data_splitT_num[it] = file_data_splitT_num[it]+'_pad'

    # merge data back along T
    file_data_resample = file_data+param.file_suffix
    sct.printv('\nMerge data back along T...', param.verbose)
    from sct_concat_data import concat_data
    import glob
    concat_data(glob.glob('data_T*r.nii'), file_data_resample, dim=3)

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

    # Generate output files
    sct.printv('\nGenerate output files...', param.verbose)
    if not param.fname_out:
        param.fname_out = path_out+file_out+param.file_suffix+ext_out
    sct.generate_output_file(path_tmp+file_data_resample+ext, param.fname_out)

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

    # to view results
    sct.printv('\nDone! To view results, type:', param.verbose)
    sct.printv('fslview '+param.fname_out+' &', param.verbose, 'info')
    print
Example #44
0
def moco(param):

    # retrieve parameters
    fsloutput = 'export FSLOUTPUTTYPE=NIFTI; '  # for faster processing, all outputs are in NIFTI
    file_data = param.file_data
    file_target = param.file_target
    folder_mat = sct.slash_at_the_end(param.mat_moco,
                                      1)  # output folder of mat file
    todo = param.todo
    suffix = param.suffix
    #file_schedule = param.file_schedule
    verbose = param.verbose
    ext = '.nii'

    # get path of the toolbox
    status, path_sct = commands.getstatusoutput('echo $SCT_DIR')

    # sct.printv(arguments)
    sct.printv('\nInput parameters:', param.verbose)
    sct.printv('  Input file ............' + file_data, param.verbose)
    sct.printv('  Reference file ........' + file_target, param.verbose)
    sct.printv('  Polynomial degree .....' + param.poly, param.verbose)
    sct.printv('  Smoothing kernel ......' + param.smooth, param.verbose)
    sct.printv('  Gradient step .........' + param.gradStep, param.verbose)
    sct.printv('  Metric ................' + param.metric, param.verbose)
    sct.printv('  Sampling ..............' + param.sampling, param.verbose)
    sct.printv('  Todo ..................' + todo, param.verbose)
    sct.printv('  Mask  .................' + param.fname_mask, param.verbose)
    sct.printv('  Output mat folder .....' + folder_mat, param.verbose)

    # create folder for mat files
    sct.create_folder(folder_mat)

    # Get size of data
    sct.printv('\nGet dimensions data...', verbose)
    data_im = Image(file_data + ext)
    nx, ny, nz, nt, px, py, pz, pt = data_im.dim
    sct.printv(('.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' +
                str(nt)), verbose)

    # copy file_target to a temporary file
    sct.printv('\nCopy file_target to a temporary file...', verbose)
    sct.run('cp ' + file_target + ext + ' target.nii')
    file_target = 'target'

    # Split data along T dimension
    sct.printv('\nSplit data along T dimension...', verbose)
    data_split_list = split_data(data_im, dim=3)
    for im in data_split_list:
        im.save()
    file_data_splitT = file_data + '_T'

    # Motion correction: initialization
    index = np.arange(nt)
    file_data_splitT_num = []
    file_data_splitT_moco_num = []
    failed_transfo = [0 for i in range(nt)]
    file_mat = [[] for i in range(nt)]

    # Motion correction: Loop across T
    for indice_index in range(nt):

        # create indices and display stuff
        it = index[indice_index]
        file_data_splitT_num.append(file_data_splitT + str(it).zfill(4))
        file_data_splitT_moco_num.append(file_data + suffix + '_T' +
                                         str(it).zfill(4))
        sct.printv(('\nVolume ' + str((it)) + '/' + str(nt - 1) + ':'),
                   verbose)
        file_mat[it] = folder_mat + 'mat.T' + str(it)

        # run 3D registration
        failed_transfo[it] = register(param, file_data_splitT_num[it],
                                      file_target, file_mat[it],
                                      file_data_splitT_moco_num[it])

        # average registered volume with target image
        # N.B. use weighted averaging: (target * nb_it + moco) / (nb_it + 1)
        if param.iterative_averaging and indice_index < 10 and failed_transfo[
                it] == 0 and not param.todo == 'apply':
            sct.run('sct_maths -i ' + file_target + ext + ' -mul ' +
                    str(indice_index + 1) + ' -o ' + file_target + ext)
            sct.run('sct_maths -i ' + file_target + ext + ' -add ' +
                    file_data_splitT_moco_num[it] + ext + ' -o ' +
                    file_target + ext)
            sct.run('sct_maths -i ' + file_target + ext + ' -div ' +
                    str(indice_index + 2) + ' -o ' + file_target + ext)

    # Replace failed transformation with the closest good one
    sct.printv(('\nReplace failed transformations...'), verbose)
    fT = [i for i, j in enumerate(failed_transfo) if j == 1]
    gT = [i for i, j in enumerate(failed_transfo) if j == 0]
    for it in range(len(fT)):
        abs_dist = [abs(gT[i] - fT[it]) for i in range(len(gT))]
        if not abs_dist == []:
            index_good = abs_dist.index(min(abs_dist))
            sct.printv(
                '  transfo #' + str(fT[it]) + ' --> use transfo #' +
                str(gT[index_good]), verbose)
            # copy transformation
            sct.run('cp ' + file_mat[gT[index_good]] + 'Warp.nii.gz' + ' ' +
                    file_mat[fT[it]] + 'Warp.nii.gz')
            # apply transformation
            sct.run(
                'sct_apply_transfo -i ' + file_data_splitT_num[fT[it]] +
                '.nii -d ' + file_target + '.nii -w ' + file_mat[fT[it]] +
                'Warp.nii.gz' + ' -o ' + file_data_splitT_moco_num[fT[it]] +
                '.nii' + ' -x ' + param.interp, verbose)
        else:
            # exit program if no transformation exists.
            sct.printv(
                '\nERROR in ' + os.path.basename(__file__) +
                ': No good transformation exist. Exit program.\n', verbose,
                'error')
            sys.exit(2)

    # Merge data along T
    file_data_moco = file_data + suffix
    if todo != 'estimate':
        sct.printv('\nMerge data back along T...', verbose)
        from sct_image import concat_data
        # im_list = []
        fname_list = []
        for indice_index in range(len(index)):
            # im_list.append(Image(file_data_splitT_moco_num[indice_index] + ext))
            fname_list.append(file_data_splitT_moco_num[indice_index] + ext)
        im_out = concat_data(fname_list, 3)
        im_out.setFileName(file_data_moco + ext)
        im_out.save()

    # delete file target.nii (to avoid conflict if this function is run another time)
    sct.printv('\nRemove temporary file...', verbose)
    # os.remove('target.nii')
    sct.run('rm target.nii')
def main():
    # Initialization to defaults parameters
    fname_data = ''  # data is empty by default
    path_label = ''  # empty by default
    method = param.method  # extraction mode by default
    labels_of_interest = param.labels_of_interest
    slices_of_interest = param.slices_of_interest
    vertebral_levels = param.vertebral_levels
    average_all_labels = param.average_all_labels
    fname_output = param.fname_output
    fname_vertebral_labeling = param.fname_vertebral_labeling
    fname_normalizing_label = ''  # optional then default is empty
    normalization_method = ''  # optional then default is empty
    actual_vert_levels = None  # variable used in case the vertebral levels asked by the user don't correspond exactly to the vertebral levels available in the metric data
    warning_vert_levels = None  # variable used to warn the user in case the vertebral levels he asked don't correspond exactly to the vertebral levels available in the metric data
    verbose = param.verbose
    flag_h = 0
    ml_clusters = param.ml_clusters
    adv_param = param.adv_param
    adv_param_user = ''

    # Parameters for debug mode
    if param.debug:
        print '\n*** WARNING: DEBUG MODE ON ***\n'
        status, path_sct_data = commands.getstatusoutput(
            'echo $SCT_TESTING_DATA_DIR')
        fname_data = '/Users/julien/data/temp/sct_example_data/mt/mtr.nii.gz'
        path_label = '/Users/julien/data/temp/sct_example_data/mt/label/atlas/'
        method = 'map'
        ml_clusters = '0:29,30,31'
        labels_of_interest = '0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29'
        slices_of_interest = ''
        vertebral_levels = ''
        average_all_labels = 1
        fname_normalizing_label = ''  #path_sct+'/testing/data/errsm_23/mt/label/template/MNI-Poly-AMU_CSF.nii.gz'
        normalization_method = ''  #'whole'
    else:
        # Check input parameters
        try:
            opts, args = getopt.getopt(
                sys.argv[1:], 'haf:i:l:m:n:o:p:v:w:z:')  # define flags
        except getopt.GetoptError as err:  # check if the arguments are defined
            print str(err)  # error
            usage()  # display usage
        if not opts:
            usage()
        for opt, arg in opts:  # explore flags
            if opt in '-a':
                average_all_labels = 1
            elif opt in '-f':
                path_label = os.path.abspath(arg)  # save path of labels folder
            elif opt == '-h':  # help option
                flag_h = 1
            elif opt in '-i':
                fname_data = arg
            elif opt in '-l':
                labels_of_interest = arg
            elif opt in '-m':  # method for metric extraction
                method = arg
            elif opt in '-n':  # filename of the label by which the user wants to normalize
                fname_normalizing_label = arg
            elif opt in '-o':  # output option
                fname_output = arg  # fname of output file
            elif opt in '-p':
                adv_param_user = arg
            elif opt in '-v':
                # vertebral levels option, if the user wants to average the metric across specific vertebral levels
                vertebral_levels = arg
            elif opt in '-w':
                # method used for the normalization by the metric estimation into the normalizing label (see flag -n): 'sbs' for slice-by-slice or 'whole' for normalization after estimation in the whole labels
                normalization_method = arg
            elif opt in '-z':  # slices numbers option
                slices_of_interest = arg  # save labels numbers

    # Display usage with tract parameters by default in case files aren't chosen in arguments inputs
    if fname_data == '' or path_label == '' or flag_h:
        param.path_label = path_label
        usage()

    # Check existence of data file
    sct.printv('\ncheck existence of input files...', verbose)
    sct.check_file_exist(fname_data)
    sct.check_folder_exist(path_label)
    if fname_normalizing_label:
        sct.check_folder_exist(fname_normalizing_label)

    # add slash at the end
    path_label = sct.slash_at_the_end(path_label, 1)

    # Find path to the vertebral labeling file if vertebral levels were specified by the user
    if vertebral_levels:
        if slices_of_interest:  # impossible to select BOTH specific slices and specific vertebral levels
            print '\nERROR: You cannot select BOTH vertebral levels AND slice numbers.'
            usage()
        else:
            fname_vertebral_labeling_list = sct.find_file_within_folder(
                fname_vertebral_labeling, path_label + '..')
            if len(fname_vertebral_labeling_list) > 1:
                print color.red + 'ERROR: More than one file named \'' + fname_vertebral_labeling + ' were found in ' + path_label + '. Exit program.' + color.end
                sys.exit(2)
            elif len(fname_vertebral_labeling_list) == 0:
                print color.red + 'ERROR: No file named \'' + fname_vertebral_labeling + ' were found in ' + path_label + '. Exit program.' + color.end
                sys.exit(2)
            else:
                fname_vertebral_labeling = os.path.abspath(
                    fname_vertebral_labeling_list[0])

    # Check input parameters
    check_method(method, fname_normalizing_label, normalization_method)

    # parse argument for param
    if not adv_param_user == '':
        adv_param = adv_param_user.replace(' ', '').split(
            ',')  # remove spaces and parse with comma
        del adv_param_user  # clean variable
        # TODO: check integrity of input

    # Extract label info
    label_id, label_name, label_file = read_label_file(path_label,
                                                       param.file_info_label)
    nb_labels_total = len(label_id)

    # check consistency of label input parameter.
    label_id_user, average_all_labels = check_labels(
        labels_of_interest, nb_labels_total, average_all_labels,
        method)  # If 'labels_of_interest' is empty, then
    # 'label_id_user' contains the index of all labels in the file info_label.txt

    # print parameters
    print '\nChecked parameters:'
    print '  data ...................... ' + fname_data
    print '  folder label .............. ' + path_label
    print '  selected labels ........... ' + str(label_id_user)
    print '  estimation method ......... ' + method
    print '  slices of interest ........ ' + slices_of_interest
    print '  vertebral levels .......... ' + vertebral_levels
    print '  vertebral labeling file.... ' + fname_vertebral_labeling
    print '  advanced parameters ....... ' + str(adv_param)

    # Check if the orientation of the data is RPI
    orientation_data = get_orientation(fname_data)

    # If orientation is not RPI, change to RPI
    if orientation_data != 'RPI':
        sct.printv(
            '\nCreate temporary folder to change the orientation of the NIFTI files into RPI...',
            verbose)
        path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"),
                                        1)
        sct.create_folder(path_tmp)
        # change orientation and load data
        sct.printv('\nChange image orientation and load it...', verbose)
        data = nib.load(
            set_orientation(fname_data, 'RPI',
                            path_tmp + 'orient_data.nii')).get_data()
        # Do the same for labels
        sct.printv('\nChange labels orientation and load them...', verbose)
        labels = np.empty([nb_labels_total],
                          dtype=object)  # labels(nb_labels_total, x, y, z)
        for i_label in range(0, nb_labels_total):
            labels[i_label] = nib.load(
                set_orientation(path_label + label_file[i_label], 'RPI',
                                path_tmp + 'orient_' +
                                label_file[i_label])).get_data()
        if fname_normalizing_label:  # if the "normalization" option is wanted,
            normalizing_label = np.empty(
                [1], dtype=object
            )  # choose this kind of structure so as to keep easily the
            # compatibility with the rest of the code (dimensions: (1, x, y, z))
            normalizing_label[0] = nib.load(
                set_orientation(fname_normalizing_label, 'RPI', path_tmp +
                                'orient_normalizing_volume.nii')).get_data()
        if vertebral_levels:  # if vertebral levels were selected,
            data_vertebral_labeling = nib.load(
                set_orientation(
                    fname_vertebral_labeling, 'RPI',
                    path_tmp + 'orient_vertebral_labeling.nii.gz')).get_data()
        # Remove the temporary folder used to change the NIFTI files orientation into RPI
        sct.printv('\nRemove the temporary folder...', verbose)
        status, output = commands.getstatusoutput('rm -rf ' + path_tmp)
    else:
        # Load image
        sct.printv('\nLoad image...', verbose)
        data = nib.load(fname_data).get_data()

        # Load labels
        sct.printv('\nLoad labels...', verbose)
        labels = np.empty([nb_labels_total],
                          dtype=object)  # labels(nb_labels_total, x, y, z)
        for i_label in range(0, nb_labels_total):
            labels[i_label] = nib.load(path_label +
                                       label_file[i_label]).get_data()
        if fname_normalizing_label:  # if the "normalization" option is wanted,
            normalizing_label = np.empty(
                [1], dtype=object
            )  # choose this kind of structure so as to keep easily the
            # compatibility with the rest of the code (dimensions: (1, x, y, z))
            normalizing_label[0] = nib.load(fname_normalizing_label).get_data(
            )  # load the data of the normalizing label
        if vertebral_levels:  # if vertebral levels were selected,
            data_vertebral_labeling = nib.load(
                fname_vertebral_labeling).get_data()

    # Change metric data type into floats for future manipulations (normalization)
    data = np.float64(data)

    # Get dimensions of data
    sct.printv('\nGet dimensions of data...', verbose)
    nx, ny, nz = data.shape
    sct.printv('  ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose)

    # Get dimensions of labels
    sct.printv('\nGet dimensions of label...', verbose)
    nx_atlas, ny_atlas, nz_atlas = labels[0].shape
    sct.printv(
        '.. ' + str(nx_atlas) + ' x ' + str(ny_atlas) + ' x ' + str(nz_atlas) +
        ' x ' + str(nb_labels_total), verbose)

    # Check dimensions consistency between atlas and data
    if (nx, ny, nz) != (nx_atlas, ny_atlas, nz_atlas):
        print '\nERROR: Metric data and labels DO NOT HAVE SAME DIMENSIONS.'
        sys.exit(2)

    # Update the flag "slices_of_interest" according to the vertebral levels selected by user (if it's the case)
    if vertebral_levels:
        slices_of_interest, actual_vert_levels, warning_vert_levels = \
            get_slices_matching_with_vertebral_levels(data, vertebral_levels, data_vertebral_labeling)

    # select slice of interest by cropping data and labels
    if slices_of_interest:
        data = remove_slices(data, slices_of_interest)
        for i_label in range(0, nb_labels_total):
            labels[i_label] = remove_slices(labels[i_label],
                                            slices_of_interest)
        if fname_normalizing_label:  # if the "normalization" option was selected,
            normalizing_label[0] = remove_slices(normalizing_label[0],
                                                 slices_of_interest)

    # if user wants to get unique value across labels, then combine all labels together
    if average_all_labels == 1:
        sum_labels_user = np.sum(
            labels[label_id_user])  # sum the labels selected by user
        if method == 'ml' or method == 'map':  # in case the maximum likelihood and the average across different labels are wanted
            labels_tmp = np.empty([nb_labels_total - len(label_id_user) + 1],
                                  dtype=object)
            labels = np.delete(
                labels, label_id_user)  # remove the labels selected by user
            labels_tmp[
                0] = sum_labels_user  # put the sum of the labels selected by user in first position of the tmp
            # variable
            for i_label in range(1, len(labels_tmp)):
                labels_tmp[i_label] = labels[
                    i_label -
                    1]  # fill the temporary array with the values of the non-selected labels
            labels = labels_tmp  # replace the initial labels value by the updated ones (with the summed labels)
            del labels_tmp  # delete the temporary labels
        else:  # in other cases than the maximum likelihood, we can remove other labels (not needed for estimation)
            labels = np.empty(1, dtype=object)
            labels[
                0] = sum_labels_user  # we create a new label array that includes only the summed labels

    if fname_normalizing_label:  # if the "normalization" option is wanted
        sct.printv('\nExtract normalization values...', verbose)
        if normalization_method == 'sbs':  # case: the user wants to normalize slice-by-slice
            for z in range(0, data.shape[-1]):
                normalizing_label_slice = np.empty(
                    [1], dtype=object
                )  # in order to keep compatibility with the function
                # 'extract_metric_within_tract', define a new array for the slice z of the normalizing labels
                normalizing_label_slice[0] = normalizing_label[0][..., z]
                metric_normalizing_label = extract_metric_within_tract(
                    data[..., z], normalizing_label_slice, method, 0)
                # estimate the metric mean in the normalizing label for the slice z
                if metric_normalizing_label[0][0] != 0:
                    data[..., z] = data[..., z] / metric_normalizing_label[0][
                        0]  # divide all the slice z by this value

        elif normalization_method == 'whole':  # case: the user wants to normalize after estimations in the whole labels
            metric_mean_norm_label, metric_std_norm_label = extract_metric_within_tract(
                data, normalizing_label, method,
                param.verbose)  # mean and std are lists

    # identify cluster for each tract (for use with robust ML)
    ml_clusters_array = get_clusters(ml_clusters, labels)

    # extract metrics within labels
    sct.printv('\nExtract metric within labels...', verbose)
    metric_mean, metric_std = extract_metric_within_tract(
        data, labels, method, verbose, ml_clusters_array,
        adv_param)  # mean and std are lists

    if fname_normalizing_label and normalization_method == 'whole':  # case: user wants to normalize after estimations in the whole labels
        metric_mean, metric_std = np.divide(metric_mean,
                                            metric_mean_norm_label), np.divide(
                                                metric_std,
                                                metric_std_norm_label)

    # update label name if average
    if average_all_labels == 1:
        label_name[0] = 'AVERAGED' + ' -'.join(
            label_name[i]
            for i in label_id_user)  # concatenate the names of the
        # labels selected by the user if the average tag was asked
        label_id_user = [
            0
        ]  # update "label_id_user" to select the "averaged" label (which is in first position)

    metric_mean = metric_mean[label_id_user]
    metric_std = metric_std[label_id_user]

    # display metrics
    sct.printv('\nEstimation results:', 1)
    for i in range(0, metric_mean.size):
        sct.printv(
            str(label_id_user[i]) + ', ' + str(label_name[label_id_user[i]]) +
            ':    ' + str(metric_mean[i]) + ' +/- ' + str(metric_std[i]), 1,
            'info')

    # save and display metrics
    save_metrics(label_id_user, label_name, slices_of_interest, metric_mean,
                 metric_std, fname_output, fname_data, method,
                 fname_normalizing_label, actual_vert_levels,
                 warning_vert_levels)
def main():
    """Main function."""
    parser = get_parser()
    args = parser.parse_args(args=None if sys.argv[1:] else ['--help'])

    fname_image = os.path.abspath(args.i)
    contrast_type = args.c

    ctr_algo = args.centerline

    if args.brain is None:
        if contrast_type in ['t2s', 'dwi']:
            brain_bool = False
        if contrast_type in ['t1', 't2']:
            brain_bool = True
    else:
        brain_bool = bool(args.brain)

    if bool(args.brain) and ctr_algo == 'svm':
        sct.printv('Please only use the flag "-brain 1" with "-centerline cnn".', 1, 'warning')
        sys.exit(1)

    kernel_size = args.kernel
    if kernel_size == '3d' and contrast_type == 'dwi':
        kernel_size = '2d'
        sct.printv('3D kernel model for dwi contrast is not available. 2D kernel model is used instead.',
                   type="warning")

    if ctr_algo == 'file' and args.file_centerline is None:
        sct.printv('Please use the flag -file_centerline to indicate the centerline filename.', 1, 'warning')
        sys.exit(1)

    if args.file_centerline is not None:
        manual_centerline_fname = args.file_centerline
        ctr_algo = 'file'
    else:
        manual_centerline_fname = None

    threshold = args.thr
    if threshold is not None:
        if threshold > 1.0 or (threshold < 0.0 and threshold != -1.0):
            raise SyntaxError("Threshold should be between 0 and 1, or equal to -1 (no threshold)")

    remove_temp_files = args.r
    verbose = args.v
    sct.init_sct(log_level=verbose, update=True)  # Update log level

    path_qc = args.qc
    qc_dataset = args.qc_dataset
    qc_subject = args.qc_subject
    output_folder = args.ofolder

    # check if input image is 2D or 3D
    sct.check_dim(fname_image, dim_lst=[2, 3])

    # Segment image
    from spinalcordtoolbox.image import Image
    from spinalcordtoolbox.deepseg_sc.core import deep_segmentation_spinalcord
    from spinalcordtoolbox.reports.qc import generate_qc

    im_image = Image(fname_image)
    # note: below we pass im_image.copy() otherwise the field absolutepath becomes None after execution of this function
    im_seg, im_image_RPI_upsamp, im_seg_RPI_upsamp = \
        deep_segmentation_spinalcord(im_image.copy(), contrast_type, ctr_algo=ctr_algo,
                                     ctr_file=manual_centerline_fname, brain_bool=brain_bool, kernel_size=kernel_size,
                                     threshold_seg=threshold, remove_temp_files=remove_temp_files, verbose=verbose)

    # Save segmentation
    fname_seg = os.path.abspath(os.path.join(output_folder, sct.extract_fname(fname_image)[1] + '_seg' +
                                             sct.extract_fname(fname_image)[2]))
    im_seg.save(fname_seg)

    # Generate QC report
    if path_qc is not None:
        generate_qc(fname_image, fname_seg=fname_seg, args=sys.argv[1:], path_qc=os.path.abspath(path_qc),
                    dataset=qc_dataset, subject=qc_subject, process='sct_deepseg_sc')
    sct.display_viewer_syntax([fname_image, fname_seg], colormaps=['gray', 'red'], opacities=['', '0.7'])
def get_slices_matching_with_vertebral_levels(metric_data, vertebral_levels,
                                              data_vertebral_labeling):

    sct.printv('\nFind slices corresponding to vertebral levels...',
               param.verbose)

    # Convert the selected vertebral levels chosen into a 2-element list [start_level end_level]
    vert_levels_list = [int(x) for x in vertebral_levels.split(':')]

    # If only one vertebral level was selected (n), consider as n:n
    if len(vert_levels_list) == 1:
        vert_levels_list = [vert_levels_list[0], vert_levels_list[0]]

    # Check if there are only two values [start_level, end_level] and if the end level is higher than the start level
    if (len(vert_levels_list) > 2) or (vert_levels_list[0] >
                                       vert_levels_list[1]):
        print '\nERROR:  "' + vertebral_levels + '" is not correct. Enter format "1:4". Exit program.\n'
        sys.exit(2)

    # Extract the vertebral levels available in the metric image
    vertebral_levels_available = np.array(
        list(set(data_vertebral_labeling[data_vertebral_labeling > 0])))

    # Check if the vertebral levels selected are available
    warning = [
    ]  # list of strings gathering the potential following warning(s) to be written in the output .txt file
    min_vert_level_available = min(
        vertebral_levels_available)  # lowest vertebral level available
    max_vert_level_available = max(
        vertebral_levels_available)  # highest vertebral level available
    if vert_levels_list[0] < min_vert_level_available:
        vert_levels_list[0] = min_vert_level_available
        warning.append(
            'WARNING: the bottom vertebral level you selected is lower to the lowest level available --> '
            'Selected the lowest vertebral level available: ' +
            str(int(vert_levels_list[0])))  # record the
        # warning to write it later in the .txt output file
        print color.yellow + 'WARNING: the bottom vertebral level you selected is lower to the lowest ' \
                                          'level available \n--> Selected the lowest vertebral level available: '+\
              str(int(vert_levels_list[0])) + color.end

    if vert_levels_list[0] > max_vert_level_available:
        vert_levels_list[1] = max_vert_level_available
        warning.append(
            'WARNING: the top vertebral level you selected is higher to the highest level available --> '
            'Selected the highest vertebral level available: ' +
            str(int(vert_levels_list[1])))  # record the
        # warning to write it later in the .txt output file

        print color.yellow + 'WARNING: the top vertebral level you selected is higher to the highest ' \
                                          'level available --> Selected the highest vertebral level available: ' + \
              str(int(vert_levels_list[1])) + color.end

    if vert_levels_list[0] not in vertebral_levels_available:
        distance = vertebral_levels_available - vert_levels_list[
            0]  # relative distance
        distance_min_among_negative_value = min(abs(
            distance[distance < 0]))  # minimal distance among the negative
        # relative distances
        vert_levels_list[0] = vertebral_levels_available[
            distance == distance_min_among_negative_value]  # element
        # of the initial list corresponding to this minimal distance
        warning.append(
            'WARNING: the bottom vertebral level you selected is not available --> Selected the nearest '
            'inferior level available: ' + str(int(vert_levels_list[0])))
        print color.yellow + 'WARNING: the bottom vertebral level you selected is not available \n--> Selected the ' \
                             'nearest inferior level available: '+str(int(vert_levels_list[0]))  # record the
        # warning to write it later in the .txt output file

    if vert_levels_list[1] not in vertebral_levels_available:
        distance = vertebral_levels_available - vert_levels_list[
            1]  # relative distance
        distance_min_among_positive_value = min(abs(
            distance[distance > 0]))  # minimal distance among the negative
        # relative distances
        vert_levels_list[1] = vertebral_levels_available[
            distance == distance_min_among_positive_value]  # element
        # of the initial list corresponding to this minimal distance
        warning.append(
            'WARNING: the top vertebral level you selected is not available --> Selected the nearest superior'
            ' level available: ' + str(int(vert_levels_list[1]))
        )  # record the warning to write it later in the .txt output file

        print color.yellow + 'WARNING: the top vertebral level you selected is not available \n--> Selected the ' \
                             'nearest superior level available: ' + str(int(vert_levels_list[1]))

    # Extract metric data size X, Y, Z
    [mx, my, mz] = metric_data.shape
    # Extract vertebral labeling data size X, Y, Z
    [vx, vy, vz] = data_vertebral_labeling.shape

    sct.printv('  Check consistency of data size...', param.verbose)

    # Initialisation of check error flag
    exit_program = 0

    # Check if sizes along X are the same
    if mx != vx:
        print '\tERROR: Size of vertebral_labeling.nii.gz along X is not the same as the metric data.'
        exit_program = 1
    # Check if sizes along Y are the same
    if my != vy:
        print '\tERROR: Size of vertebral_labeling.nii.gz along Y is not the same as the metric data.'
        exit_program = 1
    # Check if sizes along Z are the same
    if mz != vz:
        print '\tERROR: Size of vertebral_labeling.nii.gz along Z is not the same as the metric data.'
        exit_program = 1

    # Exit program if an error was detected
    if exit_program == 1:
        print '\nExit program.\n'
        sys.exit(2)
    else:
        print '    OK!'

    sct.printv('  Find slices corresponding to vertebral levels...',
               param.verbose)
    # Extract the X, Y, Z positions of voxels belonging to the first vertebral level
    X_bottom_level, Y_bottom_level, Z_bottom_level = (
        data_vertebral_labeling == vert_levels_list[0]).nonzero()
    # Record the bottom and top slices of this level
    slice_min_bottom = min(Z_bottom_level)
    slice_max_bottom = max(Z_bottom_level)

    # Extract the X, Y, Z positions of voxels belonging to the last vertebral level
    X_top_level, Y_top_level, Z_top_level = (
        data_vertebral_labeling == vert_levels_list[1]).nonzero()
    # Record the bottom and top slices of this level
    slice_min_top = min(Z_top_level)
    slice_max_top = max(Z_top_level)

    # Take into account the case where the ordering of the slice is reversed compared to the ordering of the vertebral
    # levels (usually the case) and if several slices include two different vertebral levels
    if slice_min_bottom >= slice_min_top or slice_max_bottom >= slice_max_top:
        slice_min = slice_min_top
        slice_max = slice_max_bottom
    else:
        slice_min = slice_min_bottom
        slice_max = slice_max_top

    # display info
    sct.printv('    ' + str(slice_min) + ':' + str(slice_max), param.verbose)

    # Return the slice numbers in the right format ("-1" because the function "remove_slices", which runs next, add 1
    # to the top slice
    return str(slice_min) + ':' + str(slice_max), vert_levels_list, warning
def extract_metric_within_tract(data,
                                labels,
                                method,
                                verbose,
                                ml_clusters='',
                                adv_param=[]):
    """
    :data: (nx,ny,nz) numpy array
    :labels: nlabel tuple of (nx,ny,nz) array
    """

    nb_labels = len(labels)  # number of labels

    # if user asks for binary regions, binarize atlas
    if method == 'bin':
        for i in range(0, nb_labels):
            labels[i][labels[i] < 0.5] = 0
            labels[i][labels[i] >= 0.5] = 1

    # if user asks for thresholded weighted-average, threshold atlas
    if method == 'wath':
        for i in range(0, nb_labels):
            labels[i][labels[i] < 0.5] = 0

    #  Select non-zero values in the union of all labels
    labels_sum = np.sum(labels)
    ind_positive_labels = labels_sum > ALMOST_ZERO  # labels_sum > ALMOST_ZERO
    # ind_positive_data = data > -9999999999  # data > 0
    ind_positive = ind_positive_labels  # & ind_positive_data
    data1d = data[ind_positive]
    labels2d = np.empty([nb_labels, len(data1d)], dtype=float)
    for i in range(0, nb_labels):
        labels2d[i] = labels[i][ind_positive]

    # # display labels
    # import matplotlib.pyplot as plt
    # plt.imshow(labels_sum[:,:,3])
    # plt.show()
    # plt.imshow(data[:,:,3])
    # plt.show()

    # clear memory
    del data, labels

    # Display number of non-zero values
    sct.printv('  Number of non-null voxels: ' + str(len(data1d)),
               verbose=verbose)

    # initialization
    metric_mean = np.empty([nb_labels], dtype=object)
    metric_std = np.empty([nb_labels], dtype=object)
    nb_vox = len(data1d)

    # Estimation with 3-class maximum likelihood
    if method == 'map':
        sct.printv('Estimation maximum likelihood within clustered labels...',
                   verbose=verbose)
        y = data1d  # [nb_vox x 1]
        x = labels2d.T  # [nb_vox x nb_labels]
        # construct matrix with clusters of tracts
        ml_clusters_unique = np.unique(np.sort(ml_clusters))
        nb_clusters = len(ml_clusters_unique)
        sct.printv('  Number of clusters: ' + str(nb_clusters),
                   verbose=verbose)
        # initialize cluster matrix
        x_cluster = np.zeros([nb_vox, nb_clusters])
        # loop across clusters
        for i_cluster in ml_clusters_unique:
            # find tracts belonging to cluster
            index_tracts_in_cluster = np.where(ml_clusters == i_cluster)[0]
            # sum tracts and append to matrix
            x_cluster[:, i_cluster] = x[:, index_tracts_in_cluster].sum(axis=1)
        x = x_cluster
        # estimate values using ML
        beta = np.dot(np.linalg.pinv(np.dot(x.T, x)),
                      np.dot(x.T, y))  # beta = (Xt . X)-1 . Xt . y
        # display results
        sct.printv('  Estimated beta per cluster: ' + str(beta),
                   verbose=verbose)

    # Estimation with weighted average (also works for binary)
    if method == 'wa' or method == 'bin' or method == 'wath':
        for i_label in range(0, nb_labels):
            # check if all labels are equal to zero
            if sum(labels2d[i_label, :]) == 0:
                print 'WARNING: labels #' + str(
                    i_label
                ) + ' contains only null voxels. Mean and std are set to 0.'
                metric_mean[i_label] = 0
                metric_std[i_label] = 0
            else:
                # estimate the weighted average
                metric_mean[i_label] = sum(
                    data1d * labels2d[i_label, :]) / sum(labels2d[i_label, :])
                # estimate the biased weighted standard deviation
                metric_std[i_label] = np.sqrt(
                    sum(labels2d[i_label, :] *
                        (data1d - metric_mean[i_label])**2) /
                    sum(labels2d[i_label, :]))

    # Estimation with maximum likelihood
    if method == 'ml':
        y = data1d  # [nb_vox x 1]
        x = labels2d.T  # [nb_vox x nb_labels]
        beta = np.dot(np.linalg.pinv(np.dot(x.T, x)),
                      np.dot(x.T, y))  # beta = (Xt . X)-1 . Xt . y
        #beta, residuals, rank, singular_value = np.linalg.lstsq(np.dot(x.T, x), np.dot(x.T, y), rcond=-1)
        #beta, residuals, rank, singular_value = np.linalg.lstsq(x, y)
        #print beta, residuals, rank, singular_value
        for i_label in range(0, nb_labels):
            metric_mean[i_label] = beta[i_label]
            metric_std[
                i_label] = 0  # need to assign a value for writing output file

    # Estimation with maximum a posteriori (map)
    if method == 'map':
        # perc_var_label = int(adv_param[0])^2  # variance within label, in percentage of the mean (mean is estimated using cluster-based ML)
        var_label = int(adv_param[0]) ^ 2  # variance within label
        var_noise = int(
            adv_param[1]) ^ 2  # variance of the noise (assumed Gaussian)

        y = data1d  # [nb_vox x 1]
        x = labels2d.T  # [nb_vox x nb_labels]
        # construct beta0
        beta0 = np.zeros(nb_labels)
        for i_cluster in range(nb_clusters):
            beta0[np.where(ml_clusters == i_cluster)[0]] = beta[i_cluster]
        # construct covariance matrix (variance between tracts). For simplicity, we set it to be the identity.
        Rlabel = np.diag(np.ones(nb_labels))
        # Vlabel =  np.diag(np.ones(nb_labels) * var_label)
        # Vlabel =  np.diag(beta0 * perc_var_label * 0.01)  # [nb_labels x nb_labels]
        # construct noise matrix
        # Vnoise = np.diag(np.ones(nb_labels) * var_noise)
        # beta = beta0 + (Xt . X + var_noise/Var_label * Rlabel^-1)^-1 . Xt . ( y - X . beta0 )
        # beta = beta0 +                      A                        . B  .         C
        # A = np.linalg.pinv(np.dot(x.T, x) + np.dot(Vnoise, np.linalg.pinv(Vlabel)))
        A = np.linalg.pinv(
            np.dot(x.T, x) + np.linalg.pinv(Rlabel) * var_noise / var_label)
        B = x.T
        C = y - np.dot(x, beta0)
        beta = beta0 + np.dot(A, np.dot(B, C))
        for i_label in range(0, nb_labels):
            metric_mean[i_label] = beta[i_label]
            metric_std[
                i_label] = 0  # need to assign a value for writing output file

    return metric_mean, metric_std
Example #49
0
def main():

    # Default params
    param = Param()

    # Get parser info
    parser = get_parser()
    arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help'])
    fname_data = arguments.i
    if arguments.m is not None:
        fname_mask = arguments.m
    else:
        fname_mask = ''
    method = arguments.method
    if arguments.vol is not None:
        index_vol_user = arguments.vol
    else:
        index_vol_user = ''
    verbose = arguments.v
    sct.init_sct(log_level=verbose, update=True)  # Update log level

    # Check parameters
    if method == 'diff':
        if not fname_mask:
            sct.printv('You need to provide a mask with -method diff. Exit.',
                       1,
                       type='error')

    # Load data and orient to RPI
    im_data = Image(fname_data).change_orientation('RPI')
    data = im_data.data
    if fname_mask:
        mask = Image(fname_mask).change_orientation('RPI').data

    # Retrieve selected volumes
    if index_vol_user:
        index_vol = parse_num_list(index_vol_user)
    else:
        index_vol = range(data.shape[3])

    # Make sure user selected 2 volumes with diff method
    if method == 'diff':
        if not len(index_vol) == 2:
            sct.printv(
                'Method "diff" should be used with exactly two volumes (specify with flag "-vol").',
                1, 'error')

    # Compute SNR
    # NB: "time" is assumed to be the 4th dimension of the variable "data"
    if method == 'mult':
        # Compute mean and STD across time
        data_mean = np.mean(data[:, :, :, index_vol], axis=3)
        data_std = np.std(data[:, :, :, index_vol], axis=3, ddof=1)
        # Generate mask where std is different from 0
        mask_std_nonzero = np.where(data_std > param.almost_zero)
        snr_map = np.zeros_like(data_mean)
        snr_map[mask_std_nonzero] = data_mean[mask_std_nonzero] / data_std[
            mask_std_nonzero]
        # Output SNR map
        fname_snr = sct.add_suffix(fname_data, '_SNR-' + method)
        im_snr = empty_like(im_data)
        im_snr.data = snr_map
        im_snr.save(fname_snr, dtype=np.float32)
        # Output non-zero mask
        fname_stdnonzero = sct.add_suffix(fname_data,
                                          '_mask-STD-nonzero' + method)
        im_stdnonzero = empty_like(im_data)
        data_stdnonzero = np.zeros_like(data_mean)
        data_stdnonzero[mask_std_nonzero] = 1
        im_stdnonzero.data = data_stdnonzero
        im_stdnonzero.save(fname_stdnonzero, dtype=np.float32)
        # Compute SNR in ROI
        if fname_mask:
            mean_in_roi = np.average(data_mean[mask_std_nonzero],
                                     weights=mask[mask_std_nonzero])
            std_in_roi = np.average(data_std[mask_std_nonzero],
                                    weights=mask[mask_std_nonzero])
            snr_roi = mean_in_roi / std_in_roi
            # snr_roi = np.average(snr_map[mask_std_nonzero], weights=mask[mask_std_nonzero])

    elif method == 'diff':
        data_2vol = np.take(data, index_vol, axis=3)
        # Compute mean in ROI
        data_mean = np.mean(data_2vol, axis=3)
        mean_in_roi = np.average(data_mean, weights=mask)
        data_sub = np.subtract(data_2vol[:, :, :, 1], data_2vol[:, :, :, 0])
        _, std_in_roi = weighted_avg_and_std(data_sub, mask)
        # Compute SNR, correcting for Rayleigh noise (see eq. 7 in Dietrich et al.)
        snr_roi = (2 / np.sqrt(2)) * mean_in_roi / std_in_roi

    # Display result
    if fname_mask:
        sct.printv('\nSNR_' + method + ' = ' + str(snr_roi) + '\n',
                   type='info')
def check_labels(labels_of_interest, nb_labels, average_labels, method):

    # by default, all labels are selected
    list_label_id = range(0, nb_labels)

    if labels_of_interest:
        # Check if label chosen is in the right format
        for char in labels_of_interest:
            if not char in '0123456789,:scwmg':
                sct.printv(
                    '\nERROR: ' + labels_of_interest +
                    ' is not the correct format to select labels.\n Exit program.\n',
                    type='error')
                usage()

        # if spinal cord was selected, need all 32 labels from folder atlas
        if labels_of_interest == 'sc':
            if nb_labels < 32 and (method == 'ml' or method == 'map'):
                sct.printv(
                    '\nERROR: You\'ve asked to extract metric in the all spinal cord using the method '
                    + method + ' but your atlas folder containing'
                    ' the labels only contains ' + nb_labels +
                    ' labels. You need all 32 labels from the folder /atlas of'
                    ' the SpinalCordToolbox (files WMtract_XX, with XX from 00 to 31).\nExit program.\n\n',
                    type='error')
                usage()
            if nb_labels < 31 and (method != 'ml' and method != 'map'):
                sct.printv(
                    '\nERROR: You\'ve asked to extract metric in the all spinal cord using the method '
                    + method + ' but your atlas folder containing'
                    ' the labels only contains ' + nb_labels +
                    ' labels. You need all 30 white matter tracts and the gray matter from the folder /atlas of'
                    ' the SpinalCordToolbox (files WMtract_XX, with XX from 00 to 30).\nExit program.\n\n',
                    type='error')
                usage()
            else:
                list_label_id = range(0, 31)
                average_labels = 1

        elif labels_of_interest == 'gm':
            if nb_labels < 32 and (method == 'ml' or method == 'map'):
                sct.printv(
                    '\nERROR: You\'ve asked to extract metric in the gray matter using the method '
                    + method + ' but your atlas folder containing'
                    ' the labels only contains ' + nb_labels +
                    ' labels. You need all 32 labels from the folder /atlas of'
                    ' the SpinalCordToolbox (files WMtract_XX, with XX from 00 to 31).\nExit program.\n\n',
                    type='error')
                usage()
            else:
                list_label_id = [30]

        elif labels_of_interest == 'wm':
            if nb_labels < 32 and (method == 'ml' or method == 'map'):
                sct.printv(
                    '\nERROR: You\'ve asked to extract metric in the white matter using the method '
                    + method + ' but your atlas folder containing'
                    ' the labels only contains ' + nb_labels +
                    ' labels. You need all 32 labels from the folder /atlas of'
                    ' the SpinalCordToolbox (files WMtract_XX, with XX from 00 to 31).\nExit program.\n\n',
                    type='error')
                usage()
            else:
                list_label_id = range(0, 30)
                average_labels = 1

        elif ':' in labels_of_interest:
            label_ids_range = [int(x) for x in labels_of_interest.split(':')]
            if len(label_ids_range) > 2:
                sct.printv(
                    '\nERROR: label IDs selection must be in format X:Y, with X and Y between 0 and 31.\nExit program.\n\n',
                    type='error')
                usage()
            else:
                label_ids_range.sort()
                list_label_id = [
                    int(x)
                    for x in range(label_ids_range[0], label_ids_range[1] + 1)
                ]

        else:
            list_label_id = list(
                set([int(x) for x in labels_of_interest.split(",")]))

        # Sort labels ID and remove redundant values
        list_label_id.sort()
        list_label_id = list(set(list_label_id))

    return list_label_id, average_labels
def main(args=None):

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

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(args)
    input_filename = arguments['-i']
    input_fname_output = None
    input_fname_ref = None
    input_cross_radius = 5
    input_dilate = False
    input_coordinates = None
    vertebral_levels = None
    value = None
    if '-add' in arguments:
        process_type = 'add'
        value = arguments['-add']
    elif '-create' in arguments:
        process_type = 'create'
        input_coordinates = arguments['-create']
    elif '-create-add' in arguments:
        process_type = 'create-add'
        input_coordinates = arguments['-create-add']
    elif '-create-seg' in arguments:
        process_type = 'create-seg'
        input_coordinates = arguments['-create-seg']
    elif '-cross' in arguments:
        process_type = 'cross'
        input_cross_radius = arguments['-cross']
    elif '-cubic-to-point' in arguments:
        process_type = 'cubic-to-point'
    elif '-display' in arguments:
        process_type = 'display-voxel'
    elif '-increment' in arguments:
        process_type = 'increment'
    elif '-vert-body' in arguments:
        process_type = 'vert-body'
        vertebral_levels = arguments['-vert-body']
    # elif '-vert-disc' in arguments:
    #     process_type = 'vert-disc'
    #     vertebral_levels = arguments['-vert-disc']
    elif '-vert-continuous' in arguments:
        process_type = 'vert-continuous'
    elif '-MSE' in arguments:
        process_type = 'MSE'
        input_fname_ref = arguments['-r']
    elif '-remove' in arguments:
        process_type = 'remove'
        input_fname_ref = arguments['-remove']
    elif '-remove-symm' in arguments:
        process_type = 'remove-symm'
        input_fname_ref = arguments['-r']
    elif '-create-viewer' in arguments:
        process_type = 'create-viewer'
        value = arguments['-create-viewer']
    else:
        # no process chosen
        sct.printv('ERROR: No process was chosen.', 1, 'error')
    if '-o' in arguments:
        input_fname_output = arguments['-o']
    input_verbose = int(arguments['-v'])

    processor = ProcessLabels(input_filename,
                              fname_output=input_fname_output,
                              fname_ref=input_fname_ref,
                              cross_radius=input_cross_radius,
                              dilate=input_dilate,
                              coordinates=input_coordinates,
                              verbose=input_verbose,
                              vertebral_levels=vertebral_levels,
                              value=value)
    processor.process(process_type)
Example #52
0
def main(args=None):
    if args is None:
        args = sys.argv[1:]

    parser = get_parser()
    arguments = parser.parse(args)

    # assigning variables to arguments
    input_filename = arguments["-i"]
    centerline_file = arguments["-s"]

    sc_straight = SpinalCordStraightener(input_filename, centerline_file)

    if "-dest" in arguments:
        sc_straight.use_straight_reference = True
        sc_straight.centerline_reference_filename = str(arguments["-dest"])

    if "-ldisc_input" in arguments:
        if not sc_straight.use_straight_reference:
            sct.printv('Warning: discs position are not taken into account if reference is not provided.')
        else:
            sc_straight.discs_input_filename = str(arguments["-ldisc_input"])
            sc_straight.precision = 4.0
    if "-ldisc_dest" in arguments:
        if not sc_straight.use_straight_reference:
            sct.printv('Warning: discs position are not taken into account if reference is not provided.')
        else:
            sc_straight.discs_ref_filename = str(arguments["-ldisc_dest"])
            sc_straight.precision = 4.0

    # Handling optional arguments
    if "-r" in arguments:
        sc_straight.remove_temp_files = int(arguments["-r"])
    if "-x" in arguments:
        sc_straight.interpolation_warp = str(arguments["-x"])
    if "-o" in arguments:
        sc_straight.output_filename = str(arguments["-o"])
    if '-ofolder' in arguments:
        sc_straight.path_output = arguments['-ofolder']
    else:
        sc_straight.path_output = './'

    verbose = int(arguments.get('-v'))
    sct.init_sct(log_level=verbose, update=True)  # Update log level
    sc_straight.verbose = verbose

    # if "-cpu-nb" in arguments:
    #     sc_straight.cpu_number = int(arguments["-cpu-nb"])

    if '-disable-straight2curved' in arguments:
        sc_straight.straight2curved = False
    if '-disable-curved2straight' in arguments:
        sc_straight.curved2straight = False

    if '-speed_factor' in arguments:
        sc_straight.speed_factor = arguments['-speed_factor']

    if '-xy_size' in arguments:
        sc_straight.xy_size = arguments['-xy_size']

    if "-param" in arguments:
        params_user = arguments['-param']
        # update registration parameters
        for param in params_user:
            param_split = param.split('=')
            if param_split[0] == 'algo_fitting':
                sc_straight.algo_fitting = param_split[1]
            if param_split[0] == 'degree':
                sc_straight.degree = int(param_split[1])
            if param_split[0] == 'precision':
                sc_straight.precision = float(param_split[1])
            if param_split[0] == 'threshold_distance':
                sc_straight.threshold_distance = float(param_split[1])
            if param_split[0] == 'accuracy_results':
                sc_straight.accuracy_results = int(param_split[1])
            if param_split[0] == 'template_orientation':
                sc_straight.template_orientation = int(param_split[1])

    fname_straight = sc_straight.straighten()

    sct.printv("\nFinished! Elapsed time: {} s".format(sc_straight.elapsed_time), verbose)

    sct.display_viewer_syntax([fname_straight], verbose=verbose)
    def __init__(self, fname_src, fname_transfo, warp_atlas, warp_spinal_levels, folder_out, path_template, verbose):

        # Initialization
        self.fname_src = fname_src
        self.fname_transfo = fname_transfo
        self.warp_atlas = warp_atlas
        self.warp_spinal_levels = warp_spinal_levels
        self.folder_out = folder_out
        self.path_template = path_template
        self.folder_template = param.folder_template
        self.folder_atlas = param.folder_atlas
        self.folder_spinal_levels = param.folder_spinal_levels
        self.verbose = verbose

        # sct.printv(arguments)
        sct.printv('\nCheck parameters:')
        sct.printv('  Working directory ........ ' + os.getcwd())
        sct.printv('  Destination image ........ ' + self.fname_src)
        sct.printv('  Warping field ............ ' + self.fname_transfo)
        sct.printv('  Path template ............ ' + self.path_template)
        sct.printv('  Output folder ............ ' + self.folder_out + "\n")

        # create output folder
        if not os.path.exists(self.folder_out):
            os.makedirs(self.folder_out)

        # Warp template objects
        sct.printv('\nWARP TEMPLATE:', self.verbose)
        warp_label(self.path_template, self.folder_template, param.file_info_label, self.fname_src, self.fname_transfo, self.folder_out)

        # Warp atlas
        if self.warp_atlas == 1:
            sct.printv('\nWARP ATLAS OF WHITE MATTER TRACTS:', self.verbose)
            warp_label(self.path_template, self.folder_atlas, param.file_info_label, self.fname_src, self.fname_transfo, self.folder_out)

        # Warp spinal levels
        if self.warp_spinal_levels == 1:
            sct.printv('\nWARP SPINAL LEVELS:', self.verbose)
            warp_label(self.path_template, self.folder_spinal_levels, param.file_info_label, self.fname_src, self.fname_transfo, self.folder_out)
def crop_image_around_segmentation(fname_in, fname_seg, path_output_im,
                                   path_output_seg, size_crop, offset,
                                   remove_tmp_files, verbose):
    # 1. Resample to 1mm^3 isotropic
    fname_in_resampled = sct.add_suffix(fname_in, 'r')
    sct.run('sct_resample -i ' + fname_in + ' -mm 1x1x1 -o ' +
            fname_in_resampled,
            verbose=verbose)
    fname_in = fname_in_resampled
    fname_seg_resample = sct.add_suffix(fname_seg, 'r')
    sct.run('sct_resample -i ' + fname_seg + ' -mm 1x1x1 -o ' +
            fname_seg_resample,
            verbose=verbose)
    fname_seg = fname_seg_resample

    # 2. Orient both input images to RPI for the sake of simplicity
    sct.run('sct_image -i ' + fname_in + ' -setorient RPI', verbose=verbose)
    fname_in = sct.add_suffix(fname_in, '_RPI')
    sct.run('sct_image -i ' + fname_seg + ' -setorient RPI', verbose=verbose)
    fname_seg = sct.add_suffix(fname_seg, '_RPI')

    # 3. Pad both images to avoid edge issues when cropping
    fname_in_pad = sct.add_suffix(fname_in, 'p')
    pad_image = str(int(int(size_crop) / 2))
    sct.run('sct_image -i ' + fname_in + ' -pad ' + pad_image + ',' +
            pad_image + ',0 -o ' + fname_in_pad,
            verbose=verbose)
    fname_in = fname_in_pad
    fname_seg_pad = sct.add_suffix(fname_seg, 'p')
    sct.run('sct_image -i ' + fname_seg + ' -pad ' + pad_image + ',' +
            pad_image + ',0 -o ' + fname_seg_pad,
            verbose=verbose)
    fname_seg = fname_seg_pad

    # 4. Extract centerline from segmentation
    fname_centerline = sct.add_suffix(fname_seg, '_centerline')
    sct.run('sct_process_segmentation -i ' + fname_seg + ' -p centerline',
            verbose=verbose)  # -o ' + fname_centerline)

    # 5. Create a square mask around the spinal cord centerline
    fname_mask_box = 'mask_box.nii.gz'
    sct.run('sct_create_mask -i ' + fname_in + ' -m centerline,' +
            fname_centerline + ' -s ' + str(size_crop) + ' -o ' +
            fname_mask_box + ' -f box -e 1 -k ' + offset,
            verbose=verbose)

    # 6. Crop image around the spinal cord and create a stack of square images
    sct.printv('Cropping around mask and stacking slices...', verbose=verbose)
    im_mask_box = Image(fname_mask_box)
    im_input = Image(fname_in)
    im_input.crop_and_stack(im_mask_box, suffix='_stack', save=True)
    im_seg = Image(fname_seg)
    im_seg.crop_and_stack(im_mask_box, suffix='_stack', save=True)

    # 6.5 Change name of images
    fname_stack_image = sct.add_suffix(fname_in, '_stack')
    fname_stack_seg = sct.add_suffix(fname_seg, '_stack')
    import random
    output_im_filename = str(random.randint(1, 1000000000000))
    output_im_fname = output_im_filename + '_im.nii.gz'
    output_seg_fname = output_im_filename + '_seg.nii.gz'
    sct.run('mv ' + fname_stack_image + ' ' + output_im_fname, verbose=verbose)
    sct.run('mv ' + fname_stack_seg + ' ' + output_seg_fname, verbose=verbose)

    # 7. Split the two stack images and save each slice
    sct.run('sct_image -i ' + output_im_fname + ' -split z', verbose=verbose)
    sct.run('sct_image -i ' + output_seg_fname + ' -split z', verbose=verbose)

    # 8. Move all images to output folders
    path_fname, file_fname, ext_fname = sct.extract_fname(output_im_fname)
    sct.run('mv ' + file_fname + '_* ' + path_output_im, verbose=verbose)
    path_fname, file_fname, ext_fname = sct.extract_fname(output_seg_fname)
    sct.run('mv ' + file_fname + '_* ' + path_output_seg, verbose=verbose)
Example #55
0
def validate_atlas(folder_cropped_atlas,
                   nb_bootstraps,
                   std_noise,
                   range_tract,
                   val_csf,
                   results_folder,
                   results_file,
                   mask_folder,
                   list_methods,
                   test_map=0,
                   param_map='20,20',
                   list_tracts=[]):
    # Parameters
    file_phantom = "WM_phantom.nii.gz"
    file_phantom_noise = "WM_phantom_noise.nii.gz"
    file_tract_sum = "tracts_sum.nii.gz"
    true_value = 40
    file_extract_metrics = "metric_label.txt"
    # list_tracts = ['2', '17', '0,1,15,16']
    list_tracts_txt = ['csl', 'csr', 'dc']
    index_dorsalcolumn = 2  # index of dorsal column in list_tracts
    nb_tracts_all = 32  # total number of tracts in atlas (do not include CSF tracts)
    # dorsal_column_labels = '0,1,15,16'
    # nb_tracts_dorsalcolumn = 4
    value_gm = 35  # value in gray matter
    #value_csf = 5  # value in csf
    nb_digits_results = 2  # number of digits to display for result file
    mask_prefix = 'manual_'
    mask_ext = '.nii.gz'

    # initialization
    start_time = time.time()  # save start time for duration
    folder_tmp = 'tmp.' + datetime.datetime.now().strftime("%y%m%d%H%M%S%f/")
    nb_methods = len(list_methods)
    nb_tracts = len(list_tracts)
    perc_error = np.zeros(
        shape=(nb_tracts, nb_methods, nb_bootstraps)
    )  # percent error within single tract (for comparison with manual labeling)
    perc_error_all = np.zeros(shape=(
        nb_tracts_all, nb_methods, nb_bootstraps
    ))  # percent error for all tracts (for comparing automatic methods)
    stat_perc_error_all = np.zeros(shape=(nb_methods, nb_bootstraps,
                                          4))  # statistics
    list_stat = ['MSE', 'median', 'min', 'max']
    x_true_i = np.zeros(shape=(nb_tracts))
    fname_phantom = folder_tmp + file_phantom
    fname_phantom_noise = folder_tmp + file_phantom_noise
    fname_tract_sum = folder_tmp + file_tract_sum

    # create output folder
    create_folder(results_folder, 0)

    # Extract the tracts from the atlas' folder
    tracts = get_tracts(folder_cropped_atlas)

    # get file name of the first atlas file
    fname_atlas = folder_cropped_atlas + 'WMtract__00.nii.gz'

    # Get ponderation of each tract for dorsal column average ponderation of each tract of the dorsal column
    if nb_tracts:
        list_tract_dorsalcolumn = list_tracts[index_dorsalcolumn].split(',')
        nb_tracts_dorsalcolumn = len(list_tract_dorsalcolumn)

        pond_dc = np.zeros(nb_tracts_dorsalcolumn)
        # sum of each
        pond_sum = 0
        for i in range(nb_tracts_dorsalcolumn):
            # i = int(i)
            # Sum tracts values which are higher than 0 in the tracts
            pond_dc[i] = sum(
                tracts[int(list_tract_dorsalcolumn[i]),
                       0][tracts[int(list_tract_dorsalcolumn[i]), 0] > 0])
            pond_sum = pond_sum + pond_dc[i]
        # Normalize the sum of ponderations to 1
        pond_dc = pond_dc / pond_sum

    # create temporary folder
    sct.run('mkdir ' + folder_tmp)

    # loop across bootstrap
    for i_bootstrap in range(0, nb_bootstraps):
        sct.printv(
            'Iteration:  ' + str(i_bootstrap + 1) + '/' + str(nb_bootstraps),
            1, 'warning')

        # Generate phantom
        [WM_phantom, WM_phantom_noise, values_synthetic_data,
         tracts_sum] = phantom_generation(tracts, std_noise, range_tract,
                                          true_value, folder_tmp, value_gm,
                                          true_value * val_csf / 100)
        # Save generated phantoms as nifti image (.nii.gz)
        save_3D_nparray_nifti(WM_phantom, fname_phantom, fname_atlas)
        save_3D_nparray_nifti(WM_phantom_noise, fname_phantom_noise,
                              fname_atlas)
        save_3D_nparray_nifti(tracts_sum, fname_tract_sum, fname_atlas)

        # Get the np.mean of all values in dorsal column in the generated phantom
        if nb_tracts:
            dc_val_avg = 0
            for j in range(nb_tracts_dorsalcolumn):
                dc_val_avg = dc_val_avg + values_synthetic_data[int(
                    list_tract_dorsalcolumn[j])] * pond_dc[j]
            dc_val_avg = float(dc_val_avg)
            # build variable with true values (WARNING: HARD-CODED INDICES)
            x_true_i[0] = values_synthetic_data[int(list_tracts[0])]
            x_true_i[1] = values_synthetic_data[int(list_tracts[1])]
            x_true_i[2] = dc_val_avg

        fname_extract_metrics = folder_tmp + file_extract_metrics

        if nb_tracts:
            if not test_map:
                # loop across tracts
                for i_tract in range(len(list_tracts)):
                    # loop across methods
                    for i_method in range(len(list_methods)):
                        # display stuff
                        print 'Tract: ' + list_tracts[
                            i_tract] + ', Method: ' + list_methods[i_method]
                        # check if method is manual
                        if not list_methods[i_method].find('man') == -1:
                            # find index of manual mask
                            index_manual = int(list_methods[i_method][
                                list_methods[i_method].find('man') + 3])
                            fname_mask = mask_folder[
                                index_manual] + mask_prefix + list_tracts_txt[
                                    i_tract] + mask_ext
                            # manual extraction
                            status, output = sct.run(
                                'sct_average_data_within_mask -i ' +
                                fname_phantom_noise + ' -m ' + fname_mask +
                                ' -v 0')
                            x_estim_i = float(output)
                        else:
                            # automatic extraction
                            sct.run('sct_extract_metric -i ' +
                                    fname_phantom_noise + ' -f ' +
                                    folder_cropped_atlas + ' -m ' +
                                    list_methods[i_method] + ' -l ' +
                                    list_tracts[i_tract] + ' -a -o ' +
                                    fname_extract_metrics)
                            # read in txt file
                            x_estim_i = read_results(fname_extract_metrics)
                        # Get the percent absolute deviation with the true value
                        #perc_error[i_tract, i_method, i_bootstrap] = 100 * (x_true_i[i_tract] - x_estim_i) / float(x_true_i[i_tract])
                        perc_error[i_tract, i_method, i_bootstrap] = 100 * abs(
                            x_estim_i - x_true_i[i_tract]) / float(
                                x_true_i[i_tract])

        # calculate percentage error for all tracts (only for automatic methods)
        # loop across methods
        for i_method in range(len(list_methods)):
            # check if method is automatic
            if list_methods[i_method].find('man') == -1:
                # display stuff
                print 'Tract: ALL, Method: ' + list_methods[i_method]
                # automatic extraction in all tracts
                sct.run('sct_extract_metric -i ' + fname_phantom_noise +
                        ' -f ' + folder_cropped_atlas + ' -m ' +
                        list_methods[i_method] + ' -o ' +
                        fname_extract_metrics + ' -p ' + param_map)
                # read results in txt file
                x_estim_i_all = read_results(fname_extract_metrics)
                # get nonzero values
                index_nonzero = np.nonzero(values_synthetic_data)
                perc_error_all[
                    0:nb_tracts_all, i_method, i_bootstrap] = 100 * abs(
                        x_estim_i_all[index_nonzero] -
                        values_synthetic_data[index_nonzero]
                    ) / values_synthetic_data[
                        index_nonzero]  # will be used to display boxcar
                # perc_error_all[0:nb_tracts_all, i_method, i_bootstrap] = 100 * (x_estim_i_all[index_nonzero] - values_synthetic_data[index_nonzero]) / values_synthetic_data[index_nonzero]  # will be used to display boxcar
                # compute mean squared error
                stat_perc_error_all[i_method, i_bootstrap,
                                    0] = (perc_error_all[:, i_method,
                                                         i_bootstrap]**2
                                          ).mean()  # mean squared error
                stat_perc_error_all[i_method, i_bootstrap, 1] = np.median(
                    perc_error_all[:, i_method, i_bootstrap])  # median
                stat_perc_error_all[i_method, i_bootstrap,
                                    2] = min(perc_error_all[:, i_method,
                                                            i_bootstrap])
                stat_perc_error_all[i_method, i_bootstrap,
                                    3] = max(perc_error_all[:, i_method,
                                                            i_bootstrap])

    # Calculate elapsed time
    elapsed_time = int(round(time.time() - start_time))

    # Extract time in minutes and seconds
    sec = elapsed_time % 60
    mte = (elapsed_time - sec) / 60

    # PRINT RESULTS FOR SINGLE TRACTS
    # ===============================
    if nb_tracts:

        # create output folder
        create_folder(results_folder + 'sub/', 0)

        # Open text file where results are printed
        fname_results = results_folder + 'sub/' + results_file + '.txt'
        results_text = open(fname_results, 'w+')

        # print header
        print >> results_text, '# Mean(std) percentage of absolute error within single tracts.'
        print >> results_text, '# Generated on: ' + time.strftime(
            '%Y-%m-%d %H:%M:%S')
        print >> results_text, '# true_value: ' + str(true_value)
        print >> results_text, '# sigma noise (in percentage of true value): ' + str(
            std_noise) + '%'
        print >> results_text, '# range tracts (in percentage of true value): (-' + str(
            range_tract) + '%:+' + str(range_tract) + '%)'
        print >> results_text, '# value CSF (in percentage of true value): ' + str(
            val_csf) + '%'
        print >> results_text, '# number of iterations: ' + str(nb_bootstraps)
        print >> results_text, '# elapsed time: ' + str(mte) + 'min' + str(
            sec) + 's'
        text_methods = 'Label'
        # loop across methods
        for i_method in range(len(list_methods)):
            text_methods = text_methods + ', ' + list_methods[i_method]
        print >> results_text, text_methods

        # print results
        # loop across tracts
        for i_tract in range(len(list_tracts)):
            text_results = list_tracts_txt[i_tract]
            # loop across methods
            for i_method in range(len(list_methods)):
                text_results = text_results + ', ' + str(
                    round(np.mean(perc_error[i_tract, i_method, :]),
                          ndigits=nb_digits_results)) + '(' + str(
                              round(np.std(perc_error[i_tract, i_method, :]),
                                    ndigits=nb_digits_results)) + ')'
            print >> results_text, text_results

        # close file
        results_text.close()

        # display results
        status, output = sct.run('cat ' + fname_results)
        print output

    # PRINT RESULTS FOR ALL TRACTS
    # ============================
    # Open text file where results are printed
    fname_results = results_folder + results_file + '_all.txt'
    results_text = open(fname_results, 'w+')

    # print header
    print >> results_text, '# Mean(std) percentage of absolute error within all tracts (only for automatic methods).'
    print >> results_text, '# Generated on: ' + time.strftime(
        '%Y-%m-%d %H:%M:%S')
    print >> results_text, '# true_value: ' + str(true_value)
    print >> results_text, '# sigma noise (in percentage of true value): ' + str(
        std_noise) + '%'
    print >> results_text, '# range tracts (in percentage of true value): (-' + str(
        range_tract) + '%:+' + str(range_tract) + '%)'
    print >> results_text, '# value CSF (in percentage of true value): ' + str(
        val_csf) + '%'
    print >> results_text, '# number of iterations: ' + str(nb_bootstraps)
    print >> results_text, '# elapsed time: ' + str(mte) + 'min' + str(
        sec) + 's'
    text_methods = 'Label'
    # loop across methods
    for i_method in range(len(list_methods)):
        # check if method is automatic
        if list_methods[i_method].find('man') == -1:
            text_methods = text_methods + ', ' + list_methods[i_method]
    print >> results_text, text_methods

    # print results
    # loop across tracts
    for i_tract in range(nb_tracts_all):
        text_results = str(i_tract)
        # loop across methods
        for i_method in range(len(list_methods)):
            # check if method is automatic
            if list_methods[i_method].find('man') == -1:
                text_results = text_results + ', ' + str(
                    round(np.mean(perc_error_all[i_tract, i_method, :]),
                          ndigits=nb_digits_results)) + '(' + str(
                              round(np.std(perc_error_all[i_tract,
                                                          i_method, :]),
                                    ndigits=nb_digits_results)) + ')'
        print >> results_text, text_results

    # loop across statistics
    nb_stats = len(list_stat)
    for i_stat in range(nb_stats):
        text_results = list_stat[i_stat]
        # loop across methods
        for i_method in range(len(list_methods)):
            # check if method is automatic
            if list_methods[i_method].find('man') == -1:
                text_results = text_results + ', ' + str(
                    round(np.mean(stat_perc_error_all[i_method, :, i_stat]),
                          ndigits=nb_digits_results)) + '(' + str(
                              round(np.std(stat_perc_error_all[i_method, :,
                                                               i_stat]),
                                    ndigits=nb_digits_results)) + ')'
        print >> results_text, text_results

    # close file
    results_text.close()

    # display results
    status, output = sct.run('cat ' + fname_results)
    print output
def main(file_to_denoise, param, output_file_name):

    path, file, ext = sct.extract_fname(file_to_denoise)

    img = nib.load(file_to_denoise)
    hdr_0 = img.get_header()

    data = img.get_data()
    # aff = img.get_affine()

    if min(data.shape) <= 5:
        sct.printv(
            'One of the image dimensions is <= 5 : reducing the size of the block radius.'
        )
        block_radius = min(data.shape) - 1
    else:
        block_radius = 5  # default value

    # Process for manual detecting of background
    # mask = data[:, :, :] > noise_threshold
    # data = data[:, :, :]

    from dipy.denoise.nlmeans import nlmeans

    if '-std' in arguments:
        sigma = std_noise
        # Application of NLM filter to the image
        print 'Applying Non-local mean filter...'
        if param.parameter == 'Rician':
            den = nlmeans(data,
                          sigma=sigma,
                          mask=None,
                          rician=True,
                          block_radius=block_radius)
        else:
            den = nlmeans(data,
                          sigma=sigma,
                          mask=None,
                          rician=False,
                          block_radius=block_radius)
    else:
        # # Process for manual detecting of background
        mask = data > noise_threshold
        sigma = np.std(data[~mask])
        # Application of NLM filter to the image
        print 'Applying Non-local mean filter...'
        if param.parameter == 'Rician':
            den = nlmeans(data,
                          sigma=sigma,
                          mask=mask,
                          rician=True,
                          block_radius=block_radius)
        else:
            den = nlmeans(data,
                          sigma=sigma,
                          mask=mask,
                          rician=False,
                          block_radius=block_radius)

    t = time()
    print("total time", time() - t)
    print("vol size", den.shape)

    axial_middle = data.shape[2] / 2

    before = data[:, :, axial_middle].T
    after = den[:, :, axial_middle].T

    diff_3d = np.absolute(den.astype('f8') - data.astype('f8'))
    difference = np.absolute(after.astype('f8') - before.astype('f8'))
    if '-std' not in arguments:
        difference[~mask[:, :, axial_middle].T] = 0

    if param.verbose == 2:
        import matplotlib.pyplot as plt
        fig, ax = plt.subplots(1, 3)
        ax[0].imshow(before, cmap='gray', origin='lower')
        ax[0].set_title('before')
        ax[1].imshow(after, cmap='gray', origin='lower')
        ax[1].set_title('after')
        ax[2].imshow(difference, cmap='gray', origin='lower')
        ax[2].set_title('difference')
        for i in range(3):
            ax[i].set_axis_off()

        plt.show()

    #Save files
    img_denoise = nib.Nifti1Image(den, None, hdr_0)
    img_diff = nib.Nifti1Image(diff_3d, None, hdr_0)
    if output_file_name != None:
        output_file_name = output_file_name
    else:
        output_file_name = file + '_denoised' + ext
    nib.save(img_denoise, output_file_name)
    nib.save(img_diff, file + '_difference' + ext)
def main(args=None):

    # initializations
    param = Param()

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

    # Get parser info
    parser = get_parser()
    arguments = parser.parse(args)
    fname_data = arguments['-i']
    fname_seg = arguments['-s']
    if '-l' in arguments:
        fname_landmarks = arguments['-l']
        label_type = 'body'
    elif '-ldisc' in arguments:
        fname_landmarks = arguments['-ldisc']
        label_type = 'disc'
    else:
        sct.printv('ERROR: Labels should be provided.', 1, 'error')
    if '-ofolder' in arguments:
        path_output = arguments['-ofolder']
    else:
        path_output = ''

    param.path_qc = arguments.get("-qc", None)

    path_template = arguments['-t']
    contrast_template = arguments['-c']
    ref = arguments['-ref']
    remove_temp_files = int(arguments['-r'])
    verbose = int(arguments['-v'])
    param.verbose = verbose  # TODO: not clean, unify verbose or param.verbose in code, but not both
    if '-param-straighten' in arguments:
        param.param_straighten = arguments['-param-straighten']
    # if '-cpu-nb' in arguments:
    #     arg_cpu = ' -cpu-nb '+str(arguments['-cpu-nb'])
    # else:
    #     arg_cpu = ''
    # registration parameters
    if '-param' in arguments:
        # reset parameters but keep step=0 (might be overwritten if user specified step=0)
        paramreg = ParamregMultiStep([step0])
        if ref == 'subject':
            paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz'
        # add user parameters
        for paramStep in arguments['-param']:
            paramreg.addStep(paramStep)
    else:
        paramreg = ParamregMultiStep([step0, step1, step2])
        # if ref=subject, initialize registration using different affine parameters
        if ref == 'subject':
            paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz'

    # initialize other parameters
    zsubsample = param.zsubsample

    # retrieve template file names
    file_template_vertebral_labeling = get_file_label(
        os.path.join(path_template, 'template'), 'vertebral labeling')
    file_template = get_file_label(
        os.path.join(path_template, 'template'),
        contrast_template.upper() + '-weighted template')
    file_template_seg = get_file_label(os.path.join(path_template, 'template'),
                                       'spinal cord')

    # start timer
    start_time = time.time()

    # get fname of the template + template objects
    fname_template = os.path.join(path_template, 'template', file_template)
    fname_template_vertebral_labeling = os.path.join(
        path_template, 'template', file_template_vertebral_labeling)
    fname_template_seg = os.path.join(path_template, 'template',
                                      file_template_seg)
    fname_template_disc_labeling = os.path.join(path_template, 'template',
                                                'PAM50_label_disc.nii.gz')

    # check file existence
    # TODO: no need to do that!
    sct.printv('\nCheck template files...')
    sct.check_file_exist(fname_template, verbose)
    sct.check_file_exist(fname_template_vertebral_labeling, verbose)
    sct.check_file_exist(fname_template_seg, verbose)
    path_data, file_data, ext_data = sct.extract_fname(fname_data)

    # sct.printv(arguments)
    sct.printv('\nCheck parameters:', verbose)
    sct.printv('  Data:                 ' + fname_data, verbose)
    sct.printv('  Landmarks:            ' + fname_landmarks, verbose)
    sct.printv('  Segmentation:         ' + fname_seg, verbose)
    sct.printv('  Path template:        ' + path_template, verbose)
    sct.printv('  Remove temp files:    ' + str(remove_temp_files), verbose)

    # check input labels
    labels = check_labels(fname_landmarks, label_type=label_type)

    vertebral_alignment = False
    if len(labels) > 2 and label_type == 'disc':
        vertebral_alignment = True

    path_tmp = sct.tmp_create(basename="register_to_template", verbose=verbose)

    # set temporary file names
    ftmp_data = 'data.nii'
    ftmp_seg = 'seg.nii.gz'
    ftmp_label = 'label.nii.gz'
    ftmp_template = 'template.nii'
    ftmp_template_seg = 'template_seg.nii.gz'
    ftmp_template_label = 'template_label.nii.gz'

    # copy files to temporary folder
    sct.printv('\nCopying input data to tmp folder and convert to nii...',
               verbose)
    Image(fname_data).save(os.path.join(path_tmp, ftmp_data))
    Image(fname_seg).save(os.path.join(path_tmp, ftmp_seg))
    Image(fname_landmarks).save(os.path.join(path_tmp, ftmp_label))
    Image(fname_template).save(os.path.join(path_tmp, ftmp_template))
    Image(fname_template_seg).save(os.path.join(path_tmp, ftmp_template_seg))
    Image(fname_template_vertebral_labeling).save(
        os.path.join(path_tmp, ftmp_template_label))
    if label_type == 'disc':
        Image(fname_template_disc_labeling).save(
            os.path.join(path_tmp, ftmp_template_label))

    # go to tmp folder
    curdir = os.getcwd()
    os.chdir(path_tmp)

    # Generate labels from template vertebral labeling
    if label_type == 'body':
        sct.printv('\nGenerate labels from template vertebral labeling',
                   verbose)
        ftmp_template_label_, ftmp_template_label = ftmp_template_label, sct.add_suffix(
            ftmp_template_label, "_body")
        sct_label_utils.main(args=[
            '-i', ftmp_template_label_, '-vert-body', '0', '-o',
            ftmp_template_label
        ])

    # check if provided labels are available in the template
    sct.printv('\nCheck if provided labels are available in the template',
               verbose)
    image_label_template = Image(ftmp_template_label)
    labels_template = image_label_template.getNonZeroCoordinates(
        sorting='value')
    if labels[-1].value > labels_template[-1].value:
        sct.printv(
            'ERROR: Wrong landmarks input. Labels must have correspondence in template space. \nLabel max '
            'provided: ' + str(labels[-1].value) +
            '\nLabel max from template: ' + str(labels_template[-1].value),
            verbose, 'error')

    # if only one label is present, force affine transformation to be Tx,Ty,Tz only (no scaling)
    if len(labels) == 1:
        paramreg.steps['0'].dof = 'Tx_Ty_Tz'
        sct.printv(
            'WARNING: Only one label is present. Forcing initial transformation to: '
            + paramreg.steps['0'].dof, 1, 'warning')

    # Project labels onto the spinal cord centerline because later, an affine transformation is estimated between the
    # template's labels (centered in the cord) and the subject's labels (assumed to be centered in the cord).
    # If labels are not centered, mis-registration errors are observed (see issue #1826)
    ftmp_label = project_labels_on_spinalcord(ftmp_label, ftmp_seg)

    # binarize segmentation (in case it has values below 0 caused by manual editing)
    sct.printv('\nBinarize segmentation', verbose)
    ftmp_seg_, ftmp_seg = ftmp_seg, sct.add_suffix(ftmp_seg, "_bin")
    sct.run(['sct_maths', '-i', ftmp_seg_, '-bin', '0.5', '-o', ftmp_seg])

    # Switch between modes: subject->template or template->subject
    if ref == 'template':

        # resample data to 1mm isotropic
        sct.printv('\nResample data to 1mm isotropic...', verbose)
        sct.run([
            'sct_resample', '-i', ftmp_data, '-mm', '1.0x1.0x1.0', '-x',
            'linear', '-o',
            add_suffix(ftmp_data, '_1mm')
        ])
        ftmp_data = add_suffix(ftmp_data, '_1mm')
        sct.run([
            'sct_resample', '-i', ftmp_seg, '-mm', '1.0x1.0x1.0', '-x',
            'linear', '-o',
            add_suffix(ftmp_seg, '_1mm')
        ])
        ftmp_seg = add_suffix(ftmp_seg, '_1mm')
        # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling
        # with nearest neighbour can make them disappear.
        resample_labels(ftmp_label, ftmp_data, add_suffix(ftmp_label, '_1mm'))
        ftmp_label = add_suffix(ftmp_label, '_1mm')

        # Change orientation of input images to RPI
        sct.printv('\nChange orientation of input images to RPI...', verbose)

        ftmp_data = Image(ftmp_data).change_orientation(
            "RPI", generate_path=True).save().absolutepath
        ftmp_seg = Image(ftmp_seg).change_orientation(
            "RPI", generate_path=True).save().absolutepath
        ftmp_label = Image(ftmp_label).change_orientation(
            "RPI", generate_path=True).save().absolutepath

        ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_crop')
        if vertebral_alignment:
            # cropping the segmentation based on the label coverage to ensure good registration with vertebral alignment
            # See https://github.com/neuropoly/spinalcordtoolbox/pull/1669 for details
            image_labels = Image(ftmp_label)
            coordinates_labels = image_labels.getNonZeroCoordinates(
                sorting='z')
            nx, ny, nz, nt, px, py, pz, pt = image_labels.dim
            offset_crop = 10.0 * pz  # cropping the image 10 mm above and below the highest and lowest label
            cropping_slices = [
                coordinates_labels[0].z - offset_crop,
                coordinates_labels[-1].z + offset_crop
            ]
            # make sure that the cropping slices do not extend outside of the slice range (issue #1811)
            if cropping_slices[0] < 0:
                cropping_slices[0] = 0
            if cropping_slices[1] > nz:
                cropping_slices[1] = nz
            msct_image.spatial_crop(
                Image(ftmp_seg_),
                dict(((2,
                       np.int32(np.round(cropping_slices))), ))).save(ftmp_seg)
        else:
            # if we do not align the vertebral levels, we crop the segmentation from top to bottom
            im_seg_rpi = Image(ftmp_seg_)
            bottom = 0
            for data in msct_image.SlicerOneAxis(im_seg_rpi, "IS"):
                if (data != 0).any():
                    break
                bottom += 1
            top = im_seg_rpi.data.shape[2]
            for data in msct_image.SlicerOneAxis(im_seg_rpi, "SI"):
                if (data != 0).any():
                    break
                top -= 1
            msct_image.spatial_crop(im_seg_rpi, dict(
                ((2, (bottom, top)), ))).save(ftmp_seg)

        # straighten segmentation
        sct.printv(
            '\nStraighten the spinal cord using centerline/segmentation...',
            verbose)

        # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time)
        fn_warp_curve2straight = os.path.join(curdir,
                                              "warp_curve2straight.nii.gz")
        fn_warp_straight2curve = os.path.join(curdir,
                                              "warp_straight2curve.nii.gz")
        fn_straight_ref = os.path.join(curdir, "straight_ref.nii.gz")

        cache_input_files = [ftmp_seg]
        if vertebral_alignment:
            cache_input_files += [
                ftmp_template_seg,
                ftmp_label,
                ftmp_template_label,
            ]
        cache_sig = sct.cache_signature(input_files=cache_input_files, )
        cachefile = os.path.join(curdir, "straightening.cache")
        if sct.cache_valid(
                cachefile, cache_sig
        ) and os.path.isfile(fn_warp_curve2straight) and os.path.isfile(
                fn_warp_straight2curve) and os.path.isfile(fn_straight_ref):
            sct.printv(
                'Reusing existing warping field which seems to be valid',
                verbose, 'warning')
            sct.copy(fn_warp_curve2straight, 'warp_curve2straight.nii.gz')
            sct.copy(fn_warp_straight2curve, 'warp_straight2curve.nii.gz')
            sct.copy(fn_straight_ref, 'straight_ref.nii.gz')
            # apply straightening
            sct.run([
                'sct_apply_transfo', '-i', ftmp_seg, '-w',
                'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz',
                '-o',
                add_suffix(ftmp_seg, '_straight')
            ])
        else:
            from sct_straighten_spinalcord import SpinalCordStraightener
            sc_straight = SpinalCordStraightener(ftmp_seg, ftmp_seg)
            sc_straight.output_filename = add_suffix(ftmp_seg, '_straight')
            sc_straight.path_output = './'
            sc_straight.qc = '0'
            sc_straight.remove_temp_files = remove_temp_files
            sc_straight.verbose = verbose

            if vertebral_alignment:
                sc_straight.centerline_reference_filename = ftmp_template_seg
                sc_straight.use_straight_reference = True
                sc_straight.discs_input_filename = ftmp_label
                sc_straight.discs_ref_filename = ftmp_template_label

            sc_straight.straighten()
            sct.cache_save(cachefile, cache_sig)

        # N.B. DO NOT UPDATE VARIABLE ftmp_seg BECAUSE TEMPORARY USED LATER
        # re-define warping field using non-cropped space (to avoid issue #367)
        s, o = sct.run([
            'sct_concat_transfo', '-w', 'warp_straight2curve.nii.gz', '-d',
            ftmp_data, '-o', 'warp_straight2curve.nii.gz'
        ])

        if vertebral_alignment:
            sct.copy('warp_curve2straight.nii.gz',
                     'warp_curve2straightAffine.nii.gz')
        else:
            # Label preparation:
            # --------------------------------------------------------------------------------
            # Remove unused label on template. Keep only label present in the input label image
            sct.printv(
                '\nRemove unused label on template. Keep only label present in the input label image...',
                verbose)
            sct.run([
                'sct_label_utils', '-i', ftmp_template_label, '-o',
                ftmp_template_label, '-remove', ftmp_label
            ])

            # Dilating the input label so they can be straighten without losing them
            sct.printv('\nDilating input labels using 3vox ball radius')
            sct.run([
                'sct_maths', '-i', ftmp_label, '-o',
                add_suffix(ftmp_label, '_dilate'), '-dilate', '3'
            ])
            ftmp_label = add_suffix(ftmp_label, '_dilate')

            # Apply straightening to labels
            sct.printv('\nApply straightening to labels...', verbose)
            sct.run([
                'sct_apply_transfo', '-i', ftmp_label, '-o',
                add_suffix(ftmp_label, '_straight'), '-d',
                add_suffix(ftmp_seg, '_straight'), '-w',
                'warp_curve2straight.nii.gz', '-x', 'nn'
            ])
            ftmp_label = add_suffix(ftmp_label, '_straight')

            # Compute rigid transformation straight landmarks --> template landmarks
            sct.printv('\nEstimate transformation for step #0...', verbose)
            from msct_register_landmarks import register_landmarks
            try:
                register_landmarks(ftmp_label,
                                   ftmp_template_label,
                                   paramreg.steps['0'].dof,
                                   fname_affine='straight2templateAffine.txt',
                                   verbose=verbose)
            except Exception:
                sct.printv(
                    'ERROR: input labels do not seem to be at the right place. Please check the position of the labels. See documentation for more details: https://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/',
                    verbose=verbose,
                    type='error')

            # Concatenate transformations: curve --> straight --> affine
            sct.printv(
                '\nConcatenate transformations: curve --> straight --> affine...',
                verbose)
            sct.run([
                'sct_concat_transfo', '-w',
                'warp_curve2straight.nii.gz,straight2templateAffine.txt', '-d',
                'template.nii', '-o', 'warp_curve2straightAffine.nii.gz'
            ])

        # Apply transformation
        sct.printv('\nApply transformation...', verbose)
        sct.run([
            'sct_apply_transfo', '-i', ftmp_data, '-o',
            add_suffix(ftmp_data, '_straightAffine'), '-d', ftmp_template,
            '-w', 'warp_curve2straightAffine.nii.gz'
        ])
        ftmp_data = add_suffix(ftmp_data, '_straightAffine')
        sct.run([
            'sct_apply_transfo', '-i', ftmp_seg, '-o',
            add_suffix(ftmp_seg, '_straightAffine'), '-d', ftmp_template, '-w',
            'warp_curve2straightAffine.nii.gz', '-x', 'linear'
        ])
        ftmp_seg = add_suffix(ftmp_seg, '_straightAffine')
        """
        # Benjamin: Issue from Allan Martin, about the z=0 slice that is screwed up, caused by the affine transform.
        # Solution found: remove slices below and above landmarks to avoid rotation effects
        points_straight = []
        for coord in landmark_template:
            points_straight.append(coord.z)
        min_point, max_point = int(np.round(np.min(points_straight))), int(np.round(np.max(points_straight)))
        ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_black')
        msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, (min_point,max_point)),))).save(ftmp_seg)

        """
        # open segmentation
        im = Image(ftmp_seg)
        im_new = msct_image.empty_like(im)
        # binarize
        im_new.data = im.data > 0.5
        # find min-max of anat2template (for subsequent cropping)
        zmin_template, zmax_template = msct_image.find_zmin_zmax(im_new,
                                                                 threshold=0.5)
        # save binarized segmentation
        im_new.save(add_suffix(ftmp_seg, '_bin'))  # unused?
        # crop template in z-direction (for faster processing)
        # TODO: refactor to use python module instead of doing i/o
        sct.printv('\nCrop data in template space (for faster processing)...',
                   verbose)
        ftmp_template_, ftmp_template = ftmp_template, add_suffix(
            ftmp_template, '_crop')
        msct_image.spatial_crop(Image(ftmp_template_),
                                dict(
                                    ((2,
                                      (zmin_template,
                                       zmax_template)), ))).save(ftmp_template)

        ftmp_template_seg_, ftmp_template_seg = ftmp_template_seg, add_suffix(
            ftmp_template_seg, '_crop')
        msct_image.spatial_crop(
            Image(ftmp_template_seg_),
            dict(((2, (zmin_template,
                       zmax_template)), ))).save(ftmp_template_seg)

        ftmp_data_, ftmp_data = ftmp_data, add_suffix(ftmp_data, '_crop')
        msct_image.spatial_crop(Image(ftmp_data_),
                                dict(((2, (zmin_template,
                                           zmax_template)), ))).save(ftmp_data)

        ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_crop')
        msct_image.spatial_crop(Image(ftmp_seg_),
                                dict(((2, (zmin_template,
                                           zmax_template)), ))).save(ftmp_seg)

        # sub-sample in z-direction
        # TODO: refactor to use python module instead of doing i/o
        sct.printv('\nSub-sample in z-direction (for faster processing)...',
                   verbose)
        sct.run([
            'sct_resample', '-i', ftmp_template, '-o',
            add_suffix(ftmp_template, '_sub'), '-f', '1x1x' + zsubsample
        ], verbose)
        ftmp_template = add_suffix(ftmp_template, '_sub')
        sct.run([
            'sct_resample', '-i', ftmp_template_seg, '-o',
            add_suffix(ftmp_template_seg, '_sub'), '-f', '1x1x' + zsubsample
        ], verbose)
        ftmp_template_seg = add_suffix(ftmp_template_seg, '_sub')
        sct.run([
            'sct_resample', '-i', ftmp_data, '-o',
            add_suffix(ftmp_data, '_sub'), '-f', '1x1x' + zsubsample
        ], verbose)
        ftmp_data = add_suffix(ftmp_data, '_sub')
        sct.run([
            'sct_resample', '-i', ftmp_seg, '-o',
            add_suffix(ftmp_seg, '_sub'), '-f', '1x1x' + zsubsample
        ], verbose)
        ftmp_seg = add_suffix(ftmp_seg, '_sub')

        # Registration straight spinal cord to template
        sct.printv('\nRegister straight spinal cord to template...', verbose)

        # loop across registration steps
        warp_forward = []
        warp_inverse = []
        for i_step in range(1, len(paramreg.steps)):
            sct.printv(
                '\nEstimate transformation for step #' + str(i_step) + '...',
                verbose)
            # identify which is the src and dest
            if paramreg.steps[str(i_step)].type == 'im':
                src = ftmp_data
                dest = ftmp_template
                interp_step = 'linear'
            elif paramreg.steps[str(i_step)].type == 'seg':
                src = ftmp_seg
                dest = ftmp_template_seg
                interp_step = 'nn'
            else:
                sct.printv('ERROR: Wrong image type.', 1, 'error')
            # if step>1, apply warp_forward_concat to the src image to be used
            if i_step > 1:
                # sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose)
                # apply transformation from previous step, to use as new src for registration
                sct.run([
                    'sct_apply_transfo', '-i', src, '-d', dest, '-w',
                    ','.join(warp_forward), '-o',
                    add_suffix(src,
                               '_regStep' + str(i_step - 1)), '-x', interp_step
                ], verbose)
                src = add_suffix(src, '_regStep' + str(i_step - 1))
            # register src --> dest
            # TODO: display param for debugging
            warp_forward_out, warp_inverse_out = register(
                src, dest, paramreg, param, str(i_step))
            warp_forward.append(warp_forward_out)
            warp_inverse.append(warp_inverse_out)

        # Concatenate transformations:
        sct.printv('\nConcatenate transformations: anat --> template...',
                   verbose)
        sct.run([
            'sct_concat_transfo', '-w',
            'warp_curve2straightAffine.nii.gz,' + ','.join(warp_forward), '-d',
            'template.nii', '-o', 'warp_anat2template.nii.gz'
        ], verbose)
        # sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose)
        sct.printv('\nConcatenate transformations: template --> anat...',
                   verbose)
        warp_inverse.reverse()

        if vertebral_alignment:
            sct.run([
                'sct_concat_transfo', '-w',
                ','.join(warp_inverse) + ',warp_straight2curve.nii.gz', '-d',
                'data.nii', '-o', 'warp_template2anat.nii.gz'
            ], verbose)
        else:
            sct.run([
                'sct_concat_transfo', '-w', ','.join(warp_inverse) +
                ',-straight2templateAffine.txt,warp_straight2curve.nii.gz',
                '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz'
            ], verbose)

    # register template->subject
    elif ref == 'subject':

        # Change orientation of input images to RPI
        sct.printv('\nChange orientation of input images to RPI...', verbose)
        ftmp_data = Image(ftmp_data).change_orientation(
            "RPI", generate_path=True).save().absolutepath
        ftmp_seg = Image(ftmp_seg).change_orientation(
            "RPI", generate_path=True).save().absolutepath
        ftmp_label = Image(ftmp_label).change_orientation(
            "RPI", generate_path=True).save().absolutepath

        # Remove unused label on template. Keep only label present in the input label image
        sct.printv(
            '\nRemove unused label on template. Keep only label present in the input label image...',
            verbose)
        sct.run([
            'sct_label_utils', '-i', ftmp_template_label, '-o',
            ftmp_template_label, '-remove', ftmp_label
        ])

        # Add one label because at least 3 orthogonal labels are required to estimate an affine transformation. This
        # new label is added at the level of the upper most label (lowest value), at 1cm to the right.
        for i_file in [ftmp_label, ftmp_template_label]:
            im_label = Image(i_file)
            coord_label = im_label.getCoordinatesAveragedByValue(
            )  # N.B. landmarks are sorted by value
            # Create new label
            from copy import deepcopy
            new_label = deepcopy(coord_label[0])
            # move it 5mm to the left (orientation is RAS)
            nx, ny, nz, nt, px, py, pz, pt = im_label.dim
            new_label.x = np.round(coord_label[0].x + 5.0 / px)
            # assign value 99
            new_label.value = 99
            # Add to existing image
            im_label.data[int(new_label.x),
                          int(new_label.y),
                          int(new_label.z)] = new_label.value
            # Overwrite label file
            # im_label.absolutepath = 'label_rpi_modif.nii.gz'
            im_label.save()

        # Bring template to subject space using landmark-based transformation
        sct.printv('\nEstimate transformation for step #0...', verbose)
        from msct_register_landmarks import register_landmarks
        warp_forward = ['template2subjectAffine.txt']
        warp_inverse = ['-template2subjectAffine.txt']
        try:
            register_landmarks(ftmp_template_label,
                               ftmp_label,
                               paramreg.steps['0'].dof,
                               fname_affine=warp_forward[0],
                               verbose=verbose,
                               path_qc="./")
        except Exception:
            sct.printv(
                'ERROR: input labels do not seem to be at the right place. Please check the position of the labels. See documentation for more details: https://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/',
                verbose=verbose,
                type='error')

        # loop across registration steps
        for i_step in range(1, len(paramreg.steps)):
            sct.printv(
                '\nEstimate transformation for step #' + str(i_step) + '...',
                verbose)
            # identify which is the src and dest
            if paramreg.steps[str(i_step)].type == 'im':
                src = ftmp_template
                dest = ftmp_data
                interp_step = 'linear'
            elif paramreg.steps[str(i_step)].type == 'seg':
                src = ftmp_template_seg
                dest = ftmp_seg
                interp_step = 'nn'
            else:
                sct.printv('ERROR: Wrong image type.', 1, 'error')
            # apply transformation from previous step, to use as new src for registration
            sct.run([
                'sct_apply_transfo', '-i', src, '-d', dest, '-w',
                ','.join(warp_forward), '-o',
                add_suffix(src,
                           '_regStep' + str(i_step - 1)), '-x', interp_step
            ], verbose)
            src = add_suffix(src, '_regStep' + str(i_step - 1))
            # register src --> dest
            # TODO: display param for debugging
            warp_forward_out, warp_inverse_out = register(
                src, dest, paramreg, param, str(i_step))
            warp_forward.append(warp_forward_out)
            warp_inverse.insert(0, warp_inverse_out)

        # Concatenate transformations:
        sct.printv('\nConcatenate transformations: template --> subject...',
                   verbose)
        sct.run([
            'sct_concat_transfo', '-w', ','.join(warp_forward), '-d',
            'data.nii', '-o', 'warp_template2anat.nii.gz'
        ], verbose)
        sct.printv('\nConcatenate transformations: subject --> template...',
                   verbose)
        sct.run([
            'sct_concat_transfo', '-w', ','.join(warp_inverse), '-d',
            'template.nii', '-o', 'warp_anat2template.nii.gz'
        ], verbose)

    # Apply warping fields to anat and template
    sct.run([
        'sct_apply_transfo', '-i', 'template.nii', '-o',
        'template2anat.nii.gz', '-d', 'data.nii', '-w',
        'warp_template2anat.nii.gz', '-crop', '1'
    ], verbose)
    sct.run([
        'sct_apply_transfo', '-i', 'data.nii', '-o', 'anat2template.nii.gz',
        '-d', 'template.nii', '-w', 'warp_anat2template.nii.gz', '-crop', '1'
    ], verbose)

    # come back
    os.chdir(curdir)

    # Generate output files
    sct.printv('\nGenerate output files...', verbose)
    fname_template2anat = os.path.join(path_output, 'template2anat' + ext_data)
    fname_anat2template = os.path.join(path_output, 'anat2template' + ext_data)
    sct.generate_output_file(
        os.path.join(path_tmp, "warp_template2anat.nii.gz"),
        os.path.join(path_output, "warp_template2anat.nii.gz"), verbose)
    sct.generate_output_file(
        os.path.join(path_tmp, "warp_anat2template.nii.gz"),
        os.path.join(path_output, "warp_anat2template.nii.gz"), verbose)
    sct.generate_output_file(os.path.join(path_tmp, "template2anat.nii.gz"),
                             fname_template2anat, verbose)
    sct.generate_output_file(os.path.join(path_tmp, "anat2template.nii.gz"),
                             fname_anat2template, verbose)
    if ref == 'template':
        # copy straightening files in case subsequent SCT functions need them
        sct.generate_output_file(
            os.path.join(path_tmp, "warp_curve2straight.nii.gz"),
            os.path.join(path_output, "warp_curve2straight.nii.gz"), verbose)
        sct.generate_output_file(
            os.path.join(path_tmp, "warp_straight2curve.nii.gz"),
            os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose)
        sct.generate_output_file(
            os.path.join(path_tmp, "straight_ref.nii.gz"),
            os.path.join(path_output, "straight_ref.nii.gz"), verbose)

    # Delete temporary files
    if remove_temp_files:
        sct.printv('\nDelete temporary files...', verbose)
        sct.rmtree(path_tmp, verbose=verbose)

    # display elapsed time
    elapsed_time = time.time() - start_time
    sct.printv(
        '\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's',
        verbose)

    if param.path_qc is not None:
        generate_qc(fname_data, fname_template2anat, fname_seg, args,
                    os.path.abspath(param.path_qc))

    sct.display_viewer_syntax([fname_data, fname_template2anat],
                              verbose=verbose)
    sct.display_viewer_syntax([fname_template, fname_anat2template],
                              verbose=verbose)
Example #58
0
def detect_centerline(image_fname,
                      contrast_type,
                      optic_models_path,
                      folder_output,
                      remove_temp_files=False,
                      init_option=None,
                      output_roi=False,
                      verbose=0):
    """This method will use the OptiC to detect the centerline.

    :param image_fname: The input image filename.
    :param init_option: Axial slice where the propagation starts.
    :param contrast_type: The contrast type.
    :param optic_models_path: The path with the Optic model files.
    :param folder_output: The OptiC output folder.
    :param remove_temp_files: Remove the temporary created files.
    :param verbose: Adjusts the verbosity of the logging.

    :returns: The OptiC output filename.
    """

    image_input = Image(image_fname)
    path_data, file_data, ext_data = sct.extract_fname(image_fname)

    sct.printv('Detecting the spinal cord using OptiC', verbose=verbose)
    image_input_orientation = image_input.orientation

    temp_folder = sct.TempFolder()
    temp_folder.copy_from(image_fname)
    curdir = os.getcwd()
    temp_folder.chdir()

    # convert image data type to int16, as required by opencv (backend in OptiC)
    image_int_filename = sct.add_suffix(file_data + ext_data, "_int16")
    img = Image(image_fname)
    img_int16 = img.copy()

    # rescale intensity
    min_out = np.iinfo('uint16').min
    max_out = np.iinfo('uint16').max
    min_in = np.nanmin(img.data)
    max_in = np.nanmax(img.data)
    data_rescaled = img.data.astype('float') * (max_out - min_out) / (max_in -
                                                                      min_in)
    img_int16.data = data_rescaled - (data_rescaled.min() - min_out)

    # change data type
    img_int16.save(image_int_filename, dtype=np.uint16)
    del img, img_int16

    # reorient the input image to RPI + convert to .nii
    reoriented_image_filename = sct.add_suffix(image_int_filename, "_RPI")
    img_filename = ''.join(sct.extract_fname(reoriented_image_filename)[:2])
    reoriented_image_filename_nii = img_filename + '.nii'
    cmd_reorient = 'sct_image -i "%s" -o "%s" -setorient RPI -v 0' % \
                (image_int_filename, reoriented_image_filename_nii)
    sct.run(cmd_reorient, verbose=0)

    image_rpi_init = Image(reoriented_image_filename_nii)
    nxr, nyr, nzr, ntr, pxr, pyr, pzr, ptr = image_rpi_init.dim
    if init_option is not None:
        if init_option > 1:
            init_option /= (nzr - 1)

    # call the OptiC method to generate the spinal cord centerline
    optic_input = img_filename
    optic_filename = img_filename + '_optic'

    os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR"
    cmd_optic = 'isct_spine_detect -ctype=dpdt -lambda=1 "%s" "%s" "%s"' % \
                (optic_models_path, optic_input, optic_filename)
    sct.run(cmd_optic, verbose=0)

    # convert .img and .hdr files to .nii.gz
    optic_hdr_filename = img_filename + '_optic_ctr.hdr'
    centerline_optic_RPI_filename = sct.add_suffix(file_data + ext_data,
                                                   "_centerline_optic_RPI")
    img = nib.load(optic_hdr_filename)
    nib.save(img, centerline_optic_RPI_filename)

    # reorient the output image to initial orientation
    centerline_optic_filename = sct.add_suffix(file_data + ext_data,
                                               "_centerline_optic")
    cmd_reorient = 'sct_image -i "%s" -o "%s" -setorient "%s" -v 0' % \
                   (centerline_optic_RPI_filename,
                    centerline_optic_filename,
                    image_input_orientation)
    sct.run(cmd_reorient, verbose=0)

    # copy centerline to parent folder
    folder_output_from_temp = folder_output
    if not os.path.isabs(folder_output):
        folder_output_from_temp = os.path.join(curdir, folder_output)

    sct.printv('Copy output to ' + folder_output, verbose=0)
    sct.copy(centerline_optic_filename, folder_output_from_temp)

    if output_roi:
        fname_roi_centerline = centerline2roi(
            fname_image=centerline_optic_RPI_filename,
            folder_output=folder_output_from_temp,
            verbose=verbose)

        # Note: the .roi file is defined in RPI orientation. To be used, it must be applied on the original image with
        # a RPI orientation. For this reason, this script also outputs the input image in RPI orientation
        sct.copy(reoriented_image_filename_nii, folder_output_from_temp)

    # return to initial folder
    temp_folder.chdir_undo()

    # delete temporary folder
    if remove_temp_files:
        temp_folder.cleanup()

    return init_option, os.path.join(folder_output, centerline_optic_filename)
def main():

    # Initialization
    fname_mt0 = ''
    fname_mt1 = ''
    file_out = param.file_out
    # register = param.register
    # remove_tmp_files = param.remove_tmp_files
    # verbose = param.verbose

    # get path of the toolbox
    # status, path_sct = commands.getstatusoutput('echo $SCT_DIR')

    # Check input parameters
    parser = get_parser()
    arguments = parser.parse(sys.argv[1:])

    fname_mt0 = arguments['-mt0']
    fname_mt1 = arguments['-mt1']
    remove_tmp_files = int(arguments['-r'])
    verbose = int(arguments['-v'])

    # Extract path/file/extension
    path_mt0, file_mt0, ext_mt0 = sct.extract_fname(fname_mt0)
    path_out, file_out, ext_out = '', file_out, ext_mt0

    # create temporary folder
    path_tmp = sct.tmp_create()

    # Copying input data to tmp folder and convert to nii
    sct.printv('\nCopying input data to tmp folder and convert to nii...',
               verbose)
    from sct_convert import convert
    convert(fname_mt0, path_tmp + 'mt0.nii', type='float32')
    convert(fname_mt1, path_tmp + 'mt1.nii', type='float32')

    # go to tmp folder
    os.chdir(path_tmp)

    # compute MTR
    sct.printv('\nCompute MTR...', verbose)
    from msct_image import Image
    nii_mt1 = Image('mt1.nii')
    data_mt1 = nii_mt1.data
    data_mt0 = Image('mt0.nii').data
    data_mtr = 100 * (data_mt0 - data_mt1) / data_mt0
    # save MTR file
    nii_mtr = nii_mt1
    nii_mtr.data = data_mtr
    nii_mtr.setFileName('mtr.nii')
    nii_mtr.save()
    # sct.run(fsloutput+'fslmaths -dt double mt0.nii -sub mt1.nii -mul 100 -div mt0.nii -thr 0 -uthr 100 mtr.nii', verbose)

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

    # Generate output files
    sct.printv('\nGenerate output files...', verbose)
    sct.generate_output_file(path_tmp + 'mtr.nii',
                             path_out + file_out + ext_out)

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

    # to view results
    sct.printv('\nDone! To view results, type:', verbose)
    sct.printv(
        'fslview ' + fname_mt0 + ' ' + fname_mt1 + ' ' + file_out + ' &\n',
        verbose, 'info')
Example #60
0
def main():

    # get default parameters
    step1 = Paramreg(step='1',
                     type='seg',
                     algo='slicereg',
                     metric='MeanSquares',
                     iter='10')
    step2 = Paramreg(step='2', type='im', algo='syn', metric='MI', iter='3')
    # step1 = Paramreg()
    paramreg = ParamregMultiStep([step1, step2])

    # step1 = Paramreg_step(step='1', type='seg', algo='bsplinesyn', metric='MeanSquares', iter='10', shrink='1', smooth='0', gradStep='0.5')
    # step2 = Paramreg_step(step='2', type='im', algo='syn', metric='MI', iter='10', shrink='1', smooth='0', gradStep='0.5')
    # paramreg = ParamregMultiStep([step1, step2])

    # Initialize the parser
    parser = Parser(__file__)
    parser.usage.set_description('Register anatomical image to the template.')
    parser.add_option(name="-i",
                      type_value="file",
                      description="Anatomical image.",
                      mandatory=True,
                      example="anat.nii.gz")
    parser.add_option(name="-s",
                      type_value="file",
                      description="Spinal cord segmentation.",
                      mandatory=True,
                      example="anat_seg.nii.gz")
    parser.add_option(
        name="-l",
        type_value="file",
        description=
        "Labels. See: http://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/",
        mandatory=True,
        default_value='',
        example="anat_labels.nii.gz")
    parser.add_option(name="-t",
                      type_value="folder",
                      description="Path to MNI-Poly-AMU template.",
                      mandatory=False,
                      default_value=param.path_template)
    parser.add_option(
        name="-p",
        type_value=[[':'], 'str'],
        description=
        """Parameters for registration (see sct_register_multimodal). Default:\n--\nstep=1\ntype="""
        + paramreg.steps['1'].type + """\nalgo=""" + paramreg.steps['1'].algo +
        """\nmetric=""" + paramreg.steps['1'].metric + """\npoly=""" +
        paramreg.steps['1'].poly + """\n--\nstep=2\ntype=""" +
        paramreg.steps['2'].type + """\nalgo=""" + paramreg.steps['2'].algo +
        """\nmetric=""" + paramreg.steps['2'].metric + """\niter=""" +
        paramreg.steps['2'].iter + """\nshrink=""" +
        paramreg.steps['2'].shrink + """\nsmooth=""" +
        paramreg.steps['2'].smooth + """\ngradStep=""" +
        paramreg.steps['2'].gradStep + """\n--""",
        mandatory=False,
        example=
        "step=2,type=seg,algo=bsplinesyn,metric=MeanSquares,iter=5,shrink=2:step=3,type=im,algo=syn,metric=MI,iter=5,shrink=1,gradStep=0.3"
    )
    parser.add_option(name="-r",
                      type_value="multiple_choice",
                      description="""Remove temporary files.""",
                      mandatory=False,
                      default_value='1',
                      example=['0', '1'])
    parser.add_option(
        name="-v",
        type_value="multiple_choice",
        description="""Verbose. 0: nothing. 1: basic. 2: extended.""",
        mandatory=False,
        default_value=param.verbose,
        example=['0', '1', '2'])
    if param.debug:
        print '\n*** WARNING: DEBUG MODE ON ***\n'
        fname_data = '/Users/julien/data/temp/sct_example_data/t2/t2.nii.gz'
        fname_landmarks = '/Users/julien/data/temp/sct_example_data/t2/labels.nii.gz'
        fname_seg = '/Users/julien/data/temp/sct_example_data/t2/t2_seg.nii.gz'
        path_template = param.path_template
        remove_temp_files = 0
        verbose = 2
        # speed = 'superfast'
        #param_reg = '2,BSplineSyN,0.6,MeanSquares'
    else:
        arguments = parser.parse(sys.argv[1:])

        # get arguments
        fname_data = arguments['-i']
        fname_seg = arguments['-s']
        fname_landmarks = arguments['-l']
        path_template = arguments['-t']
        remove_temp_files = int(arguments['-r'])
        verbose = int(arguments['-v'])
        if '-p' in arguments:
            paramreg_user = arguments['-p']
            # update registration parameters
            for paramStep in paramreg_user:
                paramreg.addStep(paramStep)

    # initialize other parameters
    file_template = param.file_template
    file_template_label = param.file_template_label
    file_template_seg = param.file_template_seg
    output_type = param.output_type
    zsubsample = param.zsubsample
    # smoothing_sigma = param.smoothing_sigma

    # start timer
    start_time = time.time()

    # get absolute path - TO DO: remove! NEVER USE ABSOLUTE PATH...
    path_template = os.path.abspath(path_template)

    # get fname of the template + template objects
    fname_template = sct.slash_at_the_end(path_template, 1) + file_template
    fname_template_label = sct.slash_at_the_end(path_template,
                                                1) + file_template_label
    fname_template_seg = sct.slash_at_the_end(path_template,
                                              1) + file_template_seg

    # check file existence
    sct.printv('\nCheck template files...')
    sct.check_file_exist(fname_template, verbose)
    sct.check_file_exist(fname_template_label, verbose)
    sct.check_file_exist(fname_template_seg, verbose)

    # print arguments
    sct.printv('\nCheck parameters:', verbose)
    sct.printv('.. Data:                 ' + fname_data, verbose)
    sct.printv('.. Landmarks:            ' + fname_landmarks, verbose)
    sct.printv('.. Segmentation:         ' + fname_seg, verbose)
    sct.printv('.. Path template:        ' + path_template, verbose)
    sct.printv('.. Output type:          ' + str(output_type), verbose)
    sct.printv('.. Remove temp files:    ' + str(remove_temp_files), verbose)

    sct.printv('\nParameters for registration:')
    for pStep in range(1, len(paramreg.steps) + 1):
        sct.printv('Step #' + paramreg.steps[str(pStep)].step, verbose)
        sct.printv('.. Type #' + paramreg.steps[str(pStep)].type, verbose)
        sct.printv(
            '.. Algorithm................ ' + paramreg.steps[str(pStep)].algo,
            verbose)
        sct.printv(
            '.. Metric................... ' +
            paramreg.steps[str(pStep)].metric, verbose)
        sct.printv(
            '.. Number of iterations..... ' + paramreg.steps[str(pStep)].iter,
            verbose)
        sct.printv(
            '.. Shrink factor............ ' +
            paramreg.steps[str(pStep)].shrink, verbose)
        sct.printv(
            '.. Smoothing factor......... ' +
            paramreg.steps[str(pStep)].smooth, verbose)
        sct.printv(
            '.. Gradient step............ ' +
            paramreg.steps[str(pStep)].gradStep, verbose)
        sct.printv(
            '.. Degree of polynomial..... ' + paramreg.steps[str(pStep)].poly,
            verbose)

    path_data, file_data, ext_data = sct.extract_fname(fname_data)

    sct.printv('\nCheck input labels...')
    # check if label image contains coherent labels
    image_label = Image(fname_landmarks)
    # -> all labels must be different
    labels = image_label.getNonZeroCoordinates(sorting='value')
    hasDifferentLabels = True
    for lab in labels:
        for otherlabel in labels:
            if lab != otherlabel and lab.hasEqualValue(otherlabel):
                hasDifferentLabels = False
                break
    if not hasDifferentLabels:
        sct.printv(
            'ERROR: Wrong landmarks input. All labels must be different.',
            verbose, 'error')
    # all labels must be available in tempalte
    image_label_template = Image(fname_template_label)
    labels_template = image_label_template.getNonZeroCoordinates(
        sorting='value')
    if labels[-1].value > labels_template[-1].value:
        sct.printv(
            'ERROR: Wrong landmarks input. Labels must have correspondance in tempalte space. \nLabel max '
            'provided: ' + str(labels[-1].value) +
            '\nLabel max from template: ' + str(labels_template[-1].value),
            verbose, 'error')

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

    # copy files to temporary folder
    sct.printv('\nCopy files...', verbose)
    sct.run('isct_c3d ' + fname_data + ' -o ' + path_tmp + '/data.nii')
    sct.run('isct_c3d ' + fname_landmarks + ' -o ' + path_tmp +
            '/landmarks.nii.gz')
    sct.run('isct_c3d ' + fname_seg + ' -o ' + path_tmp +
            '/segmentation.nii.gz')
    sct.run('isct_c3d ' + fname_template + ' -o ' + path_tmp + '/template.nii')
    sct.run('isct_c3d ' + fname_template_label + ' -o ' + path_tmp +
            '/template_labels.nii.gz')
    sct.run('isct_c3d ' + fname_template_seg + ' -o ' + path_tmp +
            '/template_seg.nii.gz')

    # go to tmp folder
    os.chdir(path_tmp)

    # resample data to 1mm isotropic
    sct.printv('\nResample data to 1mm isotropic...', verbose)
    sct.run(
        'isct_c3d data.nii -resample-mm 1.0x1.0x1.0mm -interpolation Linear -o datar.nii'
    )
    sct.run(
        'isct_c3d segmentation.nii.gz -resample-mm 1.0x1.0x1.0mm -interpolation NearestNeighbor -o segmentationr.nii.gz'
    )
    # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling with neighrest neighbour can make them disappear. Therefore a more clever approach is required.
    resample_labels('landmarks.nii.gz', 'datar.nii', 'landmarksr.nii.gz')
    # # TODO
    # sct.run('sct_label_utils -i datar.nii -t create -x 124,186,19,2:129,98,23,8 -o landmarksr.nii.gz')

    # Change orientation of input images to RPI
    sct.printv('\nChange orientation of input images to RPI...', verbose)
    set_orientation('datar.nii', 'RPI', 'data_rpi.nii')
    set_orientation('landmarksr.nii.gz', 'RPI', 'landmarks_rpi.nii.gz')
    set_orientation('segmentationr.nii.gz', 'RPI', 'segmentation_rpi.nii.gz')

    # # Change orientation of input images to RPI
    # sct.printv('\nChange orientation of input images to RPI...', verbose)
    # set_orientation('data.nii', 'RPI', 'data_rpi.nii')
    # set_orientation('landmarks.nii.gz', 'RPI', 'landmarks_rpi.nii.gz')
    # set_orientation('segmentation.nii.gz', 'RPI', 'segmentation_rpi.nii.gz')

    # get landmarks in native space
    # crop segmentation
    # output: segmentation_rpi_crop.nii.gz
    sct.run(
        'sct_crop_image -i segmentation_rpi.nii.gz -o segmentation_rpi_crop.nii.gz -dim 2 -bzmax'
    )

    # straighten segmentation
    sct.printv('\nStraighten the spinal cord using centerline/segmentation...',
               verbose)
    sct.run(
        'sct_straighten_spinalcord -i segmentation_rpi_crop.nii.gz -c segmentation_rpi_crop.nii.gz -r 0 -v '
        + str(verbose), verbose)
    # re-define warping field using non-cropped space (to avoid issue #367)
    sct.run(
        'sct_concat_transfo -w warp_straight2curve.nii.gz -d data_rpi.nii -o warp_straight2curve.nii.gz'
    )

    # Label preparation:
    # --------------------------------------------------------------------------------
    # Remove unused label on template. Keep only label present in the input label image
    sct.printv(
        '\nRemove unused label on template. Keep only label present in the input label image...',
        verbose)
    sct.run(
        'sct_label_utils -t remove -i template_labels.nii.gz -o template_label.nii.gz -r landmarks_rpi.nii.gz'
    )

    # Make sure landmarks are INT
    sct.printv('\nConvert landmarks to INT...', verbose)
    sct.run(
        'isct_c3d template_label.nii.gz -type int -o template_label.nii.gz',
        verbose)

    # Create a cross for the template labels - 5 mm
    sct.printv('\nCreate a 5 mm cross for the template labels...', verbose)
    sct.run(
        'sct_label_utils -t cross -i template_label.nii.gz -o template_label_cross.nii.gz -c 5'
    )

    # Create a cross for the input labels and dilate for straightening preparation - 5 mm
    sct.printv(
        '\nCreate a 5mm cross for the input labels and dilate for straightening preparation...',
        verbose)
    sct.run(
        'sct_label_utils -t cross -i landmarks_rpi.nii.gz -o landmarks_rpi_cross3x3.nii.gz -c 5 -d'
    )

    # Apply straightening to labels
    sct.printv('\nApply straightening to labels...', verbose)
    sct.run(
        'sct_apply_transfo -i landmarks_rpi_cross3x3.nii.gz -o landmarks_rpi_cross3x3_straight.nii.gz -d segmentation_rpi_crop_straight.nii.gz -w warp_curve2straight.nii.gz -x nn'
    )

    # Convert landmarks from FLOAT32 to INT
    sct.printv('\nConvert landmarks from FLOAT32 to INT...', verbose)
    sct.run(
        'isct_c3d landmarks_rpi_cross3x3_straight.nii.gz -type int -o landmarks_rpi_cross3x3_straight.nii.gz'
    )

    # Remove labels that do not correspond with each others.
    sct.printv('\nRemove labels that do not correspond with each others.',
               verbose)
    sct.run(
        'sct_label_utils -t remove-symm -i landmarks_rpi_cross3x3_straight.nii.gz -o landmarks_rpi_cross3x3_straight.nii.gz,template_label_cross.nii.gz -r template_label_cross.nii.gz'
    )

    # Estimate affine transfo: straight --> template (landmark-based)'
    sct.printv(
        '\nEstimate affine transfo: straight anat --> template (landmark-based)...',
        verbose)
    # converting landmarks straight and curved to physical coordinates
    image_straight = Image('landmarks_rpi_cross3x3_straight.nii.gz')
    landmark_straight = image_straight.getNonZeroCoordinates(sorting='value')
    image_template = Image('template_label_cross.nii.gz')
    landmark_template = image_template.getNonZeroCoordinates(sorting='value')
    # Reorganize landmarks
    points_fixed, points_moving = [], []
    landmark_straight_mean = []
    for coord in landmark_straight:
        if coord.value not in [c.value for c in landmark_straight_mean]:
            temp_landmark = coord
            temp_number = 1
            for other_coord in landmark_straight:
                if coord.hasEqualValue(other_coord) and coord != other_coord:
                    temp_landmark += other_coord
                    temp_number += 1
            landmark_straight_mean.append(temp_landmark / temp_number)

    for coord in landmark_straight_mean:
        point_straight = image_straight.transfo_pix2phys(
            [[coord.x, coord.y, coord.z]])
        points_moving.append(
            [point_straight[0][0], point_straight[0][1], point_straight[0][2]])
    for coord in landmark_template:
        point_template = image_template.transfo_pix2phys(
            [[coord.x, coord.y, coord.z]])
        points_fixed.append(
            [point_template[0][0], point_template[0][1], point_template[0][2]])

    # Register curved landmarks on straight landmarks based on python implementation
    sct.printv(
        '\nComputing rigid transformation (algo=translation-scaling-z) ...',
        verbose)
    import msct_register_landmarks
    (rotation_matrix, translation_array, points_moving_reg, points_moving_barycenter) = \
        msct_register_landmarks.getRigidTransformFromLandmarks(
            points_fixed, points_moving, constraints='translation-scaling-z', show=False)

    # writing rigid transformation file
    text_file = open("straight2templateAffine.txt", "w")
    text_file.write("#Insight Transform File V1.0\n")
    text_file.write("#Transform 0\n")
    text_file.write(
        "Transform: FixedCenterOfRotationAffineTransform_double_3_3\n")
    text_file.write(
        "Parameters: %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f\n"
        % (1.0 / rotation_matrix[0, 0], rotation_matrix[0, 1],
           rotation_matrix[0, 2], rotation_matrix[1, 0],
           1.0 / rotation_matrix[1, 1], rotation_matrix[1, 2],
           rotation_matrix[2, 0], rotation_matrix[2, 1],
           1.0 / rotation_matrix[2, 2], translation_array[0, 0],
           translation_array[0, 1], -translation_array[0, 2]))
    text_file.write("FixedParameters: %.9f %.9f %.9f\n" %
                    (points_moving_barycenter[0], points_moving_barycenter[1],
                     points_moving_barycenter[2]))
    text_file.close()

    # Apply affine transformation: straight --> template
    sct.printv('\nApply affine transformation: straight --> template...',
               verbose)
    sct.run(
        'sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt -d template.nii -o warp_curve2straightAffine.nii.gz'
    )
    sct.run(
        'sct_apply_transfo -i data_rpi.nii -o data_rpi_straight2templateAffine.nii -d template.nii -w warp_curve2straightAffine.nii.gz'
    )
    sct.run(
        'sct_apply_transfo -i segmentation_rpi.nii.gz -o segmentation_rpi_straight2templateAffine.nii.gz -d template.nii -w warp_curve2straightAffine.nii.gz -x linear'
    )

    # threshold to 0.5
    nii = Image('segmentation_rpi_straight2templateAffine.nii.gz')
    data = nii.data
    data[data < 0.5] = 0
    nii.data = data
    nii.setFileName('segmentation_rpi_straight2templateAffine_th.nii.gz')
    nii.save()
    # find min-max of anat2template (for subsequent cropping)
    zmin_template, zmax_template = find_zmin_zmax(
        'segmentation_rpi_straight2templateAffine_th.nii.gz')

    # crop template in z-direction (for faster processing)
    sct.printv('\nCrop data in template space (for faster processing)...',
               verbose)
    sct.run(
        'sct_crop_image -i template.nii -o template_crop.nii -dim 2 -start ' +
        str(zmin_template) + ' -end ' + str(zmax_template))
    sct.run(
        'sct_crop_image -i template_seg.nii.gz -o template_seg_crop.nii.gz -dim 2 -start '
        + str(zmin_template) + ' -end ' + str(zmax_template))
    sct.run(
        'sct_crop_image -i data_rpi_straight2templateAffine.nii -o data_rpi_straight2templateAffine_crop.nii -dim 2 -start '
        + str(zmin_template) + ' -end ' + str(zmax_template))
    sct.run(
        'sct_crop_image -i segmentation_rpi_straight2templateAffine.nii.gz -o segmentation_rpi_straight2templateAffine_crop.nii.gz -dim 2 -start '
        + str(zmin_template) + ' -end ' + str(zmax_template))
    # sub-sample in z-direction
    sct.printv('\nSub-sample in z-direction (for faster processing)...',
               verbose)
    sct.run(
        'sct_resample -i template_crop.nii -o template_crop_r.nii -f 1x1x' +
        zsubsample, verbose)
    sct.run(
        'sct_resample -i template_seg_crop.nii.gz -o template_seg_crop_r.nii.gz -f 1x1x'
        + zsubsample, verbose)
    sct.run(
        'sct_resample -i data_rpi_straight2templateAffine_crop.nii -o data_rpi_straight2templateAffine_crop_r.nii -f 1x1x'
        + zsubsample, verbose)
    sct.run(
        'sct_resample -i segmentation_rpi_straight2templateAffine_crop.nii.gz -o segmentation_rpi_straight2templateAffine_crop_r.nii.gz -f 1x1x'
        + zsubsample, verbose)

    # Registration straight spinal cord to template
    sct.printv('\nRegister straight spinal cord to template...', verbose)

    # loop across registration steps
    warp_forward = []
    warp_inverse = []
    for i_step in range(1, len(paramreg.steps) + 1):
        sct.printv(
            '\nEstimate transformation for step #' + str(i_step) + '...',
            verbose)
        # identify which is the src and dest
        if paramreg.steps[str(i_step)].type == 'im':
            src = 'data_rpi_straight2templateAffine_crop_r.nii'
            dest = 'template_crop_r.nii'
            interp_step = 'linear'
        elif paramreg.steps[str(i_step)].type == 'seg':
            src = 'segmentation_rpi_straight2templateAffine_crop_r.nii.gz'
            dest = 'template_seg_crop_r.nii.gz'
            interp_step = 'nn'
        else:
            sct.printv('ERROR: Wrong image type.', 1, 'error')
        # if step>1, apply warp_forward_concat to the src image to be used
        if i_step > 1:
            # sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose)
            sct.run(
                'sct_apply_transfo -i ' + src + ' -d ' + dest + ' -w ' +
                ','.join(warp_forward) + ' -o ' + sct.add_suffix(src, '_reg') +
                ' -x ' + interp_step, verbose)
            src = sct.add_suffix(src, '_reg')
        # register src --> dest
        warp_forward_out, warp_inverse_out = register(src, dest, paramreg,
                                                      param, str(i_step))
        warp_forward.append(warp_forward_out)
        warp_inverse.append(warp_inverse_out)

    # Concatenate transformations:
    sct.printv('\nConcatenate transformations: anat --> template...', verbose)
    sct.run(
        'sct_concat_transfo -w warp_curve2straightAffine.nii.gz,' +
        ','.join(warp_forward) +
        ' -d template.nii -o warp_anat2template.nii.gz', verbose)
    # sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose)
    warp_inverse.reverse()
    sct.run(
        'sct_concat_transfo -w ' + ','.join(warp_inverse) +
        ',-straight2templateAffine.txt,warp_straight2curve.nii.gz -d data.nii -o warp_template2anat.nii.gz',
        verbose)

    # Apply warping fields to anat and template
    if output_type == 1:
        sct.run(
            'sct_apply_transfo -i template.nii -o template2anat.nii.gz -d data.nii -w warp_template2anat.nii.gz -c 1',
            verbose)
        sct.run(
            'sct_apply_transfo -i data.nii -o anat2template.nii.gz -d template.nii -w warp_anat2template.nii.gz -c 1',
            verbose)

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

    # Generate output files
    sct.printv('\nGenerate output files...', verbose)
    sct.generate_output_file(path_tmp + '/warp_template2anat.nii.gz',
                             'warp_template2anat.nii.gz', verbose)
    sct.generate_output_file(path_tmp + '/warp_anat2template.nii.gz',
                             'warp_anat2template.nii.gz', verbose)
    if output_type == 1:
        sct.generate_output_file(path_tmp + '/template2anat.nii.gz',
                                 'template2anat' + ext_data, verbose)
        sct.generate_output_file(path_tmp + '/anat2template.nii.gz',
                                 'anat2template' + ext_data, verbose)

    # Delete temporary files
    if remove_temp_files:
        sct.printv('\nDelete temporary files...', verbose)
        sct.run('rm -rf ' + path_tmp)

    # display elapsed time
    elapsed_time = time.time() - start_time
    sct.printv(
        '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's',
        verbose)

    # to view results
    sct.printv('\nTo view results, type:', verbose)
    sct.printv('fslview ' + fname_data + ' template2anat -b 0,4000 &', verbose,
               'info')
    sct.printv('fslview ' + fname_template + ' -b 0,5000 anat2template &\n',
               verbose, 'info')