Exemple #1
0
def test(data_path):

    # parameters
    folder_data = [ 'template/template/', 't2/', 'dmri/']
    file_data = [get_file_label(data_path+'template/template/','T2-weighted'),
                 't2.nii.gz',
                 'warp_template2anat.nii.gz',
                 'dmri.nii.gz']

    output = ''
    status = 0

    # test function
    cmd = 'sct_apply_transfo -i ' + data_path + folder_data[0] + file_data[0] \
          + ' -d ' + data_path + folder_data[1] + file_data[1] \
          + ' -w ' + data_path + folder_data[1] + file_data[2]
    output += cmd+'\n'  # copy command
    s, o = commands.getstatusoutput(cmd)
    status += s
    output += o

    # test with 4d input
    cmd = 'sct_apply_transfo -i ' + data_path + folder_data[2] + file_data[3] \
          + ' -d ' + data_path + folder_data[1] + file_data[1] \
          + ' -w ' + data_path + folder_data[1] + file_data[2]
    output += cmd+'\n'  # copy command
    s, o = commands.getstatusoutput(cmd)
    status += s
    output += o

    # return
    #return sct.run(cmd, 0)
    return status, output
Exemple #2
0
def test(data_path):

    # parameters
    folder_data = ['template/template/', 't2/', 'dmri/']
    file_data = [
        get_file_label(data_path + 'template/template/', 'T2-weighted'),
        't2.nii.gz', 'warp_template2anat.nii.gz', 'dmri.nii.gz'
    ]

    output = ''
    status = 0

    # test function
    cmd = 'sct_apply_transfo -i ' + data_path + folder_data[0] + file_data[0] \
          + ' -d ' + data_path + folder_data[1] + file_data[1] \
          + ' -w ' + data_path + folder_data[1] + file_data[2]
    output += cmd + '\n'  # copy command
    s, o = commands.getstatusoutput(cmd)
    status += s
    output += o

    # test with 4d input
    cmd = 'sct_apply_transfo -i ' + data_path + folder_data[2] + file_data[3] \
          + ' -d ' + data_path + folder_data[1] + file_data[1] \
          + ' -w ' + data_path + folder_data[1] + file_data[2]
    output += cmd + '\n'  # copy command
    s, o = commands.getstatusoutput(cmd)
    status += s
    output += o

    # return
    #return sct.run(cmd, 0)
    return status, output
def test(data_path):

    # parameters
    folder_data = ['t2/', 'mt/', 'template/template/']
    file_data = ['warp_template2anat.nii.gz',
                 'warp_t22mt1.nii.gz',
                 get_file_label(data_path+'template/template/', 'T2-weighted')]

    # define command
    cmd = 'sct_concat_transfo -w ' + data_path + folder_data[0] + file_data[0] + ',' \
          + data_path + folder_data[1] + file_data[1]\
          + ' -d ' + data_path + folder_data[2] + file_data[2]

    # return
    #return sct.run(cmd, 0)
    return commands.getstatusoutput(cmd)
Exemple #4
0
def vertebral_detection(fname, fname_seg, contrast, param, init_disc=[], verbose=1, path_template='', initc2='auto', path_output='../'):
    """
    Find intervertebral discs in straightened image using template matching
    :param fname:
    :param fname_seg:
    :param contrast:
    :param param:  advanced parameters
    :param init_disc:
    :param verbose:
    :param path_template:
    :param path_output: output path for verbose=2 pictures
    :return:
    """
    printv('\nLook for template...', verbose)
    # if path_template == '':
    #     # get path of SCT
    #     from os import path
    #     path_script = path.dirname(__file__)
    #     path_sct = slash_at_the_end(path.dirname(path_script), 1)
    #     folder_template = 'data/template/'
    #     path_template = path_sct+folder_template
    printv('Path template: '+path_template, verbose)

    # adjust file names if MNI-Poly-AMU template is used
    fname_level = get_file_label(path_template+'template/', 'vertebral', output='filewithpath')
    fname_template = get_file_label(path_template+'template/', contrast.upper()+'-weighted', output='filewithpath')

    # if not len(glob(path_template+'MNI-Poly-AMU*.*')) == 0:
    #     contrast = contrast.upper()
    #     file_level = '*_level.nii.gz'
    # else:
    #     file_level = '*_levels.nii.gz'
    #
    # # retrieve file_template based on contrast
    # try:
    #     fname_template_list = glob(path_template + '*' + contrast + '.nii.gz')
    #     fname_template = fname_template_list[0]
    # except IndexError:
    #     printv('\nERROR: No template found. Please check the provided path.', 1, 'error')
    # retrieve disc level from template
    # try:
    #     fname_level_list = glob(path_template+file_level)
    #     fname_level = fname_level_list[0]
    # except IndexError:
    #     printv('\nERROR: File *_levels.nii.gz not found.', 1, 'error')

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

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

    # smooth data
    from scipy.ndimage.filters import gaussian_filter
    data = gaussian_filter(data, param.smooth_factor, output=None, mode="reflect")

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

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

    # if automatic mode, find C2/C3 disc
    if init_disc == [] and initc2 == 'auto':
        printv('\nDetect C2/C3 disk...', verbose)
        zrange = range(0, nz)
        ind_c2 = list_disc_value_template.index(2)
        z_peak = compute_corr_3d(src=data, target=data_template, x=xc, xshift=0, xsize=param.size_RL_initc2, y=yc, yshift=param.shift_AP_initc2, ysize=param.size_AP_initc2, z=0, zshift=param.shift_IS_initc2, zsize=param.size_IS_initc2, xtarget=xct, ytarget=yct, ztarget=list_disc_z_template[ind_c2], zrange=zrange, verbose=verbose, save_suffix='_initC2', gaussian_weighting=True, path_output=path_output)
        init_disc = [z_peak, 2]

    # if manual mode, open viewer for user to click on C2/C3 disc
    if init_disc == [] and initc2 == 'manual':
        from sct_viewer import ClickViewer
        # reorient image to SAL to be compatible with viewer
        im_input_SAL = im_input.copy()
        im_input_SAL.change_orientation('SAL')
        viewer = ClickViewer(im_input_SAL, orientation_subplot=['sag', 'ax'])
        viewer.number_of_slices = 1
        pz = 1
        viewer.gap_inter_slice = int(10 / pz)
        viewer.calculate_list_slices()
        viewer.help_url = 'https://sourceforge.net/p/spinalcordtoolbox/wiki/sct_label_vertebrae/attachment/label_vertebrae_viewer.png'
        # start the viewer that ask the user to enter a few points along the spinal cord
        mask_points = viewer.start()
        if mask_points:
            # create the mask containing either the three-points or centerline mask for initialization
            mask_filename = sct.add_suffix(fname, "_mask_viewer")
            sct.run("sct_label_utils -i " + fname + " -create " + mask_points + " -o " + mask_filename, verbose=False)
        else:
            sct.printv('\nERROR: the viewer has been closed before entering all manual points. Please try again.', verbose, type='error')
        # assign new init_disc_z value, which corresponds to the first vector of mask_points. Note, we need to substract from nz due to SAL orientation: in the viewer, orientation is S-I while in this code, it is I-S.
        init_disc = [nz-int(mask_points.split(',')[0]), 2]

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

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

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

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

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

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

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

        # if verbose == 2:
        #     # close figures
        #     plt.figure(fig_corr), plt.close()
        #     plt.figure(fig_pattern), plt.close()

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

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

    # save figure
    if verbose == 2:
        plt.figure(50), plt.savefig(path_output + 'fig_anat_straight_with_labels.png')
