Exemple #1
0
def create_line(param, fname, coord, nz):
    """
    Create vertical line in 3D volume
    :param param:
    :param fname:
    :param coord:
    :param nz:
    :return:
    """

    # duplicate volume (assumes input file is nifti)
    sct.copy(fname, 'line.nii', verbose=param.verbose)

    # set all voxels to zero
    run_proc(['sct_maths', '-i', 'line.nii', '-mul', '0', '-o', 'line.nii'],
             param.verbose)

    labels = []

    if isinstance(coord[0], Coordinate):
        for x, y, _, _ in coord:
            labels.extend([Coordinate([x, y, iz, 1]) for iz in range(nz)])
    else:
        # backwards compat
        labels.extend(
            [Coordinate([coord[0], coord[1], iz, 1]) for iz in range(nz)])

    create_labels(Image("line.nii"), labels).save(path="line.nii")

    return 'line.nii'
    def extract_sagital_slice(self):
        """Extract the sagital slice where the detection is done.

        If the segmentation is provided,
            the 2D sagital slice is choosen accoding to the segmentation.

        If the segmentation is not provided,
            the 2D sagital slice is choosen as the mid-sagital slice of the input image.
        """
        # TODO: get the mean across multiple sagittal slices to reduce noise

        if self.fname_seg is not None:
            img_seg = Image(self.fname_seg)

            z_mid_slice = img_seg.data[:, int(img_seg.dim[1] / 2), :]
            if 1 in z_mid_slice:  # if SC segmentation available at this slice
                self.rl_coord = int(
                    center_of_mass(z_mid_slice)[1])  # Right_left coordinate
            else:
                self.rl_coord = int(img_seg.dim[2] / 2)
            del img_seg

        else:
            img = Image(self.fname_im)
            self.rl_coord = int(img.dim[2] / 2)  # Right_left coordinate
            del img

        run_proc([
            'sct_crop_image', '-i', self.fname_im, '-zmin',
            str(self.rl_coord), '-zmax',
            str(self.rl_coord + 1), '-o', self.slice2D_im
        ])
Exemple #3
0
def checkRAM(os, verbose=1):
    if (os == 'linux'):
        status, output = utils.run_proc('grep MemTotal /proc/meminfo', 0)
        printv('RAM: ' + output)
        ram_split = output.split()
        ram_total = float(ram_split[1])
        status, output = utils.run_proc('free -m', 0)
        printv(output)
        return ram_total / 1024

    elif (os == 'osx'):
        status, output = utils.run_proc('hostinfo | grep memory', 0)
        printv('RAM: ' + output)
        ram_split = output.split(' ')
        ram_total = float(ram_split[3])

        # Get process info
        ps = subprocess.Popen(['ps', '-caxm', '-orss,comm'], stdout=subprocess.PIPE).communicate()[0].decode(sys.stdout.encoding)
        vm = subprocess.Popen(['vm_stat'], stdout=subprocess.PIPE).communicate()[0].decode(sys.stdout.encoding)

        # Iterate processes
        processLines = ps.split('\n')
        sep = re.compile(r'[\s]+')
        rssTotal = 0  # kB
        for row in range(1, len(processLines)):
            rowText = processLines[row].strip()
            rowElements = sep.split(rowText)
            try:
                rss = float(rowElements[0]) * 1024
            except:
                rss = 0  # ignore...
            rssTotal += rss

        # Process vm_stat
        vmLines = vm.split('\n')
        sep = re.compile(r':[\s]+')
        vmStats = {}
        for row in range(1, len(vmLines) - 2):
            rowText = vmLines[row].strip()
            rowElements = sep.split(rowText)
            vmStats[(rowElements[0])] = int(rowElements[1].strip(r'\.')) * 4096

        if verbose:
            printv('  Wired Memory:\t\t%d MB' % (vmStats["Pages wired down"] / 1024 / 1024))
            printv('  Active Memory:\t%d MB' % (vmStats["Pages active"] / 1024 / 1024))
            printv('  Inactive Memory:\t%d MB' % (vmStats["Pages inactive"] / 1024 / 1024))
            printv('  Free Memory:\t\t%d MB' % (vmStats["Pages free"] / 1024 / 1024))
            # printv('Real Mem Total (ps):\t%.3f MB' % ( rssTotal/1024/1024 ))

        return ram_total
Exemple #4
0
def init(param_test):
    """
    Initialize class: param_test
    """
    # initialization
    default_args = ['-i t2/t2.nii.gz -c t2 -qc testing-qc'
                    ]  # default parameters
    param_test.fname_seg = 't2_seg.nii.gz'  # output segmentation
    param_test.fname_gt = 't2/t2_seg-manual.nii.gz'
    # note: propseg does *not* produce the same results across platforms, hence the 0.9 Dice threahold.
    # For more details, see: https://github.com/neuropoly/spinalcordtoolbox/issues/2769
    param_test.dice_threshold = 0.9

    # check if isct_propseg compatibility
    # TODO: MAKE SURE THIS CASE WORKS AFTER MAJOR REFACTORING
    status_isct_propseg, output_isct_propseg = run_proc('isct_propseg',
                                                        verbose=0,
                                                        raise_exception=False)
    isct_propseg_version = output_isct_propseg.split('\n')[0]
    if isct_propseg_version != 'sct_propseg - Version 1.1 (2015-03-24)':
        status = 99
        param_test.output += '\nERROR: isct_propseg does not seem to be compatible with your system or is no up-to-date... Please contact SCT administrators.'
        return param_test

    # assign default params
    if not param_test.args:
        param_test.args = default_args

    return param_test
    def detect(self):
        """Run the classifier on self.slice2D_im."""
        sct.printv('\nRun PMJ detector', self.verbose, 'normal')
        os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR"
        cmd_pmj = [
            'isct_spine_detect', self.pmj_model,
            self.slice2D_im.split('.nii')[0], self.dection_map_pmj
        ]
        print(cmd_pmj)
        run_proc(cmd_pmj, verbose=0, is_sct_binary=True)

        img = nib.load(self.dection_map_pmj +
                       '_svm.hdr')  # convert .img and .hdr files to .nii
        nib.save(img, self.dection_map_pmj + '.nii')

        self.dection_map_pmj += '.nii'  # fname of the resulting detection map
def test(data_path):

    # parameters
    folder_data = ['t2/', 'dmri/']
    file_data = ['t2.nii.gz', 'dmri.nii.gz']

    # test 3d data
    cmd = 'sct_orientation -i ' + data_path + folder_data[0] + file_data[0]
    status, output = run_proc(cmd)

    # test 4d data
    if status == 0:
        cmd = 'sct_orientation -i ' + data_path + folder_data[1] + file_data[1]
        status, output = run_proc(cmd)

    # return
    return status, output
def test_isct_propseg_compatibility():
    # TODO: Move this check to `sct_check_dependencies`. (It was in `sct_testing`, so it is put here for now.)
    status_isct_propseg, output_isct_propseg = run_proc('isct_propseg', verbose=0, raise_exception=False,
                                                        is_sct_binary=True)
    isct_propseg_version = output_isct_propseg.split('\n')[0]
    assert isct_propseg_version == 'sct_propseg - Version 1.1 (2015-03-24)', \
        'isct_propseg does not seem to be compatible with your system or is no up-to-date... Please contact SCT ' \
        'administrators.'
Exemple #8
0
def resample_image(fname, suffix='_resampled.nii.gz', binary=False, npx=0.3, npy=0.3, thr=0.0, interpolation='spline'):
    """
    Resampling function: add a padding, resample, crop the padding
    :param fname: name of the image file to be resampled
    :param suffix: suffix added to the original fname after resampling
    :param binary: boolean, image is binary or not
    :param npx: new pixel size in the x direction
    :param npy: new pixel size in the y direction
    :param thr: if the image is binary, it will be thresholded at thr (default=0) after the resampling
    :param interpolation: type of interpolation used for the resampling
    :return: file name after resampling (or original fname if it was already in the correct resolution)
    """
    im_in = Image(fname)
    orientation = im_in.orientation
    if orientation != 'RPI':
        fname = im_in.change_orientation(im_in, 'RPI', generate_path=True).save().absolutepath

    nx, ny, nz, nt, px, py, pz, pt = im_in.dim

    if np.round(px, 2) != np.round(npx, 2) or np.round(py, 2) != np.round(npy, 2):
        name_resample = sct.extract_fname(fname)[1] + suffix
        if binary:
            interpolation = 'nn'

        if nz == 1:
            # when data is 2d: we convert it to a 3d image in order to avoid conversion problem with 2d data
            # TODO: check if this above problem is still present (now that we are using nibabel instead of nipy)
            run_proc(['sct_image', '-i', ','.join([fname, fname]), '-concat', 'z', '-o', fname])

        run_proc(['sct_resample', '-i', fname, '-mm', str(npx) + 'x' + str(npy) + 'x' + str(pz), '-o', name_resample, '-x', interpolation])

        if nz == 1:  # when input data was 2d: re-convert data 3d-->2d
            run_proc(['sct_image', '-i', name_resample, '-split', 'z'])
            im_split = Image(name_resample.split('.nii.gz')[0] + '_Z0000.nii.gz')
            im_split.save(name_resample)

        if binary:
            run_proc(['sct_maths', '-i', name_resample, '-bin', str(thr), '-o', name_resample])

        if orientation != 'RPI':
            name_resample = Image(name_resample) \
             .change_orientation(orientation, generate_path=True) \
             .save() \
             .absolutepath

        return name_resample
    else:
        if orientation != 'RPI':
            fname = sct.add_suffix(fname, "_RPI")
            im_in = msct_image.change_orientation(im_in, orientation).save(fname)

        sct.printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz')
        return fname
