def main(args=None): # initializations param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) fname_data = arguments['-i'] fname_seg = arguments['-s'] if '-l' in arguments: fname_landmarks = arguments['-l'] label_type = 'body' elif '-ldisc' in arguments: fname_landmarks = arguments['-ldisc'] label_type = 'disc' else: sct.printv('ERROR: Labels should be provided.', 1, 'error') if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = '' 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 fname_template_disc_labeling = path_template + 'template/' + 'PAM50_label_disc.nii.gz' # check file existence # TODO: no need to do that! sct.printv('\nCheck template files...') sct.check_file_exist(fname_template, verbose) sct.check_file_exist(fname_template_vertebral_labeling, verbose) sct.check_file_exist(fname_template_seg, verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) # sct.printv(arguments) sct.printv('\nCheck parameters:', verbose) sct.printv(' Data: ' + fname_data, verbose) sct.printv(' Landmarks: ' + fname_landmarks, verbose) sct.printv(' Segmentation: ' + fname_seg, verbose) sct.printv(' Path template: ' + path_template, verbose) sct.printv(' Remove temp files: ' + str(remove_temp_files), verbose) # 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, label_type=label_type) # 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' # ftmp_template_label_disc = 'template_label_disc.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) if label_type == 'disc': sct_convert.main(args=[ '-i', fname_template_disc_labeling, '-o', path_tmp + ftmp_template_label ]) # 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 if label_type == 'body': sct.printv('\nGenerate labels from template vertebral labeling', verbose) sct_label_utils.main(args=[ '-i', fname_template_vertebral_labeling, '-vert-body', '0', '-o', ftmp_template_label ]) # 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') # if only one label is present, force affine transformation to be Tx,Ty,Tz only (no scaling) if len(labels) == 1: paramreg.steps['0'].dof = 'Tx_Ty_Tz' sct.printv( 'WARNING: Only one label is present. Forcing initial transformation to: ' + paramreg.steps['0'].dof, 1, 'warning') # 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: import sct_straighten_spinalcord if __name__ == '__main__': sct_straighten_spinalcord.main(args=[ '-i', ftmp_seg, '-s', ftmp_seg, '-o', add_suffix(ftmp_seg, '_straight'), '-qc', '0', '-r', '0', '-v', str(verbose), '-param', 'template_orientation=1' ]) # 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(args=None): # initializations initz = '' initcenter = '' fname_initlabel = '' file_labelz = 'labelz.nii.gz' param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) fname_in = os.path.abspath(arguments["-i"]) fname_seg = os.path.abspath(arguments['-s']) contrast = arguments['-c'] path_template = os.path.abspath(arguments['-t']) scale_dist = arguments['-scale-dist'] if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = os.curdir param.path_qc = arguments.get("-qc", None) if '-discfile' in arguments: fname_disc = os.path.abspath(arguments['-discfile']) else: fname_disc = None if '-initz' in arguments: initz = arguments['-initz'] if '-initcenter' in arguments: initcenter = arguments['-initcenter'] # if user provided text file, parse and overwrite arguments if '-initfile' in arguments: file = open(arguments['-initfile'], 'r') initfile = ' ' + file.read().replace('\n', '') arg_initfile = initfile.split(' ') for idx_arg, arg in enumerate(arg_initfile): if arg == '-initz': initz = [int(x) for x in arg_initfile[idx_arg + 1].split(',')] if arg == '-initcenter': initcenter = int(arg_initfile[idx_arg + 1]) if '-initlabel' in arguments: # get absolute path of label fname_initlabel = os.path.abspath(arguments['-initlabel']) if '-param' in arguments: param.update(arguments['-param'][0]) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level remove_temp_files = int(arguments['-r']) denoise = int(arguments['-denoise']) laplacian = int(arguments['-laplacian']) path_tmp = sct.tmp_create(basename="label_vertebrae", verbose=verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder...', verbose) Image(fname_in).save(os.path.join(path_tmp, "data.nii")) Image(fname_seg).save(os.path.join(path_tmp, "segmentation.nii")) # Go go temp folder curdir = os.getcwd() os.chdir(path_tmp) # Straighten spinal cord sct.printv('\nStraighten spinal cord...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) cache_sig = sct.cache_signature(input_files=[fname_in, fname_seg], ) cachefile = os.path.join(curdir, "straightening.cache") if sct.cache_valid(cachefile, cache_sig) and os.path.isfile( os.path.join( curdir, "warp_curve2straight.nii.gz")) and os.path.isfile( os.path.join( curdir, "warp_straight2curve.nii.gz")) and os.path.isfile( os.path.join(curdir, "straight_ref.nii.gz")): # if they exist, copy them into current folder sct.printv('Reusing existing warping field which seems to be valid', verbose, 'warning') sct.copy(os.path.join(curdir, "warp_curve2straight.nii.gz"), 'warp_curve2straight.nii.gz') sct.copy(os.path.join(curdir, "warp_straight2curve.nii.gz"), 'warp_straight2curve.nii.gz') sct.copy(os.path.join(curdir, "straight_ref.nii.gz"), 'straight_ref.nii.gz') # apply straightening s, o = sct.run([ 'sct_apply_transfo', '-i', 'data.nii', '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', 'data_straight.nii' ]) else: sct_straighten_spinalcord.main(args=[ '-i', 'data.nii', '-s', 'segmentation.nii', '-r', str(remove_temp_files), '-v', str(verbose), ]) sct.cache_save(cachefile, cache_sig) # resample to 0.5mm isotropic to match template resolution sct.printv('\nResample to 0.5mm isotropic...', verbose) s, o = sct.run([ 'sct_resample', '-i', 'data_straight.nii', '-mm', '0.5x0.5x0.5', '-x', 'linear', '-o', 'data_straightr.nii' ], verbose=verbose) # Apply straightening to segmentation # N.B. Output is RPI sct.printv('\nApply straightening to segmentation...', verbose) sct.run( 'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' % ('segmentation.nii', 'data_straightr.nii', 'warp_curve2straight.nii.gz', 'segmentation_straight.nii', 'Linear'), verbose=verbose, is_sct_binary=True, ) # Threshold segmentation at 0.5 sct.run([ 'sct_maths', '-i', 'segmentation_straight.nii', '-thr', '0.5', '-o', 'segmentation_straight.nii' ], verbose) # If disc label file is provided, label vertebrae using that file instead of automatically if fname_disc: # Apply straightening to disc-label sct.printv('\nApply straightening to disc labels...', verbose) sct.run( 'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' % (fname_disc, 'data_straightr.nii', 'warp_curve2straight.nii.gz', 'labeldisc_straight.nii.gz', 'NearestNeighbor'), verbose=verbose, is_sct_binary=True, ) label_vert('segmentation_straight.nii', 'labeldisc_straight.nii.gz', verbose=1) else: # create label to identify disc sct.printv('\nCreate label to identify disc...', verbose) fname_labelz = os.path.join(path_tmp, file_labelz) if initz or initcenter: if initcenter: # find z centered in FOV nii = Image('segmentation.nii').change_orientation("RPI") nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions z_center = int(np.round(nz / 2)) # get z_center initz = [z_center, initcenter] # create single label and output as labels.nii.gz label = ProcessLabels( 'segmentation.nii', fname_output='tmp.labelz.nii.gz', coordinates=['{},{}'.format(initz[0], initz[1])]) im_label = label.process('create-seg') im_label.data = dilate( im_label.data, 3, 'ball') # TODO: create a dilation method specific to labels, # which does not apply a convolution across all voxels (highly inneficient) im_label.save(fname_labelz) elif fname_initlabel: Image(fname_initlabel).save(fname_labelz) else: # automatically finds C2-C3 disc im_data = Image('data.nii') im_seg = Image('segmentation.nii') if not remove_temp_files: # because verbose is here also used for keeping temp files verbose_detect_c2c3 = 2 else: verbose_detect_c2c3 = 0 im_label_c2c3 = detect_c2c3(im_data, im_seg, contrast, verbose=verbose_detect_c2c3) ind_label = np.where(im_label_c2c3.data) if not np.size(ind_label) == 0: im_label_c2c3.data[ind_label] = 3 else: sct.printv( 'Automatic C2-C3 detection failed. Please provide manual label with sct_label_utils', 1, 'error') sys.exit() im_label_c2c3.save(fname_labelz) # dilate label so it is not lost when applying warping dilate(Image(fname_labelz), 3, 'ball').save(fname_labelz) # Apply straightening to z-label sct.printv('\nAnd apply straightening to label...', verbose) sct.run( 'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' % (file_labelz, 'data_straightr.nii', 'warp_curve2straight.nii.gz', 'labelz_straight.nii.gz', 'NearestNeighbor'), verbose=verbose, is_sct_binary=True, ) # get z value and disk value to initialize labeling sct.printv('\nGet z and disc values from straight label...', verbose) init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz') sct.printv('.. ' + str(init_disc), verbose) # denoise data if denoise: sct.printv('\nDenoise data...', verbose) sct.run([ 'sct_maths', '-i', 'data_straightr.nii', '-denoise', 'h=0.05', '-o', 'data_straightr.nii' ], verbose) # apply laplacian filtering if laplacian: sct.printv('\nApply Laplacian filter...', verbose) sct.run([ 'sct_maths', '-i', 'data_straightr.nii', '-laplacian', '1', '-o', 'data_straightr.nii' ], verbose) # detect vertebral levels on straight spinal cord init_disc[1] = init_disc[1] - 1 vertebral_detection('data_straightr.nii', 'segmentation_straight.nii', contrast, param, init_disc=init_disc, verbose=verbose, path_template=path_template, path_output=path_output, scale_dist=scale_dist) # un-straighten labeled spinal cord sct.printv('\nUn-straighten labeling...', verbose) sct.run( 'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' % ('segmentation_straight_labeled.nii', 'segmentation.nii', 'warp_straight2curve.nii.gz', 'segmentation_labeled.nii', 'NearestNeighbor'), verbose=verbose, is_sct_binary=True, ) # Clean labeled segmentation sct.printv( '\nClean labeled segmentation (correct interpolation errors)...', verbose) clean_labeled_segmentation('segmentation_labeled.nii', 'segmentation.nii', 'segmentation_labeled.nii') # label discs sct.printv('\nLabel discs...', verbose) label_discs('segmentation_labeled.nii', verbose=verbose) # come back os.chdir(curdir) # Generate output files path_seg, file_seg, ext_seg = sct.extract_fname(fname_seg) fname_seg_labeled = os.path.join(path_output, file_seg + '_labeled' + ext_seg) sct.printv('\nGenerate output files...', verbose) sct.generate_output_file( os.path.join(path_tmp, "segmentation_labeled.nii"), fname_seg_labeled) sct.generate_output_file( os.path.join(path_tmp, "segmentation_labeled_disc.nii"), os.path.join(path_output, file_seg + '_labeled_discs' + ext_seg)) # copy straightening files in case subsequent SCT functions need them sct.generate_output_file( os.path.join(path_tmp, "warp_curve2straight.nii.gz"), os.path.join(path_output, "warp_curve2straight.nii.gz"), verbose) sct.generate_output_file( os.path.join(path_tmp, "warp_straight2curve.nii.gz"), os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "straight_ref.nii.gz"), os.path.join(path_output, "straight_ref.nii.gz"), verbose) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...', verbose) sct.rmtree(path_tmp) # Generate QC report if param.path_qc is not None: path_qc = os.path.abspath(param.path_qc) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) labeled_seg_file = os.path.join(path_output, file_seg + '_labeled' + ext_seg) generate_qc(fname_in, fname_seg=labeled_seg_file, args=args, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_label_vertebrae') sct.display_viewer_syntax([fname_in, fname_seg_labeled], colormaps=['', 'subcortical'], opacities=['1', '0.5'])