def main():
    parser = get_parser()
    param = Param()

    args = sys.argv[1:]

    arguments = parser.parse(args)

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

    # initialize other parameters
    # file_template_label = param.file_template_label
    zsubsample = param.zsubsample
    # smoothing_sigma = param.smoothing_sigma

    # retrieve template file names
    from sct_warp_template import get_file_label
    file_template_vertebral_labeling = get_file_label(path_template + 'template/', 'vertebral')
    file_template = get_file_label(path_template + 'template/', contrast_template.upper() + '-weighted')
    file_template_seg = get_file_label(path_template + 'template/', 'spinal cord')

    # start timer
    start_time = time.time()

    # get fname of the template + template objects
    fname_template = path_template + 'template/' + file_template
    fname_template_vertebral_labeling = path_template + 'template/' + file_template_vertebral_labeling
    fname_template_seg = path_template + 'template/' + file_template_seg

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

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

    # create QC folder
    sct.create_folder(param.path_qc)

    # check if data, segmentation and landmarks are in the same space
    # JULIEN 2017-04-25: removed because of issue #1168
    # sct.printv('\nCheck if data, segmentation and landmarks are in the same space...')
    # if not sct.check_if_same_space(fname_data, fname_seg):
    #     sct.printv('ERROR: Data image and segmentation are not in the same space. Please check space and orientation of your files', verbose, 'error')
    # if not sct.check_if_same_space(fname_data, fname_landmarks):
    #     sct.printv('ERROR: Data image and landmarks are not in the same space. Please check space and orientation of your files', verbose, 'error')

    # check input labels
    labels = check_labels(fname_landmarks)

    # create temporary folder
    path_tmp = sct.tmp_create(verbose=verbose)

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

    # copy files to temporary folder
    sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose)
    sct.run('sct_convert -i ' + fname_data + ' -o ' + path_tmp + ftmp_data)
    sct.run('sct_convert -i ' + fname_seg + ' -o ' + path_tmp + ftmp_seg)
    sct.run('sct_convert -i ' + fname_landmarks + ' -o ' + path_tmp + ftmp_label)
    sct.run('sct_convert -i ' + fname_template + ' -o ' + path_tmp + ftmp_template)
    sct.run('sct_convert -i ' + fname_template_seg + ' -o ' + path_tmp + ftmp_template_seg)
    # sct.run('sct_convert -i '+fname_template_label+' -o '+path_tmp+ftmp_template_label)

    # go to tmp folder
    os.chdir(path_tmp)

    # copy header of anat to segmentation (issue #1168)
    # from sct_image import copy_header
    # im_data = Image(ftmp_data)
    # im_seg = Image(ftmp_seg)
    # copy_header(im_data, im_seg)
    # im_seg.save()
    # im_label = Image(ftmp_label)
    # copy_header(im_data, im_label)
    # im_label.save()

    # Generate labels from template vertebral labeling
    sct.printv('\nGenerate labels from template vertebral labeling', verbose)
    sct.run('sct_label_utils -i ' + fname_template_vertebral_labeling + ' -vert-body 0 -o ' + ftmp_template_label)

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

    # binarize segmentation (in case it has values below 0 caused by manual editing)
    sct.printv('\nBinarize segmentation', verbose)
    sct.run('sct_maths -i seg.nii.gz -bin 0.5 -o seg.nii.gz')

    # smooth segmentation (jcohenadad, issue #613)
    # sct.printv('\nSmooth segmentation...', verbose)
    # sct.run('sct_maths -i '+ftmp_seg+' -smooth 1.5 -o '+add_suffix(ftmp_seg, '_smooth'))
    # jcohenadad: updated 2016-06-16: DO NOT smooth the seg anymore. Issue #
    # sct.run('sct_maths -i '+ftmp_seg+' -smooth 0 -o '+add_suffix(ftmp_seg, '_smooth'))
    # ftmp_seg = add_suffix(ftmp_seg, '_smooth')

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

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

        # Change orientation of input images to RPI
        sct.printv('\nChange orientation of input images to RPI...', verbose)
        sct.run('sct_image -i ' + ftmp_data + ' -setorient RPI -o ' + add_suffix(ftmp_data, '_rpi'))
        ftmp_data = add_suffix(ftmp_data, '_rpi')
        sct.run('sct_image -i ' + ftmp_seg + ' -setorient RPI -o ' + add_suffix(ftmp_seg, '_rpi'))
        ftmp_seg = add_suffix(ftmp_seg, '_rpi')
        sct.run('sct_image -i ' + ftmp_label + ' -setorient RPI -o ' + add_suffix(ftmp_label, '_rpi'))
        ftmp_label = add_suffix(ftmp_label, '_rpi')

        # get landmarks in native space
        # crop segmentation
        # output: segmentation_rpi_crop.nii.gz
        status_crop, output_crop = sct.run('sct_crop_image -i ' + ftmp_seg + ' -o ' + add_suffix(ftmp_seg, '_crop') + ' -dim 2 -bzmax', verbose)
        ftmp_seg = add_suffix(ftmp_seg, '_crop')
        cropping_slices = output_crop.split('Dimension 2: ')[1].split('\n')[0].split(' ')

        # straighten segmentation
        sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose)
        # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time)
        if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'):
            # if they exist, copy them into current folder
            sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning')
            shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz')
            shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz')
            shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz')
            # apply straightening
            sct.run('sct_apply_transfo -i ' + ftmp_seg + ' -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o ' + add_suffix(ftmp_seg, '_straight'))
        else:
            sct.run('sct_straighten_spinalcord -i ' + ftmp_seg + ' -s ' + ftmp_seg + ' -o ' + add_suffix(ftmp_seg, '_straight') + ' -qc 0 -r 0 -v ' + str(verbose), verbose)
        # N.B. DO NOT UPDATE VARIABLE ftmp_seg BECAUSE TEMPORARY USED LATER
        # re-define warping field using non-cropped space (to avoid issue #367)
        sct.run('sct_concat_transfo -w warp_straight2curve.nii.gz -d ' + ftmp_data + ' -o warp_straight2curve.nii.gz')

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

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

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

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

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

        # Apply transformation
        sct.printv('\nApply transformation...', verbose)
        sct.run('sct_apply_transfo -i ' + ftmp_data + ' -o ' + add_suffix(ftmp_data, '_straightAffine') + ' -d ' + ftmp_template + ' -w warp_curve2straightAffine.nii.gz')
        ftmp_data = add_suffix(ftmp_data, '_straightAffine')
        sct.run('sct_apply_transfo -i ' + ftmp_seg + ' -o ' + add_suffix(ftmp_seg, '_straightAffine') + ' -d ' + ftmp_template + ' -w warp_curve2straightAffine.nii.gz -x linear')
        ftmp_seg = add_suffix(ftmp_seg, '_straightAffine')

        """
        # Benjamin: Issue from Allan Martin, about the z=0 slice that is screwed up, caused by the affine transform.
        # Solution found: remove slices below and above landmarks to avoid rotation effects
        points_straight = []
        for coord in landmark_template:
            points_straight.append(coord.z)
        min_point, max_point = int(round(np.min(points_straight))), int(round(np.max(points_straight)))
        sct.run('sct_crop_image -i ' + ftmp_seg + ' -start ' + str(min_point) + ' -end ' + str(max_point) + ' -dim 2 -b 0 -o ' + add_suffix(ftmp_seg, '_black'))
        ftmp_seg = add_suffix(ftmp_seg, '_black')
        """

        # binarize
        sct.printv('\nBinarize segmentation...', verbose)
        sct.run('sct_maths -i ' + ftmp_seg + ' -bin 0.5 -o ' + add_suffix(ftmp_seg, '_bin'))
        ftmp_seg = add_suffix(ftmp_seg, '_bin')

        # find min-max of anat2template (for subsequent cropping)
        zmin_template, zmax_template = find_zmin_zmax(ftmp_seg)

        # crop template in z-direction (for faster processing)
        sct.printv('\nCrop data in template space (for faster processing)...', verbose)
        sct.run('sct_crop_image -i ' + ftmp_template + ' -o ' + add_suffix(ftmp_template, '_crop') + ' -dim 2 -start ' + str(zmin_template) + ' -end ' + str(zmax_template))
        ftmp_template = add_suffix(ftmp_template, '_crop')
        sct.run('sct_crop_image -i ' + ftmp_template_seg + ' -o ' + add_suffix(ftmp_template_seg, '_crop') + ' -dim 2 -start ' + str(zmin_template) + ' -end ' + str(zmax_template))
        ftmp_template_seg = add_suffix(ftmp_template_seg, '_crop')
        sct.run('sct_crop_image -i ' + ftmp_data + ' -o ' + add_suffix(ftmp_data, '_crop') + ' -dim 2 -start ' + str(zmin_template) + ' -end ' + str(zmax_template))
        ftmp_data = add_suffix(ftmp_data, '_crop')
        sct.run('sct_crop_image -i ' + ftmp_seg + ' -o ' + add_suffix(ftmp_seg, '_crop') + ' -dim 2 -start ' + str(zmin_template) + ' -end ' + str(zmax_template))
        ftmp_seg = add_suffix(ftmp_seg, '_crop')

        # sub-sample in z-direction
        sct.printv('\nSub-sample in z-direction (for faster processing)...', verbose)
        sct.run('sct_resample -i ' + ftmp_template + ' -o ' + add_suffix(ftmp_template, '_sub') + ' -f 1x1x' + zsubsample, verbose)
        ftmp_template = add_suffix(ftmp_template, '_sub')
        sct.run('sct_resample -i ' + ftmp_template_seg + ' -o ' + add_suffix(ftmp_template_seg, '_sub') + ' -f 1x1x' + zsubsample, verbose)
        ftmp_template_seg = add_suffix(ftmp_template_seg, '_sub')
        sct.run('sct_resample -i ' + ftmp_data + ' -o ' + add_suffix(ftmp_data, '_sub') + ' -f 1x1x' + zsubsample, verbose)
        ftmp_data = add_suffix(ftmp_data, '_sub')
        sct.run('sct_resample -i ' + ftmp_seg + ' -o ' + add_suffix(ftmp_seg, '_sub') + ' -f 1x1x' + zsubsample, verbose)
        ftmp_seg = add_suffix(ftmp_seg, '_sub')

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

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

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

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

        # Change orientation of input images to RPI
        sct.printv('\nChange orientation of input images to RPI...', verbose)
        sct.run('sct_image -i ' + ftmp_data + ' -setorient RPI -o ' + add_suffix(ftmp_data, '_rpi'))
        ftmp_data = add_suffix(ftmp_data, '_rpi')
        sct.run('sct_image -i ' + ftmp_seg + ' -setorient RPI -o ' + add_suffix(ftmp_seg, '_rpi'))
        ftmp_seg = add_suffix(ftmp_seg, '_rpi')
        sct.run('sct_image -i ' + ftmp_label + ' -setorient RPI -o ' + add_suffix(ftmp_label, '_rpi'))
        ftmp_label = add_suffix(ftmp_label, '_rpi')

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

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

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

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

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

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

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

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

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

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

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

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

        qc_param = qc.Params(fname_data, 'sct_register_to_template', args, 'Sagittal', qc_path)
        report = qc.QcReport(qc_param, '')

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

        fname_template2anat = path_output + 'template2anat' + ext_data
        test(qcslice.SagittalTemplate2Anat(Image(fname_data), Image(fname_template2anat), Image(fname_seg)))
        sct.printv('Sucessfully generate the QC results in %s' % qc_param.qc_results)
        sct.printv('Use the following command to see the results in a browser')
        sct.printv('sct_qc -folder %s' % qc_path, type='info')

    # to view results
    sct.printv('\nTo view results, type:', verbose)
    sct.printv('fslview ' + fname_data + ' ' + path_output + 'template2anat -b 0,4000 &', verbose, 'info')
    sct.printv('fslview ' + fname_template + ' -b 0,5000 ' + path_output + 'anat2template &\n', verbose, 'info')