def visualize_warp(fname_warp, fname_grid=None, step=3, rm_tmp=True):
    if fname_grid is None:
        from numpy import zeros
        tmp_dir = sct.tmp_create()
        im_warp = Image(fname_warp)
        status, out = run_proc(['fslhd', fname_warp])
        curdir = os.getcwd()
        os.chdir(tmp_dir)
        dim1 = 'dim1           '
        dim2 = 'dim2           '
        dim3 = 'dim3           '
        nx = int(out[out.find(dim1):][len(dim1):out[out.find(dim1):].find('\n')])
        ny = int(out[out.find(dim2):][len(dim2):out[out.find(dim2):].find('\n')])
        nz = int(out[out.find(dim3):][len(dim3):out[out.find(dim3):].find('\n')])
        sq = zeros((step, step))
        sq[step - 1] = 1
        sq[:, step - 1] = 1
        dat = zeros((nx, ny, nz))
        for i in range(0, dat.shape[0], step):
            for j in range(0, dat.shape[1], step):
                for k in range(dat.shape[2]):
                    if dat[i:i + step, j:j + step, k].shape == (step, step):
                        dat[i:i + step, j:j + step, k] = sq
        fname_grid = 'grid_' + str(step) + '.nii.gz'
        im_grid = Image(param=dat)
        grid_hdr = im_warp.hdr
        im_grid.hdr = grid_hdr
        im_grid.absolutepath = fname_grid
        im_grid.save()
        fname_grid_resample = sct.add_suffix(fname_grid, '_resample')
        run_proc(['sct_resample', '-i', fname_grid, '-f', '3x3x1', '-x', 'nn', '-o', fname_grid_resample])
        fname_grid = os.path.join(tmp_dir, fname_grid_resample)
        os.chdir(curdir)
    path_warp, file_warp, ext_warp = sct.extract_fname(fname_warp)
    grid_warped = os.path.join(path_warp, sct.extract_fname(fname_grid)[1] + '_' + file_warp + ext_warp)
    run_proc(['sct_apply_transfo', '-i', fname_grid, '-d', fname_grid, '-w', fname_warp, '-o', grid_warped])
    if rm_tmp:
        sct.rmtree(tmp_dir)
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:
    """
    try:
        # Read label file
        template_label_ids, template_label_names, template_label_file, combined_labels_ids, combined_labels_names, \
        combined_labels_id_groups, clusters_apriori = \
            spinalcordtoolbox.metadata.read_label_file(os.path.join(path_label, folder_label), file_label)
    except Exception as error:
        sct.printv('\nWARNING: Cannot warp label ' + folder_label + ': ' + str(error), 1, 'warning')
        raise
    else:
        # create output folder
        if not os.path.exists(os.path.join(path_out, folder_label)):
            os.makedirs(os.path.join(path_out, folder_label))
        # Warp label
        for i in range(0, len(template_label_file)):
            fname_label = os.path.join(path_label, folder_label, template_label_file[i])
            # apply transfo
            run_proc('isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' %
                    (fname_label,
                     fname_src,
                     fname_transfo,
                     os.path.join(path_out, folder_label, template_label_file[i]),
                     get_interp(template_label_file[i])),
                    is_sct_binary=True,
                    verbose=param.verbose)
        # Copy list.txt
        sct.copy(os.path.join(path_label, folder_label, param.file_info_label), os.path.join(path_out, folder_label))
Exemple #11
0
def init(param_test):
    """
    Initialize class: param_test
    """
    # Reorient image to sagittal for testing another orientation (and crop to save time)
    run_proc(
        'sct_image -i dmri/dmri.nii.gz -setorient AIL -o dmri/dmri_AIL.nii',
        verbose=0)
    run_proc(
        'sct_crop_image -i dmri/dmri_AIL.nii -zmin 19 -zmax 21 -o dmri/dmri_AIL_crop.nii',
        verbose=0)
    # Create Gaussian mask for testing
    run_proc(
        'sct_create_mask -i dmri/dmri_T0000.nii.gz -p center -size 5mm -f gaussian -o dmri/mask.nii',
        verbose=0)

    # initialization
    default_args = [
        '-i dmri/dmri.nii.gz -bvec dmri/bvecs.txt -g 3 -x nn -ofolder dmri_test1 -r 0',
        '-i dmri/dmri.nii.gz -bvec dmri/bvecs.txt -g 3 -m dmri/mask.nii -ofolder dmri_test2 -r 0',
        '-i dmri/dmri_AIL_crop.nii -bvec dmri/bvecs.txt -x nn -ofolder dmri_test3 -r 0',
    ]

    # Output moco param files
    param_test.file_mocoparam = [
        'dmri_test1/moco_params.tsv',
        'dmri_test2/moco_params.tsv',
        None,
    ]

    # Ground truth value for integrity testing (corresponds to X motion parameters column)
    param_test.groundtruth = [
        [
            0.00047529041677414337, -1.1970542445283172e-05,
            -1.1970542445283172e-05, -1.1970542445283172e-05,
            -0.1296642741802682, -0.1296642741802682, -0.1296642741802682
        ],
        [
            0.008491259664160821, 0.0029020670607778237, 0.0029020670607778237,
            0.0029020670607778237, -0.014405996135095476,
            -0.014405996135095476, -0.014405996135095476
        ],
        None,
    ]

    # assign default params
    if not param_test.args:
        param_test.args = default_args
    return param_test
def main():

    # Initialization
    fname_data = ''
    interp_factor = param.interp_factor
    remove_temp_files = param.remove_temp_files
    verbose = param.verbose
    suffix = param.suffix
    smoothing_sigma = param.smoothing_sigma

    # start timer
    start_time = time.time()

    # Parameters for debug mode
    if param.debug:
        fname_data = os.path.join(sct.__data_dir__, 'sct_testing_data', 't2',
                                  't2_seg.nii.gz')
        remove_temp_files = 0
        param.mask_size = 10
    else:
        # Check input parameters
        try:
            opts, args = getopt.getopt(sys.argv[1:], 'hi:v:r:s:')
        except getopt.GetoptError:
            usage()
        if not opts:
            usage()
        for opt, arg in opts:
            if opt == '-h':
                usage()
            elif opt in ('-i'):
                fname_data = arg
            elif opt in ('-r'):
                remove_temp_files = int(arg)
            elif opt in ('-s'):
                smoothing_sigma = arg
            elif opt in ('-v'):
                verbose = int(arg)

    # display usage if a mandatory argument is not provided
    if fname_data == '':
        usage()

    # sct.printv(arguments)
    sct.printv('\nCheck parameters:')
    sct.printv('  segmentation ........... ' + fname_data)
    sct.printv('  interp factor .......... ' + str(interp_factor))
    sct.printv('  smoothing sigma ........ ' + str(smoothing_sigma))

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

    # Extract path, file and extension
    path_data, file_data, ext_data = sct.extract_fname(fname_data)

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

    from sct_convert import convert
    sct.printv('\nCopying input data to tmp folder and convert to nii...',
               param.verbose)
    convert(fname_data, os.path.join(path_tmp, "data.nii"))

    # go to tmp folder
    curdir = os.getcwd()
    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), verbose)

    # upsample data
    sct.printv('\nUpsample data...', verbose)
    run_proc([
        "sct_resample", "-i", "data.nii", "-x", "linear", "-vox",
        str(nx * interp_factor) + 'x' + str(ny * interp_factor) + 'x' +
        str(nz * interp_factor), "-o", "data_up.nii"
    ], verbose)

    # Smooth along centerline
    sct.printv('\nSmooth along centerline...', verbose)
    run_proc([
        "sct_smooth_spinalcord", "-i", "data_up.nii", "-s", "data_up.nii",
        "-smooth",
        str(smoothing_sigma), "-r",
        str(remove_temp_files), "-v",
        str(verbose)
    ], verbose)

    # downsample data
    sct.printv('\nDownsample data...', verbose)
    run_proc([
        "sct_resample", "-i", "data_up_smooth.nii", "-x", "linear", "-vox",
        str(nx) + 'x' + str(ny) + 'x' + str(nz), "-o",
        "data_up_smooth_down.nii"
    ], verbose)

    # come back
    os.chdir(curdir)

    # Generate output files
    sct.printv('\nGenerate output files...')
    fname_out = sct.generate_output_file(
        os.path.join(path_tmp, "data_up_smooth_down.nii"),
        '' + file_data + suffix + ext_data)

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

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

    # to view results
    sct.printv('\nTo view results, type:')
    sct.printv('fslview ' + file_data + ' ' + file_data + suffix + ' &\n')
def detect_c2c3(nii_im, nii_seg, contrast, nb_sag_avg=7.0, verbose=1):
    """
    Detect the posterior edge of C2-C3 disc.

    :param nii_im:
    :param nii_seg:
    :param contrast:
    :param verbose:
    :return:
    """
    # path to the pmj detector
    path_model = os.path.join(sct.__data_dir__, 'c2c3_disc_models',
                              '{}_model'.format(contrast))
    # check if model exists
    if not os.path.isfile(path_model + '.yml'):
        raise FileNotFoundError(
            "The model file {} does not exist. Please download it using sct_download_data"
            .format(path_model + '.yml'))

    orientation_init = nii_im.orientation
    z_seg_max = np.max(np.where(nii_seg.change_orientation('PIR').data)[1])

    # Flatten sagittal
    nii_im = flatten_sagittal(nii_im, nii_seg, verbose=verbose)
    nii_seg_flat = flatten_sagittal(nii_seg, nii_seg, verbose=verbose)

    # create temporary folder with intermediate results
    logger.info("Creating temporary folder...")
    tmp_folder = sct.TempFolder()
    tmp_folder.chdir()

    # Extract mid-slice
    nii_im.change_orientation('PIR')
    nii_seg_flat.change_orientation('PIR')
    mid_RL = int(np.rint(nii_im.dim[2] * 1.0 / 2))
    nb_sag_avg_half = int(nb_sag_avg / 2 / nii_im.dim[6])
    midSlice = np.mean(nii_im.data[:, :, mid_RL - nb_sag_avg_half:mid_RL +
                                   nb_sag_avg_half + 1], 2)  # average 7 slices
    midSlice_seg = nii_seg_flat.data[:, :, mid_RL]
    nii_midSlice = zeros_like(nii_im)
    nii_midSlice.data = midSlice
    nii_midSlice.save('data_midSlice.nii')

    # Run detection
    logger.info('Run C2-C3 detector...')
    os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR"
    cmd_detection = 'isct_spine_detect -ctype=dpdt "%s" "%s" "%s"' % \
                    (path_model, 'data_midSlice', 'data_midSlice_pred')
    # The command below will fail, but we don't care because it will output an image (prediction), which we
    # will use later on.
    s, o = run_proc(cmd_detection,
                    verbose=0,
                    is_sct_binary=True,
                    raise_exception=False)
    pred = nib.load('data_midSlice_pred_svm.hdr').get_data()
    if verbose >= 2:
        # copy the "prediction data before post-processing" in an Image object
        nii_pred_before_postPro = nii_midSlice.copy()
        nii_pred_before_postPro.data = pred  # 2D data with orientation, mid sag slice of the original data
        nii_pred_before_postPro.save(
            "pred_midSlice_before_postPro.nii.gz")  # save it)
    # DEBUG trick: check if the detection succeed by running: fsleyes data_midSlice data_midSlice_pred_svm -cm red -dr 0 100
    # If a "red cluster" is observed in the neighbourhood of C2C3, then the model detected it.

    # Create mask along centerline
    midSlice_mask = np.zeros(midSlice_seg.shape)
    mask_halfSize = int(np.rint(25.0 / nii_midSlice.dim[4]))
    for z in range(midSlice_mask.shape[1]):
        row = midSlice_seg[:,
                           z]  # 2D data with PI orientation, mid sag slice of the original data
        if np.any(row > 0):
            med_y = int(np.rint(np.median(np.where(row > 0))))
            midSlice_mask[
                med_y - mask_halfSize:med_y + mask_halfSize,
                z] = 1  # 2D data with PI orientation, mid sag slice of the original data
    if verbose >= 2:
        # copy the created mask in an Image object
        nii_postPro_mask = nii_midSlice.copy()
        nii_postPro_mask.data = midSlice_mask  # 2D data with PI orientation, mid sag slice of the original data
        nii_postPro_mask.save("mask_midSlice.nii.gz")  # save it

    # mask prediction
    pred[midSlice_mask == 0] = 0
    pred[:, z_seg_max:] = 0  # Mask above SC segmentation
    if verbose >= 2:
        # copy the "prediction data after post-processing" in an Image object
        nii_pred_after_postPro = nii_midSlice.copy()
        nii_pred_after_postPro.data = pred
        nii_pred_after_postPro.save(
            "pred_midSlice_after_postPro.nii.gz")  # save it

    # assign label to voxel
    nii_c2c3 = zeros_like(nii_seg_flat)  # 3D data with PIR orientaion
    if np.any(pred > 0):
        logger.info('C2-C3 detected...')

        pred_bin = (pred > 0).astype(np.int_)
        coord_max = np.where(pred == np.max(pred))
        pa_c2c3, is_c2c3 = coord_max[0][0], coord_max[1][0]
        nii_seg.change_orientation('PIR')
        rl_c2c3 = int(
            np.rint(center_of_mass(np.array(nii_seg.data[:, is_c2c3, :]))[1]))
        nii_c2c3.data[pa_c2c3, is_c2c3, rl_c2c3] = 3
    else:
        logger.warning('C2-C3 not detected...')

    # remove temporary files
    tmp_folder.chdir_undo()
    if verbose < 2:
        logger.info("Remove temporary files...")
        tmp_folder.cleanup()
    else:
        logger.info("Temporary files saved to " + tmp_folder.get_path())

    nii_c2c3.change_orientation(orientation_init)
    return nii_c2c3
def main():

    # Initialization
    size_data = 61
    size_label = 1  # put zero for labels that are single points.
    dice_acceptable = 0.39  # computed DICE should be 0.931034
    test_passed = 0
    remove_temp_files = 1
    verbose = 1

    # Check input parameters
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'hvr:')
    except getopt.GetoptError:
        usage()
    for opt, arg in opts:
        if opt == '-h':
            usage()
        elif opt in ('-v'):
            verbose = int(arg)
        elif opt in ('-r'):
            remove_temp_files = int(arg)

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

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

    # Initialise numpy volumes
    data_src = np.zeros((size_data, size_data, size_data), dtype=np.int16)
    data_dest = np.zeros((size_data, size_data, size_data), dtype=np.int16)

    # add labels for src image (curved).
    # Labels can be big (more than single point), because when applying NN interpolation, single points might disappear
    data_src[20 - size_label:20 + size_label + 1, 20 - size_label:20 + size_label + 1, 10 - size_label:10 + size_label + 1] = 1
    data_src[30 - size_label:30 + size_label + 1, 30 - size_label:30 + size_label + 1, 30 - size_label:30 + size_label + 1] = 2
    data_src[20 - size_label:20 + size_label + 1, 20 - size_label:20 + size_label + 1, 50 - size_label:50 + size_label + 1] = 3

    # add labels for dest image (straight).
    # Here, no need for big labels (bigger than single point) because these labels will not be re-interpolated.
    data_dest[30 - size_label:30 + size_label + 1, 30 - size_label:30 + size_label + 1, 10 - size_label:10 + size_label + 1] = 1
    data_dest[30 - size_label:30 + size_label + 1, 30 - size_label:30 + size_label + 1, 30 - size_label:30 + size_label + 1] = 2
    data_dest[30 - size_label:30 + size_label + 1, 30 - size_label:30 + size_label + 1, 50 - size_label:50 + size_label + 1] = 3

    # save as nifti
    img_src = nib.Nifti1Image(data_src, np.eye(4))
    nib.save(img_src, 'data_src.nii.gz')
    img_dest = nib.Nifti1Image(data_dest, np.eye(4))
    nib.save(img_dest, 'data_dest.nii.gz')

    # Estimate rigid transformation
    sct.printv('\nEstimate rigid transformation between paired landmarks...', verbose)
    # TODO fixup isct_ants* parsers
    run_proc(['isct_antsRegistration',
     '-d', '3',
     '-t', 'syn[1,3,1]',
     '-m', 'MeanSquares[data_dest.nii.gz,data_src.nii.gz,1,3]',
     '-f', '2',
     '-s', '0',
     '-o', '[src2reg,data_src_reg.nii.gz]',
     '-c', '5',
     '-v', '1',
     '-n', 'NearestNeighbor'], verbose, is_sct_binary=True)

    # # Apply rigid transformation
    # sct.printv('\nApply rigid transformation to curved landmarks...', verbose)
    # run_proc('sct_apply_transfo -i data_src.nii.gz -o data_src_rigid.nii.gz -d data_dest.nii.gz -w curve2straight_rigid.txt -p nn', verbose)
    #
    # # Estimate b-spline transformation curve --> straight
    # sct.printv('\nEstimate b-spline transformation: curve --> straight...', verbose)
    # run_proc('isct_ANTSLandmarksBSplineTransform data_dest.nii.gz data_src_rigid.nii.gz warp_curve2straight_intermediate.nii.gz 5x5x5 3 2 0', verbose)
    #
    # # Concatenate rigid and non-linear transformations...
    # sct.printv('\nConcatenate rigid and non-linear transformations...', verbose)
    # cmd = 'isct_ComposeMultiTransform 3 warp_curve2straight.nii.gz -R data_dest.nii.gz warp_curve2straight_intermediate.nii.gz curve2straight_rigid.txt'
    # sct.printv('>> '+cmd, verbose)
    # run_proc(cmd)
    #
    # # Apply deformation to input image
    # sct.printv('\nApply transformation to input image...', verbose)
    # run_proc('sct_apply_transfo -i data_src.nii.gz -o data_src_warp.nii.gz -d data_dest.nii.gz -w warp_curve2straight.nii.gz -p nn', verbose)
    #
    # Compute DICE coefficient between src and dest
    sct.printv('\nCompute DICE coefficient...', verbose)
    run_proc(["sct_dice_coefficient",
     "-i", "data_dest.nii.gz",
     "-d", "data_src_reg.nii.gz",
     "-o", "dice.txt"], verbose)
    with open("dice.txt", "r") as file_dice:
        dice = float(file_dice.read().replace('3D Dice coefficient = ', ''))
    sct.printv('Dice coeff = ' + str(dice) + ' (should be above ' + str(dice_acceptable) + ')', verbose)

    # Check if DICE coefficient is above acceptable value
    if dice > dice_acceptable:
        test_passed = 1

    # come back
    os.chdir(curdir)

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

    # output result for parent function
    if test_passed:
        sct.printv('\nTest passed!\n', verbose)
        sys.exit(0)
    else:
        sct.printv('\nTest failed!\n', verbose)
        sys.exit(1)
Exemple #15
0
    tmp_dir = os.path.abspath(tmp_dir)

    # copy input files to tmp directory
    # for fname in [fname_input1, fname_input2]:
    sct.copy(fname_input1, tmp_dir)
    sct.copy(fname_input2, tmp_dir)
    fname_input1 = ''.join(sct.extract_fname(fname_input1)[1:])
    fname_input2 = ''.join(sct.extract_fname(fname_input2)[1:])

    curdir = os.getcwd()
    os.chdir(tmp_dir)  # go to tmp directory

    if arguments.bin is not None:
        fname_input1_bin = sct.add_suffix(fname_input1, '_bin')
        run_proc([
            'sct_maths', '-i', fname_input1, '-bin', '0', '-o',
            fname_input1_bin
        ])
        fname_input1 = fname_input1_bin
        fname_input2_bin = sct.add_suffix(fname_input2, '_bin')
        run_proc([
            'sct_maths', '-i', fname_input2, '-bin', '0', '-o',
            fname_input2_bin
        ])
        fname_input2 = fname_input2_bin

    # copy header of im_1 to im_2
    from spinalcordtoolbox.image import Image
    im_1 = Image(fname_input1)
    im_2 = Image(fname_input2)
    im_2.header = im_1.header
    im_2.save()
Exemple #16
0
def main():
    print("SCT info:")
    print("- version: {}".format(sct.__version__))
    print("- path: {0}".format(sct.__sct_dir__))

    # initialization
    install_software = 0
    e = 0
    complete_test = param.complete_test
    os_running = 'not identified'

    # Check input parameters
    parser = get_parser()
    arguments = parser.parse_args()
    if arguments.complete:
        complete_test = 1

    # use variable "verbose" when calling sct.run for more clarity
    verbose = complete_test

    # complete test
    if complete_test:
        print(run_proc('date', verbose))
        print(run_proc('whoami', verbose))
        print(run_proc('pwd', verbose))
        bash_profile = os.path.expanduser("~/.bash_profile")
        if os.path.isfile(bash_profile):
            with io.open(bash_profile, "r") as f:
                print(f.read())
        bashrc = os.path.expanduser("~/.bashrc")
        if os.path.isfile(bashrc):
            with io.open(bashrc, "r") as f:
                print(f.read())

    # check OS
    platform_running = sys.platform
    if platform_running.find('darwin') != -1:
        os_running = 'osx'
    elif platform_running.find('linux') != -1:
        os_running = 'linux'

    print('OS: ' + os_running + ' (' + platform.platform() + ')')

    # Check number of CPU cores
    from multiprocessing import cpu_count
    output = int(os.getenv('ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS', 0))
    print('CPU cores: Available: {}, Used by SCT: {}'.format(
        cpu_count(), output))

    # check RAM
    sct.checkRAM(os_running, 0)

    if arguments.short:
        sys.exit()

    # check if Python path is within SCT path
    print_line('Check Python executable')
    path_python = sys.executable
    if sct.__sct_dir__ in path_python:
        print_ok()
        print('  Using bundled python {} at {}'.format(sys.version,
                                                       path_python))
    else:
        print_warning()
        print('  Using system python which is unsupported: {}'.format(
            path_python))

    # check if data folder is empty
    print_line('Check if data are installed')
    if os.path.isdir(sct.__data_dir__):
        print_ok()
    else:
        print_fail()

    for dep_pkg, dep_ver_spec in get_dependencies():
        if dep_ver_spec is None:
            print_line('Check if %s is installed' % (dep_pkg))
        else:
            print_line('Check if %s (%s) is installed' %
                       (dep_pkg, dep_ver_spec))

        try:
            module_name, suppress_stderr = resolve_module(dep_pkg)
            module = module_import(module_name, suppress_stderr)
            version = get_version(module)

            if dep_ver_spec is not None and version is not None and dep_ver_spec != version:
                print_warning(more=(" (%s != %s mandated version))" %
                                    (version, dep_ver_spec)))
            elif version is not None:
                print_ok(more=(" (%s)" % version))
            else:
                print_ok()

        except ImportError as err:
            print_fail()
            print(err)
            install_software = 1

    print_line('Check if spinalcordtoolbox is installed')
    try:
        importlib.import_module('spinalcordtoolbox')
        print_ok()
    except ImportError:
        print_fail()
        install_software = 1

    # Check ANTs integrity
    print_line('Check ANTs compatibility with OS ')
    cmd = 'isct_test_ants'
    status, output = run_proc(cmd, verbose=0, raise_exception=False)
    if status == 0:
        print_ok()
    else:
        print_fail()
        print(output)
        e = 1
    if complete_test:
        print('>> ' + cmd)
        print((status, output), '\n')

    # check PropSeg compatibility with OS
    print_line('Check PropSeg compatibility with OS ')
    status, output = run_proc('isct_propseg',
                              verbose=0,
                              raise_exception=False,
                              is_sct_binary=True)
    if status in (0, 1):
        print_ok()
    else:
        print_fail()
        print(output)
        e = 1
    if complete_test:
        print((status, output), '\n')

    print_line('Check if DISPLAY variable is set')
    try:
        os.environ['DISPLAY']
        print_ok()

        # Further check with PyQt specifically
        print_line('Check if figure can be opened with PyQt')
        from PyQt5.QtWidgets import QApplication, QLabel
        try:
            app = QApplication([])
            label = QLabel('Hello World!')
            label.show()
            label.close()
            print_ok()
        except Exception as err:
            print_fail()
            print(err)

    except KeyError:
        print_fail()

    print('')
    sys.exit(e + install_software)
def main(args=None):

    # Initialization
    param = Param()
    start_time = time.time()

    parser = get_parser()
    arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help'])

    fname_anat = arguments.i
    fname_centerline = arguments.s
    param.algo_fitting = arguments.algo_fitting
    if arguments.smooth is not None:
        sigma = arguments.smooth
    remove_temp_files = arguments.r
    verbose = int(arguments.v)
    init_sct(log_level=verbose, update=True)  # Update log level

    # Display arguments
    sct.printv('\nCheck input arguments...')
    sct.printv('  Volume to smooth .................. ' + fname_anat)
    sct.printv('  Centerline ........................ ' + fname_centerline)
    sct.printv('  Sigma (mm) ........................ ' + str(sigma))
    sct.printv('  Verbose ........................... ' + str(verbose))

    # Check that input is 3D:
    from spinalcordtoolbox.image import Image
    nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim
    dim = 4  # by default, will be adjusted later
    if nt == 1:
        dim = 3
    if nz == 1:
        dim = 2
    if dim == 4:
        sct.printv(
            'WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n'
            'sct_image -i ' + fname_anat + ' -split t -o ' + fname_anat,
            verbose, 'warning')
        sct.printv('4D images not supported, aborting ...', verbose, 'error')

    # Extract path/file/extension
    path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat)
    path_centerline, file_centerline, ext_centerline = sct.extract_fname(
        fname_centerline)

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

    # Copying input data to tmp folder
    sct.printv('\nCopying input data to tmp folder and convert to nii...',
               verbose)
    sct.copy(fname_anat, os.path.join(path_tmp, "anat" + ext_anat))
    sct.copy(fname_centerline,
             os.path.join(path_tmp, "centerline" + ext_centerline))

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

    # convert to nii format
    convert('anat' + ext_anat, 'anat.nii')
    convert('centerline' + ext_centerline, 'centerline.nii')

    # Change orientation of the input image into RPI
    sct.printv('\nOrient input volume to RPI orientation...')
    fname_anat_rpi = msct_image.Image("anat.nii") \
     .change_orientation("RPI", generate_path=True) \
     .save() \
     .absolutepath

    # Change orientation of the input image into RPI
    sct.printv('\nOrient centerline to RPI orientation...')
    fname_centerline_rpi = msct_image.Image("centerline.nii") \
     .change_orientation("RPI", generate_path=True) \
     .save() \
     .absolutepath

    # Straighten the spinal cord
    # straighten segmentation
    sct.printv('\nStraighten the spinal cord using centerline/segmentation...',
               verbose)
    cache_sig = sct.cache_signature(
        input_files=[fname_anat_rpi, fname_centerline_rpi],
        input_params={"x": "spline"})
    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
        run_proc([
            'sct_apply_transfo', '-i', fname_anat_rpi, '-w',
            'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o',
            'anat_rpi_straight.nii', '-x', 'spline'
        ], verbose)
    else:
        run_proc([
            'sct_straighten_spinalcord', '-i', fname_anat_rpi, '-o',
            'anat_rpi_straight.nii', '-s', fname_centerline_rpi, '-x',
            'spline', '-param', 'algo_fitting=' + param.algo_fitting
        ], verbose)
        sct.cache_save(cachefile, cache_sig)
        # move warping fields locally (to use caching next time)
        sct.copy('warp_curve2straight.nii.gz',
                 os.path.join(curdir, 'warp_curve2straight.nii.gz'))
        sct.copy('warp_straight2curve.nii.gz',
                 os.path.join(curdir, 'warp_straight2curve.nii.gz'))

    # Smooth the straightened image along z
    sct.printv('\nSmooth the straightened image...')
    sigma_smooth = ",".join([str(i) for i in sigma])
    sct_maths.main(args=[
        '-i', 'anat_rpi_straight.nii', '-smooth', sigma_smooth, '-o',
        'anat_rpi_straight_smooth.nii', '-v', '0'
    ])
    # Apply the reversed warping field to get back the curved spinal cord
    sct.printv(
        '\nApply the reversed warping field to get back the curved spinal cord...'
    )
    run_proc([
        'sct_apply_transfo', '-i', 'anat_rpi_straight_smooth.nii', '-o',
        'anat_rpi_straight_smooth_curved.nii', '-d', 'anat.nii', '-w',
        'warp_straight2curve.nii.gz', '-x', 'spline'
    ], verbose)

    # replace zeroed voxels by original image (issue #937)
    sct.printv('\nReplace zeroed voxels by original image...', verbose)
    nii_smooth = Image('anat_rpi_straight_smooth_curved.nii')
    data_smooth = nii_smooth.data
    data_input = Image('anat.nii').data
    indzero = np.where(data_smooth == 0)
    data_smooth[indzero] = data_input[indzero]
    nii_smooth.data = data_smooth
    nii_smooth.save('anat_rpi_straight_smooth_curved_nonzero.nii')

    # come back
    os.chdir(curdir)

    # Generate output file
    sct.printv('\nGenerate output file...')
    sct.generate_output_file(
        os.path.join(path_tmp, "anat_rpi_straight_smooth_curved_nonzero.nii"),
        file_anat + '_smooth' + ext_anat)

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

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

    sct.display_viewer_syntax([file_anat, file_anat + '_smooth'],
                              verbose=verbose)
    def straighten(self):
        """
        Straighten spinal cord. Steps: (everything is done in physical space)
        1. open input image and centreline image
        2. extract bspline fitting of the centreline, and its derivatives
        3. compute length of centerline
        4. compute and generate straight space
        5. compute transformations
            for each voxel of one space: (done using matrices --> improves speed by a factor x300)
                a. determine which plane of spinal cord centreline it is included
                b. compute the position of the voxel in the plane (X and Y distance from centreline, along the plane)
                c. find the correspondant centreline point in the other space
                d. find the correspondance of the voxel in the corresponding plane
        6. generate warping fields for each transformations
        7. write warping fields and apply them

        step 5.b: how to find the corresponding plane?
            The centerline plane corresponding to a voxel correspond to the nearest point of the centerline.
            However, we need to compute the distance between the voxel position and the plane to be sure it is part of the plane and not too distant.
            If it is more far than a threshold, warping value should be 0.

        step 5.d: how to make the correspondance between centerline point in both images?
            Both centerline have the same lenght. Therefore, we can map centerline point via their position along the curve.
            If we use the same number of points uniformely along the spinal cord (1000 for example), the correspondance is straight-forward.

        :return:
        """
        # Initialization
        fname_anat = self.input_filename
        fname_centerline = self.centerline_filename
        fname_output = self.output_filename
        remove_temp_files = self.remove_temp_files
        verbose = self.verbose
        interpolation_warp = self.interpolation_warp  # TODO: remove this

        # start timer
        start_time = time.time()

        # Extract path/file/extension
        path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat)

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

        # Copying input data to tmp folder
        sct.printv('\nCopy files to tmp folder...', verbose)
        Image(fname_anat).save(os.path.join(path_tmp, "data.nii"))
        Image(fname_centerline).save(
            os.path.join(path_tmp, "centerline.nii.gz"))

        if self.use_straight_reference:
            Image(self.centerline_reference_filename).save(
                os.path.join(path_tmp, "centerline_ref.nii.gz"))
        if self.discs_input_filename != '':
            Image(self.discs_input_filename).save(
                os.path.join(path_tmp, "labels_input.nii.gz"))
        if self.discs_ref_filename != '':
            Image(self.discs_ref_filename).save(
                os.path.join(path_tmp, "labels_ref.nii.gz"))

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

        # Change orientation of the input centerline into RPI
        image_centerline = Image("centerline.nii.gz").change_orientation(
            "RPI").save("centerline_rpi.nii.gz", mutable=True)

        # Get dimension
        nx, ny, nz, nt, px, py, pz, pt = image_centerline.dim
        if self.speed_factor != 1.0:
            intermediate_resampling = True
            px_r, py_r, pz_r = px * self.speed_factor, py * self.speed_factor, pz * self.speed_factor
        else:
            intermediate_resampling = False

        if intermediate_resampling:
            sct.mv('centerline_rpi.nii.gz', 'centerline_rpi_native.nii.gz')
            pz_native = pz
            # TODO: remove system call
            run_proc([
                'sct_resample', '-i', 'centerline_rpi_native.nii.gz', '-mm',
                str(px_r) + 'x' + str(py_r) + 'x' + str(pz_r), '-o',
                'centerline_rpi.nii.gz'
            ])
            image_centerline = Image('centerline_rpi.nii.gz')
            nx, ny, nz, nt, px, py, pz, pt = image_centerline.dim

        if np.min(image_centerline.data) < 0 or np.max(
                image_centerline.data) > 1:
            image_centerline.data[image_centerline.data < 0] = 0
            image_centerline.data[image_centerline.data > 1] = 1
            image_centerline.save()

        # 2. extract bspline fitting of the centerline, and its derivatives
        img_ctl = Image('centerline_rpi.nii.gz')
        centerline = _get_centerline(img_ctl, self.param_centerline, verbose)
        number_of_points = centerline.number_of_points

        # ==========================================================================================
        logger.info('Create the straight space and the safe zone')
        # 3. compute length of centerline
        # compute the length of the spinal cord based on fitted centerline and size of centerline in z direction

        # Computation of the safe zone.
        # The safe zone is defined as the length of the spinal cord for which an axial segmentation will be complete
        # The safe length (to remove) is computed using the safe radius (given as parameter) and the angle of the
        # last centerline point with the inferior-superior direction. Formula: Ls = Rs * sin(angle)
        # Calculate Ls for both edges and remove appropriate number of centerline points
        radius_safe = 0.0  # mm

        # inferior edge
        u = centerline.derivatives[0]
        v = np.array([0, 0, -1])

        angle_inferior = np.arctan2(np.linalg.norm(np.cross(u, v)),
                                    np.dot(u, v))
        length_safe_inferior = radius_safe * np.sin(angle_inferior)

        # superior edge
        u = centerline.derivatives[-1]
        v = np.array([0, 0, 1])
        angle_superior = np.arctan2(np.linalg.norm(np.cross(u, v)),
                                    np.dot(u, v))
        length_safe_superior = radius_safe * np.sin(angle_superior)

        # remove points
        inferior_bound = bisect.bisect(centerline.progressive_length,
                                       length_safe_inferior) - 1
        superior_bound = centerline.number_of_points - bisect.bisect(
            centerline.progressive_length_inverse, length_safe_superior)

        z_centerline = centerline.points[:, 2]
        length_centerline = centerline.length
        size_z_centerline = z_centerline[-1] - z_centerline[0]

        # compute the size factor between initial centerline and straight bended centerline
        factor_curved_straight = length_centerline / size_z_centerline
        middle_slice = (z_centerline[0] + z_centerline[-1]) / 2.0

        bound_curved = [
            z_centerline[inferior_bound], z_centerline[superior_bound]
        ]
        bound_straight = [(z_centerline[inferior_bound] - middle_slice) *
                          factor_curved_straight + middle_slice,
                          (z_centerline[superior_bound] - middle_slice) *
                          factor_curved_straight + middle_slice]

        logger.info('Length of spinal cord: {}'.format(length_centerline))
        logger.info(
            'Size of spinal cord in z direction: {}'.format(size_z_centerline))
        logger.info('Ratio length/size: {}'.format(factor_curved_straight))
        logger.info(
            'Safe zone boundaries (curved space): {}'.format(bound_curved))
        logger.info(
            'Safe zone boundaries (straight space): {}'.format(bound_straight))

        # 4. compute and generate straight space
        # points along curved centerline are already regularly spaced.
        # calculate position of points along straight centerline

        # Create straight NIFTI volumes.
        # ==========================================================================================
        # TODO: maybe this if case is not needed?
        if self.use_straight_reference:
            image_centerline_pad = Image('centerline_rpi.nii.gz')
            nx, ny, nz, nt, px, py, pz, pt = image_centerline_pad.dim

            fname_ref = 'centerline_ref_rpi.nii.gz'
            image_centerline_straight = Image('centerline_ref.nii.gz') \
                .change_orientation("RPI") \
                .save(fname_ref, mutable=True)
            centerline_straight = _get_centerline(image_centerline_straight,
                                                  self.param_centerline,
                                                  verbose)
            nx_s, ny_s, nz_s, nt_s, px_s, py_s, pz_s, pt_s = image_centerline_straight.dim

            # Prepare warping fields headers
            hdr_warp = image_centerline_pad.hdr.copy()
            hdr_warp.set_data_dtype('float32')
            hdr_warp_s = image_centerline_straight.hdr.copy()
            hdr_warp_s.set_data_dtype('float32')

            if self.discs_input_filename != "" and self.discs_ref_filename != "":
                discs_input_image = Image('labels_input.nii.gz')
                coord = discs_input_image.getNonZeroCoordinates(
                    sorting='z', reverse_coord=True)
                coord_physical = []
                for c in coord:
                    c_p = discs_input_image.transfo_pix2phys([[c.x, c.y, c.z]
                                                              ]).tolist()[0]
                    c_p.append(c.value)
                    coord_physical.append(c_p)
                centerline.compute_vertebral_distribution(coord_physical)
                centerline.save_centerline(
                    image=discs_input_image,
                    fname_output='discs_input_image.nii.gz')

                discs_ref_image = Image('labels_ref.nii.gz')
                coord = discs_ref_image.getNonZeroCoordinates(
                    sorting='z', reverse_coord=True)
                coord_physical = []
                for c in coord:
                    c_p = discs_ref_image.transfo_pix2phys([[c.x, c.y,
                                                             c.z]]).tolist()[0]
                    c_p.append(c.value)
                    coord_physical.append(c_p)
                centerline_straight.compute_vertebral_distribution(
                    coord_physical)
                centerline_straight.save_centerline(
                    image=discs_ref_image,
                    fname_output='discs_ref_image.nii.gz')

        else:
            logger.info(
                'Pad input volume to account for spinal cord length...')

            start_point, end_point = bound_straight[0], bound_straight[1]
            offset_z = 0

            # if the destination image is resampled, we still create the straight reference space with the native
            # resolution.
            # TODO: Maybe this if case is not needed?
            if intermediate_resampling:
                padding_z = int(
                    np.ceil(1.5 *
                            ((length_centerline - size_z_centerline) / 2.0) /
                            pz_native))
                run_proc([
                    'sct_image', '-i', 'centerline_rpi_native.nii.gz', '-o',
                    'tmp.centerline_pad_native.nii.gz', '-pad',
                    '0,0,' + str(padding_z)
                ])
                image_centerline_pad = Image('centerline_rpi_native.nii.gz')
                nx, ny, nz, nt, px, py, pz, pt = image_centerline_pad.dim
                start_point_coord_native = image_centerline_pad.transfo_phys2pix(
                    [[0, 0, start_point]])[0]
                end_point_coord_native = image_centerline_pad.transfo_phys2pix(
                    [[0, 0, end_point]])[0]
                straight_size_x = int(self.xy_size / px)
                straight_size_y = int(self.xy_size / py)
                warp_space_x = [
                    int(np.round(nx / 2)) - straight_size_x,
                    int(np.round(nx / 2)) + straight_size_x
                ]
                warp_space_y = [
                    int(np.round(ny / 2)) - straight_size_y,
                    int(np.round(ny / 2)) + straight_size_y
                ]
                if warp_space_x[0] < 0:
                    warp_space_x[1] += warp_space_x[0] - 2
                    warp_space_x[0] = 0
                if warp_space_y[0] < 0:
                    warp_space_y[1] += warp_space_y[0] - 2
                    warp_space_y[0] = 0

                spec = dict((
                    (0, warp_space_x),
                    (1, warp_space_y),
                    (2, (0, end_point_coord_native[2] -
                         start_point_coord_native[2])),
                ))
                msct_image.spatial_crop(
                    Image("tmp.centerline_pad_native.nii.gz"),
                    spec).save("tmp.centerline_pad_crop_native.nii.gz")

                fname_ref = 'tmp.centerline_pad_crop_native.nii.gz'
                offset_z = 4
            else:
                fname_ref = 'tmp.centerline_pad_crop.nii.gz'

            nx, ny, nz, nt, px, py, pz, pt = image_centerline.dim
            padding_z = int(
                np.ceil(1.5 * ((length_centerline - size_z_centerline) / 2.0) /
                        pz)) + offset_z
            image_centerline_pad = pad_image(image_centerline,
                                             pad_z_i=padding_z,
                                             pad_z_f=padding_z)
            nx, ny, nz = image_centerline_pad.data.shape
            hdr_warp = image_centerline_pad.hdr.copy()
            hdr_warp.set_data_dtype('float32')
            start_point_coord = image_centerline_pad.transfo_phys2pix(
                [[0, 0, start_point]])[0]
            end_point_coord = image_centerline_pad.transfo_phys2pix(
                [[0, 0, end_point]])[0]

            straight_size_x = int(self.xy_size / px)
            straight_size_y = int(self.xy_size / py)
            warp_space_x = [
                int(np.round(nx / 2)) - straight_size_x,
                int(np.round(nx / 2)) + straight_size_x
            ]
            warp_space_y = [
                int(np.round(ny / 2)) - straight_size_y,
                int(np.round(ny / 2)) + straight_size_y
            ]

            if warp_space_x[0] < 0:
                warp_space_x[1] += warp_space_x[0] - 2
                warp_space_x[0] = 0
            if warp_space_x[1] >= nx:
                warp_space_x[1] = nx - 1
            if warp_space_y[0] < 0:
                warp_space_y[1] += warp_space_y[0] - 2
                warp_space_y[0] = 0
            if warp_space_y[1] >= ny:
                warp_space_y[1] = ny - 1

            spec = dict((
                (0, warp_space_x),
                (1, warp_space_y),
                (2, (0, end_point_coord[2] - start_point_coord[2] + offset_z)),
            ))
            image_centerline_straight = msct_image.spatial_crop(
                image_centerline_pad, spec)

            nx_s, ny_s, nz_s, nt_s, px_s, py_s, pz_s, pt_s = image_centerline_straight.dim
            hdr_warp_s = image_centerline_straight.hdr.copy()
            hdr_warp_s.set_data_dtype('float32')

            if self.template_orientation == 1:
                raise NotImplementedError()

            start_point_coord = image_centerline_pad.transfo_phys2pix(
                [[0, 0, start_point]])[0]
            end_point_coord = image_centerline_pad.transfo_phys2pix(
                [[0, 0, end_point]])[0]

            number_of_voxel = nx * ny * nz
            logger.debug('Number of voxels: {}'.format(number_of_voxel))

            time_centerlines = time.time()

            coord_straight = np.empty((number_of_points, 3))
            coord_straight[..., 0] = int(np.round(nx_s / 2))
            coord_straight[..., 1] = int(np.round(ny_s / 2))
            coord_straight[..., 2] = np.linspace(
                0, end_point_coord[2] - start_point_coord[2], number_of_points)
            coord_phys_straight = image_centerline_straight.transfo_pix2phys(
                coord_straight)
            derivs_straight = np.empty((number_of_points, 3))
            derivs_straight[..., 0] = derivs_straight[..., 1] = 0
            derivs_straight[..., 2] = 1
            dx_straight, dy_straight, dz_straight = derivs_straight.T
            centerline_straight = Centerline(coord_phys_straight[:, 0],
                                             coord_phys_straight[:, 1],
                                             coord_phys_straight[:, 2],
                                             dx_straight, dy_straight,
                                             dz_straight)

            time_centerlines = time.time() - time_centerlines
            logger.info('Time to generate centerline: {} ms'.format(
                np.round(time_centerlines * 1000.0)))

        if verbose == 2:
            # TODO: use OO
            import matplotlib.pyplot as plt
            from datetime import datetime
            curved_points = centerline.progressive_length
            straight_points = centerline_straight.progressive_length
            range_points = np.linspace(0, 1, number_of_points)
            dist_curved = np.zeros(number_of_points)
            dist_straight = np.zeros(number_of_points)
            for i in range(1, number_of_points):
                dist_curved[i] = dist_curved[
                    i - 1] + curved_points[i - 1] / centerline.length
                dist_straight[i] = dist_straight[i - 1] + straight_points[
                    i - 1] / centerline_straight.length
            plt.plot(range_points, dist_curved)
            plt.plot(range_points, dist_straight)
            plt.grid(True)
            plt.savefig('fig_straighten_' +
                        datetime.now().strftime("%y%m%d%H%M%S%f") + '.png')
            plt.close()

        # alignment_mode = 'length'
        alignment_mode = 'levels'

        lookup_curved2straight = list(range(centerline.number_of_points))
        if self.discs_input_filename != "":
            # create look-up table curved to straight
            for index in range(centerline.number_of_points):
                disc_label = centerline.l_points[index]
                if alignment_mode == 'length':
                    relative_position = centerline.dist_points[index]
                else:
                    relative_position = centerline.dist_points_rel[index]
                idx_closest = centerline_straight.get_closest_to_absolute_position(
                    disc_label,
                    relative_position,
                    backup_index=index,
                    backup_centerline=centerline_straight,
                    mode=alignment_mode)
                if idx_closest is not None:
                    lookup_curved2straight[index] = idx_closest
                else:
                    lookup_curved2straight[index] = 0
        for p in range(0, len(lookup_curved2straight) // 2):
            if lookup_curved2straight[p] == lookup_curved2straight[p + 1]:
                lookup_curved2straight[p] = 0
            else:
                break
        for p in range(
                len(lookup_curved2straight) - 1,
                len(lookup_curved2straight) // 2, -1):
            if lookup_curved2straight[p] == lookup_curved2straight[p - 1]:
                lookup_curved2straight[p] = 0
            else:
                break
        lookup_curved2straight = np.array(lookup_curved2straight)

        lookup_straight2curved = list(
            range(centerline_straight.number_of_points))
        if self.discs_input_filename != "":
            for index in range(centerline_straight.number_of_points):
                disc_label = centerline_straight.l_points[index]
                if alignment_mode == 'length':
                    relative_position = centerline_straight.dist_points[index]
                else:
                    relative_position = centerline_straight.dist_points_rel[
                        index]
                idx_closest = centerline.get_closest_to_absolute_position(
                    disc_label,
                    relative_position,
                    backup_index=index,
                    backup_centerline=centerline_straight,
                    mode=alignment_mode)
                if idx_closest is not None:
                    lookup_straight2curved[index] = idx_closest
        for p in range(0, len(lookup_straight2curved) // 2):
            if lookup_straight2curved[p] == lookup_straight2curved[p + 1]:
                lookup_straight2curved[p] = 0
            else:
                break
        for p in range(
                len(lookup_straight2curved) - 1,
                len(lookup_straight2curved) // 2, -1):
            if lookup_straight2curved[p] == lookup_straight2curved[p - 1]:
                lookup_straight2curved[p] = 0
            else:
                break
        lookup_straight2curved = np.array(lookup_straight2curved)

        # Create volumes containing curved and straight warping fields
        data_warp_curved2straight = np.zeros((nx_s, ny_s, nz_s, 1, 3))
        data_warp_straight2curved = np.zeros((nx, ny, nz, 1, 3))

        # 5. compute transformations
        # Curved and straight images and the same dimensions, so we compute both warping fields at the same time.
        # b. determine which plane of spinal cord centreline it is included
        # sct.printv(nx * ny * nz, nx_s * ny_s * nz_s)

        if self.curved2straight:
            for u in sct_progress_bar(range(nz_s)):
                x_s, y_s, z_s = np.mgrid[0:nx_s, 0:ny_s, u:u + 1]
                indexes_straight = np.array(
                    list(zip(x_s.ravel(), y_s.ravel(), z_s.ravel())))
                physical_coordinates_straight = image_centerline_straight.transfo_pix2phys(
                    indexes_straight)
                nearest_indexes_straight = centerline_straight.find_nearest_indexes(
                    physical_coordinates_straight)
                distances_straight = centerline_straight.get_distances_from_planes(
                    physical_coordinates_straight, nearest_indexes_straight)
                lookup = lookup_straight2curved[nearest_indexes_straight]
                indexes_out_distance_straight = np.logical_or(
                    np.logical_or(
                        distances_straight > self.threshold_distance,
                        distances_straight < -self.threshold_distance),
                    lookup == 0)
                projected_points_straight = centerline_straight.get_projected_coordinates_on_planes(
                    physical_coordinates_straight, nearest_indexes_straight)
                coord_in_planes_straight = centerline_straight.get_in_plans_coordinates(
                    projected_points_straight, nearest_indexes_straight)

                coord_straight2curved = centerline.get_inverse_plans_coordinates(
                    coord_in_planes_straight, lookup)
                displacements_straight = coord_straight2curved - physical_coordinates_straight
                # Invert Z coordinate as ITK & ANTs physical coordinate system is LPS- (RAI+)
                # while ours is LPI-
                # Refs: https://sourceforge.net/p/advants/discussion/840261/thread/2a1e9307/#fb5a
                #  https://www.slicer.org/wiki/Coordinate_systems
                displacements_straight[:, 2] = -displacements_straight[:, 2]
                displacements_straight[indexes_out_distance_straight] = [
                    100000.0, 100000.0, 100000.0
                ]

                data_warp_curved2straight[indexes_straight[:, 0], indexes_straight[:, 1], indexes_straight[:, 2], 0, :]\
                    = -displacements_straight

        if self.straight2curved:
            for u in sct_progress_bar(range(nz)):
                x, y, z = np.mgrid[0:nx, 0:ny, u:u + 1]
                indexes = np.array(list(zip(x.ravel(), y.ravel(), z.ravel())))
                physical_coordinates = image_centerline_pad.transfo_pix2phys(
                    indexes)
                nearest_indexes_curved = centerline.find_nearest_indexes(
                    physical_coordinates)
                distances_curved = centerline.get_distances_from_planes(
                    physical_coordinates, nearest_indexes_curved)
                lookup = lookup_curved2straight[nearest_indexes_curved]
                indexes_out_distance_curved = np.logical_or(
                    np.logical_or(distances_curved > self.threshold_distance,
                                  distances_curved < -self.threshold_distance),
                    lookup == 0)
                projected_points_curved = centerline.get_projected_coordinates_on_planes(
                    physical_coordinates, nearest_indexes_curved)
                coord_in_planes_curved = centerline.get_in_plans_coordinates(
                    projected_points_curved, nearest_indexes_curved)

                coord_curved2straight = centerline_straight.points[lookup]
                coord_curved2straight[:, 0:2] += coord_in_planes_curved[:, 0:2]
                coord_curved2straight[:, 2] += distances_curved

                displacements_curved = coord_curved2straight - physical_coordinates

                displacements_curved[:, 2] = -displacements_curved[:, 2]
                displacements_curved[indexes_out_distance_curved] = [
                    100000.0, 100000.0, 100000.0
                ]

                data_warp_straight2curved[indexes[:, 0], indexes[:, 1],
                                          indexes[:, 2],
                                          0, :] = -displacements_curved

        # Creation of the safe zone based on pre-calculated safe boundaries
        coord_bound_curved_inf, coord_bound_curved_sup = image_centerline_pad.transfo_phys2pix(
            [[0, 0, bound_curved[0]]]), image_centerline_pad.transfo_phys2pix(
                [[0, 0, bound_curved[1]]])
        coord_bound_straight_inf, coord_bound_straight_sup = image_centerline_straight.transfo_phys2pix(
            [[0, 0,
              bound_straight[0]]]), image_centerline_straight.transfo_phys2pix(
                  [[0, 0, bound_straight[1]]])

        if radius_safe > 0:
            data_warp_curved2straight[:, :, 0:coord_bound_straight_inf[0][2],
                                      0, :] = 100000.0
            data_warp_curved2straight[:, :, coord_bound_straight_sup[0][2]:,
                                      0, :] = 100000.0
            data_warp_straight2curved[:, :, 0:coord_bound_curved_inf[0][2],
                                      0, :] = 100000.0
            data_warp_straight2curved[:, :, coord_bound_curved_sup[0][2]:,
                                      0, :] = 100000.0

        # Generate warp files as a warping fields
        hdr_warp_s.set_intent('vector', (), '')
        hdr_warp_s.set_data_dtype('float32')
        hdr_warp.set_intent('vector', (), '')
        hdr_warp.set_data_dtype('float32')
        if self.curved2straight:
            img = Nifti1Image(data_warp_curved2straight, None, hdr_warp_s)
            save(img, 'tmp.curve2straight.nii.gz')
            logger.info('Warping field generated: tmp.curve2straight.nii.gz')

        if self.straight2curved:
            img = Nifti1Image(data_warp_straight2curved, None, hdr_warp)
            save(img, 'tmp.straight2curve.nii.gz')
            logger.info('Warping field generated: tmp.straight2curve.nii.gz')

        image_centerline_straight.save(fname_ref)
        if self.curved2straight:
            logger.info('Apply transformation to input image...')
            run_proc([
                'isct_antsApplyTransforms', '-d', '3', '-r', fname_ref, '-i',
                'data.nii', '-o', 'tmp.anat_rigid_warp.nii.gz', '-t',
                'tmp.curve2straight.nii.gz', '-n', 'BSpline[3]'
            ],
                     is_sct_binary=True,
                     verbose=verbose)

        if self.accuracy_results:
            time_accuracy_results = time.time()
            # compute the error between the straightened centerline/segmentation and the central vertical line.
            # Ideally, the error should be zero.
            # Apply deformation to input image
            logger.info('Apply transformation to centerline image...')
            run_proc([
                'isct_antsApplyTransforms', '-d', '3', '-r', fname_ref, '-i',
                'centerline.nii.gz', '-o', 'tmp.centerline_straight.nii.gz',
                '-t', 'tmp.curve2straight.nii.gz', '-n', 'NearestNeighbor'
            ],
                     is_sct_binary=True,
                     verbose=verbose)
            file_centerline_straight = Image('tmp.centerline_straight.nii.gz',
                                             verbose=verbose)
            nx, ny, nz, nt, px, py, pz, pt = file_centerline_straight.dim
            coordinates_centerline = file_centerline_straight.getNonZeroCoordinates(
                sorting='z')
            mean_coord = []
            for z in range(coordinates_centerline[0].z,
                           coordinates_centerline[-1].z):
                temp_mean = [
                    coord.value for coord in coordinates_centerline
                    if coord.z == z
                ]
                if temp_mean:
                    mean_value = np.mean(temp_mean)
                    mean_coord.append(
                        np.mean([[
                            coord.x * coord.value / mean_value,
                            coord.y * coord.value / mean_value
                        ] for coord in coordinates_centerline if coord.z == z],
                                axis=0))

            # compute error between the straightened centerline and the straight line.
            x0 = file_centerline_straight.data.shape[0] / 2.0
            y0 = file_centerline_straight.data.shape[1] / 2.0
            count_mean = 0
            if number_of_points >= 10:
                mean_c = mean_coord[
                    2:
                    -2]  # we don't include the four extrema because there are usually messy.
            else:
                mean_c = mean_coord
            for coord_z in mean_c:
                if not np.isnan(np.sum(coord_z)):
                    dist = ((x0 - coord_z[0]) * px)**2 + (
                        (y0 - coord_z[1]) * py)**2
                    self.mse_straightening += dist
                    dist = np.sqrt(dist)
                    if dist > self.max_distance_straightening:
                        self.max_distance_straightening = dist
                    count_mean += 1
            self.mse_straightening = np.sqrt(self.mse_straightening /
                                             float(count_mean))

            self.elapsed_time_accuracy = time.time() - time_accuracy_results

        os.chdir(curdir)

        # Generate output file (in current folder)
        # TODO: do not uncompress the warping field, it is too time consuming!
        logger.info('Generate output files...')
        if self.curved2straight:
            sct.generate_output_file(
                os.path.join(path_tmp, "tmp.curve2straight.nii.gz"),
                os.path.join(self.path_output, "warp_curve2straight.nii.gz"),
                verbose)
        if self.straight2curved:
            sct.generate_output_file(
                os.path.join(path_tmp, "tmp.straight2curve.nii.gz"),
                os.path.join(self.path_output, "warp_straight2curve.nii.gz"),
                verbose)

        # create ref_straight.nii.gz file that can be used by other SCT functions that need a straight reference space
        if self.curved2straight:
            sct.copy(os.path.join(path_tmp, "tmp.anat_rigid_warp.nii.gz"),
                     os.path.join(self.path_output, "straight_ref.nii.gz"))
            # move straightened input file
            if fname_output == '':
                fname_straight = sct.generate_output_file(
                    os.path.join(path_tmp, "tmp.anat_rigid_warp.nii.gz"),
                    os.path.join(self.path_output,
                                 file_anat + "_straight" + ext_anat), verbose)
            else:
                fname_straight = sct.generate_output_file(
                    os.path.join(path_tmp, "tmp.anat_rigid_warp.nii.gz"),
                    os.path.join(self.path_output, fname_output),
                    verbose)  # straightened anatomic

        # Remove temporary files
        if remove_temp_files:
            logger.info('Remove temporary files...')
            sct.rmtree(path_tmp)

        if self.accuracy_results:
            logger.info('Maximum x-y error: {} mm'.format(
                self.max_distance_straightening))
            logger.info('Accuracy of straightening (MSE): {} mm'.format(
                self.mse_straightening))

        # display elapsed time
        self.elapsed_time = int(np.round(time.time() - start_time))

        return fname_straight
Exemple #19
0
def register(param, file_src, file_dest, file_mat, file_out, im_mask=None):
    """
    Register two images by estimating slice-wise Tx and Ty transformations, which are regularized along Z. This function
    uses ANTs' isct_antsSliceRegularizedRegistration.

    :param param:
    :param file_src:
    :param file_dest:
    :param file_mat:
    :param file_out:
    :param im_mask: Image of mask, could be 2D or 3D
    :return:
    """

    # TODO: deal with mask

    # initialization
    failed_transfo = 0  # by default, failed matrix is 0 (i.e., no failure)
    do_registration = True

    # get metric radius (if MeanSquares, CC) or nb bins (if MI)
    if param.metric == 'MI':
        metric_radius = '16'
    else:
        metric_radius = '4'
    file_out_concat = file_out

    kw = dict()
    im_data = Image(
        file_src
    )  # TODO: pass argument to use antsReg instead of opening Image each time

    # register file_src to file_dest
    if param.todo == 'estimate' or param.todo == 'estimate_and_apply':
        # If orientation is sagittal, use antsRegistration in 2D mode
        # Note: the parameter --restrict-deformation is irrelevant with affine transfo

        if param.sampling == 'None':
            # 'None' sampling means 'fully dense' sampling
            # see https://github.com/ANTsX/ANTs/wiki/antsRegistration-reproducibility-issues
            sampling = param.sampling
        else:
            # param.sampling should be a float in [0,1], and means the
            # samplingPercentage that chooses a subset of points to
            # estimate from. We always use 'Regular' (evenly-spaced)
            # mode, though antsRegistration offers 'Random' as well.
            # Be aware: even 'Regular' is not fully deterministic:
            # > Regular includes a random perturbation on the grid sampling
            # - https://github.com/ANTsX/ANTs/issues/976#issuecomment-602313884
            sampling = 'Regular,' + param.sampling

        if im_data.orientation[2] in 'LR':
            cmd = [
                'isct_antsRegistration', '-d', '2', '--transform',
                'Affine[%s]' % param.gradStep, '--metric',
                param.metric + '[' + file_dest + ',' + file_src + ',1,' +
                metric_radius + ',' + sampling + ']', '--convergence',
                param.iter, '--shrink-factors', '1', '--smoothing-sigmas',
                param.smooth, '--verbose', '1', '--output',
                '[' + file_mat + ',' + file_out_concat + ']'
            ]
            cmd += sct.get_interpolation('isct_antsRegistration', param.interp)
            if im_mask is not None:
                # if user specified a mask, make sure there are non-null voxels in the image before running the registration
                if np.count_nonzero(im_mask.data):
                    cmd += ['--masks', im_mask.absolutepath]
                else:
                    # Mask only contains zeros. Copying the image instead of estimating registration.
                    sct.copy(file_src, file_out_concat, verbose=0)
                    do_registration = False
                    # TODO: create affine mat file with identity, in case used by -g 2
        # 3D mode
        else:
            cmd = [
                'isct_antsSliceRegularizedRegistration', '--polydegree',
                param.poly, '--transform',
                'Translation[%s]' % param.gradStep, '--metric',
                param.metric + '[' + file_dest + ',' + file_src + ',1,' +
                metric_radius + ',' + sampling + ']', '--iterations',
                param.iter, '--shrinkFactors', '1', '--smoothingSigmas',
                param.smooth, '--verbose', '1', '--output',
                '[' + file_mat + ',' + file_out_concat + ']'
            ]
            cmd += sct.get_interpolation(
                'isct_antsSliceRegularizedRegistration', param.interp)
            if im_mask is not None:
                cmd += ['--mask', im_mask.absolutepath]
        # run command
        if do_registration:
            kw.update(dict(is_sct_binary=True))
            # reducing the number of CPU used for moco (see issue #201 and #2642)
            env = {
                **os.environ,
                **{
                    "ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS": "1"
                }
            }
            status, output = run_proc(cmd,
                                      verbose=1 if param.verbose == 2 else 0,
                                      env=env,
                                      **kw)

    elif param.todo == 'apply':
        sct_apply_transfo.main(args=[
            '-i', file_src, '-d', file_dest, '-w', file_mat + param.suffix_mat,
            '-o', file_out_concat, '-x', param.interp, '-v', '0'
        ])

    # check if output file exists
    # Note (from JCA): In the past, i've tried to catch non-zero output from ANTs function (via the 'status' variable),
    # but in some OSs, the function can fail while outputing zero. So as a pragmatic approach, I decided to go with
    # the "output file checking" approach, which is 100% sensitive.
    if not os.path.isfile(file_out_concat):
        # sct.printv(output, verbose, 'error')
        sct.printv(
            'WARNING in ' + os.path.basename(__file__) +
            ': No output. Maybe related to improper calculation of '
            'mutual information. Either the mask you provided is '
            'too small, or the subject moved a lot. If you see too '
            'many messages like this try with a bigger mask. '
            'Using previous transformation for this volume (if it'
            'exists).', param.verbose, 'warning')
        failed_transfo = 1

    # If sagittal, copy header (because ANTs screws it) and add singleton in 3rd dimension (for z-concatenation)
    if im_data.orientation[2] in 'LR' and do_registration:
        im_out = Image(file_out_concat)
        im_out.header = im_data.header
        im_out.data = np.expand_dims(im_out.data, 2)
        im_out.save(file_out, verbose=0)

    # return status of failure
    return failed_transfo
Exemple #20
0
def main(args=None):
    # initialize parameters
    param = Param()
    # call main function
    parser = get_parser()
    if args:
        arguments = parser.parse_args(args)
    else:
        arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help'])

    fname_data = arguments.i
    fname_bvecs = arguments.bvec
    average = arguments.a
    verbose = int(arguments.v)
    init_sct(log_level=verbose, update=True)  # Update log level
    remove_temp_files = arguments.r
    path_out = arguments.ofolder

    fname_bvals = arguments.bval
    if arguments.bvalmin:
        param.bval_min = arguments.bvalmin

    # Initialization
    start_time = time.time()

    # sct.printv(arguments)
    sct.printv('\nInput parameters:', verbose)
    sct.printv('  input file ............' + fname_data, verbose)
    sct.printv('  bvecs file ............' + fname_bvecs, verbose)
    sct.printv('  bvals file ............' + fname_bvals, verbose)
    sct.printv('  average ...............' + str(average), verbose)

    # Get full path
    fname_data = os.path.abspath(fname_data)
    fname_bvecs = os.path.abspath(fname_bvecs)
    if fname_bvals:
        fname_bvals = os.path.abspath(fname_bvals)

    # Extract path, file and extension
    path_data, file_data, ext_data = sct.extract_fname(fname_data)

    # create temporary folder
    path_tmp = sct.tmp_create(basename="dmri_separate", verbose=verbose)

    # copy files into tmp folder and convert to nifti
    sct.printv('\nCopy files into temporary folder...', verbose)
    ext = '.nii'
    dmri_name = 'dmri'
    b0_name = file_data + '_b0'
    b0_mean_name = b0_name + '_mean'
    dwi_name = file_data + '_dwi'
    dwi_mean_name = dwi_name + '_mean'

    if not convert(fname_data, os.path.join(path_tmp, dmri_name + ext)):
        sct.printv('ERROR in convert.', 1, 'error')
    sct.copy(fname_bvecs, os.path.join(path_tmp, "bvecs"), verbose=verbose)

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

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

    # Identify b=0 and DWI images
    sct.printv(fname_bvals)
    index_b0, index_dwi, nb_b0, nb_dwi = identify_b0(fname_bvecs, fname_bvals, param.bval_min, verbose)

    # Split into T dimension
    sct.printv('\nSplit along T dimension...', verbose)
    im_dmri_split_list = split_data(im_dmri, 3)
    for im_d in im_dmri_split_list:
        im_d.save()

    # Merge b=0 images
    sct.printv('\nMerge b=0...', verbose)
    from sct_image import concat_data
    l = []
    for it in range(nb_b0):
        l.append(dmri_name + '_T' + str(index_b0[it]).zfill(4) + ext)
    im_out = concat_data(l, 3).save(b0_name + ext)

    # Average b=0 images
    if average:
        sct.printv('\nAverage b=0...', verbose)
        run_proc(['sct_maths', '-i', b0_name + ext, '-o', b0_mean_name + ext, '-mean', 't'], verbose)

    # Merge DWI
    l = []
    for it in range(nb_dwi):
        l.append(dmri_name + '_T' + str(index_dwi[it]).zfill(4) + ext)
    im_out = concat_data(l, 3).save(dwi_name + ext)

    # Average DWI images
    if average:
        sct.printv('\nAverage DWI...', verbose)
        run_proc(['sct_maths', '-i', dwi_name + ext, '-o', dwi_mean_name + ext, '-mean', 't'], verbose)

    # come back
    os.chdir(curdir)

    # Generate output files
    fname_b0 = os.path.abspath(os.path.join(path_out, b0_name + ext_data))
    fname_dwi = os.path.abspath(os.path.join(path_out, dwi_name + ext_data))
    fname_b0_mean = os.path.abspath(os.path.join(path_out, b0_mean_name + ext_data))
    fname_dwi_mean = os.path.abspath(os.path.join(path_out, dwi_mean_name + ext_data))
    sct.printv('\nGenerate output files...', verbose)
    sct.generate_output_file(os.path.join(path_tmp, b0_name + ext), fname_b0, verbose=verbose)
    sct.generate_output_file(os.path.join(path_tmp, dwi_name + ext), fname_dwi, verbose=verbose)
    if average:
        sct.generate_output_file(os.path.join(path_tmp, b0_mean_name + ext), fname_b0_mean, verbose=verbose)
        sct.generate_output_file(os.path.join(path_tmp, dwi_mean_name + ext), fname_dwi_mean, verbose=verbose)

    # Remove temporary files
    if remove_temp_files == 1:
        sct.printv('\nRemove 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)

    return fname_b0, fname_b0_mean, fname_dwi, fname_dwi_mean
def propseg(img_input, options_dict):
    """
    :param img_input: source image, to be segmented
    :param options_dict: arguments as dictionary
    :return: segmented Image
    """
    arguments = options_dict
    fname_input_data = img_input.absolutepath
    fname_data = os.path.abspath(fname_input_data)
    contrast_type = arguments.c
    contrast_type_conversion = {
        't1': 't1',
        't2': 't2',
        't2s': 't2',
        'dwi': 't1'
    }
    contrast_type_propseg = contrast_type_conversion[contrast_type]

    # Starting building the command
    cmd = ['isct_propseg', '-t', contrast_type_propseg]

    if arguments.ofolder is not None:
        folder_output = arguments.ofolder
    else:
        folder_output = './'
    cmd += ['-o', folder_output]
    if not os.path.isdir(folder_output) and os.path.exists(folder_output):
        logger.error("output directory %s is not a valid directory" %
                     folder_output)
    if not os.path.exists(folder_output):
        os.makedirs(folder_output)

    if arguments.down is not None:
        cmd += ["-down", str(arguments.down)]
    if arguments.up is not None:
        cmd += ["-up", str(arguments.up)]

    remove_temp_files = arguments.r

    verbose = int(arguments.v)
    init_sct(log_level=verbose, update=True)  # Update log level
    # Update for propseg binary
    if verbose > 0:
        cmd += ["-verbose"]

    # Output options
    if arguments.mesh is not None:
        cmd += ["-mesh"]
    if arguments.centerline_binary is not None:
        cmd += ["-centerline-binary"]
    if arguments.CSF is not None:
        cmd += ["-CSF"]
    if arguments.centerline_coord is not None:
        cmd += ["-centerline-coord"]
    if arguments.cross is not None:
        cmd += ["-cross"]
    if arguments.init_tube is not None:
        cmd += ["-init-tube"]
    if arguments.low_resolution_mesh is not None:
        cmd += ["-low-resolution-mesh"]
    # TODO: Not present. Why is this here? Was this renamed?
    # if arguments.detect_nii is not None:
    #     cmd += ["-detect-nii"]
    # TODO: Not present. Why is this here? Was this renamed?
    # if arguments.detect_png is not None:
    #     cmd += ["-detect-png"]

    # Helping options
    use_viewer = None
    use_optic = True  # enabled by default
    init_option = None
    rescale_header = arguments.rescale
    if arguments.init is not None:
        init_option = float(arguments.init)
        if init_option < 0:
            sct.printv(
                'Command-line usage error: ' + str(init_option) +
                " is not a valid value for '-init'", 1, 'error')
            sys.exit(1)
    if arguments.init_centerline is not None:
        if str(arguments.init_centerline) == "viewer":
            use_viewer = "centerline"
        elif str(arguments.init_centerline) == "hough":
            use_optic = False
        else:
            if rescale_header is not 1:
                fname_labels_viewer = func_rescale_header(str(
                    arguments.init_centerline),
                                                          rescale_header,
                                                          verbose=verbose)
            else:
                fname_labels_viewer = str(arguments.init_centerline)
            cmd += ["-init-centerline", fname_labels_viewer]
            use_optic = False
    if arguments.init_mask is not None:
        if str(arguments.init_mask) == "viewer":
            use_viewer = "mask"
        else:
            if rescale_header is not 1:
                fname_labels_viewer = func_rescale_header(
                    str(arguments.init_mask), rescale_header)
            else:
                fname_labels_viewer = str(arguments.init_mask)
            cmd += ["-init-mask", fname_labels_viewer]
            use_optic = False
    if arguments.mask_correction is not None:
        cmd += ["-mask-correction", str(arguments.mask_correction)]
    if arguments.radius is not None:
        cmd += ["-radius", str(arguments.radius)]
    # TODO: Not present. Why is this here? Was this renamed?
    # if arguments.detect_n is not None:
    #     cmd += ["-detect-n", str(arguments.detect_n)]
    # TODO: Not present. Why is this here? Was this renamed?
    # if arguments.detect_gap is not None:
    #     cmd += ["-detect-gap", str(arguments.detect_gap)]
    # TODO: Not present. Why is this here? Was this renamed?
    # if arguments.init_validation is not None:
    #     cmd += ["-init-validation"]
    if arguments.nbiter is not None:
        cmd += ["-nbiter", str(arguments.nbiter)]
    if arguments.max_area is not None:
        cmd += ["-max-area", str(arguments.max_area)]
    if arguments.max_deformation is not None:
        cmd += ["-max-deformation", str(arguments.max_deformation)]
    if arguments.min_contrast is not None:
        cmd += ["-min-contrast", str(arguments.min_contrast)]
    if arguments.d is not None:
        cmd += ["-d", str(arguments["-d"])]
    if arguments.distance_search is not None:
        cmd += ["-dsearch", str(arguments.distance_search)]
    if arguments.alpha is not None:
        cmd += ["-alpha", str(arguments.alpha)]

    # check if input image is in 3D. Otherwise itk image reader will cut the 4D image in 3D volumes and only take the first one.
    image_input = Image(fname_data)
    image_input_rpi = image_input.copy().change_orientation('RPI')
    nx, ny, nz, nt, px, py, pz, pt = image_input_rpi.dim
    if nt > 1:
        sct.printv(
            'ERROR: your input image needs to be 3D in order to be segmented.',
            1, 'error')

    path_data, file_data, ext_data = sct.extract_fname(fname_data)
    path_tmp = sct.tmp_create(basename="label_vertebrae", verbose=verbose)

    # rescale header (see issue #1406)
    if rescale_header is not 1:
        fname_data_propseg = func_rescale_header(fname_data, rescale_header)
    else:
        fname_data_propseg = fname_data

    # add to command
    cmd += ['-i', fname_data_propseg]

    # if centerline or mask is asked using viewer
    if use_viewer:
        from spinalcordtoolbox.gui.base import AnatomicalParams
        from spinalcordtoolbox.gui.centerline import launch_centerline_dialog

        params = AnatomicalParams()
        if use_viewer == 'mask':
            params.num_points = 3
            params.interval_in_mm = 15  # superior-inferior interval between two consecutive labels
            params.starting_slice = 'midfovminusinterval'
        if use_viewer == 'centerline':
            # setting maximum number of points to a reasonable value
            params.num_points = 20
            params.interval_in_mm = 30
            params.starting_slice = 'top'
        im_data = Image(fname_data_propseg)

        im_mask_viewer = msct_image.zeros_like(im_data)
        # im_mask_viewer.absolutepath = sct.add_suffix(fname_data_propseg, '_labels_viewer')
        controller = launch_centerline_dialog(im_data, im_mask_viewer, params)
        fname_labels_viewer = sct.add_suffix(fname_data_propseg,
                                             '_labels_viewer')

        if not controller.saved:
            sct.printv(
                'The viewer has been closed before entering all manual points. Please try again.',
                1, 'error')
            sys.exit(1)
        # save labels
        controller.as_niftii(fname_labels_viewer)

        # add mask filename to parameters string
        if use_viewer == "centerline":
            cmd += ["-init-centerline", fname_labels_viewer]
        elif use_viewer == "mask":
            cmd += ["-init-mask", fname_labels_viewer]

    # If using OptiC
    elif use_optic:
        image_centerline = optic.detect_centerline(image_input, contrast_type,
                                                   verbose)
        fname_centerline_optic = os.path.join(path_tmp,
                                              'centerline_optic.nii.gz')
        image_centerline.save(fname_centerline_optic)
        cmd += ["-init-centerline", fname_centerline_optic]

    if init_option is not None:
        if init_option > 1:
            init_option /= (nz - 1)
        cmd += ['-init', str(init_option)]

    # enabling centerline extraction by default (needed by check_and_correct_segmentation() )
    cmd += ['-centerline-binary']

    # run propseg
    status, output = run_proc(cmd,
                              verbose,
                              raise_exception=False,
                              is_sct_binary=True)

    # check status is not 0
    if not status == 0:
        sct.printv(
            'Automatic cord detection failed. Please initialize using -init-centerline or -init-mask (see help)',
            1, 'error')
        sys.exit(1)

    # build output filename
    fname_seg = os.path.join(
        folder_output, os.path.basename(sct.add_suffix(fname_data, "_seg")))
    fname_centerline = os.path.join(
        folder_output,
        os.path.basename(sct.add_suffix(fname_data, "_centerline")))
    # in case header was rescaled, we need to update the output file names by removing the "_rescaled"
    if rescale_header is not 1:
        sct.mv(
            os.path.join(
                folder_output,
                sct.add_suffix(os.path.basename(fname_data_propseg), "_seg")),
            fname_seg)
        sct.mv(
            os.path.join(
                folder_output,
                sct.add_suffix(os.path.basename(fname_data_propseg),
                               "_centerline")), fname_centerline)
        # if user was used, copy the labelled points to the output folder (they will then be scaled back)
        if use_viewer:
            fname_labels_viewer_new = os.path.join(
                folder_output,
                os.path.basename(sct.add_suffix(fname_data, "_labels_viewer")))
            sct.copy(fname_labels_viewer, fname_labels_viewer_new)
            # update variable (used later)
            fname_labels_viewer = fname_labels_viewer_new

    # check consistency of segmentation
    if arguments.correct_seg:
        check_and_correct_segmentation(fname_seg,
                                       fname_centerline,
                                       folder_output=folder_output,
                                       threshold_distance=3.0,
                                       remove_temp_files=remove_temp_files,
                                       verbose=verbose)

    # copy header from input to segmentation to make sure qform is the same
    sct.printv(
        "Copy header input --> output(s) to make sure qform is the same.",
        verbose)
    list_fname = [fname_seg, fname_centerline]
    if use_viewer:
        list_fname.append(fname_labels_viewer)
    for fname in list_fname:
        im = Image(fname)
        im.header = image_input.header
        im.save(dtype='int8'
                )  # they are all binary masks hence fine to save as int8

    return Image(fname_seg)
    def apply(self):
        # Initialization
        fname_src = self.input_filename  # source image (moving)
        list_warp = self.list_warp  # list of warping fields
        fname_out = self.output_filename  # output
        fname_dest = self.fname_dest  # destination image (fix)
        verbose = self.verbose
        remove_temp_files = self.remove_temp_files
        crop_reference = self.crop  # if = 1, put 0 everywhere around warping field, if = 2, real crop

        islabel = False
        if self.interp == 'label':
            islabel = True
            self.interp = 'nn'

        interp = sct.get_interpolation('isct_antsApplyTransforms', self.interp)

        # Parse list of warping fields
        sct.printv('\nParse list of warping fields...', verbose)
        use_inverse = []
        fname_warp_list_invert = []
        # list_warp = list_warp.replace(' ', '')  # remove spaces
        # list_warp = list_warp.split(",")  # parse with comma
        for idx_warp, path_warp in enumerate(self.list_warp):
            # Check if this transformation should be inverted
            if path_warp in self.list_warpinv:
                use_inverse.append('-i')
                # list_warp[idx_warp] = path_warp[1:]  # remove '-'
                fname_warp_list_invert += [[use_inverse[idx_warp], list_warp[idx_warp]]]
            else:
                use_inverse.append('')
                fname_warp_list_invert += [[path_warp]]
            path_warp = list_warp[idx_warp]
            if path_warp.endswith((".nii", ".nii.gz")) \
             and Image(list_warp[idx_warp]).header.get_intent()[0] != 'vector':
                raise ValueError("Displacement field in {} is invalid: should be encoded" \
                 " in a 5D file with vector intent code" \
                 " (see https://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1.h" \
                 .format(path_warp))
        # need to check if last warping field is an affine transfo
        isLastAffine = False
        path_fname, file_fname, ext_fname = sct.extract_fname(fname_warp_list_invert[-1][-1])
        if ext_fname in ['.txt', '.mat']:
            isLastAffine = True

        ## check if destination file is 3d
        # sct.check_dim(fname_dest, dim_lst=[3]) # PR 2598: we decided to skip this line.

        # N.B. Here we take the inverse of the warp list, because sct_WarpImageMultiTransform concatenates in the reverse order
        fname_warp_list_invert.reverse()
        fname_warp_list_invert = functools.reduce(lambda x, y: x + y, fname_warp_list_invert)

        # Extract path, file and extension
        path_src, file_src, ext_src = sct.extract_fname(fname_src)
        path_dest, file_dest, ext_dest = sct.extract_fname(fname_dest)

        # Get output folder and file name
        if fname_out == '':
            path_out = ''  # output in user's current directory
            file_out = file_src + '_reg'
            ext_out = ext_src
            fname_out = os.path.join(path_out, file_out + ext_out)

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

        # if 3d
        if nt == 1:
            # Apply transformation
            sct.printv('\nApply transformation...', verbose)
            if nz in [0, 1]:
                dim = '2'
            else:
                dim = '3'
            # if labels, dilate before resampling
            if islabel:
                sct.printv("\nDilate labels before warping...")
                path_tmp = sct.tmp_create(basename="apply_transfo", verbose=verbose)
                fname_dilated_labels = os.path.join(path_tmp, "dilated_data.nii")
                # dilate points
                dilate(Image(fname_src), 2, 'ball').save(fname_dilated_labels)
                fname_src = fname_dilated_labels

            sct.printv("\nApply transformation and resample to destination space...", verbose)
            run_proc(['isct_antsApplyTransforms',
                     '-d', dim,
                     '-i', fname_src,
                     '-o', fname_out,
                     '-t'
                     ] + fname_warp_list_invert + ['-r', fname_dest] + interp, verbose=verbose, is_sct_binary=True)

        # if 4d, loop across the T dimension
        else:
            if islabel:
                raise NotImplementedError

            dim = '4'
            path_tmp = sct.tmp_create(basename="apply_transfo", verbose=verbose)

            # convert to nifti into temp folder
            sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose)
            img_src.save(os.path.join(path_tmp, "data.nii"))
            sct.copy(fname_dest, os.path.join(path_tmp, file_dest + ext_dest))
            fname_warp_list_tmp = []
            for fname_warp in list_warp:
                path_warp, file_warp, ext_warp = sct.extract_fname(fname_warp)
                sct.copy(fname_warp, os.path.join(path_tmp, file_warp + ext_warp))
                fname_warp_list_tmp.append(file_warp + ext_warp)
            fname_warp_list_invert_tmp = fname_warp_list_tmp[::-1]

            curdir = os.getcwd()
            os.chdir(path_tmp)

            # split along T dimension
            sct.printv('\nSplit along T dimension...', verbose)

            im_dat = Image('data.nii')
            im_header = im_dat.hdr
            data_split_list = sct_image.split_data(im_dat, 3)
            for im in data_split_list:
                im.save()

            # apply transfo
            sct.printv('\nApply transformation to each 3D volume...', verbose)
            for it in range(nt):
                file_data_split = 'data_T' + str(it).zfill(4) + '.nii'
                file_data_split_reg = 'data_reg_T' + str(it).zfill(4) + '.nii'

                status, output = run_proc(['isct_antsApplyTransforms',
                                          '-d', '3',
                                          '-i', file_data_split,
                                          '-o', file_data_split_reg,
                                          '-t',
                                          ] + fname_warp_list_invert_tmp + [
                    '-r', file_dest + ext_dest,
                ] + interp, verbose, is_sct_binary=True)

            # Merge files back
            sct.printv('\nMerge file back...', verbose)
            import glob
            path_out, name_out, ext_out = sct.extract_fname(fname_out)
            # im_list = [Image(file_name) for file_name in glob.glob('data_reg_T*.nii')]
            # concat_data use to take a list of image in input, now takes a list of file names to open the files one by one (see issue #715)
            fname_list = glob.glob('data_reg_T*.nii')
            fname_list.sort()
            im_out = sct_image.concat_data(fname_list, 3, im_header['pixdim'])
            im_out.save(name_out + ext_out)

            os.chdir(curdir)
            sct.generate_output_file(os.path.join(path_tmp, name_out + ext_out), fname_out)
            # Delete temporary folder if specified
            if remove_temp_files:
                sct.printv('\nRemove temporary files...', verbose)
                sct.rmtree(path_tmp, verbose=verbose)

        # Copy affine matrix from destination space to make sure qform/sform are the same
        sct.printv("Copy affine matrix from destination space to make sure qform/sform are the same.", verbose)
        im_src_reg = Image(fname_out)
        im_src_reg.copy_qform_from_ref(Image(fname_dest))
        im_src_reg.save(verbose=0)  # set verbose=0 to avoid warning message about rewriting file

        if islabel:
            sct.printv("\nTake the center of mass of each registered dilated labels...")
            labeled_img = cubic_to_point(im_src_reg)
            labeled_img.save(path=fname_out)
            if remove_temp_files:
                sct.printv('\nRemove temporary files...', verbose)
                sct.rmtree(path_tmp, verbose=verbose)

        # Crop the resulting image using dimensions from the warping field
        warping_field = fname_warp_list_invert[-1]
        # If the last transformation is not an affine transfo, we need to compute the matrix space of the concatenated
        # warping field
        if not isLastAffine and crop_reference in [1, 2]:
            sct.printv('Last transformation is not affine.')
            if crop_reference in [1, 2]:
                # Extract only the first ndim of the warping field
                img_warp = Image(warping_field)
                if dim == '2':
                    img_warp_ndim = Image(img_src.data[:, :], hdr=img_warp.hdr)
                elif dim in ['3', '4']:
                    img_warp_ndim = Image(img_src.data[:, :, :], hdr=img_warp.hdr)
                # Set zero to everything outside the warping field
                cropper = ImageCropper(Image(fname_out))
                cropper.get_bbox_from_ref(img_warp_ndim)
                if crop_reference == 1:
                    sct.printv('Cropping strategy is: keep same matrix size, put 0 everywhere around warping field')
                    img_out = cropper.crop(background=0)
                elif crop_reference == 2:
                    sct.printv('Cropping strategy is: crop around warping field (the size of warping field will '
                               'change)')
                    img_out = cropper.crop()
                img_out.save(fname_out)

        sct.display_viewer_syntax([fname_dest, fname_out], verbose=verbose)
def test_function(param_test):
    """

    Parameters
    ----------
    file_testing

    Returns
    -------
    path_output str: path where to output testing data
    """

    # load modules of function to test
    module_function_to_test = importlib.import_module(
        param_test.function_to_test)
    module_testing = importlib.import_module('test_' +
                                             param_test.function_to_test)

    # retrieve subject name
    subject_folder = os.path.basename(param_test.path_data)

    # build path_output variable
    path_testing = os.getcwd()

    # if not param_test.path_output:
    #     param_test.path_output = tmp_create(basename=(param_test.function_to_test + '_' + subject_folder), verbose=0)
    # elif not os.path.isdir(param_test.path_output):
    #     os.makedirs(param_test.path_output)

    # # get parser information
    # parser = module_function_to_test.get_parser()
    # if '-ofolder' in parser.options and '-ofolder' not in param_test.args:
    #     param_test.args += " -ofolder " + param_test.path_output
    #
    # dict_args = parser.parse(shlex.split(param_test.args), check_file_exist=False)
    # # TODO: if file in list does not exist, raise exception and assign status=200
    # # add data path to each input argument
    # dict_args_with_path = parser.add_path_to_file(copy.deepcopy(dict_args), param_test.path_data, input_file=True)
    # # add data path to each output argument
    # dict_args_with_path = parser.add_path_to_file(copy.deepcopy(dict_args_with_path), param_test.path_output, input_file=False, output_file=True)
    # # save into class
    # param_test.dict_args_with_path = dict_args_with_path
    # param_test.args_with_path = parser.dictionary_to_string(dict_args_with_path)
    #
    # initialize panda dataframe
    param_test.results = DataFrame(index=[subject_folder],
                                   data={
                                       'status': 0,
                                       'duration': 0,
                                       'output': '',
                                       'path_data': param_test.path_data,
                                       'path_output': param_test.path_output
                                   })
    #
    # # retrieve input file (will be used later for integrity testing)00
    # if '-i' in dict_args:
    #     # check if list in case of multiple input files
    #     if not isinstance(dict_args_with_path['-i'], list):
    #         list_file_to_check = [dict_args_with_path['-i']]
    #         # assign field file_input for integrity testing
    #         param_test.file_input = dict_args['-i'].split('/')[-1]
    #         # update index of dataframe by appending file name for more clarity
    #         param_test.results = param_test.results.rename({subject_folder: os.path.join(subject_folder, dict_args['-i'])})
    #     else:
    #         list_file_to_check = dict_args_with_path['-i']
    #         # TODO: assign field file_input for integrity testing
    #     for file_to_check in list_file_to_check:
    #         # file_input = file_to_check.split('/')[1]
    #         # Check if input files exist
    #         if not (os.path.isfile(file_to_check)):
    #             param_test.status = 200
    #             param_test.output += '\nERROR: This input file does not exist: ' + file_to_check
    #             return update_param(param_test)
    #
    # # retrieve ground truth (will be used later for integrity testing)
    # if '-igt' in dict_args:
    #     param_test.fname_gt = dict_args_with_path['-igt']
    #     # Check if ground truth files exist
    #     if not os.path.isfile(param_test.fname_gt):
    #         param_test.status = 201
    #         param_test.output += '\nERROR: The following file used for ground truth does not exist: ' + param_test.fname_gt
    #         return update_param(param_test)

    # run command
    cmd = ' '.join([param_test.function_to_test, param_test.args])
    # param_test.output += '\nWill run in %s:' % (os.path.join(path_testing, param_test.path_output))
    param_test.output += '\n====================================================================================================\n' + cmd + '\n====================================================================================================\n\n'  # copy command
    time_start = time.time()
    try:
        # os.chdir(param_test.path_output)
        # if not os.path.exists(param_test.path_output):
        #     # in case of relative path, we want a subfolder too
        #     os.makedirs(param_test.path_output)
        # os.chdir(path_testing)
        param_test.status, o = run_proc(cmd, verbose=0)
        if param_test.status:
            raise Exception
    except Exception as err:
        param_test.status = 1
        param_test.output += str(err)
        return update_param(param_test)

    param_test.output += o
    param_test.results['duration'] = time.time() - time_start

    # test integrity
    if param_test.test_integrity:
        param_test.output += '\n\n====================================================================================================\n' + 'INTEGRITY TESTING' + '\n====================================================================================================\n\n'  # copy command
        try:
            # os.chdir(param_test.path_output)
            param_test = module_testing.test_integrity(param_test)
            # os.chdir(path_testing)
        except Exception as err:
            # os.chdir(path_testing)
            param_test.status = 2
            param_test.output += str(err)
            return update_param(param_test)

    return update_param(param_test)
def main(args=None):

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

    # check user arguments
    parser = get_parser()
    if args:
        arguments = parser.parse_args(args)
    else:
        arguments = parser.parse_args(
            args=None if sys.argv[1:] else ['--help'])

    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
    path_output = arguments.ofolder
    param.path_qc = arguments.qc
    if arguments.discfile is not None:
        fname_disc = os.path.abspath(arguments.discfile)
    else:
        fname_disc = None
    if arguments.initz is not None:
        initz = arguments.initz
    if arguments.initcenter is not None:
        initcenter = arguments.initcenter
    # if user provided text file, parse and overwrite arguments
    if arguments.initfile is not None:
        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 arguments.initlabel is not None:
        # get absolute path of label
        fname_initlabel = os.path.abspath(arguments.initlabel)
    if arguments.param is not None:
        param.update(arguments.param[0])
    verbose = int(arguments.v)
    init_sct(log_level=verbose, update=True)  # Update log level
    remove_temp_files = arguments.r
    denoise = arguments.denoise
    laplacian = 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 = run_proc([
            '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 = run_proc([
        '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)
    run_proc(
        '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
    run_proc([
        '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)
        run_proc(
            '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]

            im_label = create_labels_along_segmentation(
                Image('segmentation.nii'), [(initz[0], initz[1])])
            im_label.data = dilate(im_label.data, 3, 'ball')
            im_label.save(fname_labelz)

        elif fname_initlabel:
            Image(fname_initlabel).save(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:
                im_label_c2c3.data[ind_label] = 3
            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
        dilate(Image(fname_labelz), 3, 'ball').save(fname_labelz)

        # Apply straightening to z-label
        sct.printv('\nAnd apply straightening to label...', verbose)
        run_proc(
            '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)
            run_proc([
                '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)
            run_proc([
                'sct_maths', '-i', 'data_straightr.nii', '-laplacian', '1',
                '-o', 'data_straightr.nii'
            ], verbose)

        # detect vertebral levels on straight spinal cord
        init_disc[1] = init_disc[1] - 1
        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)
    run_proc(
        '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=verbose)
    sct.generate_output_file(os.path.join(path_tmp,
                                          "warp_straight2curve.nii.gz"),
                             os.path.join(path_output,
                                          "warp_straight2curve.nii.gz"),
                             verbose=verbose)
    sct.generate_output_file(os.path.join(path_tmp, "straight_ref.nii.gz"),
                             os.path.join(path_output, "straight_ref.nii.gz"),
                             verbose=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(arguments.qc)
        qc_dataset = arguments.qc_dataset
        qc_subject = arguments.qc_subject
        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'])
Exemple #25
0
def detect_centerline(img, contrast, verbose=1):
    """Detect spinal cord centerline using OptiC.

    :param img: input Image() object.
    :param contrast: str: The type of contrast. Will define the path to Optic model.
    :returns: Image(): Output centerline
    """

    # Fetch path to Optic model based on contrast
    optic_models_path = sct_dir_local_path('data', 'optic_models',
                                           '{}_model'.format(contrast))

    logger.debug('Detecting the spinal cord using OptiC')
    img_orientation = img.orientation

    temp_folder = sct.TempFolder()
    temp_folder.chdir()

    # convert image data type to int16, as required by opencv (backend in OptiC)
    img_int16 = img.copy()
    # Replace non-numeric values by zero
    img_data = img.data
    img_data[np.where(np.isnan(img_data))] = 0
    img_data[np.where(np.isinf(img_data))] = 0
    img_int16.data[np.where(np.isnan(img_int16.data))] = 0
    img_int16.data[np.where(np.isinf(img_int16.data))] = 0
    # 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.change_type(np.uint16)
    # reorient the input image to RPI + convert to .nii
    img_int16.change_orientation('RPI')
    file_img = 'img_rpi_uint16'
    img_int16.save(file_img + '.nii')

    # call the OptiC method to generate the spinal cord centerline
    optic_input = file_img
    optic_filename = file_img + '_optic'
    os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR"
    cmd_optic = [
        'isct_spine_detect',
        '-ctype=dpdt',
        '-lambda=1',
        optic_models_path,
        optic_input,
        optic_filename,
    ]
    # TODO: output coordinates, for each slice, in continuous (not discrete) values.

    run_proc(cmd_optic, is_sct_binary=True, verbose=0)

    # convert .img and .hdr files to .nii.gz
    img_ctl = Image(file_img + '_optic_ctr.hdr')
    img_ctl.change_orientation(img_orientation)

    # return to initial folder
    temp_folder.chdir_undo()
    if verbose < 2:
        logger.info("Remove temporary files...")
        temp_folder.cleanup()

    return img_ctl