def main():
    parser = get_parser()
    param = Param()

    """ Rewrite arguments and set parameters"""
    arguments = parser.parse(sys.argv[1:])
    (fname_data, fname_landmarks, path_output, path_template, contrast_template, ref, remove_temp_files,
     verbose, init_labels, first_label,nb_slice_to_mean)=rewrite_arguments(arguments)
    (param, paramreg)=write_paramaters(arguments,param,ref,verbose)

    if(init_labels):
        use_viewer_to_define_labels(fname_data,first_label,nb_slice_to_mean)
    # initialize other parameters
    # file_template_label = param.file_template_label
    zsubsample = param.zsubsample
    template = os.path.basename(os.path.normpath(pth_template))
    # smoothing_sigma = param.smoothing_sigma

    # retrieve template file names

    from sct_warp_template import get_file_label
    file_template_vertebral_labeling = get_file_label(path_template+'template/', 'vertebral')
    file_template = get_file_label(path_template+'template/', contrast_template.upper()+'-weighted')
    file_template_seg = get_file_label(path_template+'template/', 'spinal cord')


    """ Start timer"""
    start_time = time.time()

    """ Manage file of templates"""
    (fname_template, fname_template_vertebral_labeling, fname_template_seg)=make_fname_of_templates(file_template,path_template,file_template_vertebral_labeling,file_template_seg)
    check_do_files_exist(fname_template,fname_template_vertebral_labeling,fname_template_seg,verbose)
    sct.printv(arguments(verbose, fname_data, fname_landmarks, fname_seg, path_template, remove_temp_files))

    """ Create QC folder """
    sct.create_folder(param.path_qc)

    """ Check if data, segmentation and landmarks are in the same space"""
    (ext_data, path_data, file_data)=check_data_segmentation_landmarks_same_space(fname_data, fname_seg, fname_landmarks,verbose)

    ''' Check input labels'''
    labels = check_labels(fname_landmarks)

    """ Create temporary folder, set temporary file names, copy files into it and go in it """
    path_tmp = sct.tmp_create(verbose=verbose)
    (ftmp_data, ftmp_seg, ftmp_label, ftmp_template, ftmp_template_seg, ftmp_template_label)=set_temporary_files()
    copy_files_to_temporary_files(verbose, fname_data, path_tmp, ftmp_seg, ftmp_data, fname_seg, fname_landmarks,
                                  ftmp_label, fname_template, ftmp_template, fname_template_seg, ftmp_template_seg)
    os.chdir(path_tmp)

    ''' Generate labels from template vertebral labeling'''
    sct.printv('\nGenerate labels from template vertebral labeling', verbose)
    sct.run('sct_label_utils -i '+fname_template_vertebral_labeling+' -vert-body 0 -o '+ftmp_template_label)

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

    ''' Binarize segmentation (in case it has values below 0 caused by manual editing)'''
    sct.printv('\nBinarize segmentation', verbose)
    sct.run('sct_maths -i seg.nii.gz -bin 0.5 -o seg.nii.gz')

    # smooth segmentation (jcohenadad, issue #613)
    # sct.printv('\nSmooth segmentation...', verbose)
    # sct.run('sct_maths -i '+ftmp_seg+' -smooth 1.5 -o '+add_suffix(ftmp_seg, '_smooth'))
    # jcohenadad: updated 2016-06-16: DO NOT smooth the seg anymore. Issue #
    # sct.run('sct_maths -i '+ftmp_seg+' -smooth 0 -o '+add_suffix(ftmp_seg, '_smooth'))
    # ftmp_seg = add_suffix(ftmp_seg, '_smooth')

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

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

        # Change orientation of input images to RPI
        sct.printv('\nChange orientation of input images to RPI...', verbose)
        sct.run('sct_image -i '+ftmp_data+' -setorient RPI -o '+add_suffix(ftmp_data, '_rpi'))
        ftmp_data = add_suffix(ftmp_data, '_rpi')
        sct.run('sct_image -i '+ftmp_seg+' -setorient RPI -o '+add_suffix(ftmp_seg, '_rpi'))
        ftmp_seg = add_suffix(ftmp_seg, '_rpi')
        sct.run('sct_image -i '+ftmp_label+' -setorient RPI -o '+add_suffix(ftmp_label, '_rpi'))
        ftmp_label = add_suffix(ftmp_label, '_rpi')

        # get landmarks in native space
        # crop segmentation
        # output: segmentation_rpi_crop.nii.gz
        status_crop, output_crop = sct.run('sct_crop_image -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_crop')+' -dim 2 -bzmax', verbose)
        ftmp_seg = add_suffix(ftmp_seg, '_crop')
        cropping_slices = output_crop.split('Dimension 2: ')[1].split('\n')[0].split(' ')

        # straighten segmentation
        sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose)
        # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time)
        if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'):
            # if they exist, copy them into current folder
            sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning')
            shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz')
            shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz')
            shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz')
            # apply straightening
            sct.run('sct_apply_transfo -i '+ftmp_seg+' -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o '+add_suffix(ftmp_seg, '_straight'))
        else:
            sct.run('sct_straighten_spinalcord -i '+ftmp_seg+' -s '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_straight')+' -qc 0 -r 0 -v '+str(verbose), verbose)
        # N.B. DO NOT UPDATE VARIABLE ftmp_seg BECAUSE TEMPORARY USED LATER
        # re-define warping field using non-cropped space (to avoid issue #367)
        sct.run('sct_concat_transfo -w warp_straight2curve.nii.gz -d '+ftmp_data+' -o warp_straight2curve.nii.gz')

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

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

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

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

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

        # Apply transformation
        sct.printv('\nApply transformation...', verbose)
        sct.run('sct_apply_transfo -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_straightAffine')+' -d '+ftmp_template+' -w warp_curve2straightAffine.nii.gz')
        ftmp_data = add_suffix(ftmp_data, '_straightAffine')
        sct.run('sct_apply_transfo -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_straightAffine')+' -d '+ftmp_template+' -w warp_curve2straightAffine.nii.gz -x linear')
        ftmp_seg = add_suffix(ftmp_seg, '_straightAffine')

        """
        # Benjamin: Issue from Allan Martin, about the z=0 slice that is screwed up, caused by the affine transform.
        # Solution found: remove slices below and above landmarks to avoid rotation effects
        points_straight = []
        for coord in landmark_template:
            points_straight.append(coord.z)
        min_point, max_point = int(round(np.min(points_straight))), int(round(np.max(points_straight)))
        sct.run('sct_crop_image -i ' + ftmp_seg + ' -start ' + str(min_point) + ' -end ' + str(max_point) + ' -dim 2 -b 0 -o ' + add_suffix(ftmp_seg, '_black'))
        ftmp_seg = add_suffix(ftmp_seg, '_black')
        """

        # binarize
        sct.printv('\nBinarize segmentation...', verbose)
        sct.run('sct_maths -i '+ftmp_seg+' -bin 0.5 -o '+add_suffix(ftmp_seg, '_bin'))
        ftmp_seg = add_suffix(ftmp_seg, '_bin')

        # find min-max of anat2template (for subsequent cropping)
        zmin_template, zmax_template = find_zmin_zmax(ftmp_seg)

        # crop template in z-direction (for faster processing)
        sct.printv('\nCrop data in template space (for faster processing)...', verbose)
        sct.run('sct_crop_image -i '+ftmp_template+' -o '+add_suffix(ftmp_template, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template))
        ftmp_template = add_suffix(ftmp_template, '_crop')
        sct.run('sct_crop_image -i '+ftmp_template_seg+' -o '+add_suffix(ftmp_template_seg, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template))
        ftmp_template_seg = add_suffix(ftmp_template_seg, '_crop')
        sct.run('sct_crop_image -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template))
        ftmp_data = add_suffix(ftmp_data, '_crop')
        sct.run('sct_crop_image -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template))
        ftmp_seg = add_suffix(ftmp_seg, '_crop')

        # sub-sample in z-direction
        sct.printv('\nSub-sample in z-direction (for faster processing)...', verbose)
        sct.run('sct_resample -i '+ftmp_template+' -o '+add_suffix(ftmp_template, '_sub')+' -f 1x1x'+zsubsample, verbose)
        ftmp_template = add_suffix(ftmp_template, '_sub')
        sct.run('sct_resample -i '+ftmp_template_seg+' -o '+add_suffix(ftmp_template_seg, '_sub')+' -f 1x1x'+zsubsample, verbose)
        ftmp_template_seg = add_suffix(ftmp_template_seg, '_sub')
        sct.run('sct_resample -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_sub')+' -f 1x1x'+zsubsample, verbose)
        ftmp_data = add_suffix(ftmp_data, '_sub')
        sct.run('sct_resample -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_sub')+' -f 1x1x'+zsubsample, verbose)
        ftmp_seg = add_suffix(ftmp_seg, '_sub')

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

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

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

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

        # Change orientation of input images to RPI
        sct.printv('\nChange orientation of input images to RPI...', verbose)
        sct.run('sct_image -i ' + ftmp_data + ' -setorient RPI -o ' + add_suffix(ftmp_data, '_rpi'))
        ftmp_data = add_suffix(ftmp_data, '_rpi')
        sct.run('sct_image -i ' + ftmp_seg + ' -setorient RPI -o ' + add_suffix(ftmp_seg, '_rpi'))
        ftmp_seg = add_suffix(ftmp_seg, '_rpi')
        sct.run('sct_image -i ' + ftmp_label + ' -setorient RPI -o ' + add_suffix(ftmp_label, '_rpi'))
        ftmp_label = add_suffix(ftmp_label, '_rpi')

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

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

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

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

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

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

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

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

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

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

    # to view results
    sct.printv('\nTo view results, type:', verbose)
    sct.printv('fslview '+fname_data+' '+path_output+'template2anat -b 0,4000 &', verbose, 'info')
    sct.printv('fslview '+fname_template+' -b 0,5000 '+path_output+'anat2template &\n', verbose, 'info')
Exemple #7
0
def test(path_data='', parameters=''):
    verbose = 0
    dice_threshold = 0.9
    add_path_for_template = False  # if absolute path or no path to template is provided, then path to data should not be added.

    # initializations
    dice_template2anat = float('NaN')
    dice_anat2template = float('NaN')
    output = ''

    if not parameters:
        parameters = '-i t2/t2.nii.gz -l t2/labels.nii.gz -s t2/t2_seg.nii.gz ' \
                     '-param step=1,type=seg,algo=centermassrot,metric=MeanSquares:step=2,type=seg,algo=bsplinesyn,iter=5,metric=MeanSquares ' \
                     '-t template/ -r 0'
        add_path_for_template = True  # in this case, path to data should be added

    parser = sct_register_to_template.get_parser()
    dict_param = parser.parse(parameters.split(), check_file_exist=False)
    if add_path_for_template:
        dict_param_with_path = parser.add_path_to_file(deepcopy(dict_param), path_data, input_file=True)
    else:
        dict_param_with_path = parser.add_path_to_file(deepcopy(dict_param), path_data, input_file=True, do_not_add_path=['-t'])
    param_with_path = parser.dictionary_to_string(dict_param_with_path)

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

    # if template is not specified, use default
    # if not os.path.isdir(dict_param_with_path['-t']):
    #     status, path_sct = commands.getstatusoutput('echo $SCT_DIR')
    #     dict_param_with_path['-t'] = path_sct + default_template
    #     param_with_path = parser.dictionary_to_string(dict_param_with_path)

    # get contrast folder from -i option.
    # We suppose we can extract it as the first object when spliting with '/' delimiter.
    contrast_folder = ''
    input_filename = ''
    if dict_param['-i'][0] == '/':
        dict_param['-i'] = dict_param['-i'][1:]
    input_split = dict_param['-i'].split('/')
    if len(input_split) == 2:
        contrast_folder = input_split[0] + '/'
        input_filename = input_split[1]
    else:
        input_filename = input_split[0]
    if not contrast_folder:  # if no contrast folder, send error.
        status = 201
        output = 'ERROR: when extracting the contrast folder from input file in command line: ' + dict_param['-i'] + ' for ' + path_data
        return status, output, DataFrame(data={'status': int(status), 'output': output}, index=[path_data])
        # return status, output, DataFrame(
        #     data={'status': status, 'output': output, 'dice_template2anat': float('nan'), 'dice_anat2template': float('nan')}, index=[path_data])

    import time, random
    subject_folder = path_data.split('/')
    if subject_folder[-1] == '' and len(subject_folder) > 1:
        subject_folder = subject_folder[-2]
    else:
        subject_folder = subject_folder[-1]
    path_output = sct.slash_at_the_end('sct_register_to_template_' + subject_folder + '_' + time.strftime("%y%m%d%H%M%S") + '_'+str(random.randint(1, 1000000)), slash=1)
    param_with_path += ' -ofolder ' + path_output

    cmd = 'sct_register_to_template ' + param_with_path
    output += '\n====================================================================================================\n'+cmd+'\n====================================================================================================\n\n'  # copy command
    time_start = time.time()
    try:
        status, o = sct.run(cmd, verbose)
    except:
        status, o = 1, 'ERROR: Function crashed!'
    output += o
    duration = time.time() - time_start

    # if command ran without error, test integrity
    if status == 0:
        # get filename_template_seg
        fname_template_seg = get_file_label(sct.slash_at_the_end(dict_param_with_path['-t'], 1) + 'template/', 'spinal cord', output='filewithpath')
        # apply transformation to binary mask: template --> anat
        sct.run(
            'sct_apply_transfo -i ' + fname_template_seg +
            ' -d ' + dict_param_with_path['-s'] +
            ' -w ' + path_output + 'warp_template2anat.nii.gz' +
            ' -o ' + path_output + 'test_template2anat.nii.gz -x nn', verbose)
        # apply transformation to binary mask: anat --> template
        sct.run(
            'sct_apply_transfo -i ' + dict_param_with_path['-s'] +
            ' -d ' + fname_template_seg +
            ' -w ' + path_output + 'warp_anat2template.nii.gz' +
            ' -o ' + path_output + 'test_anat2template.nii.gz -x nn', verbose)
        # compute dice coefficient between template segmentation warped into anat and segmentation from anat
        cmd = 'sct_dice_coefficient -i ' + dict_param_with_path['-s'] + ' -d ' + path_output + 'test_template2anat.nii.gz'
        status1, output1 = sct.run(cmd, verbose)
        # parse output and compare to acceptable threshold
        dice_template2anat = float(output1.split('3D Dice coefficient = ')[1].split('\n')[0])
        if dice_template2anat < dice_threshold:
            status1 = 99
        # compute dice coefficient between segmentation from anat warped into template and template segmentation
        # N.B. here we use -bmax because the FOV of the anat is smaller than the template
        cmd = 'sct_dice_coefficient -i ' + fname_template_seg + ' -d ' + path_output + 'test_anat2template.nii.gz -bmax 1'
        status2, output2 = sct.run(cmd, verbose)
        # parse output and compare to acceptable threshold
        dice_anat2template = float(output2.split('3D Dice coefficient = ')[1].split('\n')[0])
        if dice_anat2template < dice_threshold:
            status2 = 99
        # check if at least one integrity status was equal to 99
        if status1 == 99 or status2 == 99:
            status = 99

        # concatenate outputs
        output = output + output1 + output2

    # transform results into Pandas structure
    results = DataFrame(data={'status': int(status), 'output': output, 'dice_template2anat': dice_template2anat, 'dice_anat2template': dice_anat2template, 'duration [s]': duration}, index=[path_data])

    return status, output, results
def vertebral_detection(fname,
                        fname_seg,
                        contrast,
                        param,
                        init_disc,
                        verbose=1,
                        path_template='',
                        initc2='auto',
                        path_output='../'):
    """
    Find intervertebral discs in straightened image using template matching
    :param fname:
    :param fname_seg:
    :param contrast:
    :param param:  advanced parameters
    :param init_disc:
    :param verbose:
    :param path_template:
    :param path_output: output path for verbose=2 pictures
    :return:
    """
    sct.printv('\nLook for template...', verbose)
    # if path_template == '':
    #     # get path of SCT
    #     from os import path
    #     path_script = path.dirname(__file__)
    #     path_sct = slash_at_the_end(path.dirname(path_script), 1)
    #     folder_template = 'data/template/'
    #     path_template = path_sct+folder_template
    sct.printv('Path template: ' + path_template, verbose)

    # adjust file names if MNI-Poly-AMU template is used
    fname_level = get_file_label(path_template + 'template/',
                                 'vertebral',
                                 output='filewithpath')
    fname_template = get_file_label(path_template + 'template/',
                                    contrast.upper() + '-weighted',
                                    output='filewithpath')

    # if not len(glob(path_template+'MNI-Poly-AMU*.*')) == 0:
    #     contrast = contrast.upper()
    #     file_level = '*_level.nii.gz'
    # else:
    #     file_level = '*_levels.nii.gz'
    #
    # # retrieve file_template based on contrast
    # try:
    #     fname_template_list = glob(path_template + '*' + contrast + '.nii.gz')
    #     fname_template = fname_template_list[0]
    # except IndexError:
    #     sct.printv('\nERROR: No template found. Please check the provided path.', 1, 'error')
    # retrieve disc level from template
    # try:
    #     fname_level_list = glob(path_template+file_level)
    #     fname_level = fname_level_list[0]
    # except IndexError:
    #     sct.printv('\nERROR: File *_levels.nii.gz not found.', 1, 'error')

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

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

    # smooth data
    from scipy.ndimage.filters import gaussian_filter
    data = gaussian_filter(data,
                           param.smooth_factor,
                           output=None,
                           mode="reflect")

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

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

    # if automatic mode, find C2/C3 disc
    if init_disc == [] and initc2 == 'auto':
        sct.printv('\nDetect C2/C3 disk...', verbose)
        zrange = range(0, nz)
        ind_c2 = list_disc_value_template.index(2)
        z_peak = compute_corr_3d(data,
                                 data_template,
                                 x=xc,
                                 xshift=0,
                                 xsize=param.size_RL_initc2,
                                 y=yc,
                                 yshift=param.shift_AP_initc2,
                                 ysize=param.size_AP_initc2,
                                 z=0,
                                 zshift=param.shift_IS_initc2,
                                 zsize=param.size_IS_initc2,
                                 xtarget=xct,
                                 ytarget=yct,
                                 ztarget=list_disc_z_template[ind_c2],
                                 zrange=zrange,
                                 verbose=verbose,
                                 save_suffix='_initC2',
                                 gaussian_std=param.gaussian_std,
                                 path_output=path_output)
        init_disc = [z_peak, 2]

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

        params = AnatomicalParams()
        params.num_points = 1
        params.vertebraes = [
            3,
        ]
        input_file = Image(fname)
        output_file = input_file.copy()
        output_file.data *= 0
        output_file.setFileName(os.path.join(path_output, 'labels.nii.gz'))
        controller = launch_sagittal_dialog(input_file, output_file, params)
        mask_points = controller.as_string()

        # assign new init_disc_z value, which corresponds to the first vector of mask_points. Note, we need to substract from nz due to SAL orientation: in the viewer, orientation is S-I while in this code, it is I-S.
        init_disc = [nz - int(mask_points.split(',')[0]), 2]

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

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

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

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

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

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

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

        # if verbose == 2:
        #     # close figures
        #     plt.figure(fig_corr), plt.close()
        #     plt.figure(fig_pattern), plt.close()

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

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

    # save figure
    if verbose == 2:
        plt.figure(50), plt.savefig(path_output +
                                    'fig_anat_straight_with_labels.png')
Exemple #9
0
def test(path_data='', parameters=''):
    verbose = 0
    dice_threshold = 0.9
    add_path_for_template = False  # if absolute path or no path to template is provided, then path to data should not be added.

    # initializations
    dice_template2anat = float('NaN')
    dice_anat2template = float('NaN')
    output = ''

    if not parameters:
        parameters = '-i t2/t2.nii.gz -l t2/labels.nii.gz -s t2/t2_seg.nii.gz ' \
                     '-param step=1,type=seg,algo=centermassrot,metric=MeanSquares:step=2,type=seg,algo=bsplinesyn,iter=5,metric=MeanSquares ' \
                     '-t template/ -r 0'
        add_path_for_template = True  # in this case, path to data should be added

    parser = sct_register_to_template.get_parser()
    dict_param = parser.parse(parameters.split(), check_file_exist=False)
    if add_path_for_template:
        dict_param_with_path = parser.add_path_to_file(deepcopy(dict_param),
                                                       path_data,
                                                       input_file=True)
    else:
        dict_param_with_path = parser.add_path_to_file(deepcopy(dict_param),
                                                       path_data,
                                                       input_file=True,
                                                       do_not_add_path=['-t'])
    param_with_path = parser.dictionary_to_string(dict_param_with_path)

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

    # if template is not specified, use default
    # if not os.path.isdir(dict_param_with_path['-t']):
    #     status, path_sct = commands.getstatusoutput('echo $SCT_DIR')
    #     dict_param_with_path['-t'] = path_sct + default_template
    #     param_with_path = parser.dictionary_to_string(dict_param_with_path)

    # get contrast folder from -i option.
    # We suppose we can extract it as the first object when spliting with '/' delimiter.
    contrast_folder = ''
    input_filename = ''
    if dict_param['-i'][0] == '/':
        dict_param['-i'] = dict_param['-i'][1:]
    input_split = dict_param['-i'].split('/')
    if len(input_split) == 2:
        contrast_folder = input_split[0] + '/'
        input_filename = input_split[1]
    else:
        input_filename = input_split[0]
    if not contrast_folder:  # if no contrast folder, send error.
        status = 201
        output = 'ERROR: when extracting the contrast folder from input file in command line: ' + dict_param[
            '-i'] + ' for ' + path_data
        return status, output, DataFrame(data={
            'status': int(status),
            'output': output
        },
                                         index=[path_data])
        # return status, output, DataFrame(
        #     data={'status': status, 'output': output, 'dice_template2anat': float('nan'), 'dice_anat2template': float('nan')}, index=[path_data])

    # create output path
    # TODO: create function for that
    import time, random
    subject_folder = path_data.split('/')
    if subject_folder[-1] == '' and len(subject_folder) > 1:
        subject_folder = subject_folder[-2]
    else:
        subject_folder = subject_folder[-1]
    path_output = sct.slash_at_the_end(
        'sct_register_to_template_' + subject_folder + '_' +
        time.strftime("%y%m%d%H%M%S") + '_' + str(random.randint(1, 1000000)),
        slash=1)
    param_with_path += ' -ofolder ' + path_output
    sct.create_folder(path_output)

    # log file
    # TODO: create function for that
    import sys
    fname_log = path_output + 'output.log'

    sct.pause_stream_logger()
    file_handler = sct.add_file_handler_to_logger(filename=fname_log,
                                                  mode='w',
                                                  log_format="%(message)s")
    #
    # stdout_log = file(fname_log, 'w')
    # redirect to log file
    # stdout_orig = sys.stdout
    # sys.stdout = stdout_log

    cmd = 'sct_register_to_template ' + param_with_path
    output += '\n====================================================================================================\n' + cmd + '\n====================================================================================================\n\n'  # copy command
    time_start = time.time()
    try:
        status, o = sct.run(cmd, verbose)
    except:
        status, o = 1, 'ERROR: Function crashed!'
    output += o
    duration = time.time() - time_start

    # if command ran without error, test integrity
    if status == 0:
        # get filename_template_seg
        fname_template_seg = get_file_label(
            sct.slash_at_the_end(dict_param_with_path['-t'], 1) + 'template/',
            'spinal cord',
            output='filewithpath')
        # apply transformation to binary mask: template --> anat
        sct.run(
            'sct_apply_transfo -i ' + fname_template_seg + ' -d ' +
            dict_param_with_path['-s'] + ' -w ' + path_output +
            'warp_template2anat.nii.gz' + ' -o ' + path_output +
            'test_template2anat.nii.gz -x nn', verbose)
        # apply transformation to binary mask: anat --> template
        sct.run(
            'sct_apply_transfo -i ' + dict_param_with_path['-s'] + ' -d ' +
            fname_template_seg + ' -w ' + path_output +
            'warp_anat2template.nii.gz' + ' -o ' + path_output +
            'test_anat2template.nii.gz -x nn', verbose)
        # compute dice coefficient between template segmentation warped into anat and segmentation from anat
        cmd = 'sct_dice_coefficient -i ' + dict_param_with_path[
            '-s'] + ' -d ' + path_output + 'test_template2anat.nii.gz'
        status1, output1 = sct.run(cmd, verbose)
        # parse output and compare to acceptable threshold
        dice_template2anat = float(
            output1.split('3D Dice coefficient = ')[1].split('\n')[0])
        if dice_template2anat < dice_threshold:
            status1 = 99
        # compute dice coefficient between segmentation from anat warped into template and template segmentation
        # N.B. here we use -bmax because the FOV of the anat is smaller than the template
        cmd = 'sct_dice_coefficient -i ' + fname_template_seg + ' -d ' + path_output + 'test_anat2template.nii.gz -bmax 1'
        status2, output2 = sct.run(cmd, verbose)
        # parse output and compare to acceptable threshold
        dice_anat2template = float(
            output2.split('3D Dice coefficient = ')[1].split('\n')[0])
        if dice_anat2template < dice_threshold:
            status2 = 99
        # check if at least one integrity status was equal to 99
        if status1 == 99 or status2 == 99:
            status = 99

        # concatenate outputs
        output = output + output1 + output2

    # transform results into Pandas structure
    results = DataFrame(data={
        'status': int(status),
        'output': output,
        'dice_template2anat': dice_template2anat,
        'dice_anat2template': dice_anat2template,
        'duration [s]': duration
    },
                        index=[path_data])

    sct.log.info(output)
    sct.remove_handler(file_handler)
    sct.start_stream_logger()

    return status, output, results
Exemple #10
0
def main():
    parser = get_parser()
    param = Param()

    arguments = parser.parse(sys.argv[1:])

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

    # initialize other parameters
    # file_template_label = param.file_template_label
    zsubsample = param.zsubsample
    template = os.path.basename(os.path.normpath(path_template))
    # smoothing_sigma = param.smoothing_sigma

    # retrieve template file names
    from sct_warp_template import get_file_label
    file_template_vertebral_labeling = get_file_label(path_template+'template/', 'vertebral')
    file_template = get_file_label(path_template+'template/', contrast_template.upper()+'-weighted')
    file_template_seg = get_file_label(path_template+'template/', 'spinal cord')

    # start timer
    start_time = time.time()

    # get fname of the template + template objects
    fname_template = path_template+'template/'+file_template
    fname_template_vertebral_labeling = path_template+'template/'+file_template_vertebral_labeling
    fname_template_seg = path_template+'template/'+file_template_seg

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

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

    # create QC folder
    sct.create_folder(param.path_qc)

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

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

    sct.printv('\nCheck if data, segmentation and landmarks are in the same space...')
    if not sct.check_if_same_space(fname_data, fname_seg):
        sct.printv('ERROR: Data image and segmentation are not in the same space. Please check space and orientation of your files', verbose, 'error')
    if not sct.check_if_same_space(fname_data, fname_landmarks):
        sct.printv('ERROR: Data image and landmarks are not in the same space. Please check space and orientation of your files', verbose, 'error')

    sct.printv('\nCheck input labels...')
    # check if label image contains coherent labels
    image_label = Image(fname_landmarks)
    # -> all labels must be different
    labels = image_label.getNonZeroCoordinates(sorting='value')
    hasDifferentLabels = True
    for lab in labels:
        for otherlabel in labels:
            if lab != otherlabel and lab.hasEqualValue(otherlabel):
                hasDifferentLabels = False
                break
    if not hasDifferentLabels:
        sct.printv('ERROR: Wrong landmarks input. All labels must be different.', verbose, 'error')

    # create temporary folder
    path_tmp = sct.tmp_create(verbose=verbose)

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

    # copy files to temporary folder
    sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose)
    sct.run('sct_convert -i '+fname_data+' -o '+path_tmp+ftmp_data)
    sct.run('sct_convert -i '+fname_seg+' -o '+path_tmp+ftmp_seg)
    sct.run('sct_convert -i '+fname_landmarks+' -o '+path_tmp+ftmp_label)
    sct.run('sct_convert -i '+fname_template+' -o '+path_tmp+ftmp_template)
    sct.run('sct_convert -i '+fname_template_seg+' -o '+path_tmp+ftmp_template_seg)
    # sct.run('sct_convert -i '+fname_template_label+' -o '+path_tmp+ftmp_template_label)

    # go to tmp folder
    os.chdir(path_tmp)

    # Generate labels from template vertebral labeling
    sct.printv('\nGenerate labels from template vertebral labeling', verbose)
    sct.run('sct_label_utils -i '+fname_template_vertebral_labeling+' -vert-body 0 -o '+ftmp_template_label)

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

    # binarize segmentation (in case it has values below 0 caused by manual editing)
    sct.printv('\nBinarize segmentation', verbose)
    sct.run('sct_maths -i seg.nii.gz -bin 0.5 -o seg.nii.gz')

    # smooth segmentation (jcohenadad, issue #613)
    # sct.printv('\nSmooth segmentation...', verbose)
    # sct.run('sct_maths -i '+ftmp_seg+' -smooth 1.5 -o '+add_suffix(ftmp_seg, '_smooth'))
    # jcohenadad: updated 2016-06-16: DO NOT smooth the seg anymore. Issue #
    # sct.run('sct_maths -i '+ftmp_seg+' -smooth 0 -o '+add_suffix(ftmp_seg, '_smooth'))
    # ftmp_seg = add_suffix(ftmp_seg, '_smooth')

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

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

        # Change orientation of input images to RPI
        sct.printv('\nChange orientation of input images to RPI...', verbose)
        sct.run('sct_image -i '+ftmp_data+' -setorient RPI -o '+add_suffix(ftmp_data, '_rpi'))
        ftmp_data = add_suffix(ftmp_data, '_rpi')
        sct.run('sct_image -i '+ftmp_seg+' -setorient RPI -o '+add_suffix(ftmp_seg, '_rpi'))
        ftmp_seg = add_suffix(ftmp_seg, '_rpi')
        sct.run('sct_image -i '+ftmp_label+' -setorient RPI -o '+add_suffix(ftmp_label, '_rpi'))
        ftmp_label = add_suffix(ftmp_label, '_rpi')

        # get landmarks in native space
        # crop segmentation
        # output: segmentation_rpi_crop.nii.gz
        status_crop, output_crop = sct.run('sct_crop_image -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_crop')+' -dim 2 -bzmax', verbose)
        ftmp_seg = add_suffix(ftmp_seg, '_crop')
        cropping_slices = output_crop.split('Dimension 2: ')[1].split('\n')[0].split(' ')

        # straighten segmentation
        sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose)
        # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time)
        if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'):
            # if they exist, copy them into current folder
            sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning')
            shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz')
            shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz')
            shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz')
            # apply straightening
            sct.run('sct_apply_transfo -i '+ftmp_seg+' -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o '+add_suffix(ftmp_seg, '_straight'))
        else:
            sct.run('sct_straighten_spinalcord -i '+ftmp_seg+' -s '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_straight')+' -qc 0 -r 0 -v '+str(verbose), verbose)
        # N.B. DO NOT UPDATE VARIABLE ftmp_seg BECAUSE TEMPORARY USED LATER
        # re-define warping field using non-cropped space (to avoid issue #367)
        sct.run('sct_concat_transfo -w warp_straight2curve.nii.gz -d '+ftmp_data+' -o warp_straight2curve.nii.gz')

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

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

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

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

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

        # Apply transformation
        sct.printv('\nApply transformation...', verbose)
        sct.run('sct_apply_transfo -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_straightAffine')+' -d '+ftmp_template+' -w warp_curve2straightAffine.nii.gz')
        ftmp_data = add_suffix(ftmp_data, '_straightAffine')
        sct.run('sct_apply_transfo -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_straightAffine')+' -d '+ftmp_template+' -w warp_curve2straightAffine.nii.gz -x linear')
        ftmp_seg = add_suffix(ftmp_seg, '_straightAffine')

        """
        # Benjamin: Issue from Allan Martin, about the z=0 slice that is screwed up, caused by the affine transform.
        # Solution found: remove slices below and above landmarks to avoid rotation effects
        points_straight = []
        for coord in landmark_template:
            points_straight.append(coord.z)
        min_point, max_point = int(round(np.min(points_straight))), int(round(np.max(points_straight)))
        sct.run('sct_crop_image -i ' + ftmp_seg + ' -start ' + str(min_point) + ' -end ' + str(max_point) + ' -dim 2 -b 0 -o ' + add_suffix(ftmp_seg, '_black'))
        ftmp_seg = add_suffix(ftmp_seg, '_black')
        """

        # binarize
        sct.printv('\nBinarize segmentation...', verbose)
        sct.run('sct_maths -i '+ftmp_seg+' -bin 0.5 -o '+add_suffix(ftmp_seg, '_bin'))
        ftmp_seg = add_suffix(ftmp_seg, '_bin')

        # find min-max of anat2template (for subsequent cropping)
        zmin_template, zmax_template = find_zmin_zmax(ftmp_seg)

        # crop template in z-direction (for faster processing)
        sct.printv('\nCrop data in template space (for faster processing)...', verbose)
        sct.run('sct_crop_image -i '+ftmp_template+' -o '+add_suffix(ftmp_template, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template))
        ftmp_template = add_suffix(ftmp_template, '_crop')
        sct.run('sct_crop_image -i '+ftmp_template_seg+' -o '+add_suffix(ftmp_template_seg, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template))
        ftmp_template_seg = add_suffix(ftmp_template_seg, '_crop')
        sct.run('sct_crop_image -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template))
        ftmp_data = add_suffix(ftmp_data, '_crop')
        sct.run('sct_crop_image -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template))
        ftmp_seg = add_suffix(ftmp_seg, '_crop')

        # sub-sample in z-direction
        sct.printv('\nSub-sample in z-direction (for faster processing)...', verbose)
        sct.run('sct_resample -i '+ftmp_template+' -o '+add_suffix(ftmp_template, '_sub')+' -f 1x1x'+zsubsample, verbose)
        ftmp_template = add_suffix(ftmp_template, '_sub')
        sct.run('sct_resample -i '+ftmp_template_seg+' -o '+add_suffix(ftmp_template_seg, '_sub')+' -f 1x1x'+zsubsample, verbose)
        ftmp_template_seg = add_suffix(ftmp_template_seg, '_sub')
        sct.run('sct_resample -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_sub')+' -f 1x1x'+zsubsample, verbose)
        ftmp_data = add_suffix(ftmp_data, '_sub')
        sct.run('sct_resample -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_sub')+' -f 1x1x'+zsubsample, verbose)
        ftmp_seg = add_suffix(ftmp_seg, '_sub')

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

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

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

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

        # Change orientation of input images to RPI
        sct.printv('\nChange orientation of input images to RPI...', verbose)
        sct.run('sct_image -i ' + ftmp_data + ' -setorient RPI -o ' + add_suffix(ftmp_data, '_rpi'))
        ftmp_data = add_suffix(ftmp_data, '_rpi')
        sct.run('sct_image -i ' + ftmp_seg + ' -setorient RPI -o ' + add_suffix(ftmp_seg, '_rpi'))
        ftmp_seg = add_suffix(ftmp_seg, '_rpi')
        sct.run('sct_image -i ' + ftmp_label + ' -setorient RPI -o ' + add_suffix(ftmp_label, '_rpi'))
        ftmp_label = add_suffix(ftmp_label, '_rpi')

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

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

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

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

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

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

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

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

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

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

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