def segment(self): before = time.time() sct.run('mkdir ' + self.tmp_dir) self.segmentation_pipeline() # Generate output files: for res_fname in self.res_names.values(): sct.generate_output_file(self.tmp_dir+res_fname, self.seg_param.output_path+res_fname) if self.ref_gm_seg_fname is not None: sct.generate_output_file(self.tmp_dir+self.dice_name, self.seg_param.output_path+self.dice_name) sct.generate_output_file(self.tmp_dir+self.hausdorff_name, self.seg_param.output_path+self.hausdorff_name) if compute_ratio: sct.generate_output_file(self.tmp_dir+self.ratio_name, self.seg_param.output_path+self.ratio_name) after = time.time() sct.printv('Done! (in ' + str(after-before) + ' sec) \nTo see the result, type :') if self.seg_param.res_type == 'binary': wm_col = 'Red' gm_col = 'Blue' b = '0,1' else: wm_col = 'Blue-Lightblue' gm_col = 'Red-Yellow' b = '0.3,1' sct.printv('fslview ' + self.target_fname + ' '+self.seg_param.output_path+self.res_names['wm_seg']+' -l '+wm_col+' -t 0.4 -b '+b+' '+self.seg_param.output_path+self.res_names['gm_seg']+' -l '+gm_col+' -t 0.4 -b '+b+' &', self.seg_param.verbose, 'info') if self.seg_param.qc: # output QC image im = Image(self.target_fname) im_gmseg = Image(self.seg_param.output_path+self.res_names['gm_seg']) im.save_quality_control(plane='axial', n_slices=5, seg=im_gmseg, thr=float(b.split(',')[0]), cmap_col='red-yellow', path_output=self.seg_param.output_path) if self.seg_param.remove_tmp: sct.printv('Remove temporary folder ...', self.seg_param.verbose, 'normal') sct.run('rm -rf '+self.tmp_dir)
def straighten(self): """ Straighten spinal cord. Steps: (everything is done in physical space) 1. open input image and centreline image 2. extract bspline fitting of the centreline, and its derivatives 3. compute length of centerline 4. compute and generate straight space 5. compute transformations for each voxel of one space: (done using matrices --> improves speed by a factor x300) a. determine which plane of spinal cord centreline it is included b. compute the position of the voxel in the plane (X and Y distance from centreline, along the plane) c. find the correspondant centreline point in the other space d. find the correspondance of the voxel in the corresponding plane 6. generate warping fields for each transformations 7. write warping fields and apply them step 5.b: how to find the corresponding plane? The centerline plane corresponding to a voxel correspond to the nearest point of the centerline. However, we need to compute the distance between the voxel position and the plane to be sure it is part of the plane and not too distant. If it is more far than a threshold, warping value should be 0. step 5.d: how to make the correspondance between centerline point in both images? Both centerline have the same lenght. Therefore, we can map centerline point via their position along the curve. If we use the same number of points uniformely along the spinal cord (1000 for example), the correspondance is straight-forward. :return: """ # Initialization fname_anat = self.input_filename fname_centerline = self.centerline_filename fname_output = self.output_filename remove_temp_files = self.remove_temp_files verbose = self.verbose interpolation_warp = self.interpolation_warp # TODO: remove this # start timer start_time = time.time() # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_tmp = sct.tmp_create(basename="straighten_spinalcord", verbose=verbose) # Copying input data to tmp folder sct.printv('\nCopy files to tmp folder...', verbose) Image(fname_anat).save(os.path.join(path_tmp, "data.nii")) Image(fname_centerline).save( os.path.join(path_tmp, "centerline.nii.gz")) if self.use_straight_reference: Image(self.centerline_reference_filename).save( os.path.join(path_tmp, "centerline_ref.nii.gz")) if self.discs_input_filename != '': Image(self.discs_input_filename).save( os.path.join(path_tmp, "labels_input.nii.gz")) if self.discs_ref_filename != '': Image(self.discs_ref_filename).save( os.path.join(path_tmp, "labels_ref.nii.gz")) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # Change orientation of the input centerline into RPI image_centerline = Image("centerline.nii.gz").change_orientation( "RPI").save("centerline_rpi.nii.gz", mutable=True) # Get dimension nx, ny, nz, nt, px, py, pz, pt = image_centerline.dim if self.speed_factor != 1.0: intermediate_resampling = True px_r, py_r, pz_r = px * self.speed_factor, py * self.speed_factor, pz * self.speed_factor else: intermediate_resampling = False if intermediate_resampling: sct.mv('centerline_rpi.nii.gz', 'centerline_rpi_native.nii.gz') pz_native = pz # TODO: remove system call sct.run([ 'sct_resample', '-i', 'centerline_rpi_native.nii.gz', '-mm', str(px_r) + 'x' + str(py_r) + 'x' + str(pz_r), '-o', 'centerline_rpi.nii.gz' ]) image_centerline = Image('centerline_rpi.nii.gz') nx, ny, nz, nt, px, py, pz, pt = image_centerline.dim if np.min(image_centerline.data) < 0 or np.max( image_centerline.data) > 1: image_centerline.data[image_centerline.data < 0] = 0 image_centerline.data[image_centerline.data > 1] = 1 image_centerline.save() # 2. extract bspline fitting of the centerline, and its derivatives img_ctl = Image('centerline_rpi.nii.gz') centerline = _get_centerline(img_ctl, self.param_centerline, verbose) number_of_points = centerline.number_of_points # ========================================================================================== logger.info('Create the straight space and the safe zone') # 3. compute length of centerline # compute the length of the spinal cord based on fitted centerline and size of centerline in z direction # Computation of the safe zone. # The safe zone is defined as the length of the spinal cord for which an axial segmentation will be complete # The safe length (to remove) is computed using the safe radius (given as parameter) and the angle of the # last centerline point with the inferior-superior direction. Formula: Ls = Rs * sin(angle) # Calculate Ls for both edges and remove appropriate number of centerline points radius_safe = 0.0 # mm # inferior edge u = centerline.derivatives[0] v = np.array([0, 0, -1]) angle_inferior = np.arctan2(np.linalg.norm(np.cross(u, v)), np.dot(u, v)) length_safe_inferior = radius_safe * np.sin(angle_inferior) # superior edge u = centerline.derivatives[-1] v = np.array([0, 0, 1]) angle_superior = np.arctan2(np.linalg.norm(np.cross(u, v)), np.dot(u, v)) length_safe_superior = radius_safe * np.sin(angle_superior) # remove points inferior_bound = bisect.bisect(centerline.progressive_length, length_safe_inferior) - 1 superior_bound = centerline.number_of_points - bisect.bisect( centerline.progressive_length_inverse, length_safe_superior) z_centerline = centerline.points[:, 2] length_centerline = centerline.length size_z_centerline = z_centerline[-1] - z_centerline[0] # compute the size factor between initial centerline and straight bended centerline factor_curved_straight = length_centerline / size_z_centerline middle_slice = (z_centerline[0] + z_centerline[-1]) / 2.0 bound_curved = [ z_centerline[inferior_bound], z_centerline[superior_bound] ] bound_straight = [(z_centerline[inferior_bound] - middle_slice) * factor_curved_straight + middle_slice, (z_centerline[superior_bound] - middle_slice) * factor_curved_straight + middle_slice] logger.info('Length of spinal cord: {}'.format(length_centerline)) logger.info( 'Size of spinal cord in z direction: {}'.format(size_z_centerline)) logger.info('Ratio length/size: {}'.format(factor_curved_straight)) logger.info( 'Safe zone boundaries (curved space): {}'.format(bound_curved)) logger.info( 'Safe zone boundaries (straight space): {}'.format(bound_straight)) # 4. compute and generate straight space # points along curved centerline are already regularly spaced. # calculate position of points along straight centerline # Create straight NIFTI volumes. # ========================================================================================== # TODO: maybe this if case is not needed? if self.use_straight_reference: image_centerline_pad = Image('centerline_rpi.nii.gz') nx, ny, nz, nt, px, py, pz, pt = image_centerline_pad.dim fname_ref = 'centerline_ref_rpi.nii.gz' image_centerline_straight = Image('centerline_ref.nii.gz') \ .change_orientation("RPI") \ .save(fname_ref, mutable=True) centerline_straight = _get_centerline(image_centerline_straight, algo_fitting, self.degree, verbose) nx_s, ny_s, nz_s, nt_s, px_s, py_s, pz_s, pt_s = image_centerline_straight.dim # Prepare warping fields headers hdr_warp = image_centerline_pad.hdr.copy() hdr_warp.set_data_dtype('float32') hdr_warp_s = image_centerline_straight.hdr.copy() hdr_warp_s.set_data_dtype('float32') if self.discs_input_filename != "" and self.discs_ref_filename != "": discs_input_image = Image('labels_input.nii.gz') coord = discs_input_image.getNonZeroCoordinates( sorting='z', reverse_coord=True) coord_physical = [] for c in coord: c_p = discs_input_image.transfo_pix2phys([[c.x, c.y, c.z] ]).tolist()[0] c_p.append(c.value) coord_physical.append(c_p) centerline.compute_vertebral_distribution(coord_physical) centerline.save_centerline( image=discs_input_image, fname_output='discs_input_image.nii.gz') discs_ref_image = Image('labels_ref.nii.gz') coord = discs_ref_image.getNonZeroCoordinates( sorting='z', reverse_coord=True) coord_physical = [] for c in coord: c_p = discs_ref_image.transfo_pix2phys([[c.x, c.y, c.z]]).tolist()[0] c_p.append(c.value) coord_physical.append(c_p) centerline_straight.compute_vertebral_distribution( coord_physical) centerline_straight.save_centerline( image=discs_ref_image, fname_output='discs_ref_image.nii.gz') else: logger.info( 'Pad input volume to account for spinal cord length...') start_point, end_point = bound_straight[0], bound_straight[1] offset_z = 0 # if the destination image is resampled, we still create the straight reference space with the native # resolution. # TODO: Maybe this if case is not needed? if intermediate_resampling: padding_z = int( np.ceil(1.5 * ((length_centerline - size_z_centerline) / 2.0) / pz_native)) sct.run([ 'sct_image', '-i', 'centerline_rpi_native.nii.gz', '-o', 'tmp.centerline_pad_native.nii.gz', '-pad', '0,0,' + str(padding_z) ]) image_centerline_pad = Image('centerline_rpi_native.nii.gz') nx, ny, nz, nt, px, py, pz, pt = image_centerline_pad.dim start_point_coord_native = image_centerline_pad.transfo_phys2pix( [[0, 0, start_point]])[0] end_point_coord_native = image_centerline_pad.transfo_phys2pix( [[0, 0, end_point]])[0] straight_size_x = int(self.xy_size / px) straight_size_y = int(self.xy_size / py) warp_space_x = [ int(np.round(nx / 2)) - straight_size_x, int(np.round(nx / 2)) + straight_size_x ] warp_space_y = [ int(np.round(ny / 2)) - straight_size_y, int(np.round(ny / 2)) + straight_size_y ] if warp_space_x[0] < 0: warp_space_x[1] += warp_space_x[0] - 2 warp_space_x[0] = 0 if warp_space_y[0] < 0: warp_space_y[1] += warp_space_y[0] - 2 warp_space_y[0] = 0 spec = dict(( (0, warp_space_x), (1, warp_space_y), (2, (0, end_point_coord_native[2] - start_point_coord_native[2])), )) msct_image.spatial_crop( Image("tmp.centerline_pad_native.nii.gz"), spec).save("tmp.centerline_pad_crop_native.nii.gz") fname_ref = 'tmp.centerline_pad_crop_native.nii.gz' offset_z = 4 else: fname_ref = 'tmp.centerline_pad_crop.nii.gz' nx, ny, nz, nt, px, py, pz, pt = image_centerline.dim padding_z = int( np.ceil(1.5 * ((length_centerline - size_z_centerline) / 2.0) / pz)) + offset_z image_centerline_pad = pad_image(image_centerline, pad_z_i=padding_z, pad_z_f=padding_z) nx, ny, nz = image_centerline_pad.data.shape hdr_warp = image_centerline_pad.hdr.copy() hdr_warp.set_data_dtype('float32') start_point_coord = image_centerline_pad.transfo_phys2pix( [[0, 0, start_point]])[0] end_point_coord = image_centerline_pad.transfo_phys2pix( [[0, 0, end_point]])[0] straight_size_x = int(self.xy_size / px) straight_size_y = int(self.xy_size / py) warp_space_x = [ int(np.round(nx / 2)) - straight_size_x, int(np.round(nx / 2)) + straight_size_x ] warp_space_y = [ int(np.round(ny / 2)) - straight_size_y, int(np.round(ny / 2)) + straight_size_y ] if warp_space_x[0] < 0: warp_space_x[1] += warp_space_x[0] - 2 warp_space_x[0] = 0 if warp_space_x[1] >= nx: warp_space_x[1] = nx - 1 if warp_space_y[0] < 0: warp_space_y[1] += warp_space_y[0] - 2 warp_space_y[0] = 0 if warp_space_y[1] >= ny: warp_space_y[1] = ny - 1 spec = dict(( (0, warp_space_x), (1, warp_space_y), (2, (0, end_point_coord[2] - start_point_coord[2] + offset_z)), )) image_centerline_straight = msct_image.spatial_crop( image_centerline_pad, spec) nx_s, ny_s, nz_s, nt_s, px_s, py_s, pz_s, pt_s = image_centerline_straight.dim hdr_warp_s = image_centerline_straight.hdr.copy() hdr_warp_s.set_data_dtype('float32') if self.template_orientation == 1: raise NotImplementedError() start_point_coord = image_centerline_pad.transfo_phys2pix( [[0, 0, start_point]])[0] end_point_coord = image_centerline_pad.transfo_phys2pix( [[0, 0, end_point]])[0] number_of_voxel = nx * ny * nz logger.debug('Number of voxels: {}'.format(number_of_voxel)) time_centerlines = time.time() coord_straight = np.empty((number_of_points, 3)) coord_straight[..., 0] = int(np.round(nx_s / 2)) coord_straight[..., 1] = int(np.round(ny_s / 2)) coord_straight[..., 2] = np.linspace( 0, end_point_coord[2] - start_point_coord[2], number_of_points) coord_phys_straight = image_centerline_straight.transfo_pix2phys( coord_straight) derivs_straight = np.empty((number_of_points, 3)) derivs_straight[..., 0] = derivs_straight[..., 1] = 0 derivs_straight[..., 2] = 1 dx_straight, dy_straight, dz_straight = derivs_straight.T centerline_straight = Centerline(coord_phys_straight[:, 0], coord_phys_straight[:, 1], coord_phys_straight[:, 2], dx_straight, dy_straight, dz_straight) time_centerlines = time.time() - time_centerlines logger.info('Time to generate centerline: {} ms'.format( np.round(time_centerlines * 1000.0))) if verbose == 2: # TODO: use OO import matplotlib.pyplot as plt from datetime import datetime curved_points = centerline.progressive_length straight_points = centerline_straight.progressive_length range_points = np.linspace(0, 1, number_of_points) dist_curved = np.zeros(number_of_points) dist_straight = np.zeros(number_of_points) for i in range(1, number_of_points): dist_curved[i] = dist_curved[ i - 1] + curved_points[i - 1] / centerline.length dist_straight[i] = dist_straight[i - 1] + straight_points[ i - 1] / centerline_straight.length plt.plot(range_points, dist_curved) plt.plot(range_points, dist_straight) plt.grid(True) plt.savefig('fig_straighten_' + datetime.now().strftime("%y%m%d%H%M%S%f") + '.png') plt.close() # alignment_mode = 'length' alignment_mode = 'levels' lookup_curved2straight = list(range(centerline.number_of_points)) if self.discs_input_filename != "": # create look-up table curved to straight for index in range(centerline.number_of_points): disc_label = centerline.l_points[index] if alignment_mode == 'length': relative_position = centerline.dist_points[index] else: relative_position = centerline.dist_points_rel[index] idx_closest = centerline_straight.get_closest_to_absolute_position( disc_label, relative_position, backup_index=index, backup_centerline=centerline_straight, mode=alignment_mode) if idx_closest is not None: lookup_curved2straight[index] = idx_closest else: lookup_curved2straight[index] = 0 for p in range(0, len(lookup_curved2straight) // 2): if lookup_curved2straight[p] == lookup_curved2straight[p + 1]: lookup_curved2straight[p] = 0 else: break for p in range( len(lookup_curved2straight) - 1, len(lookup_curved2straight) // 2, -1): if lookup_curved2straight[p] == lookup_curved2straight[p - 1]: lookup_curved2straight[p] = 0 else: break lookup_curved2straight = np.array(lookup_curved2straight) lookup_straight2curved = list( range(centerline_straight.number_of_points)) if self.discs_input_filename != "": for index in range(centerline_straight.number_of_points): disc_label = centerline_straight.l_points[index] if alignment_mode == 'length': relative_position = centerline_straight.dist_points[index] else: relative_position = centerline_straight.dist_points_rel[ index] idx_closest = centerline.get_closest_to_absolute_position( disc_label, relative_position, backup_index=index, backup_centerline=centerline_straight, mode=alignment_mode) if idx_closest is not None: lookup_straight2curved[index] = idx_closest for p in range(0, len(lookup_straight2curved) // 2): if lookup_straight2curved[p] == lookup_straight2curved[p + 1]: lookup_straight2curved[p] = 0 else: break for p in range( len(lookup_straight2curved) - 1, len(lookup_straight2curved) // 2, -1): if lookup_straight2curved[p] == lookup_straight2curved[p - 1]: lookup_straight2curved[p] = 0 else: break lookup_straight2curved = np.array(lookup_straight2curved) # Create volumes containing curved and straight warping fields data_warp_curved2straight = np.zeros((nx_s, ny_s, nz_s, 1, 3)) data_warp_straight2curved = np.zeros((nx, ny, nz, 1, 3)) # 5. compute transformations # Curved and straight images and the same dimensions, so we compute both warping fields at the same time. # b. determine which plane of spinal cord centreline it is included # sct.printv(nx * ny * nz, nx_s * ny_s * nz_s) if self.curved2straight: for u in tqdm(range(nz_s)): x_s, y_s, z_s = np.mgrid[0:nx_s, 0:ny_s, u:u + 1] indexes_straight = np.array( list(zip(x_s.ravel(), y_s.ravel(), z_s.ravel()))) physical_coordinates_straight = image_centerline_straight.transfo_pix2phys( indexes_straight) nearest_indexes_straight = centerline_straight.find_nearest_indexes( physical_coordinates_straight) distances_straight = centerline_straight.get_distances_from_planes( physical_coordinates_straight, nearest_indexes_straight) lookup = lookup_straight2curved[nearest_indexes_straight] indexes_out_distance_straight = np.logical_or( np.logical_or( distances_straight > self.threshold_distance, distances_straight < -self.threshold_distance), lookup == 0) projected_points_straight = centerline_straight.get_projected_coordinates_on_planes( physical_coordinates_straight, nearest_indexes_straight) coord_in_planes_straight = centerline_straight.get_in_plans_coordinates( projected_points_straight, nearest_indexes_straight) coord_straight2curved = centerline.get_inverse_plans_coordinates( coord_in_planes_straight, lookup) displacements_straight = coord_straight2curved - physical_coordinates_straight # Invert Z coordinate as ITK & ANTs physical coordinate system is LPS- (RAI+) # while ours is LPI- # Refs: https://sourceforge.net/p/advants/discussion/840261/thread/2a1e9307/#fb5a # https://www.slicer.org/wiki/Coordinate_systems displacements_straight[:, 2] = -displacements_straight[:, 2] displacements_straight[indexes_out_distance_straight] = [ 100000.0, 100000.0, 100000.0 ] data_warp_curved2straight[indexes_straight[:, 0], indexes_straight[:, 1], indexes_straight[:, 2], 0, :]\ = -displacements_straight if self.straight2curved: for u in tqdm(range(nz)): x, y, z = np.mgrid[0:nx, 0:ny, u:u + 1] indexes = np.array(list(zip(x.ravel(), y.ravel(), z.ravel()))) physical_coordinates = image_centerline_pad.transfo_pix2phys( indexes) nearest_indexes_curved = centerline.find_nearest_indexes( physical_coordinates) distances_curved = centerline.get_distances_from_planes( physical_coordinates, nearest_indexes_curved) lookup = lookup_curved2straight[nearest_indexes_curved] indexes_out_distance_curved = np.logical_or( np.logical_or(distances_curved > self.threshold_distance, distances_curved < -self.threshold_distance), lookup == 0) projected_points_curved = centerline.get_projected_coordinates_on_planes( physical_coordinates, nearest_indexes_curved) coord_in_planes_curved = centerline.get_in_plans_coordinates( projected_points_curved, nearest_indexes_curved) coord_curved2straight = centerline_straight.points[lookup] coord_curved2straight[:, 0:2] += coord_in_planes_curved[:, 0:2] coord_curved2straight[:, 2] += distances_curved displacements_curved = coord_curved2straight - physical_coordinates displacements_curved[:, 2] = -displacements_curved[:, 2] displacements_curved[indexes_out_distance_curved] = [ 100000.0, 100000.0, 100000.0 ] data_warp_straight2curved[indexes[:, 0], indexes[:, 1], indexes[:, 2], 0, :] = -displacements_curved # Creation of the safe zone based on pre-calculated safe boundaries coord_bound_curved_inf, coord_bound_curved_sup = image_centerline_pad.transfo_phys2pix( [[0, 0, bound_curved[0]]]), image_centerline_pad.transfo_phys2pix( [[0, 0, bound_curved[1]]]) coord_bound_straight_inf, coord_bound_straight_sup = image_centerline_straight.transfo_phys2pix( [[0, 0, bound_straight[0]]]), image_centerline_straight.transfo_phys2pix( [[0, 0, bound_straight[1]]]) if radius_safe > 0: data_warp_curved2straight[:, :, 0:coord_bound_straight_inf[0][2], 0, :] = 100000.0 data_warp_curved2straight[:, :, coord_bound_straight_sup[0][2]:, 0, :] = 100000.0 data_warp_straight2curved[:, :, 0:coord_bound_curved_inf[0][2], 0, :] = 100000.0 data_warp_straight2curved[:, :, coord_bound_curved_sup[0][2]:, 0, :] = 100000.0 # Generate warp files as a warping fields hdr_warp_s.set_intent('vector', (), '') hdr_warp_s.set_data_dtype('float32') hdr_warp.set_intent('vector', (), '') hdr_warp.set_data_dtype('float32') if self.curved2straight: img = Nifti1Image(data_warp_curved2straight, None, hdr_warp_s) save(img, 'tmp.curve2straight.nii.gz') logger.info('Warping field generated: tmp.curve2straight.nii.gz') if self.straight2curved: img = Nifti1Image(data_warp_straight2curved, None, hdr_warp) save(img, 'tmp.straight2curve.nii.gz') logger.info('Warping field generated: tmp.straight2curve.nii.gz') image_centerline_straight.save(fname_ref) if self.curved2straight: logger.info('Apply transformation to input image...') sct.run([ 'isct_antsApplyTransforms', '-d', '3', '-r', fname_ref, '-i', 'data.nii', '-o', 'tmp.anat_rigid_warp.nii.gz', '-t', 'tmp.curve2straight.nii.gz', '-n', 'BSpline[3]' ], is_sct_binary=True, verbose=verbose) if self.accuracy_results: time_accuracy_results = time.time() # compute the error between the straightened centerline/segmentation and the central vertical line. # Ideally, the error should be zero. # Apply deformation to input image logger.info('Apply transformation to centerline image...') sct.run([ 'isct_antsApplyTransforms', '-d', '3', '-r', fname_ref, '-i', 'centerline.nii.gz', '-o', 'tmp.centerline_straight.nii.gz', '-t', 'tmp.curve2straight.nii.gz', '-n', 'NearestNeighbor' ], is_sct_binary=True, verbose=verbose) file_centerline_straight = Image('tmp.centerline_straight.nii.gz', verbose=verbose) nx, ny, nz, nt, px, py, pz, pt = file_centerline_straight.dim coordinates_centerline = file_centerline_straight.getNonZeroCoordinates( sorting='z') mean_coord = [] for z in range(coordinates_centerline[0].z, coordinates_centerline[-1].z): temp_mean = [ coord.value for coord in coordinates_centerline if coord.z == z ] if temp_mean: mean_value = np.mean(temp_mean) mean_coord.append( np.mean([[ coord.x * coord.value / mean_value, coord.y * coord.value / mean_value ] for coord in coordinates_centerline if coord.z == z], axis=0)) # compute error between the straightened centerline and the straight line. x0 = file_centerline_straight.data.shape[0] / 2.0 y0 = file_centerline_straight.data.shape[1] / 2.0 count_mean = 0 if number_of_points >= 10: mean_c = mean_coord[ 2: -2] # we don't include the four extrema because there are usually messy. else: mean_c = mean_coord for coord_z in mean_c: if not np.isnan(np.sum(coord_z)): dist = ((x0 - coord_z[0]) * px)**2 + ( (y0 - coord_z[1]) * py)**2 self.mse_straightening += dist dist = np.sqrt(dist) if dist > self.max_distance_straightening: self.max_distance_straightening = dist count_mean += 1 self.mse_straightening = np.sqrt(self.mse_straightening / float(count_mean)) self.elapsed_time_accuracy = time.time() - time_accuracy_results os.chdir(curdir) # Generate output file (in current folder) # TODO: do not uncompress the warping field, it is too time consuming! logger.info('Generate output files...') if self.curved2straight: sct.generate_output_file( os.path.join(path_tmp, "tmp.curve2straight.nii.gz"), os.path.join(self.path_output, "warp_curve2straight.nii.gz"), verbose) if self.straight2curved: sct.generate_output_file( os.path.join(path_tmp, "tmp.straight2curve.nii.gz"), os.path.join(self.path_output, "warp_straight2curve.nii.gz"), verbose) # create ref_straight.nii.gz file that can be used by other SCT functions that need a straight reference space if self.curved2straight: sct.copy(os.path.join(path_tmp, "tmp.anat_rigid_warp.nii.gz"), os.path.join(self.path_output, "straight_ref.nii.gz")) # move straightened input file if fname_output == '': fname_straight = sct.generate_output_file( os.path.join(path_tmp, "tmp.anat_rigid_warp.nii.gz"), os.path.join(self.path_output, file_anat + "_straight" + ext_anat), verbose) else: fname_straight = sct.generate_output_file( os.path.join(path_tmp, "tmp.anat_rigid_warp.nii.gz"), os.path.join(self.path_output, fname_output), verbose) # straightened anatomic # Remove temporary files if remove_temp_files: logger.info('Remove temporary files...') sct.rmtree(path_tmp) if self.accuracy_results: logger.info('Maximum x-y error: {} mm'.format( self.max_distance_straightening)) logger.info('Accuracy of straightening (MSE): {} mm'.format( self.mse_straightening)) # display elapsed time self.elapsed_time = int(np.round(time.time() - start_time)) return fname_straight
def main(): # Initialization fname_data = '' suffix_out = '_crop' remove_temp_files = param.remove_temp_files verbose = param.verbose fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI remove_temp_files = param.remove_temp_files # Parameters for debug mode if param.debug: print '\n*** WARNING: DEBUG MODE ON ***\n' fname_data = path_sct + '/testing/data/errsm_23/t2/t2.nii.gz' remove_temp_files = 0 else: # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:], 'hi:r:v:') except getopt.GetoptError: usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_data = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_data == '': usage() # Check file existence sct.printv('\nCheck file existence...', verbose) sct.check_file_exist(fname_data, verbose) # Get dimensions of data sct.printv('\nGet dimensions of data...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_data).dim sct.printv('.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # check if 4D data if not nt == 1: sct.printv( '\nERROR in ' + os.path.basename(__file__) + ': Data should be 3D.\n', 1, 'error') sys.exit(2) # print arguments print '\nCheck parameters:' print ' data ................... ' + fname_data print # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(fname_data) path_out, file_out, ext_out = '', file_data + suffix_out, ext_data # create temporary folder path_tmp = 'tmp.' + time.strftime("%y%m%d%H%M%S") + '/' sct.run('mkdir ' + path_tmp) # copy files into tmp folder sct.run('isct_c3d ' + fname_data + ' -o ' + path_tmp + 'data.nii') # go to tmp folder os.chdir(path_tmp) # change orientation sct.printv('\nChange orientation to RPI...', verbose) set_orientation('data.nii', 'RPI', 'data_rpi.nii') # get image of medial slab sct.printv('\nGet image of medial slab...', verbose) image_array = nibabel.load('data_rpi.nii').get_data() nx, ny, nz = image_array.shape scipy.misc.imsave('image.jpg', image_array[math.floor(nx / 2), :, :]) # Display the image sct.printv('\nDisplay image and get cropping region...', verbose) fig = plt.figure() # fig = plt.gcf() # ax = plt.gca() ax = fig.add_subplot(111) img = mpimg.imread("image.jpg") implot = ax.imshow(img.T) implot.set_cmap('gray') plt.gca().invert_yaxis() # mouse callback ax.set_title( 'Left click on the top and bottom of your cropping field.\n Right click to remove last point.\n Close window when your done.' ) line, = ax.plot([], [], 'ro') # empty line cropping_coordinates = LineBuilder(line) plt.show() # disconnect callback # fig.canvas.mpl_disconnect(line) # check if user clicked two times if len(cropping_coordinates.xs) != 2: sct.printv('\nERROR: You have to select two points. Exit program.\n', 1, 'error') sys.exit(2) # convert coordinates to integer zcrop = [int(i) for i in cropping_coordinates.ys] # sort coordinates zcrop.sort() # crop image sct.printv('\nCrop image...', verbose) nii = Image('data_rpi.nii') data_crop = nii.data[:, :, zcrop[0]:zcrop[1]] nii.data = data_crop nii.setFileName('data_rpi_crop.nii') nii.save() # come back to parent folder os.chdir('..') sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp + 'data_rpi_crop.nii', path_out + file_out + ext_out) # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf ' + path_tmp) # to view results print '\nDone! To view results, type:' print 'fslview ' + path_out + file_out + ext_out + ' &' print
def crop_with_gui(self): import matplotlib.pyplot as plt import matplotlib.image as mpimg # Initialization fname_data = self.input_filename suffix_out = '_crop' remove_temp_files = self.rm_tmp_files verbose = self.verbose # Check file existence sct.printv('\nCheck file existence...', verbose) sct.check_file_exist(fname_data, verbose) # Get dimensions of data sct.printv('\nGet dimensions of data...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_data).dim sct.printv('.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # check if 4D data if not nt == 1: sct.printv('\nERROR in ' + os.path.basename(__file__) + ': Data should be 3D.\n', 1, 'error') sys.exit(2) # sct.printv(arguments) sct.printv('\nCheck parameters:') sct.printv(' data ................... ' + fname_data) # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(fname_data) path_out, file_out, ext_out = '', file_data + suffix_out, ext_data path_tmp = sct.tmp_create() + "/" # copy files into tmp folder from sct_convert import convert sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) convert(fname_data, os.path.join(path_tmp, "data.nii")) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # change orientation sct.printv('\nChange orientation to RPI...', verbose) Image('data.nii').change_orientation("RPI").save('data_rpi.nii') # get image of medial slab sct.printv('\nGet image of medial slab...', verbose) image_array = nibabel.load('data_rpi.nii').get_data() nx, ny, nz = image_array.shape scipy.misc.imsave('image.jpg', image_array[math.floor(nx / 2), :, :]) # Display the image sct.printv('\nDisplay image and get cropping region...', verbose) fig = plt.figure() # fig = plt.gcf() # ax = plt.gca() ax = fig.add_subplot(111) img = mpimg.imread("image.jpg") implot = ax.imshow(img.T) implot.set_cmap('gray') plt.gca().invert_yaxis() # mouse callback ax.set_title('Left click on the top and bottom of your cropping field.\n Right click to remove last point.\n Close window when your done.') line, = ax.plot([], [], 'ro') # empty line cropping_coordinates = LineBuilder(line) plt.show() # disconnect callback # fig.canvas.mpl_disconnect(line) # check if user clicked two times if len(cropping_coordinates.xs) != 2: sct.printv('\nERROR: You have to select two points. Exit program.\n', 1, 'error') sys.exit(2) # convert coordinates to integer zcrop = [int(i) for i in cropping_coordinates.ys] # sort coordinates zcrop.sort() # crop image sct.printv('\nCrop image...', verbose) nii = Image('data_rpi.nii') data_crop = nii.data[:, :, zcrop[0]:zcrop[1]] nii.data = data_crop nii.absolutepath = 'data_rpi_crop.nii' nii.save() # come back os.chdir(curdir) sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(os.path.join(path_tmp, "data_rpi_crop.nii"), os.path.join(path_out, file_out + ext_out)) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...') sct.rmtree(path_tmp) sct.display_viewer_syntax(files=[os.path.join(path_out, file_out + ext_out)])
def main(args=None): # initializations initz = '' initcenter = '' initc2 = 'auto' param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_in = arguments["-i"] fname_seg = arguments['-s'] contrast = arguments['-c'] path_template = sct.slash_at_the_end(arguments['-t'], 1) # if '-o' in arguments: # file_out = arguments["-o"] # else: # file_out = '' if '-ofolder' in arguments: path_output = sct.slash_at_the_end(os.path.abspath(arguments['-ofolder']), slash=1) else: path_output = sct.slash_at_the_end(os.path.abspath(os.curdir), slash=1) if '-initz' in arguments: initz = arguments['-initz'] if '-initcenter' in arguments: initcenter = arguments['-initcenter'] # if user provided text file, parse and overwrite arguments if '-initfile' in arguments: # open file file = open(arguments['-initfile'], 'r') initfile = ' '+file.read().replace('\n', '') arg_initfile = initfile.split(' ') for i in xrange(len(arg_initfile)): if arg_initfile[i] == '-initz': initz = [int(x) for x in arg_initfile[i+1].split(',')] if arg_initfile[i] == '-initcenter': initcenter = int(arg_initfile[i+1]) if '-initc2' in arguments: initc2 = 'manual' if '-param' in arguments: param.update(arguments['-param'][0]) verbose = int(arguments['-v']) remove_tmp_files = int(arguments['-r']) denoise = int(arguments['-denoise']) laplacian = int(arguments['-laplacian']) # if verbose, import matplotlib # if verbose == 2: # import matplotlib.pyplot as plt # create temporary folder printv('\nCreate temporary folder...', verbose) path_tmp = tmp_create(verbose=verbose) # path_tmp = '/Users/julien/Dropbox/documents/processing/20160813_wang/t12/tmp.160814213032_725693/' # Copying input data to tmp folder printv('\nCopying input data to tmp folder...', verbose) run('sct_convert -i '+fname_in+' -o '+path_tmp+'data.nii') run('sct_convert -i '+fname_seg+' -o '+path_tmp+'segmentation.nii.gz') # Go go temp folder os.chdir(path_tmp) # create label to identify disc printv('\nCreate label to identify disc...', verbose) initauto = False if initz: create_label_z('segmentation.nii.gz', initz[0], initz[1]) # create label located at z_center elif initcenter: # find z centered in FOV nii = Image('segmentation.nii.gz') nii.change_orientation('RPI') # reorient to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions z_center = int(round(nz/2)) # get z_center create_label_z('segmentation.nii.gz', z_center, initcenter) # create label located at z_center else: initauto = True # printv('\nERROR: You need to initialize the disc detection algorithm using one of these two options: -initz, -initcenter\n', 1, 'error') # Straighten spinal cord printv('\nStraighten spinal cord...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'): # if they exist, copy them into current folder sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning') shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz') shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz') shutil.copy('../straight_ref.nii.gz', 'straight_ref.nii.gz') # apply straightening sct.run('sct_apply_transfo -i data.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o data_straight.nii') else: run('sct_straighten_spinalcord -i data.nii -s segmentation.nii.gz -r 0 -qc 0') # resample to 0.5mm isotropic to match template resolution printv('\nResample to 0.5mm isotropic...', verbose) run('sct_resample -i data_straight.nii -mm 0.5x0.5x0.5 -x linear -o data_straightr.nii', verbose) # run('sct_resample -i segmentation.nii.gz -mm 0.5x0.5x0.5 -x linear -o segmentationr.nii.gz', verbose) # run('sct_resample -i labelz.nii.gz -mm 0.5x0.5x0.5 -x linear -o labelzr.nii', verbose) # Apply straightening to segmentation # N.B. Output is RPI printv('\nApply straightening to segmentation...', verbose) run('sct_apply_transfo -i segmentation.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear', verbose) # Threshold segmentation at 0.5 run('sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz', verbose) if initauto: init_disc = [] else: # Apply straightening to z-label printv('\nDilate z-label and apply straightening...', verbose) run('sct_apply_transfo -i labelz.nii.gz -d data_straightr.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn', verbose) # get z value and disk value to initialize labeling printv('\nGet z and disc values from straight label...', verbose) init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz') printv('.. '+str(init_disc), verbose) # denoise data if denoise: printv('\nDenoise data...', verbose) run('sct_maths -i data_straightr.nii -denoise h=0.05 -o data_straightr.nii', verbose) # apply laplacian filtering if laplacian: printv('\nApply Laplacian filter...', verbose) run('sct_maths -i data_straightr.nii -laplacian 1 -o data_straightr.nii', verbose) # detect vertebral levels on straight spinal cord vertebral_detection('data_straightr.nii', 'segmentation_straight.nii.gz', contrast, param, init_disc=init_disc, verbose=verbose, path_template=path_template, initc2=initc2, path_output=path_output) # un-straighten labeled spinal cord printv('\nUn-straighten labeling...', verbose) run('sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn', verbose) # Clean labeled segmentation printv('\nClean labeled segmentation (correct interpolation errors)...', verbose) clean_labeled_segmentation('segmentation_labeled.nii.gz', 'segmentation.nii.gz', 'segmentation_labeled.nii.gz') # label discs printv('\nLabel discs...', verbose) label_discs('segmentation_labeled.nii.gz', verbose=verbose) # come back to parent folder os.chdir('..') # Generate output files path_seg, file_seg, ext_seg = extract_fname(fname_seg) printv('\nGenerate output files...', verbose) generate_output_file(path_tmp+'segmentation_labeled.nii.gz', path_output+file_seg+'_labeled'+ext_seg) generate_output_file(path_tmp+'segmentation_labeled_disc.nii.gz', path_output+file_seg+'_labeled_discs'+ext_seg) # copy straightening files in case subsequent SCT functions need them generate_output_file(path_tmp+'warp_curve2straight.nii.gz', path_output+'warp_curve2straight.nii.gz', verbose) generate_output_file(path_tmp+'warp_straight2curve.nii.gz', path_output+'warp_straight2curve.nii.gz', verbose) generate_output_file(path_tmp+'straight_ref.nii.gz', path_output+'straight_ref.nii.gz', verbose) # Remove temporary files if remove_tmp_files == 1: printv('\nRemove temporary files...', verbose) run('rm -rf '+path_tmp) # to view results printv('\nDone! To view results, type:', verbose) printv('fslview '+fname_in+' '+path_output+file_seg+'_labeled'+' -l Random-Rainbow -t 0.5 &\n', verbose, 'info')
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(args=None): if not args: args = sys.argv[1:] # initialize parameters param = Param() # call main function parser = get_parser() arguments = parser.parse(args) fname_data = arguments['-i'] fname_bvecs = arguments['-bvec'] average = arguments['-a'] verbose = int(arguments['-v']) remove_temp_files = int(arguments['-r']) path_out = arguments['-ofolder'] if '-bval' in arguments: fname_bvals = arguments['-bval'] else: fname_bvals = '' if '-bvalmin' in arguments: param.bval_min = arguments['-bvalmin'] # Initialization start_time = time.time() # sct.printv(arguments) sct.printv('\nInput parameters:', verbose) sct.printv(' input file ............' + fname_data, verbose) sct.printv(' bvecs file ............' + fname_bvecs, verbose) sct.printv(' bvals file ............' + fname_bvals, verbose) sct.printv(' average ...............' + str(average), verbose) # Get full path fname_data = os.path.abspath(fname_data) fname_bvecs = os.path.abspath(fname_bvecs) if fname_bvals: fname_bvals = os.path.abspath(fname_bvals) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(fname_data) # create temporary folder path_tmp = sct.tmp_create(basename="dmri_separate", verbose=verbose) # copy files into tmp folder and convert to nifti sct.printv('\nCopy files into temporary folder...', verbose) ext = '.nii' dmri_name = 'dmri' b0_name = file_data + '_b0' b0_mean_name = b0_name + '_mean' dwi_name = file_data + '_dwi' dwi_mean_name = dwi_name + '_mean' if not convert(fname_data, os.path.join(path_tmp, dmri_name + ext)): sct.printv('ERROR in convert.', 1, 'error') sct.copy(fname_bvecs, os.path.join(path_tmp, "bvecs"), verbose=verbose) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # Get size of data im_dmri = Image(dmri_name + ext) sct.printv('\nGet dimensions data...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_dmri.dim sct.printv( '.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), verbose) # Identify b=0 and DWI images sct.printv(fname_bvals) index_b0, index_dwi, nb_b0, nb_dwi = identify_b0(fname_bvecs, fname_bvals, param.bval_min, verbose) # Split into T dimension sct.printv('\nSplit along T dimension...', verbose) im_dmri_split_list = split_data(im_dmri, 3) for im_d in im_dmri_split_list: im_d.save() # Merge b=0 images sct.printv('\nMerge b=0...', verbose) from sct_image import concat_data l = [] for it in range(nb_b0): l.append(dmri_name + '_T' + str(index_b0[it]).zfill(4) + ext) im_out = concat_data(l, 3).save(b0_name + ext) # Average b=0 images if average: sct.printv('\nAverage b=0...', verbose) sct.run([ 'sct_maths', '-i', b0_name + ext, '-o', b0_mean_name + ext, '-mean', 't' ], verbose) # Merge DWI l = [] for it in range(nb_dwi): l.append(dmri_name + '_T' + str(index_dwi[it]).zfill(4) + ext) im_out = concat_data(l, 3).save(dwi_name + ext) # Average DWI images if average: sct.printv('\nAverage DWI...', verbose) sct.run([ 'sct_maths', '-i', dwi_name + ext, '-o', dwi_mean_name + ext, '-mean', 't' ], verbose) # come back os.chdir(curdir) # Generate output files fname_b0 = os.path.abspath(os.path.join(path_out, b0_name + ext_data)) fname_dwi = os.path.abspath(os.path.join(path_out, dwi_name + ext_data)) fname_b0_mean = os.path.abspath( os.path.join(path_out, b0_mean_name + ext_data)) fname_dwi_mean = os.path.abspath( os.path.join(path_out, dwi_mean_name + ext_data)) sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(os.path.join(path_tmp, b0_name + ext), fname_b0, verbose) sct.generate_output_file(os.path.join(path_tmp, dwi_name + ext), fname_dwi, verbose) if average: sct.generate_output_file(os.path.join(path_tmp, b0_mean_name + ext), fname_b0_mean, verbose) sct.generate_output_file(os.path.join(path_tmp, dwi_mean_name + ext), fname_dwi_mean, verbose) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', verbose) return fname_b0, fname_b0_mean, fname_dwi, fname_dwi_mean
def main(): # Initialization fname_data = '' fname_landmarks = '' fname_seg = '' folder_template = param.folder_template file_template = param.file_template file_template_label = param.file_template_label file_template_seg = param.file_template_seg output_type = param.output_type speed = param.speed remove_temp_files = param.remove_temp_files verbose = param.verbose smoothing_sigma = param.smoothing_sigma # start timer start_time = time.time() # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # # get path of the template # path_template = path_sct+folder_template # get fname of the template + template objects fname_template = path_sct+folder_template+'/'+file_template fname_template_label = path_sct+folder_template+'/'+file_template_label fname_template_seg = path_sct+folder_template+'/'+file_template_seg # Parameters for debug mode if param.debug: print '\n*** WARNING: DEBUG MODE ON ***\n' fname_data = path_sct+'/testing/data/errsm_23/t2/t2.nii.gz' fname_landmarks = path_sct+'/testing/data/errsm_23/t2/t2_landmarks_C2_T2_center.nii.gz' fname_seg = path_sct+'/testing/data/errsm_23/t2/t2_segmentation_PropSeg.nii.gz' speed = 'superfast' # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:],'hi:l:m:o:r:s:') except getopt.GetoptError: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ("-i"): fname_data = arg elif opt in ('-l'): fname_landmarks = arg elif opt in ("-m"): fname_seg = arg elif opt in ("-o"): output_type = int(arg) elif opt in ("-r"): remove_temp_files = int(arg) elif opt in ("-s"): speed = arg # display usage if a mandatory argument is not provided if fname_data == '' or fname_landmarks == '' or fname_seg == '': usage() # print arguments print '\nCheck parameters:' print '.. Data: '+fname_data print '.. Landmarks: '+fname_landmarks print '.. Segmentation: '+fname_seg print '.. Output type: '+str(output_type) print '.. Speed: '+speed print '.. Remove temp files: '+str(remove_temp_files) # Check speed parameter and create registration mode: slow 50x30, normal 50x15, fast 10x3 (default) print('\nAssign number of iterations based on speed...') if speed == "slow": nb_iterations = "50x30" elif speed == "normal": nb_iterations = "50x15" elif speed == "fast": nb_iterations = "10x3" elif speed == "superfast": nb_iterations = "3x1" # only for debugging purpose-- do not inform the user about this option else: print 'ERROR: Wrong input registration speed {slow, normal, fast}.' sys.exit(2) print '.. '+nb_iterations # Get full path # fname_data = os.path.abspath(fname_data) # fname_landmarks = os.path.abspath(fname_landmarks) # fname_seg = os.path.abspath(fname_seg) # check existence of input files print('\nCheck existence of input files...') sct.check_file_exist(fname_data,verbose) sct.check_file_exist(fname_landmarks,verbose) sct.check_file_exist(fname_seg,verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) # create temporary folder print('\nCreate temporary folder...') path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") status, output = sct.run('mkdir '+path_tmp) # copy files to temporary folder print('\nCopy files...') status, output = sct.run('c3d '+fname_data+' -o '+path_tmp+'/data.nii') status, output = sct.run('c3d '+fname_landmarks+' -o '+path_tmp+'/landmarks.nii.gz') status, output = sct.run('c3d '+fname_seg+' -o '+path_tmp+'/segmentation.nii.gz') # go to tmp folder os.chdir(path_tmp) # Change orientation of input images to RPI print('\nChange orientation of input images to RPI...') status, output = sct.run('sct_orientation -i data.nii -o data_rpi.nii -orientation RPI') status, output = sct.run('sct_orientation -i landmarks.nii.gz -o landmarks_rpi.nii.gz -orientation RPI') status, output = sct.run('sct_orientation -i segmentation.nii.gz -o segmentation_rpi.nii.gz -orientation RPI') # Straighten the spinal cord using centerline/segmentation print('\nStraighten the spinal cord using centerline/segmentation...') status, output = sct.run('sct_straighten_spinalcord.py -i data_rpi.nii -c segmentation_rpi.nii.gz -r '+str(remove_temp_files)) # Apply straightening to segmentation print('\nApply straightening to segmentation...') sct.run('WarpImageMultiTransform 3 segmentation_rpi.nii.gz segmentation_rpi_straight.nii.gz -R data_rpi_straight.nii warp_curve2straight.nii.gz') # Smoothing along centerline to improve accuracy and remove step effects print('\nSmoothing along centerline to improve accuracy and remove step effects...') sct.run('c3d data_rpi_straight.nii -smooth 0x0x'+str(smoothing_sigma)+'vox -o data_rpi_straight.nii') sct.run('c3d segmentation_rpi_straight.nii.gz -smooth 0x0x'+str(smoothing_sigma)+'vox -o segmentation_rpi_straight.nii.gz') # Label preparation: # -------------------------------------------------------------------------------- # Remove unused label on template. Keep only label present in the input label image print('\nRemove unused label on template. Keep only label present in the input label image...') status, output = sct.run('sct_label_utils.py -t remove -i '+fname_template_label+' -o template_label.nii.gz -r landmarks_rpi.nii.gz') # Create a cross for the template labels - 5 mm print('\nCreate a 5 mm cross for the template labels...') status, output = sct.run('sct_label_utils.py -t cross -i template_label.nii.gz -o template_label_cross.nii.gz -c 5') # Create a cross for the input labels and dilate for straightening preparation - 5 mm print('\nCreate a 5mm cross for the input labels and dilate for straightening preparation...') status, output = sct.run('sct_label_utils.py -t cross -i landmarks_rpi.nii.gz -o landmarks_rpi_cross3x3.nii.gz -c 5 -d') # Push the input labels in the template space print('\nPush the input labels to the straight space...') status, output = sct.run('WarpImageMultiTransform 3 landmarks_rpi_cross3x3.nii.gz landmarks_rpi_cross3x3_straight.nii.gz -R data_rpi_straight.nii warp_curve2straight.nii.gz --use-NN') # Convert landmarks from FLOAT32 to INT print '\nConvert landmarks from FLOAT32 to INT...' sct.run('c3d landmarks_rpi_cross3x3_straight.nii.gz -type int -o landmarks_rpi_cross3x3_straight.nii.gz') # Estimate affine transfo: straight --> template (landmark-based)' print '\nEstimate affine transfo: straight anat --> template (landmark-based)...' sct.run('ANTSUseLandmarkImagesToGetAffineTransform template_label_cross.nii.gz landmarks_rpi_cross3x3_straight.nii.gz affine straight2templateAffine.txt') # Apply affine transformation: straight --> template print '\nApply affine transformation: straight --> template...' sct.run('WarpImageMultiTransform 3 data_rpi_straight.nii data_rpi_straight2templateAffine.nii straight2templateAffine.txt -R '+fname_template) sct.run('WarpImageMultiTransform 3 segmentation_rpi_straight.nii.gz segmentation_rpi_straight2templateAffine.nii.gz straight2templateAffine.txt -R '+fname_template) # now threshold at 0.5 (for partial volume interpolation) # do not do that anymore-- better to estimate transformation using trilinear interp image to avoid step effect. See issue #31 on github. # sct.run('c3d segmentation_rpi_straight2templateAffine.nii.gz -threshold -inf 0.5 0 1 -o segmentation_rpi_straight2templateAffine.nii.gz') # Registration straight spinal cord to template print('\nRegister straight spinal cord to template...') nb_iterations = '50x15' # TODO: nb iteration for step 2 sct.run('sct_register_multimodal.py -i data_rpi_straight2templateAffine.nii -d '+fname_template+' -s segmentation_rpi_straight2templateAffine.nii.gz -t '+fname_template_seg+' -r '+str(remove_temp_files)+' -n '+nb_iterations+' -v '+str(verbose)+' -x 1',verbose) # status, output = sct.run('sct_register_straight_spinalcord_to_template.py -i data_rpi_straight.nii.gz -l landmarks_rpi_cross3x3_straight.nii.gz -t '+path_template+'/MNI-Poly-AMU_T2.nii.gz -f template_label_cross.nii.gz -m '+path_template+'/mask_gaussian_templatespace_sigma20.nii.gz -r 1 -n '+nb_iterations+' -v 1') # Concatenate warping fields: template2anat & anat2template print('\nConcatenate warping fields: template2anat & anat2template...') cmd = 'ComposeMultiTransform 3 warp_template2anat.nii.gz -R data.nii warp_straight2curve.nii.gz -i straight2templateAffine.txt warp_dest2src.nii.gz' print '>> '+cmd commands.getstatusoutput(cmd) cmd = 'ComposeMultiTransform 3 warp_anat2template.nii.gz -R '+fname_template+' warp_src2dest.nii.gz straight2templateAffine.txt warp_curve2straight.nii.gz' print '>> '+cmd commands.getstatusoutput(cmd) # Apply warping fields to anat and template if output_type == 1: sct.run('WarpImageMultiTransform 3 '+fname_template+' template2anat.nii.gz -R data.nii warp_template2anat.nii.gz') sct.run('WarpImageMultiTransform 3 data.nii.gz anat2template.nii.gz -R '+fname_template+' warp_anat2template.nii.gz') # come back to parent folder os.chdir('..') # Generate output files print('\nGenerate output files...') sct.generate_output_file(path_tmp+'/warp_template2anat.nii.gz','','warp_template2anat','.nii.gz') sct.generate_output_file(path_tmp+'/warp_anat2template.nii.gz','','warp_anat2template','.nii.gz') if output_type == 1: sct.generate_output_file(path_tmp+'/template2anat.nii.gz','','template2anat',ext_data) sct.generate_output_file(path_tmp+'/anat2template.nii.gz','','anat2template',ext_data) # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm -rf '+path_tmp) # display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s' # to view results print '\nTo view results, type:' print 'fslview template2anat '+fname_data+' &' print 'fslview anat2template '+fname_template+' &\n'
def compute_csa(fname_segmentation, verbose, remove_temp_files, step, smoothing_param, figure_fit, file_csa_volume, slices, vert_levels, fname_vertebral_labeling='', algo_fitting = 'hanning', type_window = 'hanning', window_length = 80): # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S") + '_'+str(randint(1, 1000000)), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('sct_convert -i '+fname_segmentation+' -o '+path_tmp+'segmentation.nii.gz', verbose) # go to tmp folder os.chdir(path_tmp) # Change orientation of the input segmentation into RPI sct.printv('\nChange orientation to RPI...', verbose) sct.run('sct_image -i segmentation.nii.gz -setorient RPI -o segmentation_RPI.nii.gz', verbose) # Open segmentation volume sct.printv('\nOpen segmentation volume...', verbose) im_seg = Image('segmentation_RPI.nii.gz') data_seg = im_seg.data # hdr_seg = im_seg.hdr # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_seg.dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # # Extract min and max index in Z direction X, Y, Z = (data_seg > 0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline('segmentation_RPI.nii.gz', algo_fitting=algo_fitting, type_window=type_window, window_length=window_length, verbose=verbose) z_centerline_scaled = [x*pz for x in z_centerline] # Compute CSA sct.printv('\nCompute CSA...', verbose) # Empty arrays in which CSA for each z slice will be stored csa = np.zeros(max_z_index-min_z_index+1) for iz in xrange(min_z_index, max_z_index+1): # compute the vector normal to the plane normal = normalize(np.array([x_centerline_deriv[iz-min_z_index], y_centerline_deriv[iz-min_z_index], z_centerline_deriv[iz-min_z_index]])) # compute the angle between the normal vector of the plane and the vector z angle = np.arccos(np.dot(normal, [0, 0, 1])) # compute the number of voxels, assuming the segmentation is coded for partial volume effect between 0 and 1. number_voxels = np.sum(data_seg[:, :, iz]) # compute CSA, by scaling with voxel size (in mm) and adjusting for oblique plane csa[iz-min_z_index] = number_voxels * px * py * np.cos(angle) sct.printv('\nSmooth CSA across slices...', verbose) if smoothing_param: from msct_smooth import smoothing_window sct.printv('.. Hanning window: '+str(smoothing_param)+' mm', verbose) csa_smooth = smoothing_window(csa, window_len=smoothing_param/pz, window='hanning', verbose=0) # display figure if verbose == 2: import matplotlib.pyplot as plt plt.figure() pltx, = plt.plot(z_centerline_scaled, csa, 'bo') pltx_fit, = plt.plot(z_centerline_scaled, csa_smooth, 'r', linewidth=2) plt.title("Cross-sectional area (CSA)") plt.xlabel('z (mm)') plt.ylabel('CSA (mm^2)') plt.legend([pltx, pltx_fit], ['Raw', 'Smoothed']) plt.show() # update variable csa = csa_smooth else: sct.printv('.. No smoothing!', verbose) # Create output text file sct.printv('\nWrite text file...', verbose) file_results = open('csa.txt', 'w') for i in range(min_z_index, max_z_index+1): file_results.write(str(int(i)) + ',' + str(csa[i-min_z_index])+'\n') # Display results sct.printv('z='+str(i-min_z_index)+': '+str(csa[i-min_z_index])+' mm^2', verbose, 'bold') file_results.close() # output volume of csa values sct.printv('\nCreate volume of CSA values...', verbose) data_csa = data_seg.astype(np.float32, copy=False) # loop across slices for iz in range(min_z_index, max_z_index+1): # retrieve seg pixels x_seg, y_seg = (data_csa[:, :, iz] > 0).nonzero() seg = [[x_seg[i],y_seg[i]] for i in range(0, len(x_seg))] # loop across pixels in segmentation for i in seg: # replace value with csa value data_csa[i[0], i[1], iz] = csa[iz-min_z_index] # replace data im_seg.data = data_csa # set original orientation # TODO: FIND ANOTHER WAY!! # im_seg.change_orientation(orientation) --> DOES NOT WORK! # set file name -- use .gz because faster to write im_seg.setFileName('csa_volume_RPI.nii.gz') im_seg.changeType('float32') # save volume im_seg.save() # get orientation of the input data im_seg_original = Image('segmentation.nii.gz') orientation = im_seg_original.orientation sct.run('sct_image -i csa_volume_RPI.nii.gz -setorient '+orientation+' -o '+file_csa_volume) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) copyfile(path_tmp+'csa.txt', path_data+param.fname_csa) # sct.generate_output_file(path_tmp+'csa.txt', path_data+param.fname_csa) # extension already included in param.fname_csa sct.generate_output_file(path_tmp+file_csa_volume, path_data+file_csa_volume) # extension already included in name_output # average csa across vertebral levels or slices if asked (flag -z or -l) if slices or vert_levels: from sct_extract_metric import save_metrics warning = '' if vert_levels and not fname_vertebral_labeling: sct.printv('\nERROR: Vertebral labeling file is missing. See usage.\n', 1, 'error') elif vert_levels and fname_vertebral_labeling: # from sct_extract_metric import get_slices_matching_with_vertebral_levels sct.printv('\tSelected vertebral levels... '+vert_levels) # convert the vertebral labeling file to RPI orientation im_vertebral_labeling = set_orientation(Image(fname_vertebral_labeling), 'RPI', fname_out=path_tmp+'vertebral_labeling_RPI.nii') # get the slices corresponding to the vertebral levels # slices, vert_levels_list, warning = get_slices_matching_with_vertebral_levels(data_seg, vert_levels, im_vertebral_labeling.data, 1) slices, vert_levels_list, warning = get_slices_matching_with_vertebral_levels_based_centerline(vert_levels, im_vertebral_labeling.data, x_centerline_fit, y_centerline_fit, z_centerline) elif not vert_levels: vert_levels_list = [] sct.printv('Average CSA across slices...', type='info') # parse the selected slices slices_lim = slices.strip().split(':') slices_list = range(int(slices_lim[0]), int(slices_lim[1])+1) CSA_for_selected_slices = [] # Read the file csa.txt and get the CSA for the selected slices with open(path_data+param.fname_csa) as openfile: for line in openfile: line_split = line.strip().split(',') if int(line_split[0]) in slices_list: CSA_for_selected_slices.append(float(line_split[1])) # average the CSA mean_CSA = np.mean(np.asarray(CSA_for_selected_slices)) std_CSA = np.std(np.asarray(CSA_for_selected_slices)) sct.printv('Mean CSA: '+str(mean_CSA)+' +/- '+str(std_CSA)+' mm^2', type='info') # write result into output file save_metrics([0], [file_data], slices, [mean_CSA], [std_CSA], path_data + 'csa_mean.txt', path_data+file_csa_volume, 'nb_voxels x px x py x cos(theta) slice-by-slice (in mm^3)', '', actual_vert=vert_levels_list, warning_vert_levels=warning) # compute volume between the selected slices sct.printv('Compute the volume in between the selected slices...', type='info') nb_vox = np.sum(data_seg[:, :, slices_list]) volume = nb_vox*px*py*pz sct.printv('Volume in between the selected slices: '+str(volume)+' mm^3', type='info') # write result into output file save_metrics([0], [file_data], slices, [volume], [np.nan], path_data + 'volume.txt', path_data+file_data, 'nb_voxels x px x py x pz (in mm^3)', '', actual_vert=vert_levels_list, warning_vert_levels=warning) # Remove temporary files if remove_temp_files: sct.printv('\nRemove temporary files...') sct.run('rm -rf '+path_tmp, error_exit='warning')
def extract_centerline(fname_segmentation, remove_temp_files, verbose = 0, algo_fitting = 'hanning', type_window = 'hanning', window_length = 80): # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S") + '_'+str(randint(1, 1000000)), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying data to tmp folder...', verbose) sct.run('sct_convert -i '+fname_segmentation+' -o '+path_tmp+'segmentation.nii.gz', verbose) # go to tmp folder os.chdir(path_tmp) # Change orientation of the input centerline into RPI sct.printv('\nOrient centerline to RPI orientation...', verbose) # fname_segmentation_orient = 'segmentation_RPI.nii.gz' # BELOW DOES NOT WORK (JULIEN, 2015-10-17) # im_seg = Image(file_data+ext_data) # set_orientation(im_seg, 'RPI') # im_seg.setFileName(fname_segmentation_orient) # im_seg.save() sct.run('sct_image -i segmentation.nii.gz -setorient RPI -o segmentation_RPI.nii.gz', verbose) # Open segmentation volume sct.printv('\nOpen segmentation volume...', verbose) im_seg = Image('segmentation_RPI.nii.gz') data = im_seg.data # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_seg.dim sct.printv('.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz), verbose) sct.printv('.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm', verbose) # # Get dimension # sct.printv('\nGet dimensions...', verbose) # nx, ny, nz, nt, px, py, pz, pt = im_seg.dim # # # Extract orientation of the input segmentation # orientation = get_orientation(im_seg) # sct.printv('\nOrientation of segmentation image: ' + orientation, verbose) # # sct.printv('\nOpen segmentation volume...', verbose) # data = im_seg.data # hdr = im_seg.hdr # Extract min and max index in Z direction X, Y, Z = (data>0).nonzero() min_z_index, max_z_index = min(Z), max(Z) x_centerline = [0 for i in range(0,max_z_index-min_z_index+1)] y_centerline = [0 for i in range(0,max_z_index-min_z_index+1)] z_centerline = [iz for iz in range(min_z_index, max_z_index+1)] # Extract segmentation points and average per slice for iz in range(min_z_index, max_z_index+1): x_seg, y_seg = (data[:,:,iz]>0).nonzero() x_centerline[iz-min_z_index] = np.mean(x_seg) y_centerline[iz-min_z_index] = np.mean(y_seg) for k in range(len(X)): data[X[k], Y[k], Z[k]] = 0 # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline_fit, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline('segmentation_RPI.nii.gz', type_window = type_window, window_length = window_length, algo_fitting = algo_fitting, verbose = verbose) if verbose == 2: import matplotlib.pyplot as plt #Creation of a vector x that takes into account the distance between the labels nz_nonz = len(z_centerline) x_display = [0 for i in range(x_centerline_fit.shape[0])] y_display = [0 for i in range(y_centerline_fit.shape[0])] for i in range(0, nz_nonz, 1): x_display[int(z_centerline[i]-z_centerline[0])] = x_centerline[i] y_display[int(z_centerline[i]-z_centerline[0])] = y_centerline[i] plt.figure(1) plt.subplot(2,1,1) plt.plot(z_centerline_fit, x_display, 'ro') plt.plot(z_centerline_fit, x_centerline_fit) plt.xlabel("Z") plt.ylabel("X") plt.title("x and x_fit coordinates") plt.subplot(2,1,2) plt.plot(z_centerline_fit, y_display, 'ro') plt.plot(z_centerline_fit, y_centerline_fit) plt.xlabel("Z") plt.ylabel("Y") plt.title("y and y_fit coordinates") plt.show() # Create an image with the centerline for iz in range(min_z_index, max_z_index+1): data[round(x_centerline_fit[iz-min_z_index]), round(y_centerline_fit[iz-min_z_index]), iz] = 1 # if index is out of bounds here for hanning: either the segmentation has holes or labels have been added to the file # Write the centerline image in RPI orientation # hdr.set_data_dtype('uint8') # set imagetype to uint8 sct.printv('\nWrite NIFTI volumes...', verbose) im_seg.data = data im_seg.setFileName('centerline_RPI.nii.gz') im_seg.changeType('uint8') im_seg.save() sct.printv('\nSet to original orientation...', verbose) # get orientation of the input data im_seg_original = Image('segmentation.nii.gz') orientation = im_seg_original.orientation sct.run('sct_image -i centerline_RPI.nii.gz -setorient '+orientation+' -o centerline.nii.gz') # create a txt file with the centerline name_output_txt = 'centerline.txt' sct.printv('\nWrite text file...', verbose) file_results = open(name_output_txt, 'w') for i in range(min_z_index, max_z_index+1): file_results.write(str(int(i)) + ' ' + str(x_centerline_fit[i-min_z_index]) + ' ' + str(y_centerline_fit[i-min_z_index]) + '\n') file_results.close() # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp+'centerline.nii.gz', file_data+'_centerline.nii.gz') sct.generate_output_file(path_tmp+'centerline.txt', file_data+'_centerline.txt') # Remove temporary files if remove_temp_files: sct.printv('\nRemove temporary files...', verbose) sct.run('rm -rf '+path_tmp, verbose) return file_data+'_centerline.nii.gz'
def main(args=None): if not args: args = sys.argv[1:] # initialize parameters param = Param() # call main function parser = get_parser() arguments = parser.parse(args) fname_data = arguments['-i'] fname_bvecs = arguments['-bvec'] average = arguments['-a'] verbose = int(arguments['-v']) remove_tmp_files = int(arguments['-r']) path_out = arguments['-ofolder'] if '-bval' in arguments: fname_bvals = arguments['-bval'] else: fname_bvals = '' if '-bvalmin' in arguments: param.bval_min = arguments['-bvalmin'] # Initialization start_time = time.time() # sct.printv(arguments) sct.printv('\nInput parameters:', verbose) sct.printv(' input file ............' + fname_data, verbose) sct.printv(' bvecs file ............' + fname_bvecs, verbose) sct.printv(' bvals file ............' + fname_bvals, verbose) sct.printv(' average ...............' + str(average), verbose) # Get full path fname_data = os.path.abspath(fname_data) fname_bvecs = os.path.abspath(fname_bvecs) if fname_bvals: fname_bvals = os.path.abspath(fname_bvals) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(fname_data) # # get output folder # if path_out == '': # path_out = '' # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir ' + path_tmp, verbose) # copy files into tmp folder and convert to nifti sct.printv('\nCopy files into temporary folder...', verbose) ext = '.nii' dmri_name = 'dmri' b0_name = 'b0' b0_mean_name = b0_name + '_mean' dwi_name = 'dwi' dwi_mean_name = dwi_name + '_mean' from sct_convert import convert if not convert(fname_data, path_tmp + dmri_name + ext): sct.printv('ERROR in convert.', 1, 'error') sct.run('cp ' + fname_bvecs + ' ' + path_tmp + 'bvecs', verbose) # go to tmp folder os.chdir(path_tmp) # Get size of data im_dmri = Image(dmri_name + ext) sct.printv('\nGet dimensions data...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_dmri.dim sct.printv('.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), verbose) # Identify b=0 and DWI images sct.printv(fname_bvals) index_b0, index_dwi, nb_b0, nb_dwi = identify_b0(fname_bvecs, fname_bvals, param.bval_min, verbose) # Split into T dimension sct.printv('\nSplit along T dimension...', verbose) im_dmri_split_list = split_data(im_dmri, 3) for im_d in im_dmri_split_list: im_d.save() # Merge b=0 images sct.printv('\nMerge b=0...', verbose) cmd = 'sct_image -concat t -o ' + b0_name + ext + ' -i ' for it in range(nb_b0): cmd = cmd + dmri_name + '_T' + str(index_b0[it]).zfill(4) + ext + ',' cmd = cmd[:-1] # remove ',' at the end of the string # WARNING: calling concat_data in python instead of in command line causes a non understood issue status, output = sct.run(cmd, param.verbose) # Average b=0 images if average: sct.printv('\nAverage b=0...', verbose) sct.run('sct_maths -i ' + b0_name + ext + ' -o ' + b0_mean_name + ext + ' -mean t', verbose) # Merge DWI cmd = 'sct_image -concat t -o ' + dwi_name + ext + ' -i ' for it in range(nb_dwi): cmd = cmd + dmri_name + '_T' + str(index_dwi[it]).zfill(4) + ext + ',' cmd = cmd[:-1] # remove ',' at the end of the string # WARNING: calling concat_data in python instead of in command line causes a non understood issue status, output = sct.run(cmd, param.verbose) # Average DWI images if average: sct.printv('\nAverage DWI...', verbose) sct.run('sct_maths -i ' + dwi_name + ext + ' -o ' + dwi_mean_name + ext + ' -mean t', verbose) # if not average_data_across_dimension('dwi.nii', 'dwi_mean.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run(fsloutput + 'fslmaths dwi -Tmean dwi_mean', verbose) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp + b0_name + ext, path_out + b0_name + ext_data, verbose) sct.generate_output_file(path_tmp + dwi_name + ext, path_out + dwi_name + ext_data, verbose) if average: sct.generate_output_file(path_tmp + b0_mean_name + ext, path_out + b0_mean_name + ext_data, verbose) sct.generate_output_file(path_tmp + dwi_mean_name + ext, path_out + dwi_mean_name + ext_data, verbose) # Remove temporary files if remove_tmp_files == 1: sct.printv('\nRemove temporary files...', verbose) sct.run('rm -rf ' + path_tmp, verbose) # 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) if average: sct.printv('fslview b0 b0_mean dwi dwi_mean &\n', verbose) else: sct.printv('fslview b0 dwi &\n', verbose)
def main(): # Initialization fname_anat = '' fname_centerline = '' gapxy = param.gapxy gapz = param.gapz padding = param.padding centerline_fitting = param.fitting_method remove_temp_files = param.remove_temp_files verbose = param.verbose interpolation_warp = param.interpolation_warp # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') print path_sct # extract path of the script path_script = os.path.dirname(__file__) + '/' # Parameters for debug mode if param.debug == 1: print '\n*** WARNING: DEBUG MODE ON ***\n' # fname_anat = path_sct+'/testing/data/errsm_23/t2/t2.nii.gz' # fname_centerline = path_sct+'/testing/data/errsm_23/t2/t2_segmentation_PropSeg.nii.gz' fname_anat = '/home/django/jtouati/data/cover_z_slices/errsm13_t2.nii.gz' fname_centerline = '/home/django/jtouati/data/cover_z_slices/segmentation_centerline_binary.nii.gz' remove_temp_files = 0 centerline_fitting = 'splines' import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D verbose = 2 # Check input param try: opts, args = getopt.getopt(sys.argv[1:], 'hi:c:r:w:f:v:') except getopt.GetoptError as err: print str(err) usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_anat = arg elif opt in ('-c'): fname_centerline = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ('-w'): interpolation_warp = str(arg) elif opt in ('-f'): centerline_fitting = str(arg) elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_anat == '' or fname_centerline == '': usage() # Display usage if optional arguments are not correctly provided if centerline_fitting == '': centerline_fitting = 'splines' elif not centerline_fitting == '' and not centerline_fitting == 'splines' and not centerline_fitting == 'polynomial': print '\n \n -f argument is not valid \n \n' usage() # check existence of input files sct.check_file_exist(fname_anat) sct.check_file_exist(fname_centerline) # check interp method if interpolation_warp == 'spline': interpolation_warp_ants = '--use-BSpline' elif interpolation_warp == 'trilinear': interpolation_warp_ants = '' elif interpolation_warp == 'nearestneighbor': interpolation_warp_ants = '--use-NN' else: print '\WARNING: Interpolation method not recognized. Using: ' + param.interpolation_warp interpolation_warp_ants = '--use-BSpline' # Display arguments print '\nCheck input arguments...' print ' Input volume ...................... ' + fname_anat print ' Centerline ........................ ' + fname_centerline print ' Centerline fitting option ......... ' + centerline_fitting print ' Final interpolation ............... ' + interpolation_warp print ' Verbose ........................... ' + str(verbose) print '' # if verbose 2, import matplotlib if verbose == 2: import matplotlib.pyplot as plt # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname( fname_centerline) # create temporary folder path_tmp = 'tmp.' + time.strftime("%y%m%d%H%M%S") sct.run('mkdir ' + path_tmp) # copy files into tmp folder sct.run('cp ' + fname_anat + ' ' + path_tmp) sct.run('cp ' + fname_centerline + ' ' + path_tmp) # go to tmp folder os.chdir(path_tmp) # Open centerline #========================================================================================== # Change orientation of the input centerline into RPI print '\nOrient centerline to RPI orientation...' fname_centerline_orient = 'tmp.centerline_rpi' + ext_centerline sct.run('sct_orientation -i ' + file_centerline + ext_centerline + ' -o ' + fname_centerline_orient + ' -orientation RPI') print '\nGet dimensions of input centerline...' nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(fname_centerline_orient) print '.. matrix size: ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) print '.. voxel size: ' + str(px) + 'mm x ' + str(py) + 'mm x ' + str( pz) + 'mm' print '\nOpen centerline volume...' file = nibabel.load(fname_centerline_orient) data = file.get_data() # loop across z and associate x,y coordinate with the point having maximum intensity x_centerline = [0 for iz in range(0, nz, 1)] y_centerline = [0 for iz in range(0, nz, 1)] z_centerline = [iz for iz in range(0, nz, 1)] x_centerline_deriv = [0 for iz in range(0, nz, 1)] y_centerline_deriv = [0 for iz in range(0, nz, 1)] z_centerline_deriv = [0 for iz in range(0, nz, 1)] # Two possible scenario: # 1. the centerline is probabilistic: each slice contains voxels with the probability of containing the centerline [0:...:1] # We only take the maximum value of the image to aproximate the centerline. # 2. The centerline/segmentation image contains many pixels per slice with values {0,1}. # We take all the points and approximate the centerline on all these points. # # x_seg_start, y_seg_start = (data[:,:,0]>0).nonzero() # x_seg_end, y_seg_end = (data[:,:,-1]>0).nonzero() # REMOVED: 2014-07-18 # check if centerline covers all the image # if len(x_seg_start)==0 or len(x_seg_end)==0: # print '\nERROR: centerline/segmentation must cover all "z" slices of the input image.\n' \ # 'To solve the problem, you need to crop the input image (you can use \'sct_crop_image\') and generate one' \ # 'more time the spinal cord centerline/segmentation from this cropped image.\n' # usage() # # X, Y, Z = ((data<1)*(data>0)).nonzero() # X is empty if binary image # if (len(X) > 0): # Scenario 1 # for iz in range(0, nz, 1): # x_centerline[iz], y_centerline[iz] = numpy.unravel_index(data[:,:,iz].argmax(), data[:,:,iz].shape) # else: # Scenario 2 # for iz in range(0, nz, 1): # print (data[:,:,iz]>0).nonzero() # x_seg, y_seg = (data[:,:,iz]>0).nonzero() # x_centerline[iz] = numpy.mean(x_seg) # y_centerline[iz] = numpy.mean(y_seg) # # TODO: find a way to do the previous loop with this, which is more neat: # # [numpy.unravel_index(data[:,:,iz].argmax(), data[:,:,iz].shape) for iz in range(0,nz,1)] # get center of mass of the centerline/segmentation print '\nGet center of mass of the centerline/segmentation...' for iz in range(0, nz, 1): x_centerline[iz], y_centerline[ iz] = ndimage.measurements.center_of_mass( numpy.array(data[:, :, iz])) #print len(x_centerline),len(y_centerline) #print len((numpy.array(x_centerline)>=0).nonzero()[0]),len((numpy.array(y_centerline)>=0).nonzero()[0]) x_seg_start, y_seg_start = (data[:, :, 0] > 0).nonzero() x_seg_end, y_seg_end = (data[:, :, -1] > 0).nonzero() #check if centerline covers all the image if len(x_seg_start) == 0 or len(x_seg_end) == 0: sct.printv( '\nWARNING : the centerline/segmentation you gave does not cover all "z" slices of the input image. Results should be improved if you crop the input image (you can use \'sct_crop_image\') and generate a new spinalcord centerline/segmentation from this cropped image.\n', 1, 'warning') # print '\nWARNING : the centerline/segmentation you gave does not cover all "z" slices of the input image.\n' \ # 'Results should be improved if you crop the input image (you can use \'sct_crop_image\') and generate\n'\ # 'a new spinalcord centerline/segmentation from this cropped image.\n' #print len((numpy.array(x_centerline)>=0).nonzero()[0]),len((numpy.array(y_centerline)>=0).nonzero()[0]) min_centerline = min((numpy.array(x_centerline) >= 0).nonzero()[0]) max_centerline = max((numpy.array(x_centerline) >= 0).nonzero()[0]) z_centerline = z_centerline[(min_centerline):(max_centerline + 1)] #print len(z_centerline) nz = len(z_centerline) x_centerline = [x for x in x_centerline if not isnan(x)] y_centerline = [y for y in y_centerline if not isnan(y)] #print len(x_centerline),len(y_centerline) # clear variable del data # Fit the centerline points with the kind of curve given as argument of the script and return the new fitted coordinates if centerline_fitting == 'splines': x_centerline_fit, y_centerline_fit, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = msct_smooth.b_spline_nurbs( x_centerline, y_centerline, z_centerline) #x_centerline_fit, y_centerline_fit, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = b_spline_centerline(x_centerline,y_centerline,z_centerline) elif centerline_fitting == 'polynomial': x_centerline_fit, y_centerline_fit, polyx, polyy = polynome_centerline( x_centerline, y_centerline, z_centerline) #numpy.interp([i for i in xrange(0,min_centerline+1)], #y_centerline_fit #print z_centerline if verbose == 2: # plot centerline ax = plt.subplot(1, 2, 1) plt.plot(x_centerline, z_centerline, 'b:', label='centerline') plt.plot(x_centerline_fit, z_centerline, 'r-', label='fit') plt.xlabel('x') plt.ylabel('z') ax = plt.subplot(1, 2, 2) plt.plot(y_centerline, z_centerline, 'b:', label='centerline') plt.plot(y_centerline_fit, z_centerline, 'r-', label='fit') plt.xlabel('y') plt.ylabel('z') handles, labels = ax.get_legend_handles_labels() ax.legend(handles, labels) plt.show() # Get coordinates of landmarks along curved centerline #========================================================================================== print '\nGet coordinates of landmarks along curved centerline...' # landmarks are created along the curved centerline every z=gapz. They consist of a "cross" of size gapx and gapy. # find derivative of polynomial step_z = round(nz / gapz) #iz_curved = [i for i in range (0, nz, gapz)] iz_curved = [(min(z_centerline) + i * step_z) for i in range(0, gapz)] iz_curved.append(max(z_centerline)) #print iz_curved, len(iz_curved) n_iz_curved = len(iz_curved) #print n_iz_curved landmark_curved = [[[0 for i in range(0, 3)] for i in range(0, 5)] for i in iz_curved] # print x_centerline_deriv,len(x_centerline_deriv) # landmark[a][b][c] # a: index along z. E.g., the first cross with have index=0, the next index=1, and so on... # b: index of element on the cross. I.e., 0: center of the cross, 1: +x, 2 -x, 3: +y, 4: -y # c: dimension, i.e., 0: x, 1: y, 2: z # loop across index, which corresponds to iz (points along the centerline) if centerline_fitting == 'polynomial': for index in range(0, n_iz_curved, 1): # set coordinates for landmark at the center of the cross landmark_curved[index][0][0], landmark_curved[index][0][ 1], landmark_curved[index][0][2] = x_centerline_fit[ iz_curved[index]], y_centerline_fit[ iz_curved[index]], iz_curved[index] # set x and z coordinates for landmarks +x and -x landmark_curved[index][1][2], landmark_curved[index][1][ 0], landmark_curved[index][2][2], landmark_curved[index][2][ 0] = get_points_perpendicular_to_curve( polyx, polyx.deriv(), iz_curved[index], gapxy) # set y coordinate to y_centerline_fit[iz] for elements 1 and 2 of the cross for i in range(1, 3): landmark_curved[index][i][1] = y_centerline_fit[ iz_curved[index]] # set coordinates for landmarks +y and -y. Here, x coordinate is 0 (already initialized). landmark_curved[index][3][2], landmark_curved[index][3][ 1], landmark_curved[index][4][2], landmark_curved[index][4][ 1] = get_points_perpendicular_to_curve( polyy, polyy.deriv(), iz_curved[index], gapxy) # set x coordinate to x_centerline_fit[iz] for elements 3 and 4 of the cross for i in range(3, 5): landmark_curved[index][i][0] = x_centerline_fit[ iz_curved[index]] elif centerline_fitting == 'splines': for index in range(0, n_iz_curved, 1): # calculate d (ax+by+cz+d=0) # print iz_curved[index] a = x_centerline_deriv[iz_curved[index] - min(z_centerline)] b = y_centerline_deriv[iz_curved[index] - min(z_centerline)] c = z_centerline_deriv[iz_curved[index] - min(z_centerline)] x = x_centerline_fit[iz_curved[index] - min(z_centerline)] y = y_centerline_fit[iz_curved[index] - min(z_centerline)] z = iz_curved[index] d = -(a * x + b * y + c * z) #print a,b,c,d,x,y,z # set coordinates for landmark at the center of the cross landmark_curved[index][0][0], landmark_curved[index][0][ 1], landmark_curved[index][0][2] = x_centerline_fit[ iz_curved[index] - min(z_centerline)], y_centerline_fit[ iz_curved[index] - min(z_centerline)], iz_curved[index] # set y coordinate to y_centerline_fit[iz] for elements 1 and 2 of the cross for i in range(1, 3): landmark_curved[index][i][1] = y_centerline_fit[ iz_curved[index] - min(z_centerline)] # set x and z coordinates for landmarks +x and -x, forcing de landmark to be in the orthogonal plan and the distance landmark/curve to be gapxy x_n = Symbol('x_n') landmark_curved[index][2][0], landmark_curved[index][1][0] = solve( (x_n - x)**2 + ((-1 / c) * (a * x_n + b * y + d) - z)**2 - gapxy**2, x_n) #x for -x and +x landmark_curved[index][1][2] = (-1 / c) * ( a * landmark_curved[index][1][0] + b * y + d) #z for +x landmark_curved[index][2][2] = (-1 / c) * ( a * landmark_curved[index][2][0] + b * y + d) #z for -x # set x coordinate to x_centerline_fit[iz] for elements 3 and 4 of the cross for i in range(3, 5): landmark_curved[index][i][0] = x_centerline_fit[ iz_curved[index] - min(z_centerline)] # set coordinates for landmarks +y and -y. Here, x coordinate is 0 (already initialized). y_n = Symbol('y_n') landmark_curved[index][4][1], landmark_curved[index][3][1] = solve( (y_n - y)**2 + ((-1 / c) * (a * x + b * y_n + d) - z)**2 - gapxy**2, y_n) #y for -y and +y landmark_curved[index][3][2] = (-1 / c) * ( a * x + b * landmark_curved[index][3][1] + d) #z for +y landmark_curved[index][4][2] = (-1 / c) * ( a * x + b * landmark_curved[index][4][1] + d) #z for -y # #display # fig = plt.figure() # ax = fig.add_subplot(111, projection='3d') # ax.plot(x_centerline_fit, y_centerline_fit,z_centerline, 'g') # ax.plot(x_centerline, y_centerline,z_centerline, 'r') # ax.plot([landmark_curved[i][j][0] for i in range(0, n_iz_curved) for j in range(0, 5)], \ # [landmark_curved[i][j][1] for i in range(0, n_iz_curved) for j in range(0, 5)], \ # [landmark_curved[i][j][2] for i in range(0, n_iz_curved) for j in range(0, 5)], '.') # ax.set_xlabel('x') # ax.set_ylabel('y') # ax.set_zlabel('z') # plt.show() # Get coordinates of landmarks along straight centerline #========================================================================================== print '\nGet coordinates of landmarks along straight centerline...' landmark_straight = [[[0 for i in range(0, 3)] for i in range(0, 5)] for i in iz_curved ] # same structure as landmark_curved # calculate the z indices corresponding to the Euclidean distance between two consecutive points on the curved centerline (approximation curve --> line) iz_straight = [(min(z_centerline) + 0) for i in range(0, gapz + 1)] #print iz_straight,len(iz_straight) for index in range(1, n_iz_curved, 1): # compute vector between two consecutive points on the curved centerline vector_centerline = [x_centerline_fit[iz_curved[index]-min(z_centerline)] - x_centerline_fit[iz_curved[index-1]-min(z_centerline)], \ y_centerline_fit[iz_curved[index]-min(z_centerline)] - y_centerline_fit[iz_curved[index-1]-min(z_centerline)], \ iz_curved[index] - iz_curved[index-1]] # compute norm of this vector norm_vector_centerline = numpy.linalg.norm(vector_centerline, ord=2) # round to closest integer value norm_vector_centerline_rounded = int(round(norm_vector_centerline, 0)) # assign this value to the current z-coordinate on the straight centerline iz_straight[index] = iz_straight[index - 1] + norm_vector_centerline_rounded # initialize x0 and y0 to be at the center of the FOV x0 = int(round(nx / 2)) y0 = int(round(ny / 2)) for index in range(0, n_iz_curved, 1): # set coordinates for landmark at the center of the cross landmark_straight[index][0][0], landmark_straight[index][0][ 1], landmark_straight[index][0][2] = x0, y0, iz_straight[index] # set x, y and z coordinates for landmarks +x landmark_straight[index][1][0], landmark_straight[index][1][ 1], landmark_straight[index][1][2] = x0 + gapxy, y0, iz_straight[ index] # set x, y and z coordinates for landmarks -x landmark_straight[index][2][0], landmark_straight[index][2][ 1], landmark_straight[index][2][2] = x0 - gapxy, y0, iz_straight[ index] # set x, y and z coordinates for landmarks +y landmark_straight[index][3][0], landmark_straight[index][3][ 1], landmark_straight[index][3][2] = x0, y0 + gapxy, iz_straight[ index] # set x, y and z coordinates for landmarks -y landmark_straight[index][4][0], landmark_straight[index][4][ 1], landmark_straight[index][4][2] = x0, y0 - gapxy, iz_straight[ index] # # display # fig = plt.figure() # ax = fig.add_subplot(111, projection='3d') # #ax.plot(x_centerline_fit, y_centerline_fit,z_centerline, 'r') # ax.plot([landmark_straight[i][j][0] for i in range(0, n_iz_curved) for j in range(0, 5)], \ # [landmark_straight[i][j][1] for i in range(0, n_iz_curved) for j in range(0, 5)], \ # [landmark_straight[i][j][2] for i in range(0, n_iz_curved) for j in range(0, 5)], '.') # ax.set_xlabel('x') # ax.set_ylabel('y') # ax.set_zlabel('z') # plt.show() # # Create NIFTI volumes with landmarks #========================================================================================== # Pad input volume to deal with the fact that some landmarks on the curved centerline might be outside the FOV # N.B. IT IS VERY IMPORTANT TO PAD ALSO ALONG X and Y, OTHERWISE SOME LANDMARKS MIGHT GET OUT OF THE FOV!!! print '\nPad input volume to deal with the fact that some landmarks on the curved centerline might be outside the FOV...' sct.run('isct_c3d ' + fname_centerline_orient + ' -pad ' + str(padding) + 'x' + str(padding) + 'x' + str(padding) + 'vox ' + str(padding) + 'x' + str(padding) + 'x' + str(padding) + 'vox 0 -o tmp.centerline_pad.nii.gz') # TODO: don't pad input volume: no need for that! instead, try to increase size of hdr when saving landmarks. # Open padded centerline for reading print '\nOpen padded centerline for reading...' file = nibabel.load('tmp.centerline_pad.nii.gz') data = file.get_data() hdr = file.get_header() # Create volumes containing curved and straight landmarks data_curved_landmarks = data * 0 data_straight_landmarks = data * 0 # initialize landmark value landmark_value = 1 # Loop across cross index for index in range(0, n_iz_curved, 1): # loop across cross element index for i_element in range(0, 5, 1): # get x, y and z coordinates of curved landmark (rounded to closest integer) x, y, z = int(round(landmark_curved[index][i_element][0])), int( round(landmark_curved[index][i_element][1])), int( round(landmark_curved[index][i_element][2])) # attribute landmark_value to the voxel and its neighbours data_curved_landmarks[x + padding - 1:x + padding + 2, y + padding - 1:y + padding + 2, z + padding - 1:z + padding + 2] = landmark_value # get x, y and z coordinates of straight landmark (rounded to closest integer) x, y, z = int(round(landmark_straight[index][i_element][0])), int( round(landmark_straight[index][i_element][1])), int( round(landmark_straight[index][i_element][2])) # attribute landmark_value to the voxel and its neighbours data_straight_landmarks[x + padding - 1:x + padding + 2, y + padding - 1:y + padding + 2, z + padding - 1:z + padding + 2] = landmark_value # increment landmark value landmark_value = landmark_value + 1 # Write NIFTI volumes hdr.set_data_dtype( 'uint32') # set imagetype to uint8 #TODO: maybe use int32 print '\nWrite NIFTI volumes...' img = nibabel.Nifti1Image(data_curved_landmarks, None, hdr) nibabel.save(img, 'tmp.landmarks_curved.nii.gz') print '.. File created: tmp.landmarks_curved.nii.gz' img = nibabel.Nifti1Image(data_straight_landmarks, None, hdr) nibabel.save(img, 'tmp.landmarks_straight.nii.gz') print '.. File created: tmp.landmarks_straight.nii.gz' # Estimate deformation field by pairing landmarks #========================================================================================== # Dilate landmarks (because nearest neighbour interpolation will be later used, therefore some landmarks may "disapear" if they are single points) #print '\nDilate landmarks...' #sct.run(fsloutput+'fslmaths tmp.landmarks_curved.nii -kernel box 3x3x3 -dilD tmp.landmarks_curved_dilated -odt short') #sct.run(fsloutput+'fslmaths tmp.landmarks_straight.nii -kernel box 3x3x3 -dilD tmp.landmarks_straight_dilated -odt short') # Estimate rigid transformation print '\nEstimate rigid transformation between paired landmarks...' sct.run( 'isct_ANTSUseLandmarkImagesToGetAffineTransform tmp.landmarks_straight.nii.gz tmp.landmarks_curved.nii.gz rigid tmp.curve2straight_rigid.txt' ) # Apply rigid transformation print '\nApply rigid transformation to curved landmarks...' sct.run( 'sct_WarpImageMultiTransform 3 tmp.landmarks_curved.nii.gz tmp.landmarks_curved_rigid.nii.gz -R tmp.landmarks_straight.nii.gz tmp.curve2straight_rigid.txt --use-NN' ) # Estimate b-spline transformation curve --> straight print '\nEstimate b-spline transformation: curve --> straight...' sct.run( 'isct_ANTSUseLandmarkImagesToGetBSplineDisplacementField tmp.landmarks_straight.nii.gz tmp.landmarks_curved_rigid.nii.gz tmp.warp_curve2straight.nii.gz 5x5x5 3 2 0' ) # Concatenate rigid and non-linear transformations... print '\nConcatenate rigid and non-linear transformations...' #sct.run('isct_ComposeMultiTransform 3 tmp.warp_rigid.nii -R tmp.landmarks_straight.nii tmp.warp.nii tmp.curve2straight_rigid.txt') # TODO: use sct.run() when output from the following command will be different from 0 (currently there seem to be a bug) cmd = 'isct_ComposeMultiTransform 3 tmp.curve2straight.nii.gz -R tmp.landmarks_straight.nii.gz tmp.warp_curve2straight.nii.gz tmp.curve2straight_rigid.txt' print('>> ' + cmd) commands.getstatusoutput(cmd) # Estimate b-spline transformation straight --> curve # TODO: invert warping field instead of estimating a new one print '\nEstimate b-spline transformation: straight --> curve...' sct.run( 'isct_ANTSUseLandmarkImagesToGetBSplineDisplacementField tmp.landmarks_curved_rigid.nii.gz tmp.landmarks_straight.nii.gz tmp.warp_straight2curve.nii.gz 5x5x5 3 2 0' ) # Concatenate rigid and non-linear transformations... print '\nConcatenate rigid and non-linear transformations...' #sct.run('isct_ComposeMultiTransform 3 tmp.warp_rigid.nii -R tmp.landmarks_straight.nii tmp.warp.nii tmp.curve2straight_rigid.txt') # TODO: use sct.run() when output from the following command will be different from 0 (currently there seem to be a bug) cmd = 'isct_ComposeMultiTransform 3 tmp.straight2curve.nii.gz -R tmp.landmarks_straight.nii.gz -i tmp.curve2straight_rigid.txt tmp.warp_straight2curve.nii.gz' print('>> ' + cmd) commands.getstatusoutput(cmd) #print '\nPad input image...' #sct.run('isct_c3d '+fname_anat+' -pad '+str(padz)+'x'+str(padz)+'x'+str(padz)+'vox '+str(padz)+'x'+str(padz)+'x'+str(padz)+'vox 0 -o tmp.anat_pad.nii') # Unpad landmarks... # THIS WAS REMOVED ON 2014-06-03 because the output data was cropped at the edge, which caused landmarks to sometimes disappear # print '\nUnpad landmarks...' # sct.run('fslroi tmp.landmarks_straight.nii.gz tmp.landmarks_straight_crop.nii.gz '+str(padding)+' '+str(nx)+' '+str(padding)+' '+str(ny)+' '+str(padding)+' '+str(nz)) # Apply deformation to input image print '\nApply transformation to input image...' sct.run('sct_WarpImageMultiTransform 3 ' + file_anat + ext_anat + ' tmp.anat_rigid_warp.nii.gz -R tmp.landmarks_straight.nii.gz ' + interpolation_warp + ' tmp.curve2straight.nii.gz') # sct.run('sct_WarpImageMultiTransform 3 '+fname_anat+' tmp.anat_rigid_warp.nii.gz -R tmp.landmarks_straight_crop.nii.gz '+interpolation_warp+ ' tmp.curve2straight.nii.gz') # come back to parent folder os.chdir('..') # Generate output file (in current folder) # TODO: do not uncompress the warping field, it is too time consuming! print '\nGenerate output file (in current folder)...' sct.generate_output_file(path_tmp + '/tmp.curve2straight.nii.gz', '', 'warp_curve2straight', '.nii.gz') # warping field sct.generate_output_file(path_tmp + '/tmp.straight2curve.nii.gz', '', 'warp_straight2curve', '.nii.gz') # warping field sct.generate_output_file(path_tmp + '/tmp.anat_rigid_warp.nii.gz', '', file_anat + '_straight', ext_anat) # straightened anatomic # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf ' + path_tmp) print '\nDone!\n'
def main(): # Initialization fname_anat = '' fname_centerline = '' fwhm = param.fwhm width=param.width remove_temp_files = param.remove_temp_files start_time = time.time() verbose = param.verbose # extract path of the script path_script = os.path.dirname(__file__) + '/' # Parameters for debug mode if param.debug == 1: print '\n*** WARNING: DEBUG MODE ON ***\n' fname_anat = '/home/django/ibouchard/errsm_22_t2_cropped_rpi.nii.gz' fname_centerline = '/home/django/ibouchard//errsm_22_t2_cropped_centerline.nii.gz' fwhm=1 width=20 # Check input param try: opts, args = getopt.getopt(sys.argv[1:], 'hi:c:f:w:r:') except getopt.GetoptError as err: print str(err) usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_anat = arg elif opt in ('-c'): fname_centerline = arg elif opt in ('-f'): fwhm = int(arg) elif opt in ('w'): width=int(arg) elif opt in ('-r'): remove_temp_files = int(arg) # display usage if a mandatory argument is not provided if fname_anat == '' or fname_centerline == '': usage() # check existence of input files sct.check_file_exist(fname_anat) sct.check_file_exist(fname_centerline) # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) # extract path/file/extension path_centerline, file_centerline, ext_centerline = sct.extract_fname(fname_centerline) # Display arguments print '\nCheck input arguments...' print '.. Anatomical image: ' + fname_anat print '.. Centerline: ' + fname_centerline print '.. Full width at half maximum: ' + str(fwhm) print '.. Width of the square window: ' + str(width) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('cp '+fname_anat+' '+path_tmp+'data'+ext_anat, verbose) sct.run('cp '+fname_centerline+' '+path_tmp+'centerline'+ext_centerline, verbose) # go to tmp folder os.chdir(path_tmp) # convert to nii format convert('data'+ext_anat, 'data.nii') convert('centerline'+ext_centerline, 'centerline.nii') # # Get dimensions of data # sct.printv('\nGet dimensions of data...', param.verbose) # nx, ny, nz, nt, px, py, pz, pt = Image('data.nii').dim # # #Delete existing tmp file in the current folder to avoid problems # #Delete existing tmp file in the current folder to avoid problems # if os.path.isfile('tmp.anat.nii'): # sct.run('rm tmp.anat.nii') # if os.path.isfile('tmp.centerline.nii'): # sct.run('rm tmp.centerline.nii') # # # Convert to nii and delete nii.gz if still existing # print '\nCopy input data...' # sct.run('cp ' + fname_anat + ' tmp.anat'+ext_anat) # convert('data'+ext_data, 'data.nii') # # sct.run('fslchfiletype NIFTI tmp.anat') # if os.path.isfile('tmp.anat.nii.gz'): # sct.run('rm tmp.anat.nii.gz') # print '.. Anatomical image copied' # sct.run('cp ' + fname_centerline + ' tmp.centerline'+ext_centerline) # sct.run('fslchfiletype NIFTI tmp.centerline') # if os.path.isfile('tmp.centerline.nii.gz'): # sct.run('rm tmp.centerline.nii.gz') # print '.. Centerline image copied' # Open anatomical image #========================================================================================== # Reorient input anatomical volume into RL PA IS orientation print '\nReorient input volume to RL PA IS orientation...' sct.run(sct.fsloutput + 'fslswapdim tmp.anat RL PA IS tmp.anat_orient') print '\nGet dimensions of input anatomical image...' nx_a, ny_a, nz_a, nt_a, px_a, py_a, pz_a, pt_a = sct.get_dimension('tmp.anat_orient') #nx_a, ny_a, nz_a, nt_a, px_a, py_a, pz_a, pt_a = sct.get_dimension(fname_anat) print '.. matrix size: ' + str(nx_a) + ' x ' + str(ny_a) + ' x ' + str(nz_a) print '.. voxel size: ' + str(px_a) + 'mm x ' + str(py_a) + 'mm x ' + str(pz_a) + 'mm' print '\nOpen anatomical volume...' file = nibabel.load('tmp.anat_orient.nii') #file = nibabel.load(fname_anat) data_anat = file.get_data() data_anat=np.array(data_anat) data_anat_smoothed=np.copy(data_anat) # Open centerline #========================================================================================== # Reorient binary point into RL PA IS orientation print '\nReorient centerline volume into RL PA IS orientation...' sct.run(sct.fsloutput + 'fslswapdim tmp.centerline RL PA IS tmp.centerline_orient') print '\nGet dimensions of input centerline...' nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension('tmp.centerline_orient') #nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(fname_centerline) print '.. matrix size: ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) print '.. voxel size: ' + str(px) + 'mm x ' + str(py) + 'mm x ' + str(pz) + 'mm' print '\nOpen centerline volume...' file = nibabel.load('tmp.centerline_orient.nii') #file = nibabel.load(fname_centerline) data_centerline = file.get_data() #Loop across z and associate x,y coordinate with the point having maximum intensity x_centerline = [0 for iz in range(0, nz, 1)] y_centerline = [0 for iz in range(0, nz, 1)] z_centerline = [iz for iz in range(0, nz, 1)] for iz in range(0, nz, 1): x_centerline[iz], y_centerline[iz] = np.unravel_index(data_centerline[:, :, iz].argmax(), data_centerline[:, :, iz].shape) del data_centerline # Fit polynomial function through centerline #========================================================================================== #Fit centerline in the Z-X plane using polynomial function print '\nFit centerline in the Z-X plane using polynomial function...' coeffsx = np.polyfit(z_centerline, x_centerline, deg=param.deg_poly) polyx = np.poly1d(coeffsx) x_centerline_fit = np.polyval(polyx, z_centerline) #Fit centerline in the Z-Y plane using polynomial function print '\nFit centerline in the Z-Y plane using polynomial function...' coeffsy = np.polyfit(z_centerline, y_centerline, deg=param.deg_poly) polyy = np.poly1d(coeffsy) y_centerline_fit = np.polyval(polyy, z_centerline) # Find tangent function of centerline along z #========================================================================================== # Find tangent to centerline in zx plane, along z print '\nFind tangent to centerline along z, in the Z-X plane...' poly_tangent_xz = np.polyder(polyx) tangent_xz = np.polyval(poly_tangent_xz, z_centerline) # Find tangent to centerline in zy plane, along z print '\nFind tangent to centerline along z, in the Z-Y plane...' poly_tangent_yz = np.polyder(polyy) tangent_yz = np.polyval(poly_tangent_yz, z_centerline) # Create a Gaussian kernel with users parameters #========================================================================================== print '\nGenerate a Gaussian kernel with users parameters... ' # Convert the fwhm given by users in standard deviation (sigma) and find the size of gaussian kernel knowing # that size_kernel=(6*sigma-1) must be odd sigma = int(np.round((fwhm/pz_a)*(math.sqrt(1/(2*(math.log(2))))))) size_kernel= (np.round(6*sigma)) if size_kernel%2==0: size_kernel=size_kernel-1 #Creates an 1D-array impulsion and apply a gaussian filter. The result is a Gaussian kernel. kernel_temp = np.zeros(size_kernel) kernel_temp[math.ceil(size_kernel/2)] = 1 kernel= ndimage.filters.gaussian_filter1d(kernel_temp, sigma, order=0) sum_kernel=np.sum(kernel) print '.. Full width at half maximum: ' + str(fwhm) print '.. Kernel size : '+str(size_kernel) print '.. Sigma (Standard deviation): ' + str(sigma) del kernel_temp ## Smooth along the spinal cord ##========================================================================================== print '\nSmooth along the spinal cord...' print '\n Voxel position along z axis...' # Initialisations position=np.zeros(3) flag=np.zeros((nx_a,ny_a,nz_a)) data_weight=np.ones((nx_a,ny_a,nz_a)) smoothing_array=np.zeros(size_kernel) x_near=np.zeros(2) y_near=np.zeros(2) z_near=np.zeros(2) floor_position=np.zeros(3) ceil_position=np.zeros(3) position_d=np.zeros(3) #For every voxel along z axis, for iz in range(0,nz_a,1): print '.. '+str(iz+1)+ '/'+str(nz_a) # Determine the square area to smooth around the centerline xmin=x_centerline[iz]-int(width/2) xmax=x_centerline[iz]+int(width/2) ymin=y_centerline[iz]-int(width/2) ymax=y_centerline[iz]+int(width/2) #Find the angle between the tangent and the x axis in xz plane. theta_xz = -(math.atan(tangent_xz[iz])) #Find the angle between the tangent and the y axis in yz plane. theta_yz = -(math.atan(tangent_yz[iz])) #Construct a rotation array around y axis. Rxz=np.zeros((3,3)) Rxz[1,1]=1 Rxz[0,0]=(math.cos(theta_xz)) Rxz[2,0]=(math.sin(theta_xz)) Rxz[0,2]=-(math.sin(theta_xz)) Rxz[2,2]=(math.cos(theta_xz)) #Construct a rotation array around x axis. Ryz=np.zeros((3,3)) Ryz[0,0]=1 Ryz[1,1]=(math.cos(theta_yz)) Ryz[1,2]=(math.sin(theta_yz)) Ryz[2,1]=-(math.sin(theta_yz)) Ryz[2,2]=(math.cos(theta_yz)) #For every voxels in the given plane, included in the square area for ix in range(xmin,xmax,1): for iy in range(ymin,ymax,1): #The area to smooth has the same high as the 1D mask length isize=0 centerline_point=[np.copy(x_centerline[iz]), np.copy(y_centerline[iz]), np.copy(iz)] #For every voxels along the line orthogonal to the considered plane and included in the kernel. #(Here we full a vector called smoothing_array, which has the same length as the kernel, is oriented in the direction of centerline and contains interpolated values of intensity) for isize in range(0,size_kernel, 1): #Find the position in the xy plane, before rotation position = [ix, iy, iz+isize-(np.floor(size_kernel/2))] #Find the position after rotation by multiplying the position centered on centerline point with rotation array around x and y axis. new_position= np.dot((np.dot((np.subtract(np.copy(position),centerline_point)), Rxz)), Ryz) + centerline_point #If the resulting voxel is out of image boundaries, pad the smoothing array with a zero if (new_position[0]<0)or (new_position[1]<0)or(new_position[2]<0)or(new_position[0]>nx_a-1)or (new_position[1]>ny_a-1)or(new_position[2]>nz_a-1): smoothing_array[isize]=0 #Otherwise, fill the smoothing array with the linear interpolation of values around the actual position else: # Trilinear interpolation #========================================================================================================================================== # Determine the coordinates in grid surrounding the position of the central voxel and perform a trilinear interpolation x_near[0]=np.copy(np.floor(new_position[0])) x_near[1]=np.copy(np.ceil(new_position[0])) xd=(new_position[0]-x_near[0]) y_near[0]=np.copy(np.floor(new_position[1])) y_near[1]=np.copy(np.ceil(new_position[1])) yd=(new_position[1]-y_near[0]) z_near[0]=np.copy(np.floor(new_position[2])) z_near[1]=np.copy(np.ceil(new_position[2])) zd=(new_position[2]-z_near[0]) c00=((data_anat[x_near[0],y_near[0],z_near[0]])*(1-xd))+((data_anat[x_near[1],y_near[0],z_near[0]])*(xd)) c10=((data_anat[x_near[0],y_near[1],z_near[0]])*(1-xd))+((data_anat[x_near[1],y_near[1],z_near[0]])*(xd)) c01=((data_anat[x_near[0],y_near[0],z_near[1]])*(1-xd))+((data_anat[x_near[1],y_near[0],z_near[1]])*(xd)) c11=((data_anat[x_near[0],y_near[1],z_near[1]])*(1-xd))+((data_anat[x_near[1],y_near[1],z_near[1]])*(xd)) c0=c00*(1-yd)+c10*yd c1=c01*(1-yd)+c11*yd smoothing_array[isize]=c0*(1-zd)+c1*zd #If actual position is in the z=z_centerline plane, save the coordinates in the variable central_position. (Otherwise, don't save it). if isize==(np.floor(size_kernel/2)): central_position=np.copy(new_position) #If the central_position is out of boundaries, don't consider it anymore. if (central_position[0]<0)or (central_position[1]<0)or(central_position[2]<0)or(central_position[0]>nx_a-1)or (central_position[1]>ny_a-1)or(central_position[2]>nz_a-1): continue else: #Otherwise, perform the convolution of the smoothing_array and the kernel for the central voxel only (equivalent to element-wise multiply). Normalize the result. result=((np.sum(np.copy(smoothing_array)*kernel))/sum_kernel) # Determine the coordinates in grid surrounding the position of the central voxel for i in range(0,3,1): floor_position[i]=math.floor(central_position[i]) ceil_position[i]=math.ceil(central_position[i]) position_d[i]=central_position[i]-floor_position[i] # Reverse trilinear interpolation #========================================================================================================================================== # Split the resuling intensity given by the convolution between the 8 voxels surrounding the point where the convolution is calculated (central_position). # The array data_anat_smoothed is the the volume os the anatomical image smoothed alog the spinal cord. # The array flag is a volume that indicates if a the corresponding voxel in the anatomical image is inside the smoothing area around the spinal cord and if there is already been an operation on this voxel. # The default value of flag is 0. If it is set to 1, it means there is an operation on the corresponding voxel in anatomical image. Then we clear both the data_anat_smoothed and data_weight corresponding voxel to 0. # The array data_weight represent the is represent the sum of weights used to calculate the intensity for every voxel. In a perfect case, this sum would be 1, but because there is an angle between # two adjacent planes, the sum will be lower so we need to normalize the result. The default value for data_weight is 1, but once there is an operation on the corresponding voxel (flag=1), we accumulate the weights used. if (flag[ceil_position[0],ceil_position[1],ceil_position[2]]==0): data_anat_smoothed[ceil_position[0],ceil_position[1],ceil_position[2]]=0 data_weight[ceil_position[0],ceil_position[1],ceil_position[2]]=0 flag[ceil_position[0],ceil_position[1],ceil_position[2]]=1 weight=(position_d[0])*(position_d[1])*(position_d[2]) data_anat_smoothed[ceil_position[0],ceil_position[1],ceil_position[2]]=data_anat_smoothed[ceil_position[0],ceil_position[1],ceil_position[2]]+(weight*result) data_weight[ceil_position[0],ceil_position[1],ceil_position[2]]=data_weight[ceil_position[0],ceil_position[1],ceil_position[2]]+(weight) if (flag[floor_position[0],floor_position[1],floor_position[2]]==0): data_anat_smoothed[floor_position[0],floor_position[1],floor_position[2]]=0 data_weight[floor_position[0],floor_position[1],floor_position[2]]=0 flag[floor_position[0],floor_position[1],floor_position[2]]=1 weight=(1-position_d[0])*(1-position_d[1])*(1-position_d[2]) data_anat_smoothed[floor_position[0],floor_position[1],floor_position[2]]=data_anat_smoothed[floor_position[0],floor_position[1],floor_position[2]]+(weight*result) data_weight[floor_position[0],floor_position[1],floor_position[2]]=data_weight[floor_position[0],floor_position[1],floor_position[2]]+(weight) if (flag[ceil_position[0],floor_position[1],floor_position[2]]==0): data_anat_smoothed[ceil_position[0],floor_position[1],floor_position[2]]=0 data_weight[ceil_position[0],floor_position[1],floor_position[2]]=0 flag[ceil_position[0],floor_position[1],floor_position[2]]=1 weight=(position_d[0])*(1-position_d[1])*(1-position_d[2]) data_anat_smoothed[ceil_position[0],floor_position[1],floor_position[2]]=data_anat_smoothed[ceil_position[0],floor_position[1],floor_position[2]]+(weight*result) data_weight[ceil_position[0],floor_position[1],floor_position[2]]=data_weight[ceil_position[0],floor_position[1],floor_position[2]]+(weight) if (flag[ceil_position[0],ceil_position[1],floor_position[2]]==0): data_anat_smoothed[ceil_position[0],ceil_position[1],floor_position[2]]=0 data_weight[ceil_position[0],ceil_position[1],floor_position[2]]=0 flag[ceil_position[0],ceil_position[1],floor_position[2]]=1 weight=(position_d[0])*(position_d[1])*(1-position_d[2]) data_anat_smoothed[ceil_position[0],ceil_position[1],floor_position[2]]=data_anat_smoothed[ceil_position[0],ceil_position[1],floor_position[2]]+(weight*result) data_weight[ceil_position[0],ceil_position[1],floor_position[2]]=data_weight[ceil_position[0],ceil_position[1],floor_position[2]]+(weight) if (flag[ceil_position[0],floor_position[1],ceil_position[2]]==0): data_anat_smoothed[ceil_position[0],floor_position[1],ceil_position[2]]=0 data_weight[ceil_position[0],floor_position[1],ceil_position[2]]=0 flag[ceil_position[0],floor_position[1],ceil_position[2]]=1 weight=(position_d[0])*(1-position_d[1])*(position_d[2]) data_anat_smoothed[ceil_position[0],floor_position[1],ceil_position[2]]=data_anat_smoothed[ceil_position[0],floor_position[1],ceil_position[2]]+(weight*result) data_weight[ceil_position[0],floor_position[1],ceil_position[2]]=data_weight[ceil_position[0],floor_position[1],ceil_position[2]]+(weight) if (flag[floor_position[0],ceil_position[1],floor_position[2]]==0): data_anat_smoothed[floor_position[0],ceil_position[1],floor_position[2]]=0 data_weight[floor_position[0],ceil_position[1],floor_position[2]]=0 flag[floor_position[0],ceil_position[1],floor_position[2]]=1 weight=(1-position_d[0])*(position_d[1])*(1-position_d[2]) data_anat_smoothed[floor_position[0],ceil_position[1],floor_position[2]]=data_anat_smoothed[floor_position[0],ceil_position[1],floor_position[2]]+(weight*result) data_weight[floor_position[0],ceil_position[1],floor_position[2]]=data_weight[floor_position[0],ceil_position[1],floor_position[2]]+(weight) if (flag[floor_position[0],ceil_position[1],ceil_position[2]]==0): data_anat_smoothed[floor_position[0],ceil_position[1],ceil_position[2]]=0 data_weight[floor_position[0],ceil_position[1],ceil_position[2]]=0 flag[floor_position[0],ceil_position[1],ceil_position[2]]=1 weight=(1-position_d[0])*(position_d[1])*(position_d[2]) data_anat_smoothed[floor_position[0],ceil_position[1], ceil_position[2]]= data_anat_smoothed[floor_position[0],ceil_position[1], ceil_position[2]]+(weight*result) data_weight[floor_position[0],ceil_position[1], ceil_position[2]]= data_weight[floor_position[0],ceil_position[1], ceil_position[2]]+(weight) if (flag[floor_position[0],floor_position[1],ceil_position[2]]==0): data_anat_smoothed[floor_position[0],floor_position[1],ceil_position[2]]=0 flag[floor_position[0],floor_position[1],ceil_position[2]]=1 data_weight[floor_position[0],floor_position[1],ceil_position[2]]=0 weight=(1-position_d[0])*(1-position_d[1])*(position_d[2]) data_anat_smoothed[floor_position[0],floor_position[1],ceil_position[2]]=data_anat_smoothed[floor_position[0],floor_position[1],ceil_position[2]]+(weight*result) data_weight[floor_position[0],floor_position[1],ceil_position[2]]=data_weight[floor_position[0],floor_position[1],ceil_position[2]]+(weight) # Once we covered the whole spinal cord along z, we normalize the resulting image considering the weight used to calculate each voxel intensity data_anat_smoothed=data_anat_smoothed/data_weight #Generate output file #========================================================================================== # Write NIFTI volumes print '\nWrite NIFTI volumes...' if os.path.isfile('tmp.im_smoothed.nii'): sct.run('rm tmp.im_smoothed.nii') img = nibabel.Nifti1Image(data_anat_smoothed, None) nibabel.save(img, 'tmp.im_smoothed.nii') print '.. File created: tmp.im_smoothed.nii' #Copy header geometry from input data print '\nCopy header geometry from input data and reorient the volume...' sct.run(sct.fsloutput+'fslcpgeom tmp.anat_orient.nii tmp.im_smoothed.nii ') #Generate output file print '\nGenerate output file (in current folder)...' sct.generate_output_file('tmp.im_smoothed.nii','./',file_anat+'_smoothed',ext_anat) # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm tmp.anat.nii') sct.run('rm tmp.centerline.nii') sct.run('rm tmp.anat_orient.nii') sct.run('rm tmp.centerline_orient.nii') #Display elapsed time elapsed_time = time.time() - start_time print '\nFinished!' print '.. '+str(int(round(elapsed_time)))+'s\n'
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'])
def main(): # Initialization fname_mt0 = '' fname_mt1 = '' file_out = param.file_out # register = param.register # remove_tmp_files = param.remove_tmp_files # verbose = param.verbose # get path of the toolbox # status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # Check input parameters parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_mt0 = arguments['-mt0'] fname_mt1 = arguments['-mt1'] remove_tmp_files = int(arguments['-r']) verbose = int(arguments['-v']) # Extract path/file/extension path_mt0, file_mt0, ext_mt0 = sct.extract_fname(fname_mt0) path_out, file_out, ext_out = '', file_out, ext_mt0 # create temporary folder path_tmp = sct.tmp_create() # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) from sct_convert import convert convert(fname_mt0, path_tmp + 'mt0.nii', type='float32') convert(fname_mt1, path_tmp + 'mt1.nii', type='float32') # go to tmp folder os.chdir(path_tmp) # compute MTR sct.printv('\nCompute MTR...', verbose) from msct_image import Image nii_mt1 = Image('mt1.nii') data_mt1 = nii_mt1.data data_mt0 = Image('mt0.nii').data data_mtr = 100 * (data_mt0 - data_mt1) / data_mt0 # save MTR file nii_mtr = nii_mt1 nii_mtr.data = data_mtr nii_mtr.setFileName('mtr.nii') nii_mtr.save() # sct.run(fsloutput+'fslmaths -dt double mt0.nii -sub mt1.nii -mul 100 -div mt0.nii -thr 0 -uthr 100 mtr.nii', verbose) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp + 'mtr.nii', path_out + file_out + ext_out) # Remove temporary files if remove_tmp_files == 1: sct.printv('\nRemove temporary files...') sct.run('rm -rf ' + path_tmp) # to view results sct.printv('\nDone! To view results, type:', verbose) sct.printv( 'fslview ' + fname_mt0 + ' ' + fname_mt1 + ' ' + file_out + ' &\n', verbose, 'info')
def main(): # Initialization fname_data = '' interp_factor = param.interp_factor remove_temp_files = param.remove_temp_files verbose = param.verbose suffix = param.suffix smoothing_sigma = param.smoothing_sigma # start timer start_time = time.time() # get path of the toolbox path_sct = os.environ.get("SCT_DIR", os.path.dirname(os.path.dirname(__file__))) # Parameters for debug mode if param.debug: fname_data = os.path.join(path_sct, 'testing', 'data', 'errsm_23', 't2', 't2_manual_segmentation.nii.gz') remove_temp_files = 0 param.mask_size = 10 else: # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:], 'hi:v:r:s:') except getopt.GetoptError: usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_data = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ('-s'): smoothing_sigma = arg elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_data == '': usage() # sct.printv(arguments) sct.printv('\nCheck parameters:') sct.printv(' segmentation ........... ' + fname_data) sct.printv(' interp factor .......... ' + str(interp_factor)) sct.printv(' smoothing sigma ........ ' + str(smoothing_sigma)) # check existence of input files sct.printv('\nCheck existence of input files...') sct.check_file_exist(fname_data, verbose) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(fname_data) path_tmp = sct.tmp_create(basename="binary_to_trilinear", verbose=verbose) from sct_convert import convert sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(fname_data, os.path.join(path_tmp, "data.nii")) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # Get dimensions of data sct.printv('\nGet dimensions of data...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image('data.nii').dim sct.printv('.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # upsample data sct.printv('\nUpsample data...', verbose) sct.run([ "sct_resample", "-i", "data.nii", "-x", "linear", "-vox", str(nx * interp_factor) + 'x' + str(ny * interp_factor) + 'x' + str(nz * interp_factor), "-o", "data_up.nii" ], verbose) # Smooth along centerline sct.printv('\nSmooth along centerline...', verbose) sct.run([ "sct_smooth_spinalcord", "-i", "data_up.nii", "-s", "data_up.nii", "-smooth", str(smoothing_sigma), "-r", str(remove_temp_files), "-v", str(verbose) ], verbose) # downsample data sct.printv('\nDownsample data...', verbose) sct.run([ "sct_resample", "-i", "data_up_smooth.nii", "-x", "linear", "-vox", str(nx) + 'x' + str(ny) + 'x' + str(nz), "-o", "data_up_smooth_down.nii" ], verbose) # come back os.chdir(curdir) # Generate output files sct.printv('\nGenerate output files...') fname_out = sct.generate_output_file( os.path.join(path_tmp, "data_up_smooth_down.nii"), '' + file_data + suffix + ext_data) # Delete temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...') sct.rmtree(path_tmp) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's') # to view results sct.printv('\nTo view results, type:') sct.printv('fslview ' + file_data + ' ' + file_data + suffix + ' &\n')
def apply(self): # Initialization fname_src = self.input_filename # source image (moving) list_warp = self.list_warp # list of warping fields fname_out = self.output_filename # output fname_dest = self.fname_dest # destination image (fix) verbose = self.verbose remove_temp_files = self.remove_temp_files crop_reference = self.crop # if = 1, put 0 everywhere around warping field, if = 2, real crop interp = sct.get_interpolation('isct_antsApplyTransforms', self.interp) # Parse list of warping fields sct.printv('\nParse list of warping fields...', verbose) use_inverse = [] fname_warp_list_invert = [] # list_warp = list_warp.replace(' ', '') # remove spaces # list_warp = list_warp.split(",") # parse with comma for idx_warp, path_warp in enumerate(self.list_warp): # Check if this transformation should be inverted if path_warp in self.list_warpinv: use_inverse.append('-i') # list_warp[idx_warp] = path_warp[1:] # remove '-' fname_warp_list_invert += [[ use_inverse[idx_warp], list_warp[idx_warp] ]] else: use_inverse.append('') fname_warp_list_invert += [[path_warp]] path_warp = list_warp[idx_warp] if path_warp.endswith((".nii", ".nii.gz")) \ and msct_image.Image(list_warp[idx_warp]).header.get_intent()[0] != 'vector': raise ValueError("Displacement field in {} is invalid: should be encoded" \ " in a 5D file with vector intent code" \ " (see https://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1.h" \ .format(path_warp)) # need to check if last warping field is an affine transfo isLastAffine = False path_fname, file_fname, ext_fname = sct.extract_fname( fname_warp_list_invert[-1][-1]) if ext_fname in ['.txt', '.mat']: isLastAffine = True # check if destination file is 3d if not sct.check_if_3d(fname_dest): sct.printv('ERROR: Destination data must be 3d') # N.B. Here we take the inverse of the warp list, because sct_WarpImageMultiTransform concatenates in the reverse order fname_warp_list_invert.reverse() fname_warp_list_invert = functools.reduce(lambda x, y: x + y, fname_warp_list_invert) # Extract path, file and extension path_src, file_src, ext_src = sct.extract_fname(fname_src) path_dest, file_dest, ext_dest = sct.extract_fname(fname_dest) # Get output folder and file name if fname_out == '': path_out = '' # output in user's current directory file_out = file_src + '_reg' ext_out = ext_src fname_out = os.path.join(path_out, file_out + ext_out) # Get dimensions of data sct.printv('\nGet dimensions of data...', verbose) img_src = msct_image.Image(fname_src) nx, ny, nz, nt, px, py, pz, pt = img_src.dim # nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(fname_src) sct.printv( ' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), verbose) # if 3d if nt == 1: # Apply transformation sct.printv('\nApply transformation...', verbose) if nz in [0, 1]: dim = '2' else: dim = '3' sct.run([ 'isct_antsApplyTransforms', '-d', dim, '-i', fname_src, '-o', fname_out, '-t' ] + fname_warp_list_invert + ['-r', fname_dest] + interp, verbose=verbose, is_sct_binary=True) # if 4d, loop across the T dimension else: path_tmp = sct.tmp_create(basename="apply_transfo", verbose=verbose) # convert to nifti into temp folder sct.printv( '\nCopying input data to tmp folder and convert to nii...', verbose) img_src.save(os.path.join(path_tmp, "data.nii")) sct.copy(fname_dest, os.path.join(path_tmp, file_dest + ext_dest)) fname_warp_list_tmp = [] for fname_warp in list_warp: path_warp, file_warp, ext_warp = sct.extract_fname(fname_warp) sct.copy(fname_warp, os.path.join(path_tmp, file_warp + ext_warp)) fname_warp_list_tmp.append(file_warp + ext_warp) fname_warp_list_invert_tmp = fname_warp_list_tmp[::-1] curdir = os.getcwd() os.chdir(path_tmp) # split along T dimension sct.printv('\nSplit along T dimension...', verbose) im_dat = msct_image.Image('data.nii') im_header = im_dat.hdr data_split_list = sct_image.split_data(im_dat, 3) for im in data_split_list: im.save() # apply transfo sct.printv('\nApply transformation to each 3D volume...', verbose) for it in range(nt): file_data_split = 'data_T' + str(it).zfill(4) + '.nii' file_data_split_reg = 'data_reg_T' + str(it).zfill(4) + '.nii' status, output = sct.run([ 'isct_antsApplyTransforms', '-d', '3', '-i', file_data_split, '-o', file_data_split_reg, '-t', ] + fname_warp_list_invert_tmp + [ '-r', file_dest + ext_dest, ] + interp, verbose, is_sct_binary=True) # Merge files back sct.printv('\nMerge file back...', verbose) import glob path_out, name_out, ext_out = sct.extract_fname(fname_out) # im_list = [Image(file_name) for file_name in glob.glob('data_reg_T*.nii')] # concat_data use to take a list of image in input, now takes a list of file names to open the files one by one (see issue #715) fname_list = glob.glob('data_reg_T*.nii') fname_list.sort() im_out = sct_image.concat_data(fname_list, 3, im_header['pixdim']) im_out.save(name_out + ext_out) os.chdir(curdir) sct.generate_output_file( os.path.join(path_tmp, name_out + ext_out), fname_out) # Delete temporary folder if specified if int(remove_temp_files): sct.printv('\nRemove temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # 2. crop the resulting image using dimensions from the warping field warping_field = fname_warp_list_invert[-1] # if last warping field is an affine transfo, we need to compute the space of the concatenate warping field: if isLastAffine: sct.printv( 'WARNING: the resulting image could have wrong apparent results. You should use an affine transformation as last transformation...', verbose, 'warning') elif crop_reference == 1: ImageCropper(input_file=fname_out, output_file=fname_out, ref=warping_field, background=0).crop() # sct.run('sct_crop_image -i '+fname_out+' -o '+fname_out+' -ref '+warping_field+' -b 0') elif crop_reference == 2: ImageCropper(input_file=fname_out, output_file=fname_out, ref=warping_field).crop() # sct.run('sct_crop_image -i '+fname_out+' -o '+fname_out+' -ref '+warping_field) sct.display_viewer_syntax([fname_dest, fname_out], verbose=verbose)
def main(args=None): # initializations param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) fname_data = arguments['-i'] fname_seg = arguments['-s'] if '-l' in arguments: fname_landmarks = arguments['-l'] label_type = 'body' elif '-ldisc' in arguments: fname_landmarks = arguments['-ldisc'] label_type = 'disc' else: sct.printv('ERROR: Labels should be provided.', 1, 'error') if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = '' param.path_qc = arguments.get("-qc", None) path_template = arguments['-t'] contrast_template = arguments['-c'] ref = arguments['-ref'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) param.verbose = verbose # TODO: not clean, unify verbose or param.verbose in code, but not both if '-param-straighten' in arguments: param.param_straighten = arguments['-param-straighten'] # if '-cpu-nb' in arguments: # arg_cpu = ' -cpu-nb '+str(arguments['-cpu-nb']) # else: # arg_cpu = '' # registration parameters if '-param' in arguments: # reset parameters but keep step=0 (might be overwritten if user specified step=0) paramreg = ParamregMultiStep([step0]) if ref == 'subject': paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz' # add user parameters for paramStep in arguments['-param']: paramreg.addStep(paramStep) else: paramreg = ParamregMultiStep([step0, step1, step2]) # if ref=subject, initialize registration using different affine parameters if ref == 'subject': paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz' # initialize other parameters # file_template_label = param.file_template_label zsubsample = param.zsubsample # smoothing_sigma = param.smoothing_sigma # retrieve template file names file_template_vertebral_labeling = get_file_label( os.path.join(path_template, 'template'), 'vertebral labeling') file_template = get_file_label( os.path.join(path_template, 'template'), contrast_template.upper() + '-weighted template') file_template_seg = get_file_label(os.path.join(path_template, 'template'), 'spinal cord') # start timer start_time = time.time() # get fname of the template + template objects fname_template = os.path.join(path_template, 'template', file_template) fname_template_vertebral_labeling = os.path.join( path_template, 'template', file_template_vertebral_labeling) fname_template_seg = os.path.join(path_template, 'template', file_template_seg) fname_template_disc_labeling = os.path.join(path_template, 'template', 'PAM50_label_disc.nii.gz') # check file existence # TODO: no need to do that! sct.printv('\nCheck template files...') sct.check_file_exist(fname_template, verbose) sct.check_file_exist(fname_template_vertebral_labeling, verbose) sct.check_file_exist(fname_template_seg, verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) # sct.printv(arguments) sct.printv('\nCheck parameters:', verbose) sct.printv(' Data: ' + fname_data, verbose) sct.printv(' Landmarks: ' + fname_landmarks, verbose) sct.printv(' Segmentation: ' + fname_seg, verbose) sct.printv(' Path template: ' + path_template, verbose) sct.printv(' Remove temp files: ' + str(remove_temp_files), verbose) # check 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) vertebral_alignment = False if len(labels) > 2 and label_type == 'disc': vertebral_alignment = True path_tmp = sct.tmp_create(basename="register_to_template", verbose=verbose) # set temporary file names ftmp_data = 'data.nii' ftmp_seg = 'seg.nii.gz' ftmp_label = 'label.nii.gz' ftmp_template = 'template.nii' ftmp_template_seg = 'template_seg.nii.gz' ftmp_template_label = 'template_label.nii.gz' # 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', os.path.join(path_tmp, ftmp_data) ]) sct.run([ 'sct_convert', '-i', fname_seg, '-o', os.path.join(path_tmp, ftmp_seg) ]) sct.run([ 'sct_convert', '-i', fname_landmarks, '-o', os.path.join(path_tmp, ftmp_label) ]) sct.run([ 'sct_convert', '-i', fname_template, '-o', os.path.join(path_tmp, ftmp_template) ]) sct.run([ 'sct_convert', '-i', fname_template_seg, '-o', os.path.join(path_tmp, ftmp_template_seg) ]) sct_convert.main(args=[ '-i', fname_template_vertebral_labeling, '-o', os.path.join(path_tmp, ftmp_template_label) ]) if label_type == 'disc': sct_convert.main(args=[ '-i', fname_template_disc_labeling, '-o', os.path.join(path_tmp, ftmp_template_label) ]) # sct.run('sct_convert -i '+fname_template_label+' -o '+os.path.join(path_tmp, ftmp_template_label)) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # Generate labels from template vertebral labeling if label_type == 'body': sct.printv('\nGenerate labels from template vertebral labeling', verbose) sct_label_utils.main(args=[ '-i', ftmp_template_label, '-vert-body', '0', '-o', ftmp_template_label ]) # check if provided labels are available in the template sct.printv('\nCheck if provided labels are available in the template', verbose) image_label_template = Image(ftmp_template_label) labels_template = image_label_template.getNonZeroCoordinates( sorting='value') if labels[-1].value > labels_template[-1].value: sct.printv( 'ERROR: Wrong landmarks input. Labels must have correspondence in template space. \nLabel max ' 'provided: ' + str(labels[-1].value) + '\nLabel max from template: ' + str(labels_template[-1].value), verbose, 'error') # if only one label is present, force affine transformation to be Tx,Ty,Tz only (no scaling) if len(labels) == 1: paramreg.steps['0'].dof = 'Tx_Ty_Tz' sct.printv( 'WARNING: Only one label is present. Forcing initial transformation to: ' + paramreg.steps['0'].dof, 1, 'warning') # Project labels onto the spinal cord centerline because later, an affine transformation is estimated between the # template's labels (centered in the cord) and the subject's labels (assumed to be centered in the cord). # If labels are not centered, mis-registration errors are observed (see issue #1826) ftmp_label = project_labels_on_spinalcord(ftmp_label, ftmp_seg) # binarize segmentation (in case it has values below 0 caused by manual editing) sct.printv('\nBinarize segmentation', verbose) 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 nearest neighbour can make them disappear. resample_labels(ftmp_label, ftmp_data, add_suffix(ftmp_label, '_1mm')) ftmp_label = add_suffix(ftmp_label, '_1mm') # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) 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') if vertebral_alignment: # cropping the segmentation based on the label coverage to ensure good registration with vertebral alignment # See https://github.com/neuropoly/spinalcordtoolbox/pull/1669 for details image_labels = Image(ftmp_label) coordinates_labels = image_labels.getNonZeroCoordinates( sorting='z') nx, ny, nz, nt, px, py, pz, pt = image_labels.dim offset_crop = 10.0 * pz # cropping the image 10 mm above and below the highest and lowest label cropping_slices = [ coordinates_labels[0].z - offset_crop, coordinates_labels[-1].z + offset_crop ] # make sure that the cropping slices do not extend outside of the slice range (issue #1811) if cropping_slices[0] < 0: cropping_slices[0] = 0 if cropping_slices[1] > nz: cropping_slices[1] = nz status_crop, output_crop = sct.run([ 'sct_crop_image', '-i', ftmp_seg, '-o', add_suffix(ftmp_seg, '_crop'), '-dim', '2', '-start', str(cropping_slices[0]), '-end', str(cropping_slices[1]) ], verbose) else: # if we do not align the vertebral levels, we crop the segmentation from top to bottom status_crop, output_crop = sct.run([ 'sct_crop_image', '-i', ftmp_seg, '-o', add_suffix(ftmp_seg, '_crop'), '-dim', '2', '-bzmax' ], verbose) cropping_slices = output_crop.split('Dimension 2: ')[1].split( '\n')[0].split(' ') # output: segmentation_rpi_crop.nii.gz ftmp_seg = add_suffix(ftmp_seg, '_crop') # straighten segmentation sct.printv( '\nStraighten the spinal cord using centerline/segmentation...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) fn_warp_curve2straight = os.path.join(curdir, "warp_curve2straight.nii.gz") fn_warp_straight2curve = os.path.join(curdir, "warp_straight2curve.nii.gz") fn_straight_ref = os.path.join(curdir, "straight_ref.nii.gz") cache_input_files = [ftmp_seg] if vertebral_alignment: cache_input_files += [ ftmp_template_seg, ftmp_label, ftmp_template_label, ] cache_sig = sct.cache_signature(input_files=cache_input_files, ) cachefile = os.path.join(curdir, "straightening.cache") if sct.cache_valid( cachefile, cache_sig ) and os.path.isfile(fn_warp_curve2straight) and os.path.isfile( fn_warp_straight2curve) and os.path.isfile(fn_straight_ref): sct.printv( 'Reusing existing warping field which seems to be valid', verbose, 'warning') sct.copy(fn_warp_curve2straight, 'warp_curve2straight.nii.gz') sct.copy(fn_warp_straight2curve, 'warp_straight2curve.nii.gz') sct.copy(fn_straight_ref, 'straight_ref.nii.gz') # apply straightening sct.run([ 'sct_apply_transfo', '-i', ftmp_seg, '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', add_suffix(ftmp_seg, '_straight') ]) else: from sct_straighten_spinalcord import SpinalCordStraightener sc_straight = SpinalCordStraightener(ftmp_seg, ftmp_seg) sc_straight.output_filename = add_suffix(ftmp_seg, '_straight') sc_straight.path_output = './' sc_straight.qc = '0' sc_straight.remove_temp_files = remove_temp_files sc_straight.verbose = verbose if vertebral_alignment: sc_straight.centerline_reference_filename = ftmp_template_seg sc_straight.use_straight_reference = True sc_straight.discs_input_filename = ftmp_label sc_straight.discs_ref_filename = ftmp_template_label sc_straight.straighten() sct.cache_save(cachefile, cache_sig) # N.B. DO NOT UPDATE VARIABLE ftmp_seg BECAUSE TEMPORARY USED LATER # re-define warping field using non-cropped space (to avoid issue #367) sct.run([ 'sct_concat_transfo', '-w', 'warp_straight2curve.nii.gz', '-d', ftmp_data, '-o', 'warp_straight2curve.nii.gz' ]) if vertebral_alignment: sct.copy('warp_curve2straight.nii.gz', 'warp_curve2straightAffine.nii.gz') else: # Label preparation: # -------------------------------------------------------------------------------- # Remove unused label on template. Keep only label present in the input label image sct.printv( '\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run([ 'sct_label_utils', '-i', ftmp_template_label, '-o', ftmp_template_label, '-remove', ftmp_label ]) # Dilating the input label so they can be straighten without losing them sct.printv('\nDilating input labels using 3vox ball radius') sct.run([ 'sct_maths', '-i', ftmp_label, '-o', add_suffix(ftmp_label, '_dilate'), '-dilate', '3' ]) ftmp_label = add_suffix(ftmp_label, '_dilate') # Apply straightening to labels sct.printv('\nApply straightening to labels...', verbose) sct.run([ 'sct_apply_transfo', '-i', ftmp_label, '-o', add_suffix(ftmp_label, '_straight'), '-d', add_suffix(ftmp_seg, '_straight'), '-w', 'warp_curve2straight.nii.gz', '-x', 'nn' ]) ftmp_label = add_suffix(ftmp_label, '_straight') # Compute rigid transformation straight landmarks --> template landmarks sct.printv('\nEstimate transformation for step #0...', verbose) from msct_register_landmarks import register_landmarks try: register_landmarks(ftmp_label, ftmp_template_label, paramreg.steps['0'].dof, fname_affine='straight2templateAffine.txt', verbose=verbose) except Exception: sct.printv( 'ERROR: input labels do not seem to be at the right place. Please check the position of the labels. See documentation for more details: https://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/', verbose=verbose, type='error') # Concatenate transformations: curve --> straight --> affine sct.printv( '\nConcatenate transformations: curve --> straight --> affine...', verbose) sct.run([ 'sct_concat_transfo', '-w', 'warp_curve2straight.nii.gz,straight2templateAffine.txt', '-d', 'template.nii', '-o', 'warp_curve2straightAffine.nii.gz' ]) # Apply transformation sct.printv('\nApply transformation...', verbose) sct.run([ 'sct_apply_transfo', '-i', ftmp_data, '-o', add_suffix(ftmp_data, '_straightAffine'), '-d', ftmp_template, '-w', 'warp_curve2straightAffine.nii.gz' ]) ftmp_data = add_suffix(ftmp_data, '_straightAffine') sct.run([ 'sct_apply_transfo', '-i', ftmp_seg, '-o', add_suffix(ftmp_seg, '_straightAffine'), '-d', ftmp_template, '-w', 'warp_curve2straightAffine.nii.gz', '-x', 'linear' ]) ftmp_seg = add_suffix(ftmp_seg, '_straightAffine') """ # Benjamin: Issue from Allan Martin, about the z=0 slice that is screwed up, caused by the affine transform. # Solution found: remove slices below and above landmarks to avoid rotation effects points_straight = [] for coord in landmark_template: points_straight.append(coord.z) min_point, max_point = int(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() if vertebral_alignment: sct.run([ 'sct_concat_transfo', '-w', ','.join(warp_inverse) + ',warp_straight2curve.nii.gz', '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz' ], verbose) else: sct.run([ 'sct_concat_transfo', '-w', ','.join(warp_inverse) + ',-straight2templateAffine.txt,warp_straight2curve.nii.gz', '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz' ], verbose) # register template->subject elif ref == 'subject': # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) 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="./") except Exception: sct.printv( 'ERROR: input labels do not seem to be at the right place. Please check the position of the labels. See documentation for more details: https://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/', verbose=verbose, type='error') # loop across registration steps for i_step in range(1, len(paramreg.steps)): sct.printv( '\nEstimate transformation for step #' + str(i_step) + '...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = ftmp_template dest = ftmp_data interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = ftmp_template_seg dest = ftmp_seg interp_step = 'nn' else: sct.printv('ERROR: Wrong image type.', 1, 'error') # apply transformation from previous step, to use as new src for registration sct.run([ 'sct_apply_transfo', '-i', src, '-d', dest, '-w', ','.join(warp_forward), '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step ], verbose) src = add_suffix(src, '_regStep' + str(i_step - 1)) # register src --> dest # TODO: display param for debugging warp_forward_out, warp_inverse_out = register( src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.insert(0, warp_inverse_out) # Concatenate transformations: sct.printv('\nConcatenate transformations: template --> subject...', verbose) sct.run([ 'sct_concat_transfo', '-w', ','.join(warp_forward), '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz' ], verbose) sct.printv('\nConcatenate transformations: subject --> template...', verbose) sct.run([ 'sct_concat_transfo', '-w', ','.join(warp_inverse), '-d', 'template.nii', '-o', 'warp_anat2template.nii.gz' ], verbose) # Apply warping fields to anat and template sct.run([ 'sct_apply_transfo', '-i', 'template.nii', '-o', 'template2anat.nii.gz', '-d', 'data.nii', '-w', 'warp_template2anat.nii.gz', '-crop', '1' ], verbose) sct.run([ 'sct_apply_transfo', '-i', 'data.nii', '-o', 'anat2template.nii.gz', '-d', 'template.nii', '-w', 'warp_anat2template.nii.gz', '-crop', '1' ], verbose) # come back os.chdir(curdir) # Generate output files sct.printv('\nGenerate output files...', verbose) fname_template2anat = os.path.join(path_output, 'template2anat' + ext_data) fname_anat2template = os.path.join(path_output, 'anat2template' + ext_data) sct.generate_output_file( os.path.join(path_tmp, "warp_template2anat.nii.gz"), os.path.join(path_output, "warp_template2anat.nii.gz"), verbose) sct.generate_output_file( os.path.join(path_tmp, "warp_anat2template.nii.gz"), os.path.join(path_output, "warp_anat2template.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "template2anat.nii.gz"), fname_template2anat, verbose) sct.generate_output_file(os.path.join(path_tmp, "anat2template.nii.gz"), fname_anat2template, verbose) if ref == 'template': # copy straightening files in case subsequent SCT functions need them sct.generate_output_file( os.path.join(path_tmp, "warp_curve2straight.nii.gz"), os.path.join(path_output, "warp_curve2straight.nii.gz"), verbose) sct.generate_output_file( os.path.join(path_tmp, "warp_straight2curve.nii.gz"), os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose) sct.generate_output_file( os.path.join(path_tmp, "straight_ref.nii.gz"), os.path.join(path_output, "straight_ref.nii.gz"), verbose) # Delete temporary files if remove_temp_files: sct.printv('\nDelete temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's', verbose) if param.path_qc is not None: generate_qc(fname_data, fname_template2anat, fname_seg, args, os.path.abspath(param.path_qc)) sct.display_viewer_syntax([fname_data, fname_template2anat], verbose=verbose) sct.display_viewer_syntax([fname_template, fname_anat2template], verbose=verbose)
def main(args=None): # initialization start_time = time.time() path_out = '.' param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] param.fname_bvecs = arguments['-bvec'] if '-bval' in arguments: param.fname_bvals = arguments['-bval'] if '-bvalmin' in arguments: param.bval_min = arguments['-bvalmin'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-thr' in arguments: param.otsu = arguments['-thr'] if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_temp_files = int(arguments['-r']) param.verbose = int(arguments.get('-v')) sct.init_sct(log_level=param.verbose, update=True) # Update log level # Get full path param.fname_data = os.path.abspath(param.fname_data) param.fname_bvecs = os.path.abspath(param.fname_bvecs) if param.fname_bvals != '': param.fname_bvals = os.path.abspath(param.fname_bvals) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_mask, file_mask, ext_mask = sct.extract_fname(param.fname_mask) path_tmp = sct.tmp_create(basename="dmri_moco", verbose=param.verbose) # names of files in temporary folder mask_name = 'mask' bvecs_fname = 'bvecs.txt' # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, os.path.join(path_tmp, "dmri.nii")) sct.copy(param.fname_bvecs, os.path.join(path_tmp, bvecs_fname), verbose=param.verbose) if param.fname_mask != '': sct.copy(param.fname_mask, os.path.join(path_tmp, mask_name + ext_mask), verbose=param.verbose) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # update field in param (because used later). # TODO: make this cleaner... if param.fname_mask != '': param.fname_mask = mask_name + ext_mask # run moco fname_data_moco_tmp = dmri_moco(param) # generate b0_moco_mean and dwi_moco_mean args = [ '-i', fname_data_moco_tmp, '-bvec', 'bvecs.txt', '-a', '1', '-v', '0' ] if not param.fname_bvals == '': # if bvals file is provided args += ['-bval', param.fname_bvals] fname_b0, fname_b0_mean, fname_dwi, fname_dwi_mean = sct_dmri_separate_b0_and_dwi.main( args=args) # come back os.chdir(curdir) # Generate output files fname_dmri_moco = os.path.join(path_out, file_data + param.suffix + ext_data) fname_dmri_moco_b0_mean = sct.add_suffix(fname_dmri_moco, '_b0_mean') fname_dmri_moco_dwi_mean = sct.add_suffix(fname_dmri_moco, '_dwi_mean') sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(fname_data_moco_tmp, fname_dmri_moco, param.verbose) sct.generate_output_file(fname_b0_mean, fname_dmri_moco_b0_mean, param.verbose) sct.generate_output_file(fname_dwi_mean, fname_dmri_moco_dwi_mean, param.verbose) # Delete temporary files if param.remove_temp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.rmtree(path_tmp, verbose=param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', param.verbose) sct.display_viewer_syntax([fname_dmri_moco, file_data], mode='ortho,ortho')
def main(args=None): # initialization start_time = time.time() param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_temp_files = int(arguments['-r']) param.verbose = int(arguments.get('-v')) sct.init_sct(log_level=param.verbose, update=True) # Update log level sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............' + param.fname_data, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_tmp = sct.tmp_create(basename="fmri_moco", verbose=param.verbose) # Copying input data to tmp folder and convert to nii # TODO: no need to do that (takes time for nothing) sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, os.path.join(path_tmp, "fmri.nii"), squeeze_data=False) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # run moco fmri_moco(param) # come back os.chdir(curdir) # Generate output files fname_fmri_moco = os.path.join(path_out, file_data + param.suffix + ext_data) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(os.path.join(path_tmp, "fmri" + param.suffix + '.nii'), fname_fmri_moco, param.verbose) sct.generate_output_file(os.path.join(path_tmp, "fmri" + param.suffix + '_mean.nii'), os.path.join(path_out, file_data + param.suffix + '_mean' + ext_data), param.verbose) # Delete temporary files if param.remove_temp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.rmtree(path_tmp, verbose=param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', param.verbose) sct.display_viewer_syntax([fname_fmri_moco, file_data], mode='ortho,ortho')
def main(): # get default parameters step1 = Paramreg(step='1', type='seg', algo='slicereg', metric='MeanSquares', iter='10') step2 = Paramreg(step='2', type='im', algo='syn', metric='MI', iter='3') # step1 = Paramreg() paramreg = ParamregMultiStep([step1, step2]) # step1 = Paramreg_step(step='1', type='seg', algo='bsplinesyn', metric='MeanSquares', iter='10', shrink='1', smooth='0', gradStep='0.5') # step2 = Paramreg_step(step='2', type='im', algo='syn', metric='MI', iter='10', shrink='1', smooth='0', gradStep='0.5') # paramreg = ParamregMultiStep([step1, step2]) # Initialize the parser parser = Parser(__file__) parser.usage.set_description('Register anatomical image to the template.') parser.add_option(name="-i", type_value="file", description="Anatomical image.", mandatory=True, example="anat.nii.gz") parser.add_option(name="-s", type_value="file", description="Spinal cord segmentation.", mandatory=True, example="anat_seg.nii.gz") parser.add_option(name="-l", type_value="file", description="Labels. See: http://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/", mandatory=True, default_value='', example="anat_labels.nii.gz") parser.add_option(name="-t", type_value="folder", description="Path to MNI-Poly-AMU template.", mandatory=False, default_value=param.path_template) parser.add_option(name="-p", type_value=[[':'], 'str'], description="""Parameters for registration (see sct_register_multimodal). Default:\n--\nstep=1\ntype="""+paramreg.steps['1'].type+"""\nalgo="""+paramreg.steps['1'].algo+"""\nmetric="""+paramreg.steps['1'].metric+"""\npoly="""+paramreg.steps['1'].poly+"""\n--\nstep=2\ntype="""+paramreg.steps['2'].type+"""\nalgo="""+paramreg.steps['2'].algo+"""\nmetric="""+paramreg.steps['2'].metric+"""\niter="""+paramreg.steps['2'].iter+"""\nshrink="""+paramreg.steps['2'].shrink+"""\nsmooth="""+paramreg.steps['2'].smooth+"""\ngradStep="""+paramreg.steps['2'].gradStep+"""\n--""", mandatory=False, example="step=2,type=seg,algo=bsplinesyn,metric=MeanSquares,iter=5,shrink=2:step=3,type=im,algo=syn,metric=MI,iter=5,shrink=1,gradStep=0.3") parser.add_option(name="-r", type_value="multiple_choice", description="""Remove temporary files.""", mandatory=False, default_value='1', example=['0', '1']) parser.add_option(name="-v", type_value="multiple_choice", description="""Verbose. 0: nothing. 1: basic. 2: extended.""", mandatory=False, default_value=param.verbose, example=['0', '1', '2']) if param.debug: print '\n*** WARNING: DEBUG MODE ON ***\n' fname_data = '/Users/julien/data/temp/sct_example_data/t2/t2.nii.gz' fname_landmarks = '/Users/julien/data/temp/sct_example_data/t2/labels.nii.gz' fname_seg = '/Users/julien/data/temp/sct_example_data/t2/t2_seg.nii.gz' path_template = param.path_template remove_temp_files = 0 verbose = 2 # speed = 'superfast' #param_reg = '2,BSplineSyN,0.6,MeanSquares' else: arguments = parser.parse(sys.argv[1:]) # get arguments fname_data = arguments['-i'] fname_seg = arguments['-s'] fname_landmarks = arguments['-l'] path_template = arguments['-t'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) if '-p' in arguments: paramreg_user = arguments['-p'] # update registration parameters for paramStep in paramreg_user: paramreg.addStep(paramStep) # initialize other parameters file_template = param.file_template file_template_label = param.file_template_label file_template_seg = param.file_template_seg output_type = param.output_type zsubsample = param.zsubsample # smoothing_sigma = param.smoothing_sigma # start timer start_time = time.time() # get absolute path - TO DO: remove! NEVER USE ABSOLUTE PATH... path_template = os.path.abspath(path_template) # get fname of the template + template objects fname_template = sct.slash_at_the_end(path_template, 1)+file_template fname_template_label = sct.slash_at_the_end(path_template, 1)+file_template_label fname_template_seg = sct.slash_at_the_end(path_template, 1)+file_template_seg # check file existence sct.printv('\nCheck template files...') sct.check_file_exist(fname_template, verbose) sct.check_file_exist(fname_template_label, verbose) sct.check_file_exist(fname_template_seg, verbose) # print arguments sct.printv('\nCheck parameters:', verbose) sct.printv('.. Data: '+fname_data, verbose) sct.printv('.. Landmarks: '+fname_landmarks, verbose) sct.printv('.. Segmentation: '+fname_seg, verbose) sct.printv('.. Path template: '+path_template, verbose) sct.printv('.. Output type: '+str(output_type), verbose) sct.printv('.. Remove temp files: '+str(remove_temp_files), verbose) sct.printv('\nParameters for registration:') for pStep in range(1, len(paramreg.steps)+1): sct.printv('Step #'+paramreg.steps[str(pStep)].step, verbose) sct.printv('.. Type #'+paramreg.steps[str(pStep)].type, verbose) sct.printv('.. Algorithm................ '+paramreg.steps[str(pStep)].algo, verbose) sct.printv('.. Metric................... '+paramreg.steps[str(pStep)].metric, verbose) sct.printv('.. Number of iterations..... '+paramreg.steps[str(pStep)].iter, verbose) sct.printv('.. Shrink factor............ '+paramreg.steps[str(pStep)].shrink, verbose) sct.printv('.. Smoothing factor......... '+paramreg.steps[str(pStep)].smooth, verbose) sct.printv('.. Gradient step............ '+paramreg.steps[str(pStep)].gradStep, verbose) sct.printv('.. Degree of polynomial..... '+paramreg.steps[str(pStep)].poly, verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) sct.printv('\nCheck input labels...') # check if label image contains coherent labels image_label = Image(fname_landmarks) # -> all labels must be different labels = image_label.getNonZeroCoordinates(sorting='value') hasDifferentLabels = True for lab in labels: for otherlabel in labels: if lab != otherlabel and lab.hasEqualValue(otherlabel): hasDifferentLabels = False break if not hasDifferentLabels: sct.printv('ERROR: Wrong landmarks input. All labels must be different.', verbose, 'error') # all labels must be available in tempalte image_label_template = Image(fname_template_label) labels_template = image_label_template.getNonZeroCoordinates(sorting='value') if labels[-1].value > labels_template[-1].value: sct.printv('ERROR: Wrong landmarks input. Labels must have correspondance in tempalte space. \nLabel max ' 'provided: ' + str(labels[-1].value) + '\nLabel max from template: ' + str(labels_template[-1].value), verbose, 'error') # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") status, output = sct.run('mkdir '+path_tmp) # copy files to temporary folder sct.printv('\nCopy files...', verbose) sct.run('isct_c3d '+fname_data+' -o '+path_tmp+'/data.nii') sct.run('isct_c3d '+fname_landmarks+' -o '+path_tmp+'/landmarks.nii.gz') sct.run('isct_c3d '+fname_seg+' -o '+path_tmp+'/segmentation.nii.gz') sct.run('isct_c3d '+fname_template+' -o '+path_tmp+'/template.nii') sct.run('isct_c3d '+fname_template_label+' -o '+path_tmp+'/template_labels.nii.gz') sct.run('isct_c3d '+fname_template_seg+' -o '+path_tmp+'/template_seg.nii.gz') # go to tmp folder os.chdir(path_tmp) # resample data to 1mm isotropic sct.printv('\nResample data to 1mm isotropic...', verbose) sct.run('isct_c3d data.nii -resample-mm 1.0x1.0x1.0mm -interpolation Linear -o datar.nii') sct.run('isct_c3d segmentation.nii.gz -resample-mm 1.0x1.0x1.0mm -interpolation NearestNeighbor -o segmentationr.nii.gz') # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling with neighrest neighbour can make them disappear. Therefore a more clever approach is required. resample_labels('landmarks.nii.gz', 'datar.nii', 'landmarksr.nii.gz') # # TODO # sct.run('sct_label_utils -i datar.nii -t create -x 124,186,19,2:129,98,23,8 -o landmarksr.nii.gz') # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) set_orientation('datar.nii', 'RPI', 'data_rpi.nii') set_orientation('landmarksr.nii.gz', 'RPI', 'landmarks_rpi.nii.gz') set_orientation('segmentationr.nii.gz', 'RPI', 'segmentation_rpi.nii.gz') # # Change orientation of input images to RPI # sct.printv('\nChange orientation of input images to RPI...', verbose) # set_orientation('data.nii', 'RPI', 'data_rpi.nii') # set_orientation('landmarks.nii.gz', 'RPI', 'landmarks_rpi.nii.gz') # set_orientation('segmentation.nii.gz', 'RPI', 'segmentation_rpi.nii.gz') # get landmarks in native space # crop segmentation # output: segmentation_rpi_crop.nii.gz sct.run('sct_crop_image -i segmentation_rpi.nii.gz -o segmentation_rpi_crop.nii.gz -dim 2 -bzmax') # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) sct.run('sct_straighten_spinalcord -i segmentation_rpi_crop.nii.gz -c segmentation_rpi_crop.nii.gz -r 0 -v '+str(verbose), verbose) # re-define warping field using non-cropped space (to avoid issue #367) sct.run('sct_concat_transfo -w warp_straight2curve.nii.gz -d data_rpi.nii -o warp_straight2curve.nii.gz') # Label preparation: # -------------------------------------------------------------------------------- # Remove unused label on template. Keep only label present in the input label image sct.printv('\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run('sct_label_utils -t remove -i template_labels.nii.gz -o template_label.nii.gz -r landmarks_rpi.nii.gz') # Make sure landmarks are INT sct.printv('\nConvert landmarks to INT...', verbose) sct.run('isct_c3d template_label.nii.gz -type int -o template_label.nii.gz', verbose) # Create a cross for the template labels - 5 mm sct.printv('\nCreate a 5 mm cross for the template labels...', verbose) sct.run('sct_label_utils -t cross -i template_label.nii.gz -o template_label_cross.nii.gz -c 5') # Create a cross for the input labels and dilate for straightening preparation - 5 mm sct.printv('\nCreate a 5mm cross for the input labels and dilate for straightening preparation...', verbose) sct.run('sct_label_utils -t cross -i landmarks_rpi.nii.gz -o landmarks_rpi_cross3x3.nii.gz -c 5 -d') # Apply straightening to labels sct.printv('\nApply straightening to labels...', verbose) sct.run('sct_apply_transfo -i landmarks_rpi_cross3x3.nii.gz -o landmarks_rpi_cross3x3_straight.nii.gz -d segmentation_rpi_crop_straight.nii.gz -w warp_curve2straight.nii.gz -x nn') # Convert landmarks from FLOAT32 to INT sct.printv('\nConvert landmarks from FLOAT32 to INT...', verbose) sct.run('isct_c3d landmarks_rpi_cross3x3_straight.nii.gz -type int -o landmarks_rpi_cross3x3_straight.nii.gz') # Remove labels that do not correspond with each others. sct.printv('\nRemove labels that do not correspond with each others.', verbose) sct.run('sct_label_utils -t remove-symm -i landmarks_rpi_cross3x3_straight.nii.gz -o landmarks_rpi_cross3x3_straight.nii.gz,template_label_cross.nii.gz -r template_label_cross.nii.gz') # Estimate affine transfo: straight --> template (landmark-based)' sct.printv('\nEstimate affine transfo: straight anat --> template (landmark-based)...', verbose) # converting landmarks straight and curved to physical coordinates image_straight = Image('landmarks_rpi_cross3x3_straight.nii.gz') landmark_straight = image_straight.getNonZeroCoordinates(sorting='value') image_template = Image('template_label_cross.nii.gz') landmark_template = image_template.getNonZeroCoordinates(sorting='value') # Reorganize landmarks points_fixed, points_moving = [], [] landmark_straight_mean = [] for coord in landmark_straight: if coord.value not in [c.value for c in landmark_straight_mean]: temp_landmark = coord temp_number = 1 for other_coord in landmark_straight: if coord.hasEqualValue(other_coord) and coord != other_coord: temp_landmark += other_coord temp_number += 1 landmark_straight_mean.append(temp_landmark / temp_number) for coord in landmark_straight_mean: point_straight = image_straight.transfo_pix2phys([[coord.x, coord.y, coord.z]]) points_moving.append([point_straight[0][0], point_straight[0][1], point_straight[0][2]]) for coord in landmark_template: point_template = image_template.transfo_pix2phys([[coord.x, coord.y, coord.z]]) points_fixed.append([point_template[0][0], point_template[0][1], point_template[0][2]]) # Register curved landmarks on straight landmarks based on python implementation sct.printv('\nComputing rigid transformation (algo=translation-scaling-z) ...', verbose) import msct_register_landmarks (rotation_matrix, translation_array, points_moving_reg, points_moving_barycenter) = \ msct_register_landmarks.getRigidTransformFromLandmarks( points_fixed, points_moving, constraints='translation-scaling-z', show=False) # writing rigid transformation file text_file = open("straight2templateAffine.txt", "w") text_file.write("#Insight Transform File V1.0\n") text_file.write("#Transform 0\n") text_file.write("Transform: FixedCenterOfRotationAffineTransform_double_3_3\n") text_file.write("Parameters: %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f\n" % ( 1.0/rotation_matrix[0, 0], rotation_matrix[0, 1], rotation_matrix[0, 2], rotation_matrix[1, 0], 1.0/rotation_matrix[1, 1], rotation_matrix[1, 2], rotation_matrix[2, 0], rotation_matrix[2, 1], 1.0/rotation_matrix[2, 2], translation_array[0, 0], translation_array[0, 1], -translation_array[0, 2])) text_file.write("FixedParameters: %.9f %.9f %.9f\n" % (points_moving_barycenter[0], points_moving_barycenter[1], points_moving_barycenter[2])) text_file.close() # Apply affine transformation: straight --> template sct.printv('\nApply affine transformation: straight --> template...', verbose) sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt -d template.nii -o warp_curve2straightAffine.nii.gz') sct.run('sct_apply_transfo -i data_rpi.nii -o data_rpi_straight2templateAffine.nii -d template.nii -w warp_curve2straightAffine.nii.gz') sct.run('sct_apply_transfo -i segmentation_rpi.nii.gz -o segmentation_rpi_straight2templateAffine.nii.gz -d template.nii -w warp_curve2straightAffine.nii.gz -x linear') # threshold to 0.5 nii = Image('segmentation_rpi_straight2templateAffine.nii.gz') data = nii.data data[data < 0.5] = 0 nii.data = data nii.setFileName('segmentation_rpi_straight2templateAffine_th.nii.gz') nii.save() # find min-max of anat2template (for subsequent cropping) zmin_template, zmax_template = find_zmin_zmax('segmentation_rpi_straight2templateAffine_th.nii.gz') # crop template in z-direction (for faster processing) sct.printv('\nCrop data in template space (for faster processing)...', verbose) sct.run('sct_crop_image -i template.nii -o template_crop.nii -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) sct.run('sct_crop_image -i template_seg.nii.gz -o template_seg_crop.nii.gz -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) sct.run('sct_crop_image -i data_rpi_straight2templateAffine.nii -o data_rpi_straight2templateAffine_crop.nii -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) sct.run('sct_crop_image -i segmentation_rpi_straight2templateAffine.nii.gz -o segmentation_rpi_straight2templateAffine_crop.nii.gz -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) # sub-sample in z-direction sct.printv('\nSub-sample in z-direction (for faster processing)...', verbose) sct.run('sct_resample -i template_crop.nii -o template_crop_r.nii -f 1x1x'+zsubsample, verbose) sct.run('sct_resample -i template_seg_crop.nii.gz -o template_seg_crop_r.nii.gz -f 1x1x'+zsubsample, verbose) sct.run('sct_resample -i data_rpi_straight2templateAffine_crop.nii -o data_rpi_straight2templateAffine_crop_r.nii -f 1x1x'+zsubsample, verbose) sct.run('sct_resample -i segmentation_rpi_straight2templateAffine_crop.nii.gz -o segmentation_rpi_straight2templateAffine_crop_r.nii.gz -f 1x1x'+zsubsample, verbose) # Registration straight spinal cord to template sct.printv('\nRegister straight spinal cord to template...', verbose) # loop across registration steps warp_forward = [] warp_inverse = [] for i_step in range(1, len(paramreg.steps)+1): sct.printv('\nEstimate transformation for step #'+str(i_step)+'...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = 'data_rpi_straight2templateAffine_crop_r.nii' dest = 'template_crop_r.nii' interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = 'segmentation_rpi_straight2templateAffine_crop_r.nii.gz' dest = 'template_seg_crop_r.nii.gz' interp_step = 'nn' else: sct.printv('ERROR: Wrong image type.', 1, 'error') # if step>1, apply warp_forward_concat to the src image to be used if i_step > 1: # sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose) sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose) src = sct.add_suffix(src, '_reg') # register src --> dest warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.append(warp_inverse_out) # Concatenate transformations: sct.printv('\nConcatenate transformations: anat --> template...', verbose) sct.run('sct_concat_transfo -w warp_curve2straightAffine.nii.gz,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose) # sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose) warp_inverse.reverse() sct.run('sct_concat_transfo -w '+','.join(warp_inverse)+',-straight2templateAffine.txt,warp_straight2curve.nii.gz -d data.nii -o warp_template2anat.nii.gz', verbose) # Apply warping fields to anat and template if output_type == 1: sct.run('sct_apply_transfo -i template.nii -o template2anat.nii.gz -d data.nii -w warp_template2anat.nii.gz -c 1', verbose) sct.run('sct_apply_transfo -i data.nii -o anat2template.nii.gz -d template.nii -w warp_anat2template.nii.gz -c 1', verbose) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp+'/warp_template2anat.nii.gz', 'warp_template2anat.nii.gz', verbose) sct.generate_output_file(path_tmp+'/warp_anat2template.nii.gz', 'warp_anat2template.nii.gz', verbose) if output_type == 1: sct.generate_output_file(path_tmp+'/template2anat.nii.gz', 'template2anat'+ext_data, verbose) sct.generate_output_file(path_tmp+'/anat2template.nii.gz', 'anat2template'+ext_data, verbose) # Delete temporary files if remove_temp_files: sct.printv('\nDelete temporary files...', verbose) sct.run('rm -rf '+path_tmp) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s', verbose) # to view results sct.printv('\nTo view results, type:', verbose) sct.printv('fslview '+fname_data+' template2anat -b 0,4000 &', verbose, 'info') sct.printv('fslview '+fname_template+' -b 0,5000 anat2template &\n', verbose, 'info')
def main(): # Initialization fname_data = '' fname_bvecs = '' mask_size = param.mask_size interp_final = param.interp remove_temp_files = param.remove_temp_files verbose = param.verbose start_time = time.time() # Parameters for debug mode if param.debug: fname_data = path_sct+'/testing/data/errsm_23/dmri/dmri.nii.gz' fname_bvecs = path_sct+'/testing/data/errsm_23/dmri/bvecs.txt' remove_temp_files = 0 param.mask_size = 10 # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:],'hi:b:g:s:c:p:v:r:') except getopt.GetoptError: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-b'): fname_bvecs = arg elif opt in ('-c'): param.cost_function_flirt = arg elif opt in ('-i'): fname_data = arg elif opt in ('-g'): param.dwi_group_size = int(arg) elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ('-s'): mask_size = arg elif opt in ('-p'): param.interp = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_data == '' or fname_bvecs == '': print '\n\nAll mandatory arguments are not provided \n' usage() # print arguments print '\nCheck parameters:' print '.. dmri data: '+fname_data print '.. bvecs file: '+fname_bvecs print '.. DWI group size: '+str(param.dwi_group_size) print '.. Gaussian mask size: '+str(mask_size) + 'mm' print '' # Get full path fname_data = os.path.abspath(fname_data) fname_bvecs = os.path.abspath(fname_bvecs) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(fname_data) #param.path_script = os.path.dirname(__file__) #param.path_script = os.path.abspath(param.path_script) # create temporary folder param.path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.run('mkdir '+ param.path_tmp) #param.path_tmp = param.path_script + '/' + param.path_tmp + '/' # go to tmp folder os.chdir(param.path_tmp) # run moco sct_moco_process_dmri(param, fname_data, fname_bvecs) # Generate output files print('\nGenerate output files...') fname_out = sct.generate_output_file('dmri_moco.nii', '../', file_data+param.suffix, ext_data) # come back to parent folder os.chdir('..') # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm -rf '+ param.path_tmp) # display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s' # to view results print '\nTo view results, type:' print 'fslview '+file_data+' '+file_data+'_moco &\n'
def main(args=None): # initialization start_time = time.time() param = Param() # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_temp_files = int(arguments['-r']) if '-v' in arguments: param.verbose = int(arguments['-v']) sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............' + param.fname_data, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_tmp = sct.tmp_create(basename="fmri_moco", verbose=param.verbose) # Copying input data to tmp folder and convert to nii # TODO: no need to do that (takes time for nothing) sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, os.path.join(path_tmp, "fmri.nii"), squeeze_data=False) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # run moco fmri_moco(param) # come back os.chdir(curdir) # Generate output files fname_fmri_moco = os.path.join(path_out, file_data + param.suffix + ext_data) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) if os.path.isfile(os.path.join(path_tmp, "fmri" + param.suffix + '.nii')): sct.printv(os.path.join(path_tmp, "fmri" + param.suffix + '.nii')) sct.printv(os.path.join(path_out, file_data + param.suffix + ext_data)) sct.generate_output_file(os.path.join(path_tmp, "fmri" + param.suffix + '.nii'), os.path.join(path_out, file_data + param.suffix + ext_data), param.verbose) sct.generate_output_file(os.path.join(path_tmp, "fmri" + param.suffix + '_mean.nii'), os.path.join(path_out, file_data + param.suffix + '_mean' + ext_data), param.verbose) # Delete temporary files if param.remove_temp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.rmtree(path_tmp, verbose=param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', param.verbose) sct.display_viewer_syntax([fname_fmri_moco, file_data], mode='ortho,ortho')
def extract_centerline(fname_segmentation, remove_temp_files, name_output='', verbose=0, algo_fitting='hanning', type_window='hanning', window_length=80): # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder path_tmp = 'tmp.' + time.strftime("%y%m%d%H%M%S") sct.run('mkdir ' + path_tmp) # copy files into tmp folder sct.run('cp ' + fname_segmentation + ' ' + path_tmp) # go to tmp folder os.chdir(path_tmp) # Change orientation of the input centerline into RPI sct.printv('\nOrient centerline to RPI orientation...', verbose) fname_segmentation_orient = 'segmentation_rpi' + ext_data set_orientation(file_data + ext_data, 'RPI', fname_segmentation_orient) # Get dimension sct.printv('\nGet dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_segmentation_orient).dim sct.printv( '.. matrix size: ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) sct.printv( '.. voxel size: ' + str(px) + 'mm x ' + str(py) + 'mm x ' + str(pz) + 'mm', verbose) # Extract orientation of the input segmentation orientation = get_orientation(file_data + ext_data) sct.printv('\nOrientation of segmentation image: ' + orientation, verbose) sct.printv('\nOpen segmentation volume...', verbose) file = nibabel.load(fname_segmentation_orient) data = file.get_data() hdr = file.get_header() # Extract min and max index in Z direction X, Y, Z = (data > 0).nonzero() min_z_index, max_z_index = min(Z), max(Z) x_centerline = [0 for i in range(0, max_z_index - min_z_index + 1)] y_centerline = [0 for i in range(0, max_z_index - min_z_index + 1)] z_centerline = [iz for iz in range(min_z_index, max_z_index + 1)] # Extract segmentation points and average per slice for iz in range(min_z_index, max_z_index + 1): x_seg, y_seg = (data[:, :, iz] > 0).nonzero() x_centerline[iz - min_z_index] = np.mean(x_seg) y_centerline[iz - min_z_index] = np.mean(y_seg) for k in range(len(X)): data[X[k], Y[k], Z[k]] = 0 # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline_fit, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline( fname_segmentation_orient, type_window=type_window, window_length=window_length, algo_fitting=algo_fitting, verbose=verbose) if verbose == 2: import matplotlib.pyplot as plt #Creation of a vector x that takes into account the distance between the labels nz_nonz = len(z_centerline) x_display = [0 for i in range(x_centerline_fit.shape[0])] y_display = [0 for i in range(y_centerline_fit.shape[0])] for i in range(0, nz_nonz, 1): x_display[int(z_centerline[i] - z_centerline[0])] = x_centerline[i] y_display[int(z_centerline[i] - z_centerline[0])] = y_centerline[i] plt.figure(1) plt.subplot(2, 1, 1) plt.plot(z_centerline_fit, x_display, 'ro') plt.plot(z_centerline_fit, x_centerline_fit) plt.xlabel("Z") plt.ylabel("X") plt.title("x and x_fit coordinates") plt.subplot(2, 1, 2) plt.plot(z_centerline_fit, y_display, 'ro') plt.plot(z_centerline_fit, y_centerline_fit) plt.xlabel("Z") plt.ylabel("Y") plt.title("y and y_fit coordinates") plt.show() # Create an image with the centerline for iz in range(min_z_index, max_z_index + 1): data[ round(x_centerline_fit[iz - min_z_index]), round(y_centerline_fit[iz - min_z_index]), iz] = 1 # if index is out of bounds here for hanning: either the segmentation has holes or labels have been added to the file # Write the centerline image in RPI orientation hdr.set_data_dtype('uint8') # set imagetype to uint8 sct.printv('\nWrite NIFTI volumes...', verbose) img = nibabel.Nifti1Image(data, None, hdr) nibabel.save(img, 'centerline.nii.gz') # Define name if output name is not specified if name_output == 'csa_volume.nii.gz' or name_output == '': # sct.generate_output_file('centerline.nii.gz', file_data+'_centerline'+ext_data, verbose) name_output = file_data + '_centerline' + ext_data sct.generate_output_file('centerline.nii.gz', name_output, verbose) # create a txt file with the centerline path, rad_output, ext = sct.extract_fname(name_output) name_output_txt = rad_output + '.txt' sct.printv('\nWrite text file...', verbose) file_results = open(name_output_txt, 'w') for i in range(min_z_index, max_z_index + 1): file_results.write( str(int(i)) + ' ' + str(x_centerline_fit[i - min_z_index]) + ' ' + str(y_centerline_fit[i - min_z_index]) + '\n') file_results.close() # Copy result into parent folder sct.run('cp ' + name_output_txt + ' ../') del data # come back to parent folder os.chdir('..') # Change orientation of the output centerline into input orientation sct.printv( '\nOrient centerline image to input orientation: ' + orientation, verbose) fname_segmentation_orient = 'tmp.segmentation_rpi' + ext_data set_orientation(path_tmp + '/' + name_output, orientation, name_output) # Remove temporary files if remove_temp_files: sct.printv('\nRemove temporary files...', verbose) sct.run('rm -rf ' + path_tmp, verbose) return name_output
def crop_with_gui(self): import matplotlib.pyplot as plt import matplotlib.image as mpimg # Initialization fname_data = self.input_filename suffix_out = '_crop' remove_temp_files = self.rm_tmp_files verbose = self.verbose # Check file existence sct.printv('\nCheck file existence...', verbose) sct.check_file_exist(fname_data, verbose) # Get dimensions of data sct.printv('\nGet dimensions of data...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_data).dim sct.printv('.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # check if 4D data if not nt == 1: sct.printv( '\nERROR in ' + os.path.basename(__file__) + ': Data should be 3D.\n', 1, 'error') sys.exit(2) # sct.printv(arguments) sct.printv('\nCheck parameters:') sct.printv(' data ................... ' + fname_data) # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(fname_data) path_out, file_out, ext_out = '', file_data + suffix_out, ext_data path_tmp = sct.tmp_create() + "/" # copy files into tmp folder from sct_convert import convert sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) convert(fname_data, os.path.join(path_tmp, "data.nii")) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # change orientation sct.printv('\nChange orientation to RPI...', verbose) Image('data.nii').change_orientation("RPI").save('data_rpi.nii') # get image of medial slab sct.printv('\nGet image of medial slab...', verbose) image_array = nibabel.load('data_rpi.nii').get_data() nx, ny, nz = image_array.shape scipy.misc.imsave('image.jpg', image_array[math.floor(nx / 2), :, :]) # Display the image sct.printv('\nDisplay image and get cropping region...', verbose) fig = plt.figure() # fig = plt.gcf() # ax = plt.gca() ax = fig.add_subplot(111) img = mpimg.imread("image.jpg") implot = ax.imshow(img.T) implot.set_cmap('gray') plt.gca().invert_yaxis() # mouse callback ax.set_title( 'Left click on the top and bottom of your cropping field.\n Right click to remove last point.\n Close window when your done.' ) line, = ax.plot([], [], 'ro') # empty line cropping_coordinates = LineBuilder(line) plt.show() # disconnect callback # fig.canvas.mpl_disconnect(line) # check if user clicked two times if len(cropping_coordinates.xs) != 2: sct.printv( '\nERROR: You have to select two points. Exit program.\n', 1, 'error') sys.exit(2) # convert coordinates to integer zcrop = [int(i) for i in cropping_coordinates.ys] # sort coordinates zcrop.sort() # crop image sct.printv('\nCrop image...', verbose) nii = Image('data_rpi.nii') data_crop = nii.data[:, :, zcrop[0]:zcrop[1]] nii.data = data_crop nii.absolutepath = 'data_rpi_crop.nii' nii.save() # come back os.chdir(curdir) sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(os.path.join(path_tmp, "data_rpi_crop.nii"), os.path.join(path_out, file_out + ext_out)) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...') sct.rmtree(path_tmp) sct.display_viewer_syntax( files=[os.path.join(path_out, file_out + ext_out)])
def compute_csa(fname_segmentation, name_method, volume_output, verbose, remove_temp_files, step, smoothing_param, figure_fit, name_output, slices, vert_levels, path_to_template, algo_fitting='hanning', type_window='hanning', window_length=80): # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir ' + path_tmp, verbose) # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('isct_c3d ' + fname_segmentation + ' -o ' + path_tmp + 'segmentation.nii') # go to tmp folder os.chdir(path_tmp) # Change orientation of the input segmentation into RPI sct.printv('\nChange orientation of the input segmentation into RPI...', verbose) fname_segmentation_orient = set_orientation('segmentation.nii', 'RPI', 'segmentation_orient.nii') # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_segmentation_orient).dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # Open segmentation volume sct.printv('\nOpen segmentation volume...', verbose) file_seg = nibabel.load(fname_segmentation_orient) data_seg = file_seg.get_data() hdr_seg = file_seg.get_header() # # Extract min and max index in Z direction X, Y, Z = (data_seg > 0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # Xp, Yp = (data_seg[:, :, 0] >= 0).nonzero() # X and Y range # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline( fname_segmentation_orient, algo_fitting=algo_fitting, type_window=type_window, window_length=window_length, verbose=verbose) z_centerline_scaled = [x * pz for x in z_centerline] # Compute CSA sct.printv('\nCompute CSA...', verbose) # Empty arrays in which CSA for each z slice will be stored csa = np.zeros(max_z_index - min_z_index + 1) # csa = [0.0 for i in xrange(0, max_z_index-min_z_index+1)] for iz in xrange(0, len(z_centerline)): # compute the vector normal to the plane normal = normalize( np.array([ x_centerline_deriv[iz], y_centerline_deriv[iz], z_centerline_deriv[iz] ])) # compute the angle between the normal vector of the plane and the vector z angle = np.arccos(np.dot(normal, [0, 0, 1])) # compute the number of voxels, assuming the segmentation is coded for partial volume effect between 0 and 1. number_voxels = sum(sum(data_seg[:, :, iz + min_z_index])) # compute CSA, by scaling with voxel size (in mm) and adjusting for oblique plane csa[iz] = number_voxels * px * py * np.cos(angle) if smoothing_param: from msct_smooth import smoothing_window sct.printv('\nSmooth CSA across slices...', verbose) sct.printv('.. Hanning window: ' + str(smoothing_param) + ' mm', verbose) csa_smooth = smoothing_window(csa, window_len=smoothing_param / pz, window='hanning', verbose=0) # display figure if verbose == 2: import matplotlib.pyplot as plt plt.figure() pltx, = plt.plot(z_centerline_scaled, csa, 'bo') pltx_fit, = plt.plot(z_centerline_scaled, csa_smooth, 'r', linewidth=2) plt.title("Cross-sectional area (CSA)") plt.xlabel('z (mm)') plt.ylabel('CSA (mm^2)') plt.legend([pltx, pltx_fit], ['Raw', 'Smoothed']) plt.show() # update variable csa = csa_smooth # Create output text file sct.printv('\nWrite text file...', verbose) file_results = open('csa.txt', 'w') for i in range(min_z_index, max_z_index + 1): file_results.write( str(int(i)) + ',' + str(csa[i - min_z_index]) + '\n') # Display results sct.printv( 'z=' + str(i - min_z_index) + ': ' + str(csa[i - min_z_index]) + ' mm^2', verbose, 'bold') file_results.close() # output volume of csa values if volume_output: sct.printv('\nCreate volume of CSA values...', verbose) # get orientation of the input data orientation = get_orientation('segmentation.nii') data_seg = data_seg.astype(np.float32, copy=False) # loop across slices for iz in range(min_z_index, max_z_index + 1): # retrieve seg pixels x_seg, y_seg = (data_seg[:, :, iz] > 0).nonzero() seg = [[x_seg[i], y_seg[i]] for i in range(0, len(x_seg))] # loop across pixels in segmentation for i in seg: # replace value with csa value data_seg[i[0], i[1], iz] = csa[iz - min_z_index] # create header hdr_seg.set_data_dtype('float32') # set imagetype to uint8 # save volume img = nibabel.Nifti1Image(data_seg, None, hdr_seg) nibabel.save(img, 'csa_RPI.nii') # Change orientation of the output centerline into input orientation fname_csa_volume = set_orientation('csa_RPI.nii', orientation, 'csa_RPI_orient.nii') # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) from shutil import copyfile copyfile(path_tmp + 'csa.txt', path_data + param.fname_csa) # sct.generate_output_file(path_tmp+'csa.txt', path_data+param.fname_csa) # extension already included in param.fname_csa if volume_output: sct.generate_output_file( fname_csa_volume, path_data + name_output) # extension already included in name_output # average csa across vertebral levels or slices if asked (flag -z or -l) if slices or vert_levels: if vert_levels and not path_to_template: sct.printv('\nERROR: Path to template is missing. See usage.\n', 1, 'error') sys.exit(2) elif vert_levels and path_to_template: abs_path_to_template = os.path.abspath(path_to_template) # go to tmp folder os.chdir(path_tmp) # create temporary folder sct.printv('\nCreate temporary folder to average csa...', verbose) path_tmp_extract_metric = sct.slash_at_the_end('label_temp', 1) sct.run('mkdir ' + path_tmp_extract_metric, verbose) # Copying output CSA volume in the temporary folder sct.printv('\nCopy data to tmp folder...', verbose) sct.run('cp ' + fname_segmentation + ' ' + path_tmp_extract_metric) # create file info_label path_fname_seg, file_fname_seg, ext_fname_seg = sct.extract_fname( fname_segmentation) create_info_label('info_label.txt', path_tmp_extract_metric, file_fname_seg + ext_fname_seg) # average CSA if slices: os.system("sct_extract_metric -i " + path_data + name_output + " -f " + path_tmp_extract_metric + " -m wa -o ../csa_mean.txt -z " + slices) if vert_levels: sct.run('cp -R ' + abs_path_to_template + ' .') os.system("sct_extract_metric -i " + path_data + name_output + " -f " + path_tmp_extract_metric + " -m wa -o ../csa_mean.txt -v " + vert_levels) os.chdir('..') # Remove temporary files print('\nRemove temporary folder used to average CSA...') sct.run('rm -rf ' + path_tmp_extract_metric) # Remove temporary files if remove_temp_files: print('\nRemove temporary files...') sct.run('rm -rf ' + path_tmp)
def main(): # initialization start_time = time.time() path_out = '.' param_user = '' # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # get path of the toolbox status, param.path_sct = commands.getstatusoutput('echo $SCT_DIR') # Parameters for debug mode if param.debug: # get path of the testing data status, path_sct_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') param.fname_data = path_sct_data+'/dmri/dmri.nii.gz' param.fname_bvecs = path_sct_data+'/dmri/bvecs.txt' param.fname_mask = path_sct_data+'/dmri/dmri.nii.gz' param.remove_tmp_files = 0 param.verbose = 1 param.run_eddy = 0 param.otsu = 0 param.group_size = 5 param.iterative_averaging = 1 else: # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:], 'hi:a:b:e:f:g:m:o:p:r:t:v:x:') except getopt.GetoptError: usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-a'): param.fname_bvals = arg elif opt in ('-b'): param.fname_bvecs = arg elif opt in ('-e'): param.run_eddy = int(arg) elif opt in ('-f'): param.spline_fitting = int(arg) elif opt in ('-g'): param.group_size = int(arg) elif opt in ('-i'): param.fname_data = arg elif opt in ('-m'): param.fname_mask = arg elif opt in ('-o'): path_out = arg elif opt in ('-p'): param_user = arg elif opt in ('-r'): param.remove_tmp_files = int(arg) elif opt in ('-t'): param.otsu = int(arg) elif opt in ('-v'): param.verbose = int(arg) elif opt in ('-x'): param.interp = arg # display usage if a mandatory argument is not provided if param.fname_data == '' or param.fname_bvecs == '': sct.printv('ERROR: All mandatory arguments are not provided. See usage.', 1, 'error') usage() # parse argument for param if not param_user == '': param.param = param_user.replace(' ', '').split(',') # remove spaces and parse with comma # TODO: check integrity of input # param.param = [i for i in range(len(param_user))] del param_user sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............'+param.fname_data, param.verbose) sct.printv(' bvecs file ............'+param.fname_bvecs, param.verbose) sct.printv(' bvals file ............'+param.fname_bvals, param.verbose) sct.printv(' mask file .............'+param.fname_mask, param.verbose) # check existence of input files sct.printv('\nCheck file existence...', param.verbose) sct.check_file_exist(param.fname_data, param.verbose) sct.check_file_exist(param.fname_bvecs, param.verbose) if not param.fname_bvals == '': sct.check_file_exist(param.fname_bvals, param.verbose) if not param.fname_mask == '': sct.check_file_exist(param.fname_mask, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) param.fname_bvecs = os.path.abspath(param.fname_bvecs) if param.fname_bvals != '': param.fname_bvals = os.path.abspath(param.fname_bvals) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_mask, file_mask, ext_mask = sct.extract_fname(param.fname_mask) # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, param.verbose) # Copying input data to tmp folder # NB: cannot use c3d here because c3d cannot convert 4D data. sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) sct.run('cp '+param.fname_data+' '+path_tmp+'dmri'+ext_data, param.verbose) sct.run('cp '+param.fname_bvecs+' '+path_tmp+'bvecs.txt', param.verbose) if param.fname_mask != '': sct.run('cp '+param.fname_mask+' '+path_tmp+'mask'+ext_mask, param.verbose) # go to tmp folder os.chdir(path_tmp) # convert dmri to nii format convert('dmri'+ext_data, 'dmri.nii') # update field in param (because used later). # TODO: make this cleaner... if param.fname_mask != '': param.fname_mask = 'mask'+ext_mask # run moco dmri_moco(param) # come back to parent folder os.chdir('..') # Generate output files path_out = sct.slash_at_the_end(path_out, 1) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp+'dmri'+param.suffix+'.nii', path_out+file_data+param.suffix+ext_data, param.verbose) sct.generate_output_file(path_tmp+'b0_mean.nii', path_out+'b0'+param.suffix+'_mean'+ext_data, param.verbose) sct.generate_output_file(path_tmp+'dwi_mean.nii', path_out+'dwi'+param.suffix+'_mean'+ext_data, param.verbose) # Delete temporary files if param.remove_tmp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.run('rm -rf '+path_tmp, param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s', param.verbose) #To view results sct.printv('\nTo view results, type:', param.verbose) sct.printv('fslview -m ortho,ortho '+param.path_out+file_data+param.suffix+' '+file_data+' &\n', param.verbose, 'info')
def main(): # Initialization fname_warp_final = '' # concatenated transformations # Check input parameters parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_dest = arguments['-d'] fname_warp_list = arguments['-w'] if '-o' in arguments: fname_warp_final = arguments['-o'] verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level # Parse list of warping fields sct.printv('\nParse list of transformations...', verbose) use_inverse = [] fname_warp_list_invert = [] for i in range(len(fname_warp_list)): # Check if inverse matrix is specified with '-' at the beginning of file name if fname_warp_list[i].find('-') == 0: use_inverse.append('-i') fname_warp_list[i] = fname_warp_list[i][1:] # remove '-' fname_warp_list_invert += [[use_inverse[i], fname_warp_list[i]]] else: use_inverse.append('') fname_warp_list_invert += [[fname_warp_list[i]]] sct.printv( ' Transfo #' + str(i) + ': ' + use_inverse[i] + fname_warp_list[i], verbose) # Check file existence sct.printv('\nCheck file existence...', verbose) sct.check_file_exist(fname_dest, verbose) for i in range(len(fname_warp_list)): sct.check_file_exist(fname_warp_list[i], verbose) # Get output folder and file name if fname_warp_final == '': path_out, file_out, ext_out = sct.extract_fname(param.fname_warp_final) else: path_out, file_out, ext_out = sct.extract_fname(fname_warp_final) # Check dimension of destination data (cf. issue #1419, #1429) im_dest = Image(fname_dest) if im_dest.dim[2] == 1: dimensionality = '2' else: dimensionality = '3' # Concatenate warping fields sct.printv('\nConcatenate warping fields...', verbose) # N.B. Here we take the inverse of the warp list fname_warp_list_invert.reverse() fname_warp_list_invert = functools.reduce(lambda x, y: x + y, fname_warp_list_invert) cmd = [ 'isct_ComposeMultiTransform', dimensionality, 'warp_final' + ext_out, '-R', fname_dest ] + fname_warp_list_invert status, output = sct.run(cmd, verbose=verbose, is_sct_binary=True) # check if output was generated if not os.path.isfile('warp_final' + ext_out): sct.printv('ERROR: Warping field was not generated.\n' + output, 1, 'error') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file('warp_final' + ext_out, os.path.join(path_out, file_out + ext_out))
def create_mask(): fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI # parse argument for method method_type = param.process[0] # check method val if not method_type == 'center': method_val = param.process[1] # check existence of input files if method_type == 'centerline': sct.check_file_exist(method_val, param.verbose) # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) # Get output folder and file name if param.fname_out == '': param.fname_out = param.file_prefix + file_data + ext_data # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.tmp_create(param.verbose) # )sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) # sct.run('mkdir '+path_tmp, param.verbose) sct.printv('\nCheck orientation...', param.verbose) orientation_input = get_orientation(Image(param.fname_data)) sct.printv('.. ' + orientation_input, param.verbose) reorient_coordinates = False # copy input data to tmp folder convert(param.fname_data, path_tmp + 'data.nii') if method_type == 'centerline': convert(method_val, path_tmp + 'centerline.nii.gz') if method_type == 'point': convert(method_val, path_tmp + 'point.nii.gz') # go to tmp folder os.chdir(path_tmp) # reorient to RPI sct.printv('\nReorient to RPI...', param.verbose) # if not orientation_input == 'RPI': sct.run('sct_image -i data.nii -o data_RPI.nii -setorient RPI -v 0', verbose=False) if method_type == 'centerline': sct.run( 'sct_image -i centerline.nii.gz -o centerline_RPI.nii.gz -setorient RPI -v 0', verbose=False) if method_type == 'point': sct.run( 'sct_image -i point.nii.gz -o point_RPI.nii.gz -setorient RPI -v 0', verbose=False) # # if method_type == 'centerline': # orientation_centerline = get_orientation_3d(method_val, filename=True) # if not orientation_centerline == 'RPI': # sct.run('sct_image -i ' + method_val + ' -o ' + path_tmp + 'centerline.nii.gz' + ' -setorient RPI -v 0', verbose=False) # else: # convert(method_val, path_tmp+'centerline.nii.gz') # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image('data_RPI.nii').dim sct.printv( ' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), param.verbose) # in case user input 4d data if nt != 1: sct.printv( 'WARNING in ' + os.path.basename(__file__) + ': Input image is 4d but output mask will 3D.', param.verbose, 'warning') # extract first volume to have 3d reference nii = Image('data_RPI.nii') data3d = nii.data[:, :, :, 0] nii.data = data3d nii.save() if method_type == 'coord': # parse to get coordinate coord = map(int, method_val.split('x')) if method_type == 'point': # get file name fname_point = method_val # extract coordinate of point sct.printv('\nExtract coordinate of point...', param.verbose) # TODO: change this way to remove dependence to sct.run. ProcessLabels.display_voxel returns list of coordinates status, output = sct.run( 'sct_label_utils -i point_RPI.nii.gz -display', param.verbose) # parse to get coordinate coord = output[output.find('Position=') + 10:-17].split(',') if method_type == 'center': # set coordinate at center of FOV coord = round(float(nx) / 2), round(float(ny) / 2) if method_type == 'centerline': # get name of centerline from user argument fname_centerline = 'centerline_RPI.nii.gz' else: # generate volume with line along Z at coordinates 'coord' sct.printv('\nCreate line...', param.verbose) fname_centerline = create_line('data_RPI.nii', coord, nz) # create mask sct.printv('\nCreate mask...', param.verbose) centerline = nibabel.load(fname_centerline) # open centerline hdr = centerline.get_header() # get header hdr.set_data_dtype('uint8') # set imagetype to uint8 spacing = hdr.structarr['pixdim'] data_centerline = centerline.get_data() # get centerline # if data is 2D, reshape with empty third dimension if len(data_centerline.shape) == 2: data_centerline_shape = list(data_centerline.shape) data_centerline_shape.append(1) data_centerline = data_centerline.reshape(data_centerline_shape) z_centerline_not_null = [ iz for iz in range(0, nz, 1) if data_centerline[:, :, iz].any() ] # get center of mass of the centerline cx = [0] * nz cy = [0] * nz for iz in range(0, nz, 1): if iz in z_centerline_not_null: cx[iz], cy[iz] = ndimage.measurements.center_of_mass( numpy.array(data_centerline[:, :, iz])) # create 2d masks file_mask = 'data_mask' for iz in range(nz): if iz not in z_centerline_not_null: # write an empty nifty volume img = nibabel.Nifti1Image(data_centerline[:, :, iz], None, hdr) nibabel.save(img, (file_mask + str(iz) + '.nii')) else: center = numpy.array([cx[iz], cy[iz]]) mask2d = create_mask2d(center, param.shape, param.size, nx, ny, even=param.even, spacing=spacing) # Write NIFTI volumes img = nibabel.Nifti1Image(mask2d, None, hdr) nibabel.save(img, (file_mask + str(iz) + '.nii')) # merge along Z # cmd = 'fslmerge -z mask ' # CHANGE THAT CAN IMPACT SPEED: # related to issue #755, we cannot open more than 256 files at one time. # to solve this issue, we do not open more than 100 files ''' im_list = [] im_temp = [] for iz in range(nz_not_null): if iz != 0 and iz % 100 == 0: im_temp.append(concat_data(im_list, 2)) im_list = [Image(file_mask + str(iz) + '.nii')] else: im_list.append(Image(file_mask+str(iz)+'.nii')) if im_temp: im_temp.append(concat_data(im_list, 2)) im_out = concat_data(im_temp, 2, no_expand=True) else: im_out = concat_data(im_list, 2) ''' fname_list = [file_mask + str(iz) + '.nii' for iz in range(nz)] im_out = concat_data(fname_list, dim=2) im_out.setFileName('mask_RPI.nii.gz') im_out.save() # reorient if necessary # if not orientation_input == 'RPI': sct.run( 'sct_image -i mask_RPI.nii.gz -o mask.nii.gz -setorient ' + orientation_input, param.verbose) # copy header input --> mask im_dat = Image('data.nii') im_mask = Image('mask.nii.gz') im_mask = copy_header(im_dat, im_mask) im_mask.save() # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp + 'mask.nii.gz', param.fname_out) # Remove temporary files if param.remove_tmp_files == 1: sct.printv('\nRemove temporary files...', param.verbose) sct.run('rm -rf ' + path_tmp, param.verbose, error_exit='warning') # to view results sct.printv('\nDone! To view results, type:', param.verbose) sct.printv( 'fslview ' + param.fname_data + ' ' + param.fname_out + ' -l Red -t 0.5 &', param.verbose, 'info') print
def main(): # Initialization fname_src = '' fname_dest = '' fname_src_seg = '' fname_dest_seg = '' fname_output = '' padding = param.padding gradientStepLength = '0.1' # TODO: use that? numberIterations = param.numberIterations numberIterationsStep2 = "20" remove_temp_files = param.remove_temp_files verbose = param.verbose use_segmentation = 0 # use spinal cord segmentation to improve robustness fname_init_transfo = '' fname_init_transfo_inv = '' use_init_transfo = '' compute_dest2src = param.compute_dest2sr #output_warping_field = "tmp.regSeg0Warp.nii.gz" start_time = time.time() # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') print path_sct # Parameters for debug mode if param.debug: fname_src = path_sct+'/data/template/MNI-Poly-AMU_T2.nii.gz' #fname_src = path_sct+'/testing/data/errsm_23/mt/mtc0.nii.gz' fname_dest = path_sct+'/testing/data/errsm_23/mt/mtc1.nii.gz' fname_src_seg = path_sct+'/data/template/MNI-Poly-AMU_cord.nii.gz' fname_dest_seg = path_sct+'/testing/data/errsm_23/mt/segmentation_binary.nii.gz' fname_init_transfo = path_sct+'/testing/data/errsm_23/template/warp_template2anat.nii.gz' fname_init_transfo_inv = path_sct+'/testing/data/errsm_23/template/warp_anat2template.nii.gz' numberIterations = '3x1' numberIterationsStep2 = "1" compute_dest2src = 0 verbose = 1 # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:],'he:d:i:m:n:o:p:q:r:s:t:v:x:z:') except getopt.GetoptError: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ("-d"): fname_dest = arg elif opt in ('-e'): extentDist = arg elif opt in ("-i"): fname_src = arg elif opt in ("-m"): fname_mask = arg elif opt in ("-n"): numberIterations = arg elif opt in ("-o"): fname_output = arg elif opt in ('-p'): padding = arg elif opt in ('-q'): fname_init_transfo = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ("-s"): fname_src_seg = arg elif opt in ("-t"): fname_dest_seg = arg elif opt in ('-v'): verbose = int(arg) elif opt in ('-x'): compute_dest2src = int(arg) elif opt in ('-z'): fname_init_transfo_inv = arg # display usage if a mandatory argument is not provided if fname_src == '' or fname_dest == '': usage() # check segmentation data if (fname_src_seg != '' and fname_dest_seg == '') or (fname_src_seg == '' and fname_dest_seg != ''): print "ERROR: You have to select a segmentation file for BOTH the source and the destination image.\nExit program." sys.exit(2) elif fname_src_seg != '' and fname_dest_seg != '': use_segmentation = 1 # check existence of input files sct.check_file_exist(fname_src) sct.check_file_exist(fname_dest) if use_segmentation: sct.check_file_exist(fname_src_seg) sct.check_file_exist(fname_dest_seg) # print arguments print '\nCheck parameters:' print '.. Source: '+fname_src print '.. Destination: '+fname_dest print '.. Segmentation source: '+fname_src_seg print '.. Segmentation dest: '+fname_dest_seg print '.. Init transfo: '+fname_init_transfo print '.. Output name: '+fname_output #print '.. Mask: '+fname_mask print '.. number of iterations: '+str(numberIterations) print '.. Verbose: '+str(verbose) print '.. Remove temp files: '+str(remove_temp_files) #print '.. gradient step: '+str(gradientStepLength) #print '.. metric type: '+metricType # Extract path, file and extension path_src, file_src, ext_src = sct.extract_fname(fname_src) path_dest, file_dest, ext_dest = sct.extract_fname(fname_dest) if use_segmentation: path_src_seg, file_src_seg, ext_src_seg = sct.extract_fname(fname_src_seg) path_dest_seg, file_dest_seg, ext_dest_seg = sct.extract_fname(fname_dest_seg) # define output folder and file name if fname_output == '': path_out = path_src file_out = file_src+"_reg" ext_out = ext_src else: path_out, file_out, ext_out = sct.extract_fname(fname_output) # create local temp files print('\nCreate local temp files...') file_src_tmp = 'tmp.src' file_dest_tmp = 'tmp.dest' sct.run('c3d '+fname_src+' -o tmp.src.nii') # here we use c3d to make sure output is nii. TODO: cleaner way to do it. sct.run('c3d '+fname_dest+' -o tmp.dest.nii') if use_segmentation: file_src_seg_tmp = 'tmp.src_seg' file_dest_seg_tmp = 'tmp.dest_seg' sct.run('c3d '+fname_src_seg+' -o tmp.src_seg.nii') sct.run('c3d '+fname_dest_seg+' -o tmp.dest_seg.nii') # if use initial transformation (!! needs to be inserted before the --transform field in antsRegistration) if fname_init_transfo != '': file_src_reg_tmp = file_src_tmp+'_reg' if use_segmentation: file_src_seg_reg_tmp = file_src_seg_tmp+'_reg' # apply initial transformation to moving image, and then estimate transformation between this output and # destination image. This approach was chosen instead of inputting the transfo into ANTs, because if the transfo # does not bring the image to the same space as the destination image, then warping fields cannot be concatenated at the end. print('\nApply initial transformation to moving image...') sct.run('WarpImageMultiTransform 3 '+file_src_tmp+'.nii '+file_src_reg_tmp+'.nii -R '+file_dest_tmp+'.nii '+fname_init_transfo+' --use-BSpline') file_src_tmp = file_src_reg_tmp if use_segmentation: sct.run('WarpImageMultiTransform 3 '+file_src_seg_tmp+'.nii '+file_src_seg_reg_tmp+'.nii -R '+file_dest_seg_tmp+'.nii '+fname_init_transfo+' --use-BSpline') file_src_seg_tmp = file_src_seg_reg_tmp # Pad the target and source image (because ants doesn't deform the extremities) if padding: # Pad source image print('\nPad source...') pad_image(file_src_tmp,file_src_tmp+'_pad.nii',padding) file_src_tmp = file_src_tmp+'_pad' # update file name # Pad destination image print('\nPad destination...') pad_image(file_dest_tmp,file_dest_tmp+'_pad.nii',padding) file_dest_tmp = file_dest_tmp+'_pad' # update file name if use_segmentation: # Pad source image print('\nPad source segmentation...') pad_image(file_src_seg_tmp,file_src_seg_tmp+'_pad.nii',padding) file_src_seg_tmp = file_src_seg_tmp+'_pad' # update file name # Pad destination image print('\nPad destination segmentation...') pad_image(file_dest_seg_tmp,file_dest_seg_tmp+'_pad.nii',padding) file_dest_seg_tmp = file_dest_seg_tmp+'_pad' # update file name # don't use spinal cord segmentation if use_segmentation == 0: # Estimate transformation using ANTS print('\nEstimate transformation using ANTS (might take a couple of minutes)...') cmd = 'antsRegistration \ --dimensionality 3 \ '+use_init_transfo+' \ --transform SyN[0.1,3,0] \ --metric MI['+file_dest_tmp+'.nii,'+file_src_tmp+'.nii,1,32] \ --convergence '+numberIterations+' \ --shrink-factors 2x1 \ --smoothing-sigmas 0x0mm \ --Restrict-Deformation 1x1x0 \ --output [tmp.reg,'+file_src_tmp+'_reg.nii] \ --collapse-output-transforms 1 \ --interpolation BSpline[3] \ --winsorize-image-intensities [0.005,0.995]' status, output = sct.run(cmd) if verbose: print output # use spinal cord segmentation elif use_segmentation == 1: ## if use initial transformation (!! needs to be inserted before the --transform field in antsRegistration) #if fname_init_transfo != '': # file_src_reg_tmp = file_src_tmp+'_reg' # file_src_seg_reg_tmp = file_src_seg_tmp+'_reg' # # apply initial transformation to moving image, and then estimate transformation between this output and # # destination image. This approach was chosen instead of inputting the transfo into ANTs, because if the transfo # # does not bring the image to the same space as the destination image, then warping fields cannot be concatenated at the end. # print('\nApply initial transformation to moving image...') # #cmd = 'WarpImageMultiTransform 3 '+file_src_tmp+'.nii '+file_src_reg_tmp+'.nii -R '+file_dest_tmp+'.nii '+fname_init_transfo+' --use-BSpline' # sct.run('WarpImageMultiTransform 3 '+file_src_tmp+'.nii '+file_src_reg_tmp+'.nii -R '+file_dest_tmp+'.nii '+fname_init_transfo) # # smooth image # sct.run('c3d tmp.src_pad_reg.nii -smooth 0.5mm -o tmp.src_pad_reg_smooth.nii') # sct.run('WarpImageMultiTransform 3 '+file_src_seg_tmp+'.nii '+file_src_seg_reg_tmp+'.nii -R '+file_dest_seg_tmp+'.nii '+fname_init_transfo) # file_src_tmp = file_src_reg_tmp # file_src_seg_tmp = file_src_seg_reg_tmp # #cmd = 'WarpImageMultiTransform 3 '+file_src_seg_tmp+' '+file_src_seg_reg_tmp+' -R '+file_dest_seg_tmp+' '+fname_init_transfo # #use_init_transfo = ' --initial-moving-transform '+fname_init_transfo # #output_warping_field = "tmp.regSeg1Warp.nii.gz" # Estimate transformation using ANTS print('\nStep #1: Estimate transformation using spinal cord segmentations...') cmd = 'antsRegistration \ --dimensionality 3 \ --transform SyN[0.5,3,0] \ --metric MI['+file_dest_seg_tmp+'.nii,'+file_src_seg_tmp+'.nii,1,32] \ --convergence '+numberIterations+' \ --shrink-factors 4x1 \ --smoothing-sigmas 1x1mm \ --Restrict-Deformation 1x1x0 \ --output [tmp.regSeg,tmp.regSeg.nii]' #'+use_init_transfo+' \ #if fname_init_transfo != '': # cmd = cmd+' --initial-moving-transform '+fname_init_transfo # output_warping_field = "tmp.regSeg1Warp.nii.gz" status, output = sct.run(cmd) if verbose: print output print('\nStep #2: Improve local deformation using images (start from previous transformation)...') cmd = 'antsRegistration \ --dimensionality 3 \ --initial-moving-transform tmp.regSeg0Warp.nii.gz \ --transform SyN[0.1,1,0] \ --metric MI['+file_dest_tmp+'.nii,'+file_src_tmp+'.nii,1,32] \ --convergence '+numberIterationsStep2+' \ --shrink-factors 1 \ --smoothing-sigmas 0mm \ --Restrict-Deformation 1x1x0 \ --output [tmp.reg,'+file_src_tmp+'_reg.nii] \ --collapse-output-transforms 0 \ --interpolation BSpline[3]' #if fname_init_transfo != '': # cmd = cmd+' --initial-moving-transform '+fname_init_transfo status, output = sct.run(cmd) if verbose: print output # update file name file_src_tmp = file_src_tmp+'_reg' file_warp_final = 'tmp.reg0Warp.nii.gz' # Concatenate transformations print('\nConcatenate transformations...') # if user has initial transfo if fname_init_transfo != '': if use_segmentation == 0: # src --> dest cmd1 = 'ComposeMultiTransform 3 tmp.warp_src2dest.nii.gz -R tmp.dest.nii tmp.reg0Warp.nii.gz '+fname_init_transfo # dest --> src if compute_dest2src: cmd2 = 'ComposeMultiTransform 3 tmp.warp_dest2src.nii.gz -R tmp.src.nii '+fname_init_transfo_inv+' tmp.reg0InverseWarp.nii.gz' elif use_segmentation == 1: # src --> dest cmd1 = 'ComposeMultiTransform 3 tmp.warp_src2dest.nii.gz -R tmp.dest.nii tmp.reg1Warp.nii.gz tmp.regSeg0Warp.nii.gz '+fname_init_transfo # dest --> src if compute_dest2src: cmd2 = 'ComposeMultiTransform 3 tmp.warp_dest2src.nii.gz -R tmp.src.nii '+fname_init_transfo_inv+' tmp.regSeg0InverseWarp.nii.gz tmp.reg1InverseWarp.nii.gz' # if user does not have initial transfo else: if use_segmentation == 0: # src --> dest cmd1 = 'ComposeMultiTransform 3 tmp.warp_src2dest.nii.gz -R tmp.dest.nii tmp.reg0Warp.nii.gz' # dest --> src if compute_dest2src: cmd2 = 'ComposeMultiTransform 3 tmp.warp_dest2src.nii.gz -R tmp.src.nii tmp.reg0InverseWarp.nii.gz' elif use_segmentation == 1: # src --> dest cmd1 = 'ComposeMultiTransform 3 tmp.warp_src2dest.nii.gz -R tmp.dest.nii tmp.reg1Warp.nii.gz tmp.regSeg0Warp.nii.gz' # dest --> src if compute_dest2src: cmd2 = 'ComposeMultiTransform 3 tmp.warp_dest2src.nii.gz -R tmp.src.nii tmp.regSeg0InverseWarp.nii.gz tmp.reg1InverseWarp.nii.gz' print('>> ' + cmd1) commands.getstatusoutput(cmd1) # here cannot use sct.run() because of wrong output status in ComposeMultiTransform if compute_dest2src: print('>> ' + cmd2) commands.getstatusoutput(cmd2) # here cannot use sct.run() because of wrong output status in ComposeMultiTransform # Apply warping field to src data print('\nApply transfo source --> dest...') status, output = sct.run('WarpImageMultiTransform 3 tmp.src.nii tmp.src_reg.nii -R tmp.dest.nii tmp.warp_src2dest.nii.gz --use-BSpline') if compute_dest2src: print('\nApply transfo dest --> source...') status, output = sct.run('WarpImageMultiTransform 3 tmp.dest.nii tmp.dest_reg.nii -R tmp.src.nii tmp.warp_dest2src.nii.gz --use-BSpline') ## Remove padding #if padding: # print('\nRemove padding...') # remove_padding(fname_dest,file_src_tmp,file_src_tmp+'_nopad.nii') # file_src_tmp = file_src_tmp+'_nopad' # update file name # Generate output files print('\nGenerate output files...') # if fname_init_transfo == '': fname_src2dest = sct.generate_output_file('tmp.src_reg.nii', path_out, file_out, ext_out) sct.generate_output_file('tmp.warp_src2dest.nii.gz', path_out, 'warp_src2dest', '.nii.gz') if compute_dest2src: fname_dest2src = sct.generate_output_file('tmp.dest_reg.nii', path_out, file_dest+'_reg', ext_dest) sct.generate_output_file('tmp.warp_dest2src.nii.gz', path_out, 'warp_dest2src', '.nii.gz') # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm tmp.*') # display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s' # to view results print '\nTo view results, type:' print 'fslview '+fname_dest+' '+fname_src2dest+' &' if compute_dest2src: print 'fslview '+fname_src+' '+fname_dest2src+' &' print ''
def main(): # Initialization fname_output = '' fname_mask = param.fname_mask fname_src_seg = '' fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI' start_time = time.time() # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # get default registration parameters # step1 = Paramreg(step='1', type='im', algo='syn', metric='MI', iter='5', shrink='1', smooth='0', gradStep='0.5') step0 = Paramreg(step='0', type='im', algo='syn', metric='MI', iter='0', shrink='1', smooth='0', gradStep='0.5') # only used to put src into dest space step1 = Paramreg() paramreg = ParamregMultiStep([step0, step1]) # Initialize the parser parser = Parser(__file__) parser.usage.set_description( 'This program co-registers two 3D volumes. The deformation is non-rigid and is ' 'constrained along Z direction (i.e., axial plane). Hence, this function assumes ' 'that orientation of the destination image is axial (RPI). If you need to register ' 'two volumes with large deformations and/or different contrasts, it is recommended to ' 'input spinal cord segmentations (binary mask) in order to achieve maximum robustness.' ' The program outputs a warping field that can be used to register other images to the' ' destination image. To apply the warping field to another image, use ' 'sct_apply_transfo') parser.add_option(name="-i", type_value="file", description="Image source.", mandatory=True, example="src.nii.gz") parser.add_option(name="-d", type_value="file", description="Image destination.", mandatory=True, example="dest.nii.gz") parser.add_option(name="-iseg", type_value="file", description="Segmentation source.", mandatory=False, example="src_seg.nii.gz") parser.add_option(name="-dseg", type_value="file", description="Segmentation destination.", mandatory=False, example="dest_seg.nii.gz") parser.add_option( name="-m", type_value="file", description= "Mask that can be created with sct_create_mask to improve accuracy over region of interest. " "This mask will be used on the destination image.", mandatory=False, example="mask.nii.gz") parser.add_option(name="-o", type_value="file_output", description="Name of output file.", mandatory=False, example="src_reg.nii.gz") parser.add_option( name="-p", type_value=[[':'], 'str'], description= """Parameters for registration. Separate arguments with ",". Separate steps with ":".\nstep: <int> Step number (starts at 1).\ntype: {im,seg} type of data used for registration.\nalgo: Default=""" + paramreg.steps['1'].algo + """\n global registration: {rigid, affine, syn, bsplinesyn}\n Slice By Slice registration: {slicereg: regularized translations (see: goo.gl/Sj3ZeU), slicereg2d_translation: regularized using moving average (Hanning window), slicereg2d_rigid, slicereg2d_affine, slicereg2d_pointwise: registration based on the Center of Mass of each slice (use only with type:Seg. Designed for centerlines), slicereg2d_bsplinesyn, slicereg2d_syn}\nmetric: {CC,MI,MeanSquares}. Default=""" + paramreg.steps['1'].metric + """\niter: <int> Number of iterations. Default=""" + paramreg.steps['1'].iter + """\nshrink: <int> Shrink factor (only for SyN). Default=""" + paramreg.steps['1'].shrink + """\nsmooth: <int> Smooth factor (only for SyN). Default=""" + paramreg.steps['1'].smooth + """\ngradStep: <float> Gradient step. Default=""" + paramreg.steps['1'].gradStep + """\npoly: <int> Polynomial degree (only for slicereg). Default=""" + paramreg.steps['1'].poly + """\nwindow_length: <int> size of hanning window for smoothing along z for slicereg2d_pointwise, slicereg2d_translation, slicereg2d_rigid, slicereg2d_affine, slicereg2d_syn and slicereg2d_bsplinesyn.. Default=""" + paramreg.steps['1'].window_length, mandatory=False, example= "step=1,type=seg,algo=slicereg,metric=MeanSquares:step=2,type=im,algo=syn,metric=MI,iter=5,shrink=2" ) parser.add_option( name="-z", type_value="int", description= """size of z-padding to enable deformation at edges when using SyN.""", mandatory=False, default_value=param.padding) parser.add_option(name="-x", type_value="multiple_choice", description="""Final interpolation.""", mandatory=False, default_value='linear', example=['nn', 'linear', 'spline']) parser.add_option(name="-r", type_value="multiple_choice", description="""Remove temporary files.""", mandatory=False, default_value='1', example=['0', '1']) parser.add_option(name="-v", type_value="multiple_choice", description="""Verbose.""", mandatory=False, default_value='1', example=['0', '1', '2']) arguments = parser.parse(sys.argv[1:]) # get arguments fname_src = arguments['-i'] fname_dest = arguments['-d'] if '-iseg' in arguments: fname_src_seg = arguments['-iseg'] if '-dseg' in arguments: fname_dest_seg = arguments['-dseg'] if '-o' in arguments: fname_output = arguments['-o'] if "-m" in arguments: fname_mask = arguments['-m'] padding = arguments['-z'] if "-p" in arguments: paramreg_user = arguments['-p'] # update registration parameters for paramStep in paramreg_user: paramreg.addStep(paramStep) interp = arguments['-x'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) # Parameters for debug mode if param.debug: print '\n*** WARNING: DEBUG MODE ON ***\n' status, path_sct_data = commands.getstatusoutput( 'echo $SCT_TESTING_DATA_DIR') fname_dest = path_sct_data + '/mt/mt1.nii.gz' fname_src = path_sct_data + '/t2/t2.nii.gz' param_user = '******' remove_temp_files = '0' verbose = 1 # print arguments print '\nInput parameters:' print ' Source .............. ' + fname_src print ' Destination ......... ' + fname_dest print ' Mask ................ ' + fname_mask print ' Output name ......... ' + fname_output # print ' Algorithm ........... '+paramreg.algo # print ' Number of iterations '+paramreg.iter # print ' Metric .............. '+paramreg.metric print ' Remove temp files ... ' + str(remove_temp_files) print ' Verbose ............. ' + str(verbose) # update param param.verbose = verbose param.padding = padding param.fname_mask = fname_mask param.remove_temp_files = remove_temp_files # Get if input is 3D sct.printv('\nCheck if input data are 3D...', verbose) sct.check_if_3d(fname_src) sct.check_if_3d(fname_dest) # check if destination data is RPI sct.printv('\nCheck if destination data is RPI...', verbose) sct.check_if_rpi(fname_dest) # Extract path, file and extension path_src, file_src, ext_src = sct.extract_fname(fname_src) path_dest, file_dest, ext_dest = sct.extract_fname(fname_dest) # define output folder and file name if fname_output == '': path_out = '' # output in user's current directory file_out = file_src + "_reg" ext_out = ext_src else: path_out, file_out, ext_out = sct.extract_fname(fname_output) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = 'tmp.' + time.strftime("%y%m%d%H%M%S") status, output = sct.run('mkdir ' + path_tmp, verbose) # copy files to temporary folder sct.printv('\nCopy files...', verbose) sct.run('isct_c3d ' + fname_src + ' -o ' + path_tmp + '/src.nii', verbose) sct.run('isct_c3d ' + fname_dest + ' -o ' + path_tmp + '/dest.nii', verbose) if fname_src_seg: sct.run( 'isct_c3d ' + fname_src_seg + ' -o ' + path_tmp + '/src_seg.nii', verbose) sct.run( 'isct_c3d ' + fname_dest_seg + ' -o ' + path_tmp + '/dest_seg.nii', verbose) if not fname_mask == '': sct.run('isct_c3d ' + fname_mask + ' -o ' + path_tmp + '/mask.nii.gz', verbose) # go to tmp folder os.chdir(path_tmp) # Put source into destination space using header (no estimation -- purely based on header) # TODO: use c3d? # TODO: Check if necessary to do that # TODO: use that as step=0 # sct.printv('\nPut source into destination space using header...', verbose) # sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[dest_pad.nii,src.nii,1,16] -c 0 -f 1 -s 0 -o [regAffine,src_regAffine.nii] -n BSpline[3]', verbose) # if segmentation, also do it for seg # loop across registration steps warp_forward = [] warp_inverse = [] for i_step in range(0, len(paramreg.steps)): sct.printv( '\nEstimate transformation for step #' + str(i_step) + '...', param.verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = 'src.nii' dest = 'dest.nii' interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = 'src_seg.nii' dest = 'dest_seg.nii' interp_step = 'nn' else: sct.run('ERROR: Wrong image type.', 1, 'error') # if step>0, apply warp_forward_concat to the src image to be used if i_step > 0: sct.run( 'sct_apply_transfo -i ' + src + ' -d ' + dest + ' -w ' + ','.join(warp_forward) + ' -o ' + sct.add_suffix(src, '_reg') + ' -x ' + interp_step, verbose) src = sct.add_suffix(src, '_reg') # register src --> dest warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.append(warp_inverse_out) # Put warp_forward_0 at the end of the list warp_forward_0 = warp_forward.pop(0) warp_forward.append(warp_forward_0) # Concatenate transformations sct.printv('\nConcatenate transformations...', verbose) sct.run( 'sct_concat_transfo -w ' + ','.join(warp_forward) + ' -d dest.nii -o warp_src2dest.nii.gz', verbose) warp_inverse.reverse() sct.run( 'sct_concat_transfo -w ' + ','.join(warp_inverse) + ' -d dest.nii -o warp_dest2src.nii.gz', verbose) # Apply warping field to src data sct.printv('\nApply transfo source --> dest...', verbose) sct.run( 'sct_apply_transfo -i src.nii -o src_reg.nii -d dest.nii -w warp_src2dest.nii.gz -x ' + interp, verbose) sct.printv('\nApply transfo dest --> source...', verbose) sct.run( 'sct_apply_transfo -i dest.nii -o dest_reg.nii -d src.nii -w warp_dest2src.nii.gz -x ' + interp, verbose) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) fname_src2dest = sct.generate_output_file(path_tmp + '/src_reg.nii', path_out + file_out + ext_out, verbose) sct.generate_output_file( path_tmp + '/warp_src2dest.nii.gz', path_out + 'warp_' + file_src + '2' + file_dest + '.nii.gz', verbose) fname_dest2src = sct.generate_output_file( path_tmp + '/dest_reg.nii', path_out + file_dest + '_reg' + ext_dest, verbose) sct.generate_output_file( path_tmp + '/warp_dest2src.nii.gz', path_out + 'warp_' + file_dest + '2' + file_src + '.nii.gz', verbose) # sct.generate_output_file(path_tmp+'/warp_dest2src.nii.gz', path_out+'warp_dest2src.nii.gz') # Delete temporary files if remove_temp_files: sct.printv('\nRemove temporary files...', verbose) sct.run('rm -rf ' + path_tmp, verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's', verbose) sct.printv('\nTo view results, type:', verbose) sct.printv('fslview ' + fname_dest + ' ' + fname_src2dest + ' &', verbose, 'info') sct.printv('fslview ' + fname_src + ' ' + fname_dest2src + ' &\n', verbose, 'info')
def main(): # Initialization fname_data = '' interp_factor = param.interp_factor remove_temp_files = param.remove_temp_files verbose = param.verbose suffix = param.suffix smoothing_sigma = param.smoothing_sigma # start timer start_time = time.time() # Parameters for debug mode if param.debug: fname_data = path_sct+'/testing/data/errsm_23/t2/t2_manual_segmentation.nii.gz' remove_temp_files = 0 param.mask_size = 10 # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:],'hi:v:r:') except getopt.GetoptError: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_data = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ('-s'): smoothing_sigma = arg elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_data == '': usage() # print arguments print '\nCheck parameters:' print ' segmentation ........... '+fname_data print ' interp factor .......... '+str(interp_factor) print ' smoothing sigma ........ '+str(smoothing_sigma) # check existence of input files print('\nCheck existence of input files...') sct.check_file_exist(fname_data, verbose) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(fname_data) # create temporary folder print('\nCreate temporary folder...') path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.run('mkdir '+path_tmp) # copy files to temporary folder print('\nCopy files...') sct.run('c3d '+fname_data+' -o '+path_tmp+'/data.nii') # go to tmp folder os.chdir(path_tmp) # Get dimensions of data sct.printv('\nGet dimensions of data...', verbose) nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension('data.nii') sct.printv('.. '+str(nx)+' x '+str(ny)+' x '+str(nz), verbose) # upsample data sct.printv('\nUpsample data...', verbose) sct.run('c3d data.nii -interpolation Linear -resample '+str(nx*interp_factor)+'x'+str(ny*interp_factor)+'x'+str(nz*interp_factor)+'vox -o data_up.nii', verbose) # Smooth along centerline sct.printv('\nSmooth along centerline...', verbose) sct.run('sct_smooth_spinalcord.py -i data_up.nii -c data_up.nii'+' -s '+str(smoothing_sigma)+' -r '+str(remove_temp_files)+' -v '+str(verbose), verbose) # downsample data sct.printv('\nDownsample data...', verbose) sct.run('c3d data_up_smooth.nii -interpolation Linear -resample '+str(nx)+'x'+str(ny)+'x'+str(nz)+'vox -o data_up_smooth_down.nii', verbose) # come back to parent folder os.chdir('..') # Generate output files print('\nGenerate output files...') fname_out = sct.generate_output_file(path_tmp+'/data_up_smooth_down.nii', '', file_data+suffix, ext_data) # Delete temporary files if remove_temp_files == 1: print '\nRemove temporary files...' sct.run('rm -rf '+ path_tmp) # display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s' # to view results print '\nTo view results, type:' print 'fslview '+file_data+' '+file_data+suffix+' &\n'
def main(): # Initialization fname_warp_list = '' # list of warping fields fname_dest = '' # destination image (fix) fname_warp_final = '' # concatenated transformations verbose = 1 # Parameters for debug mode if param.debug: sct.printv('\n*** WARNING: DEBUG MODE ON ***\n') status, path_sct_data = getstatusoutput('echo $SCT_TESTING_DATA_DIR') fname_warp_list = path_sct_data + '/t2/warp_template2anat.nii.gz,-' + path_sct_data + '/mt/warp_template2mt.nii.gz' fname_dest = path_sct_data + '/mt/mtr.nii.gz' verbose = 1 else: # Check input parameters parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_dest = arguments['-d'] fname_warp_list = arguments['-w'] if '-o' in arguments: fname_warp_final = arguments['-o'] verbose = int(arguments['-v']) # Parse list of warping fields sct.printv('\nParse list of transformations...', verbose) use_inverse = [] fname_warp_list_invert = [] for i in range(len(fname_warp_list)): # Check if inverse matrix is specified with '-' at the beginning of file name if fname_warp_list[i].find('-') == 0: use_inverse.append('-i ') fname_warp_list[i] = fname_warp_list[i][1:] # remove '-' else: use_inverse.append('') sct.printv( ' Transfo #' + str(i) + ': ' + use_inverse[i] + fname_warp_list[i], verbose) fname_warp_list_invert.append(use_inverse[i] + fname_warp_list[i]) # Check file existence sct.printv('\nCheck file existence...', verbose) sct.check_file_exist(fname_dest, verbose) for i in range(len(fname_warp_list)): sct.check_file_exist(fname_warp_list[i], verbose) # Get output folder and file name if fname_warp_final == '': path_out, file_out, ext_out = sct.extract_fname(param.fname_warp_final) else: path_out, file_out, ext_out = sct.extract_fname(fname_warp_final) # Check dimension of destination data (cf. issue #1419, #1429) im_dest = Image(fname_dest) if im_dest.dim[2] == 1: dimensionality = '2' else: dimensionality = '3' # Concatenate warping fields sct.printv('\nConcatenate warping fields...', verbose) # N.B. Here we take the inverse of the warp list fname_warp_list_invert.reverse() cmd = 'isct_ComposeMultiTransform ' + dimensionality + ' warp_final' + ext_out + ' -R ' + fname_dest + ' ' + ' '.join( fname_warp_list_invert) sct.printv('>> ' + cmd, verbose) status, output = getstatusoutput( cmd ) # here cannot use sct.run() because of wrong output status in isct_ComposeMultiTransform # check if output was generated if not os.path.isfile('warp_final' + ext_out): sct.printv('ERROR: Warping field was not generated.\n' + output, 1, 'error') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file('warp_final' + ext_out, path_out + file_out + ext_out)
def main(args=None): import numpy as np import spinalcordtoolbox.image as msct_image # Initialization fname_mt0 = '' fname_mt1 = '' file_out = param.file_out # register = param.register # remove_temp_files = param.remove_temp_files # verbose = param.verbose # check user arguments if not args: args = sys.argv[1:] # Check input parameters parser = get_parser() arguments = parser.parse(args) fname_mt0 = arguments['-mt0'] fname_mt1 = arguments['-mt1'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) # Extract path/file/extension path_mt0, file_mt0, ext_mt0 = sct.extract_fname(fname_mt0) path_out, file_out, ext_out = '', file_out, ext_mt0 # create temporary folder path_tmp = sct.tmp_create() # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) from sct_convert import convert convert(fname_mt0, os.path.join(path_tmp, "mt0.nii"), dtype=np.float32) convert(fname_mt1, os.path.join(path_tmp, "mt1.nii"), dtype=np.float32) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # compute MTR sct.printv('\nCompute MTR...', verbose) nii_mt1 = msct_image.Image('mt1.nii') data_mt1 = nii_mt1.data data_mt0 = msct_image.Image('mt0.nii').data data_mtr = 100 * (data_mt0 - data_mt1) / data_mt0 # save MTR file nii_mtr = nii_mt1 nii_mtr.data = data_mtr nii_mtr.save("mtr.nii") # sct.run(fsloutput+'fslmaths -dt double mt0.nii -sub mt1.nii -mul 100 -div mt0.nii -thr 0 -uthr 100 mtr.nii', verbose) # come back os.chdir(curdir) # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(os.path.join(path_tmp, "mtr.nii"), os.path.join(path_out, file_out + ext_out)) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...') sct.rmtree(path_tmp) sct.display_viewer_syntax([fname_mt0, fname_mt1, file_out])
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'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) if '-param-straighten' in arguments: param.param_straighten = arguments['-param-straighten'] if 'cpu-nb' in arguments: arg_cpu = ' -cpu-nb '+arguments['-cpu-nb'] else: arg_cpu = '' if '-param' in arguments: paramreg_user = arguments['-param'] # update registration parameters for paramStep in paramreg_user: paramreg.addStep(paramStep) # initialize other parameters file_template_label = param.file_template_label output_type = param.output_type zsubsample = param.zsubsample # smoothing_sigma = param.smoothing_sigma # capitalize letters for contrast if contrast_template == 't1': contrast_template = 'T1' elif contrast_template == 't2': contrast_template = 'T2' # retrieve file_template based on contrast fname_template_list = glob(path_template+param.folder_template+'*'+contrast_template+'.nii.gz') # TODO: make sure there is only one file -- check if file is there otherwise it crashes fname_template = fname_template_list[0] # retrieve file_template_seg fname_template_seg_list = glob(path_template+param.folder_template+'*cord.nii.gz') # TODO: make sure there is only one file fname_template_seg = fname_template_seg_list[0] # start timer start_time = time.time() # get absolute path - TO DO: remove! NEVER USE ABSOLUTE PATH... path_template = os.path.abspath(path_template+param.folder_template) # get fname of the template + template objects # fname_template = sct.slash_at_the_end(path_template, 1)+file_template fname_template_label = sct.slash_at_the_end(path_template, 1)+file_template_label # fname_template_seg = sct.slash_at_the_end(path_template, 1)+file_template_seg # check file existence sct.printv('\nCheck template files...') sct.check_file_exist(fname_template, verbose) sct.check_file_exist(fname_template_label, verbose) sct.check_file_exist(fname_template_seg, verbose) # print arguments sct.printv('\nCheck parameters:', verbose) sct.printv('.. Data: '+fname_data, verbose) sct.printv('.. Landmarks: '+fname_landmarks, verbose) sct.printv('.. Segmentation: '+fname_seg, verbose) sct.printv('.. Path template: '+path_template, verbose) sct.printv('.. Path output: '+path_output, verbose) sct.printv('.. Output type: '+str(output_type), verbose) sct.printv('.. Remove temp files: '+str(remove_temp_files), verbose) sct.printv('\nParameters for registration:') for pStep in range(1, len(paramreg.steps)+1): sct.printv('Step #'+paramreg.steps[str(pStep)].step, verbose) sct.printv('.. Type #'+paramreg.steps[str(pStep)].type, verbose) sct.printv('.. Algorithm................ '+paramreg.steps[str(pStep)].algo, verbose) sct.printv('.. Metric................... '+paramreg.steps[str(pStep)].metric, verbose) sct.printv('.. Number of iterations..... '+paramreg.steps[str(pStep)].iter, verbose) sct.printv('.. Shrink factor............ '+paramreg.steps[str(pStep)].shrink, verbose) sct.printv('.. Smoothing factor......... '+paramreg.steps[str(pStep)].smooth, verbose) sct.printv('.. Gradient step............ '+paramreg.steps[str(pStep)].gradStep, verbose) sct.printv('.. Degree of polynomial..... '+paramreg.steps[str(pStep)].poly, verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) sct.printv('\nCheck input labels...') # check if label image contains coherent labels image_label = Image(fname_landmarks) # -> all labels must be different labels = image_label.getNonZeroCoordinates(sorting='value') hasDifferentLabels = True for lab in labels: for otherlabel in labels: if lab != otherlabel and lab.hasEqualValue(otherlabel): hasDifferentLabels = False break if not hasDifferentLabels: sct.printv('ERROR: Wrong landmarks input. All labels must be different.', verbose, 'error') # all labels must be available in tempalte image_label_template = Image(fname_template_label) labels_template = image_label_template.getNonZeroCoordinates(sorting='value') if labels[-1].value > labels_template[-1].value: sct.printv('ERROR: Wrong landmarks input. Labels must have correspondence in template space. \nLabel max ' 'provided: ' + str(labels[-1].value) + '\nLabel max from template: ' + str(labels_template[-1].value), 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) # 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')) ftmp_seg = add_suffix(ftmp_seg, '_smooth') # 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) sct.run('sct_straighten_spinalcord -i '+ftmp_seg+' -s '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_straight')+' -qc 0 -r 0 -v '+str(verbose)+' '+param.param_straighten+arg_cpu, 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 -p remove -i '+ftmp_template_label+' -o '+ftmp_template_label+' -r '+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') # Create crosses for the template labels and get coordinates sct.printv('\nCreate a 15 mm cross for the template labels...', verbose) template_image = Image(ftmp_template_label) coordinates_input = template_image.getNonZeroCoordinates(sorting='value') # jcohenadad, issue #628 <<<<< # landmark_template = ProcessLabels.get_crosses_coordinates(coordinates_input, gapxy=15) landmark_template = coordinates_input # >>>>> if verbose == 2: # TODO: assign cross to image before saving template_image.setFileName(add_suffix(ftmp_template_label, '_cross')) template_image.save(type='minimize_int') # Create crosses for the input labels into straight space and get coordinates sct.printv('\nCreate a 15 mm cross for the input labels...', verbose) label_straight_image = Image(ftmp_label) coordinates_input = label_straight_image.getCoordinatesAveragedByValue() # landmarks are sorted by value # jcohenadad, issue #628 <<<<< # landmark_straight = ProcessLabels.get_crosses_coordinates(coordinates_input, gapxy=15) landmark_straight = coordinates_input # >>>>> if verbose == 2: # TODO: assign cross to image before saving label_straight_image.setFileName(add_suffix(ftmp_label, '_cross')) label_straight_image.save(type='minimize_int') # Reorganize landmarks points_fixed, points_moving = [], [] for coord in landmark_straight: point_straight = label_straight_image.transfo_pix2phys([[coord.x, coord.y, coord.z]]) points_moving.append([point_straight[0][0], point_straight[0][1], point_straight[0][2]]) for coord in landmark_template: point_template = template_image.transfo_pix2phys([[coord.x, coord.y, coord.z]]) points_fixed.append([point_template[0][0], point_template[0][1], point_template[0][2]]) # Register curved landmarks on straight landmarks based on python implementation sct.printv('\nComputing rigid transformation (algo=translation-scaling-z) ...', verbose) import msct_register_landmarks # for some reason, the moving and fixed points are inverted between ITK transform and our python-based transform. # and for another unknown reason, x and y dimensions have a negative sign (at least for translation and center of rotation). if verbose == 2: show_transfo = True else: show_transfo = False (rotation_matrix, translation_array, points_moving_reg, points_moving_barycenter) = msct_register_landmarks.getRigidTransformFromLandmarks(points_moving, points_fixed, constraints='translation-scaling-z', show=show_transfo) # writing rigid transformation file text_file = open("straight2templateAffine.txt", "w") text_file.write("#Insight Transform File V1.0\n") text_file.write("#Transform 0\n") text_file.write("Transform: AffineTransform_double_3_3\n") text_file.write("Parameters: %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f %.9f\n" % ( rotation_matrix[0, 0], rotation_matrix[0, 1], rotation_matrix[0, 2], rotation_matrix[1, 0], rotation_matrix[1, 1], rotation_matrix[1, 2], rotation_matrix[2, 0], rotation_matrix[2, 1], rotation_matrix[2, 2], -translation_array[0, 0], -translation_array[0, 1], translation_array[0, 2])) text_file.write("FixedParameters: %.9f %.9f %.9f\n" % (-points_moving_barycenter[0], -points_moving_barycenter[1], points_moving_barycenter[2])) text_file.close() # 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') # threshold and binarize sct.printv('\nBinarize segmentation...', verbose) sct.run('sct_maths -i '+ftmp_seg+' -thr 0.4 -o '+add_suffix(ftmp_seg, '_thr')) sct.run('sct_maths -i '+add_suffix(ftmp_seg, '_thr')+' -bin -o '+add_suffix(ftmp_seg, '_thr_bin')) ftmp_seg = add_suffix(ftmp_seg, '_thr_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)+1): sct.printv('\nEstimate transformation for step #'+str(i_step)+'...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = 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) sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+add_suffix(src, '_reg')+' -x '+interp_step, verbose) src = add_suffix(src, '_reg') # register src --> dest warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.append(warp_inverse_out) # Concatenate transformations: sct.printv('\nConcatenate transformations: anat --> template...', verbose) sct.run('sct_concat_transfo -w warp_curve2straightAffine.nii.gz,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose) # sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose) 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) # Apply warping fields to anat and template if output_type == 1: sct.run('sct_apply_transfo -i template.nii -o template2anat.nii.gz -d data.nii -w warp_template2anat.nii.gz -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) if output_type == 1: 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) # 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')
def main(args=None): # initializations initz = '' initcenter = '' # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_in = arguments["-i"] fname_seg = arguments['-s'] # contrast = arguments['-t'] if '-o' in arguments: fname_out = arguments["-o"] else: fname_out = '' if '-initz' in arguments: initz = arguments['-initz'] if '-initcenter' in arguments: initcenter = arguments['-initcenter'] verbose = int(arguments['-v']) remove_tmp_files = int(arguments['-r']) denoise = int(arguments['-denoise']) laplacian = int(arguments['-laplacian']) # create temporary folder printv('\nCreate temporary folder...', verbose) path_tmp = slash_at_the_end('tmp.'+strftime("%y%m%d%H%M%S"), 1) run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder printv('\nCopying input data to tmp folder...', verbose) run('sct_convert -i '+fname_in+' -o '+path_tmp+'data.nii') run('sct_convert -i '+fname_seg+' -o '+path_tmp+'segmentation.nii.gz') # Go go temp folder # path_tmp = '/Users/julien/data/biospective/20151013_demo_spinalcordv2.1.b9/200_006_s2_T2/tmp.151013175622/' chdir(path_tmp) # create label to identify disc printv('\nCreate label to identify disc...', verbose) if initz: create_label_z('segmentation.nii.gz', initz[0], initz[1]) # create label located at z_center elif initcenter: # find z centered in FOV nii = Image('segmentation.nii.gz') nii.change_orientation('RPI') # reorient to RPI nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions z_center = int(round(nz/2)) # get z_center create_label_z('segmentation.nii.gz', z_center, initcenter) # create label located at z_center else: printv('\nERROR: You need to initialize the disc detection algorithm using one of these two options: -initz, -initcenter\n', 1, 'error') # Straighten spinal cord printv('\nStraighten spinal cord...', verbose) run('sct_straighten_spinalcord -i data.nii -s segmentation.nii.gz -r 0 -param all_labels=0,bspline_meshsize=3x3x5 -qc 0') # here using all_labels=0 because of issue #610 # Apply straightening to segmentation # N.B. Output is RPI printv('\nApply straightening to segmentation...', verbose) run('sct_apply_transfo -i segmentation.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o segmentation_straight.nii.gz -x linear') # Threshold segmentation to 0.5 run('sct_maths -i segmentation_straight.nii.gz -thr 0.5 -o segmentation_straight.nii.gz') # Apply straightening to z-label printv('\nDilate z-label and apply straightening...', verbose) run('sct_apply_transfo -i labelz.nii.gz -d data_straight.nii -w warp_curve2straight.nii.gz -o labelz_straight.nii.gz -x nn') # get z value and disk value to initialize labeling printv('\nGet z and disc values from straight label...', verbose) init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz') printv('.. '+str(init_disc), verbose) # denoise data if denoise: printv('\nDenoise data...', verbose) run('sct_maths -i data_straight.nii -denoise h=0.05 -o data_straight.nii') # apply laplacian filtering if laplacian: printv('\nApply Laplacian filter...', verbose) run('sct_maths -i data_straight.nii -laplace 1 -o data_straight.nii') # detect vertebral levels on straight spinal cord vertebral_detection('data_straight.nii', 'segmentation_straight.nii.gz', init_disc, verbose) # un-straighten labelled spinal cord printv('\nUn-straighten labeling...', verbose) run('sct_apply_transfo -i segmentation_straight_labeled.nii.gz -d segmentation.nii.gz -w warp_straight2curve.nii.gz -o segmentation_labeled.nii.gz -x nn') # Clean labeled segmentation printv('\nClean labeled segmentation (correct interpolation errors)...', verbose) clean_labeled_segmentation('segmentation_labeled.nii.gz', 'segmentation.nii.gz', 'segmentation_labeled.nii.gz') # Build fname_out if fname_out == '': path_seg, file_seg, ext_seg = extract_fname(fname_seg) fname_out = path_seg+file_seg+'_labeled'+ext_seg # come back to parent folder chdir('..') # Generate output files printv('\nGenerate output files...', verbose) generate_output_file(path_tmp+'segmentation_labeled.nii.gz', fname_out) # Remove temporary files if remove_tmp_files == 1: printv('\nRemove temporary files...', verbose) run('rm -rf '+path_tmp) # to view results printv('\nDone! To view results, type:', verbose) printv('fslview '+fname_in+' '+fname_out+' -l Random-Rainbow -t 0.5 &\n', verbose, 'info')
def main(): # Initialization path_script = os.path.dirname(__file__) fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI # THIS DOES NOT WORK IN MY LAPTOP: path_sct = os.environ['SCT_DIR'] # path to spinal cord toolbox path_sct = path_script[:-8] # TODO: make it cleaner! fname_data = '' fname_bvecs = '' verbose = param.verbose start_time = time.time() # Parameters for debug mode if param.debug: fname_data = os.path.expanduser("~")+'/code/spinalcordtoolbox_dev/testing/data/errsm_22/dmri/dmri.nii.gz' fname_bvecs = os.path.expanduser("~")+'/code/spinalcordtoolbox_dev/testing/data/errsm_22/dmri/bvecs.txt' verbose = 1 # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:],'hb:i:v:') except getopt.GetoptError: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ("-b"): fname_bvecs = arg elif opt in ("-i"): fname_data = arg elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_data == '' or fname_bvecs == '': usage() # check existence of input files sct.check_file_exist(fname_data) sct.check_file_exist(fname_bvecs) # print arguments print '\nCheck parameters:' print '.. DWI data: '+fname_data print '.. bvecs file: '+fname_bvecs # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(fname_data) # create temporary folder path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.run('mkdir '+path_tmp) # copy files into tmp folder sct.run('cp '+fname_data+' '+path_tmp) sct.run('cp '+fname_bvecs+' '+path_tmp) # go to tmp folder os.chdir(path_tmp) # Get size of data print '\nGet dimensions data...' nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(fname_data) print '.. '+str(nx)+' x '+str(ny)+' x '+str(nz)+' x '+str(nt) # Open bvecs file bvecs = [] with open(fname_bvecs) as f: for line in f: bvecs_new = map(float, line.split()) bvecs.append(bvecs_new) # Check if bvecs file is nx3 if not len(bvecs[0][:]) == 3: print 'WARNING: bvecs file is 3xn instead of nx3. Consider using sct_dmri_transpose_bvecs' # transpose bvecs bvecs = zip(*bvecs) # Identify b=0 and DW images print '\nIdentify b=0 and DW images...' index_b0 = [] index_dwi = [] for it in xrange(0,nt): if math.sqrt(math.fsum([i**2 for i in bvecs[it]])) < 0.01: index_b0.append(it) else: index_dwi.append(it) nb_b0 = len(index_b0) nb_dwi = len(index_dwi) print '.. Number of b=0: '+str(nb_b0)+' '+str(index_b0) print '.. Number of DWI: '+str(nb_dwi)+' '+str(index_dwi) #TODO: check if number of bvecs and nt match # Split into T dimension print '\nSplit along T dimension...' sct.run(fsloutput+' fslsplit '+fname_data+' data_splitT') # retrieve output names status, output = sct.run('ls data_splitT*.*') file_data_split = output.split() # Remove .nii extension file_data_split = [file_data_split[i].replace('.nii','') for i in xrange (0,len(file_data_split))] # Merge b=0 images print '\nMerge b=0...' cmd = fsloutput+'fslmerge -t b0' for it in xrange(0,nb_b0): cmd += ' '+file_data_split[index_b0[it]] sct.run(cmd) # Merge DWI images print '\nMerge DWI...' cmd = fsloutput+'fslmerge -t dwi' for it in xrange(0,nb_dwi): cmd += ' '+file_data_split[index_dwi[it]] sct.run(cmd) # come back to parent folder os.chdir('..') # Generate output files print('\nGenerate output files...') sct.generate_output_file(path_tmp+'/b0.nii',path_data,'b0',ext_data) sct.generate_output_file(path_tmp+'/dwi.nii',path_data,'dwi',ext_data) # Remove temporary files print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp) # display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s' # to view results print '\nTo view results, type:' print 'fslview b0 dwi &\n'
def main(): # Initialization fname_warp_list = "" # list of warping fields fname_dest = "" # destination image (fix) fname_warp_final = "" # concatenated transformations verbose = 1 # Parameters for debug mode if param.debug: print "\n*** WARNING: DEBUG MODE ON ***\n" status, path_sct_data = getstatusoutput("echo $SCT_TESTING_DATA_DIR") fname_warp_list = ( path_sct_data + "/t2/warp_template2anat.nii.gz,-" + path_sct_data + "/mt/warp_template2mt.nii.gz" ) fname_dest = path_sct_data + "/mt/mtr.nii.gz" verbose = 1 else: # Check input parameters parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_dest = arguments["-d"] fname_warp_list = arguments["-w"] if "-o" in arguments: fname_warp_final = arguments["-o"] verbose = int(arguments["-v"]) # Parse list of warping fields sct.printv("\nParse list of transformations...", verbose) use_inverse = [] fname_warp_list_invert = [] for i in range(len(fname_warp_list)): # Check if inverse matrix is specified with '-' at the beginning of file name if fname_warp_list[i].find("-") == 0: use_inverse.append("-i ") fname_warp_list[i] = fname_warp_list[i][1:] # remove '-' else: use_inverse.append("") sct.printv(" Transfo #" + str(i) + ": " + use_inverse[i] + fname_warp_list[i], verbose) fname_warp_list_invert.append(use_inverse[i] + fname_warp_list[i]) # Check file existence sct.printv("\nCheck file existence...", verbose) sct.check_file_exist(fname_dest, verbose) for i in range(len(fname_warp_list)): sct.check_file_exist(fname_warp_list[i], verbose) # Get output folder and file name if fname_warp_final == "": path_out, file_out, ext_out = sct.extract_fname(param.fname_warp_final) else: path_out, file_out, ext_out = sct.extract_fname(fname_warp_final) # Concatenate warping fields sct.printv("\nConcatenate warping fields...", verbose) # N.B. Here we take the inverse of the warp list fname_warp_list_invert.reverse() cmd = ( "isct_ComposeMultiTransform 3 warp_final" + ext_out + " -R " + fname_dest + " " + " ".join(fname_warp_list_invert) ) sct.printv(">> " + cmd, verbose) status, output = getstatusoutput( cmd ) # here cannot use sct.run() because of wrong output status in isct_ComposeMultiTransform # check if output was generated if not os.path.isfile("warp_final" + ext_out): sct.printv("ERROR: Warping field was not generated.\n" + output, 1, "error") # Generate output files sct.printv("\nGenerate output files...", verbose) sct.generate_output_file("warp_final" + ext_out, path_out + file_out + ext_out) print ""
def main(args=None): # Initialization # fname_anat = '' # fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) # remove_temp_files = param.remove_temp_files # verbose = param.verbose start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-r' in arguments: remove_temp_files = int(arguments['-r']) if '-v' in arguments: verbose = int(arguments['-v']) # Display arguments print '\nCheck input arguments...' print ' Volume to smooth .................. ' + fname_anat print ' Centerline ........................ ' + fname_centerline print ' Sigma (mm) ........................ '+str(sigma) print ' Verbose ........................... '+str(verbose) # Check that input is 3D: from msct_image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv('WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i '+fname_anat+' -split t -o '+fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname(fname_centerline) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('cp '+fname_anat+' '+path_tmp+'anat'+ext_anat, verbose) sct.run('cp '+fname_centerline+' '+path_tmp+'centerline'+ext_centerline, verbose) # go to tmp folder os.chdir(path_tmp) # convert to nii format convert('anat'+ext_anat, 'anat.nii') convert('centerline'+ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI print '\nOrient input volume to RPI orientation...' fname_anat_rpi = set_orientation('anat.nii', 'RPI', filename=True) move(fname_anat_rpi, 'anat_rpi.nii') # Change orientation of the input image into RPI print '\nOrient centerline to RPI orientation...' fname_centerline_rpi = set_orientation('centerline.nii', 'RPI', filename=True) move(fname_centerline_rpi, 'centerline_rpi.nii') # Straighten the spinal cord # 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 anat_rpi.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o anat_rpi_straight.nii -x spline', verbose) else: sct.run('sct_straighten_spinalcord -i anat_rpi.nii -s centerline_rpi.nii -qc 0 -x spline', verbose) # Smooth the straightened image along z print '\nSmooth the straightened image along z...' sct.run('sct_maths -i anat_rpi_straight.nii -smooth 0,0,'+str(sigma)+' -o anat_rpi_straight_smooth.nii', verbose) # Apply the reversed warping field to get back the curved spinal cord print '\nApply the reversed warping field to get back the curved spinal cord...' sct.run('sct_apply_transfo -i anat_rpi_straight_smooth.nii -o anat_rpi_straight_smooth_curved.nii -d anat.nii -w warp_straight2curve.nii.gz -x spline', verbose) # replace zeroed voxels by original image (issue #937) sct.printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.setFileName('anat_rpi_straight_smooth_curved_nonzero.nii') nii_smooth.save() # come back to parent folder os.chdir('..') # Generate output file print '\nGenerate output file...' sct.generate_output_file(path_tmp+'/anat_rpi_straight_smooth_curved_nonzero.nii', file_anat+'_smooth'+ext_anat) # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp) # Display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s\n' # to view results sct.printv('Done! To view results, type:', verbose) sct.printv('fslview '+file_anat+' '+file_anat+'_smooth &\n', verbose, 'info')
def extract_centerline(fname_segmentation, remove_temp_files, name_output='', verbose = 0, algo_fitting = 'hanning', type_window = 'hanning', window_length = 80): # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.run('mkdir '+path_tmp) # copy files into tmp folder sct.run('cp '+fname_segmentation+' '+path_tmp) # go to tmp folder os.chdir(path_tmp) # Change orientation of the input centerline into RPI sct.printv('\nOrient centerline to RPI orientation...', verbose) fname_segmentation_orient = 'segmentation_rpi' + ext_data set_orientation(file_data+ext_data, 'RPI', fname_segmentation_orient) # Get dimension sct.printv('\nGet dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_segmentation_orient).dim sct.printv('.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz), verbose) sct.printv('.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm', verbose) # Extract orientation of the input segmentation orientation = get_orientation(file_data+ext_data) sct.printv('\nOrientation of segmentation image: ' + orientation, verbose) sct.printv('\nOpen segmentation volume...', verbose) file = nibabel.load(fname_segmentation_orient) data = file.get_data() hdr = file.get_header() # Extract min and max index in Z direction X, Y, Z = (data>0).nonzero() min_z_index, max_z_index = min(Z), max(Z) x_centerline = [0 for i in range(0,max_z_index-min_z_index+1)] y_centerline = [0 for i in range(0,max_z_index-min_z_index+1)] z_centerline = [iz for iz in range(min_z_index, max_z_index+1)] # Extract segmentation points and average per slice for iz in range(min_z_index, max_z_index+1): x_seg, y_seg = (data[:,:,iz]>0).nonzero() x_centerline[iz-min_z_index] = np.mean(x_seg) y_centerline[iz-min_z_index] = np.mean(y_seg) for k in range(len(X)): data[X[k], Y[k], Z[k]] = 0 # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline_fit, x_centerline_deriv,y_centerline_deriv,z_centerline_deriv = smooth_centerline(fname_segmentation_orient, type_window = type_window, window_length = window_length, algo_fitting = algo_fitting, verbose = verbose) if verbose == 2: import matplotlib.pyplot as plt #Creation of a vector x that takes into account the distance between the labels nz_nonz = len(z_centerline) x_display = [0 for i in range(x_centerline_fit.shape[0])] y_display = [0 for i in range(y_centerline_fit.shape[0])] for i in range(0, nz_nonz, 1): x_display[int(z_centerline[i]-z_centerline[0])] = x_centerline[i] y_display[int(z_centerline[i]-z_centerline[0])] = y_centerline[i] plt.figure(1) plt.subplot(2,1,1) plt.plot(z_centerline_fit, x_display, 'ro') plt.plot(z_centerline_fit, x_centerline_fit) plt.xlabel("Z") plt.ylabel("X") plt.title("x and x_fit coordinates") plt.subplot(2,1,2) plt.plot(z_centerline_fit, y_display, 'ro') plt.plot(z_centerline_fit, y_centerline_fit) plt.xlabel("Z") plt.ylabel("Y") plt.title("y and y_fit coordinates") plt.show() # Create an image with the centerline for iz in range(min_z_index, max_z_index+1): data[round(x_centerline_fit[iz-min_z_index]), round(y_centerline_fit[iz-min_z_index]), iz] = 1 # if index is out of bounds here for hanning: either the segmentation has holes or labels have been added to the file # Write the centerline image in RPI orientation hdr.set_data_dtype('uint8') # set imagetype to uint8 sct.printv('\nWrite NIFTI volumes...', verbose) img = nibabel.Nifti1Image(data, None, hdr) nibabel.save(img, 'centerline.nii.gz') # Define name if output name is not specified if name_output=='csa_volume.nii.gz' or name_output=='': # sct.generate_output_file('centerline.nii.gz', file_data+'_centerline'+ext_data, verbose) name_output = file_data+'_centerline'+ext_data sct.generate_output_file('centerline.nii.gz', name_output, verbose) # create a txt file with the centerline path, rad_output, ext = sct.extract_fname(name_output) name_output_txt = rad_output + '.txt' sct.printv('\nWrite text file...', verbose) file_results = open(name_output_txt, 'w') for i in range(min_z_index, max_z_index+1): file_results.write(str(int(i)) + ' ' + str(x_centerline_fit[i-min_z_index]) + ' ' + str(y_centerline_fit[i-min_z_index]) + '\n') file_results.close() # Copy result into parent folder sct.run('cp '+name_output_txt+' ../') del data # come back to parent folder os.chdir('..') # Change orientation of the output centerline into input orientation sct.printv('\nOrient centerline image to input orientation: ' + orientation, verbose) fname_segmentation_orient = 'tmp.segmentation_rpi' + ext_data set_orientation(path_tmp+'/'+name_output, orientation, name_output) # Remove temporary files if remove_temp_files: sct.printv('\nRemove temporary files...', verbose) sct.run('rm -rf '+path_tmp, verbose) return name_output
def main(): # Initialization fname_anat = '' fname_landmark_anat = '' fname_template = '' fname_landmark_template = '' fname_mask = '' remove_temp_files = param.remove_temp_files number_iterations = param.number_iterations verbose = param.verbose start_time = time.time() # extract path of the script path_script = os.path.dirname(__file__)+'/' # Parameters for debug mode if param.debug == 1: print '\n*** WARNING: DEBUG MODE ON ***\n' fname_anat = path_script+'../testing/sct_register_straight_spinalcord_to_template/data/errsm_22_t2_cropped_rpi_straight.nii.gz' fname_landmark_anat = path_script+'../testing/sct_register_straight_spinalcord_to_template/data/landmarks_C2_T5.nii.gz' fname_seg_anat = path_script+'../testing/sct_register_straight_spinalcord_to_template/data/landmarks_C2_T5.nii.gz' fname_template = path_script+'../data/template/MNI-Poly-AMU_T2.nii.gz' fname_landmark_template = path_script+'../data/template/landmarks_C2_T5.nii.gz' # Check input param try: opts, args = getopt.getopt(sys.argv[1:],'hi:f:l:m:n:o:r:s:t:v:') except getopt.GetoptError as err: print str(err) usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-f'): fname_landmark_template = arg elif opt in ('-i'): fname_anat = arg elif opt in ('-l'): fname_landmark_anat = arg elif opt in ('-m'): fname_mask = arg elif opt in ('-n'): number_iterations = arg elif opt in ("-o"): fname_template_seg = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ("-s"): fname_anat_seg = arg elif opt in ('-t'): fname_template = arg elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_anat == '' or fname_landmark_anat == '' or fname_template == '' or fname_landmark_template == '': usage() # check existence of input files sct.check_file_exist(fname_anat) sct.check_file_exist(fname_landmark_anat) sct.check_file_exist(fname_template) sct.check_file_exist(fname_landmark_template) sct.check_file_exist(fname_seg_template) # Display arguments print '\nCheck input arguments:' print ' straight anatomic: '+fname_anat print ' landmarks anatomic: '+fname_landmark_anat print ' template T2: '+fname_template print ' template landmarks: '+fname_landmark_template print ' template segmentation:'+fname_landmark_template print ' number of iterations: '+str(number_iterations) print ' mask anatomic: '+fname_mask print ' Verbose: '+str(verbose) # Get full path fname_anat = os.path.abspath(fname_anat) fname_landmark_anat = os.path.abspath(fname_landmark_anat) fname_template = os.path.abspath(fname_template) fname_landmark_template = os.path.abspath(fname_landmark_template) # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_template, file_template, ext_template = sct.extract_fname(fname_template) # create temporary folder path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.run('mkdir '+path_tmp) # go to tmp folder os.chdir(path_tmp) # Estimate transfo: straight --> template (affine landmark-based)' print '\nEstimate transfo: straight anat --> template (affine landmark-based)...' sct.run('ANTSUseLandmarkImagesToGetAffineTransform '+fname_landmark_template+' '+fname_landmark_anat+' affine tmp.straight2templateAffine.txt') # Apply transformation: straight --> template print '\nApply transformation straight --> template...' sct.run('WarpImageMultiTransform 3 '+fname_anat+' tmp.straight2templateAffine.nii tmp.straight2templateAffine.txt -R '+fname_template) # Estimate transformation: straight --> template (deformation) print '\nEstimate transformation: straight --> template (diffeomorphic transformation). Takes ~15-45 minutes...' cmd = 'antsRegistration \ --dimensionality 3 \ --transform SyN[0.2,3] \ --metric MI['+fname_template+',tmp.straight2templateAffine.nii,1,32] \ --convergence '+number_iterations+' \ --shrink-factors 4x1 \ --smoothing-sigmas 1x0mm \ --Restrict-Deformation 1x1x0 \ --output [tmp.straight2template,tmp.straight2template.nii.gz] \ --collapse-output-transforms 1 \ --interpolation BSpline[3] \ --winsorize-image-intensities [0.005,0.995]' if fname_mask != '': # TODO: check if mask exist cmd = cmd+' -x '+fname_mask # run command status, output = sct.run(cmd) if verbose: print output # Concatenate affine and non-linear transformations... print '\nConcatenate affine and non-linear transformations: straight --> template...' # NB: cannot use sct.run() because output of ComposeMultiTransform is not 0, even if there is no error (bug in ANTS-- already reported on 2013-12-30) cmd = 'ComposeMultiTransform 3 tmp.warp_straight2template.nii.gz -R '+fname_template+' tmp.straight2template0Warp.nii.gz tmp.straight2templateAffine.txt' print('>> '+cmd) commands.getstatusoutput(cmd) # Concatenate affine and non-linear transformations... print '\nConcatenate affine and non-linear transformations: template --> straight...' # NB: cannot use sct.run() because output of ComposeMultiTransform is not 0, even if there is no error (bug in ANTS-- already reported on 2013-12-30) cmd = 'ComposeMultiTransform 3 tmp.warp_template2straight.nii.gz -R '+fname_anat+' -i tmp.straight2templateAffine.txt tmp.straight2template0InverseWarp.nii.gz' print('>> '+cmd) commands.getstatusoutput(cmd) # Apply transformation: template --> straight print '\nApply transformation: template --> straight...' sct.run('WarpImageMultiTransform 3 '+fname_template+' tmp.template2straight.nii.gz'+' -R '+fname_anat+' tmp.warp_template2straight.nii.gz') # THIS CODE USES 2-STEP METHOD WITH SEGMENTATION # # Estimate transfo: straight --> template (affine landmark-based)' # print '\nEstimate transfo: straight anat --> template (affine landmark-based)...' # sct.run('ANTSUseLandmarkImagesToGetAffineTransform '+fname_landmark_template+' '+fname_landmark_anat+' affine tmp.straight2templateAffine.txt') # # # Apply transformation: straight --> template # print '\nApply transformation straight --> template...' # sct.run('WarpImageMultiTransform 3 '+fname_anat+' tmp.straight2templateAffine.nii tmp.straight2templateAffine.txt -R '+fname_template) # sct.run('WarpImageMultiTransform 3 '+fname_anat_seg+' tmp.straightSeg2templateAffine.nii tmp.straight2templateAffine.txt -R '+fname_template) # # # Estimate transformation using ANTS # print('\nStep #1: Estimate transformation using spinal cord segmentations...') # # cmd = 'antsRegistration \ # --dimensionality 3 \ # --transform SyN[0.2,3,0] \ # --metric MI['+fname_template_seg+',tmp.straightSeg2templateAffine.nii,1,32] \ # --convergence 50x10 \ # --shrink-factors 2x1 \ # --smoothing-sigmas 2x1mm \ # --Restrict-Deformation 1x1x0 \ # --output [tmp.regSeg,tmp.straightSeg2template.nii.gz]' # # # run command # status, output = sct.run(cmd) # if verbose: # print output # # # Apply warping field: seg --> template_seg # print '\nApply transformation anat_seg --> template_seg...' # sct.run('WarpImageMultiTransform 3 '+fname_anat+' tmp.straight2templateStep1.nii tmp.regSeg0Warp.nii.gz -R '+fname_template) # # print('\nStep #2: Improve local deformation using images (start from previous transformation)...') # # # Estimate transformation: straight --> template (deformation) # print '\nEstimate transformation: straight --> template (diffeomorphic transformation). Takes 10-45 minutes...' # cmd = 'antsRegistration \ # --dimensionality 3 \ # --transform SyN[0.1,1,0] \ # --metric CC['+fname_template+',tmp.straight2templateStep1.nii,1,4] \ # --convergence 20 \ # --shrink-factors 1 \ # --smoothing-sigmas 0mm \ # --Restrict-Deformation 1x1x0 \ # --output [tmp.straight2template,tmp.straight2template.nii.gz] \ # --interpolation BSpline[3]' # # # use mask (if provided by user) # if fname_mask != '': # # TODO: check if mask exist # cmd = cmd+' -x '+fname_mask # # # run command # status, output = sct.run(cmd) # if verbose: # print output # # # Concatenate affine and non-linear transformations... # print '\nConcatenate affine and non-linear transformations: straight --> template...' # # NB: cannot use sct.run() because output of ComposeMultiTransform is not 0, even if there is no error (bug in ANTS-- already reported on 2013-12-30) # cmd = 'ComposeMultiTransform 3 tmp.warp_straight2template.nii.gz -R '+fname_template+' tmp.straight2template0Warp.nii.gz tmp.regSeg0Warp.nii.gz tmp.straight2templateAffine.txt' # print('>> '+cmd) # commands.getstatusoutput(cmd) # # # Concatenate affine and non-linear transformations... # print '\nConcatenate affine and non-linear transformations: template --> straight...' # # NB: cannot use sct.run() because output of ComposeMultiTransform is not 0, even if there is no error (bug in ANTS-- already reported on 2013-12-30) # cmd = 'ComposeMultiTransform 3 tmp.warp_template2straight.nii.gz -R '+fname_anat+' -i tmp.straight2templateAffine.txt tmp.straight2template0InverseWarp.nii.gz' # print('>> '+cmd) # commands.getstatusoutput(cmd) # # # Apply transformation: template --> straight # print '\nApply transformation: template --> straight...' # sct.run('WarpImageMultiTransform 3 '+fname_template+' tmp.template2straight.nii.gz'+' -R '+fname_anat+' tmp.warp_template2straight.nii.gz') # # Generate output file (in current folder) print '\nGenerate output file...' sct.generate_output_file('tmp.warp_template2straight.nii.gz','./','warp_template2straight',ext_anat) # warping field template --> straight sct.generate_output_file('tmp.warp_straight2template.nii.gz','./','warp_straight2template',ext_anat) # warping field straight --> template sct.generate_output_file('tmp.straight2template.nii.gz','./',file_anat+'2template',ext_anat) # anat --> template sct.generate_output_file('tmp.template2straight.nii.gz','./',file_template+'2straight',ext_anat) # anat --> template # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm tmp.*') elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s\n'
def compute_csa(fname_segmentation, name_method, volume_output, verbose, remove_temp_files, step, smoothing_param, figure_fit, name_output, slices, vert_levels, path_to_template, algo_fitting = 'hanning', type_window = 'hanning', window_length = 80): # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('isct_c3d '+fname_segmentation+' -o '+path_tmp+'segmentation.nii') # go to tmp folder os.chdir(path_tmp) # Change orientation of the input segmentation into RPI sct.printv('\nChange orientation of the input segmentation into RPI...', verbose) fname_segmentation_orient = set_orientation('segmentation.nii', 'RPI', 'segmentation_orient.nii') # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_segmentation_orient).dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # Open segmentation volume sct.printv('\nOpen segmentation volume...', verbose) file_seg = nibabel.load(fname_segmentation_orient) data_seg = file_seg.get_data() hdr_seg = file_seg.get_header() # # Extract min and max index in Z direction X, Y, Z = (data_seg > 0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # Xp, Yp = (data_seg[:, :, 0] >= 0).nonzero() # X and Y range # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv, y_centerline_deriv, z_centerline_deriv = smooth_centerline(fname_segmentation_orient, algo_fitting=algo_fitting, type_window=type_window, window_length=window_length, verbose=verbose) z_centerline_scaled = [x*pz for x in z_centerline] # Compute CSA sct.printv('\nCompute CSA...', verbose) # Empty arrays in which CSA for each z slice will be stored csa = np.zeros(max_z_index-min_z_index+1) # csa = [0.0 for i in xrange(0, max_z_index-min_z_index+1)] for iz in xrange(0, len(z_centerline)): # compute the vector normal to the plane normal = normalize(np.array([x_centerline_deriv[iz], y_centerline_deriv[iz], z_centerline_deriv[iz]])) # compute the angle between the normal vector of the plane and the vector z angle = np.arccos(np.dot(normal, [0, 0, 1])) # compute the number of voxels, assuming the segmentation is coded for partial volume effect between 0 and 1. number_voxels = sum(sum(data_seg[:, :, iz+min_z_index])) # compute CSA, by scaling with voxel size (in mm) and adjusting for oblique plane csa[iz] = number_voxels * px * py * np.cos(angle) if smoothing_param: from msct_smooth import smoothing_window sct.printv('\nSmooth CSA across slices...', verbose) sct.printv('.. Hanning window: '+str(smoothing_param)+' mm', verbose) csa_smooth = smoothing_window(csa, window_len=smoothing_param/pz, window='hanning', verbose=0) # display figure if verbose == 2: import matplotlib.pyplot as plt plt.figure() pltx, = plt.plot(z_centerline_scaled, csa, 'bo') pltx_fit, = plt.plot(z_centerline_scaled, csa_smooth, 'r', linewidth=2) plt.title("Cross-sectional area (CSA)") plt.xlabel('z (mm)') plt.ylabel('CSA (mm^2)') plt.legend([pltx, pltx_fit], ['Raw', 'Smoothed']) plt.show() # update variable csa = csa_smooth # Create output text file sct.printv('\nWrite text file...', verbose) file_results = open('csa.txt', 'w') for i in range(min_z_index, max_z_index+1): file_results.write(str(int(i)) + ',' + str(csa[i-min_z_index])+'\n') # Display results sct.printv('z='+str(i-min_z_index)+': '+str(csa[i-min_z_index])+' mm^2', verbose, 'bold') file_results.close() # output volume of csa values if volume_output: sct.printv('\nCreate volume of CSA values...', verbose) # get orientation of the input data orientation = get_orientation('segmentation.nii') data_seg = data_seg.astype(np.float32, copy=False) # loop across slices for iz in range(min_z_index, max_z_index+1): # retrieve seg pixels x_seg, y_seg = (data_seg[:, :, iz] > 0).nonzero() seg = [[x_seg[i],y_seg[i]] for i in range(0, len(x_seg))] # loop across pixels in segmentation for i in seg: # replace value with csa value data_seg[i[0], i[1], iz] = csa[iz-min_z_index] # create header hdr_seg.set_data_dtype('float32') # set imagetype to uint8 # save volume img = nibabel.Nifti1Image(data_seg, None, hdr_seg) nibabel.save(img, 'csa_RPI.nii') # Change orientation of the output centerline into input orientation fname_csa_volume = set_orientation('csa_RPI.nii', orientation, 'csa_RPI_orient.nii') # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) from shutil import copyfile copyfile(path_tmp+'csa.txt', path_data+param.fname_csa) # sct.generate_output_file(path_tmp+'csa.txt', path_data+param.fname_csa) # extension already included in param.fname_csa if volume_output: sct.generate_output_file(fname_csa_volume, path_data+name_output) # extension already included in name_output # average csa across vertebral levels or slices if asked (flag -z or -l) if slices or vert_levels: if vert_levels and not path_to_template: sct.printv('\nERROR: Path to template is missing. See usage.\n', 1, 'error') sys.exit(2) elif vert_levels and path_to_template: abs_path_to_template = os.path.abspath(path_to_template) # go to tmp folder os.chdir(path_tmp) # create temporary folder sct.printv('\nCreate temporary folder to average csa...', verbose) path_tmp_extract_metric = sct.slash_at_the_end('label_temp', 1) sct.run('mkdir '+path_tmp_extract_metric, verbose) # Copying output CSA volume in the temporary folder sct.printv('\nCopy data to tmp folder...', verbose) sct.run('cp '+fname_segmentation+' '+path_tmp_extract_metric) # create file info_label path_fname_seg, file_fname_seg, ext_fname_seg = sct.extract_fname(fname_segmentation) create_info_label('info_label.txt', path_tmp_extract_metric, file_fname_seg+ext_fname_seg) # average CSA if slices: os.system("sct_extract_metric -i "+path_data+name_output+" -f "+path_tmp_extract_metric+" -m wa -o ../csa_mean.txt -z "+slices) if vert_levels: sct.run('cp -R '+abs_path_to_template+' .') os.system("sct_extract_metric -i "+path_data+name_output+" -f "+path_tmp_extract_metric+" -m wa -o ../csa_mean.txt -v "+vert_levels) os.chdir('..') # Remove temporary files print('\nRemove temporary folder used to average CSA...') sct.run('rm -rf '+path_tmp_extract_metric) # Remove temporary files if remove_temp_files: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp)
def get_or_set_orientation(): fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI # display usage if a mandatory argument is not provided if param.fname_data == '': sct.printv('ERROR: All mandatory arguments are not provided. See usage.', 1, 'error') # check existence of input files sct.printv('\ncheck existence of input files...', param.verbose) sct.check_file_exist(param.fname_data, param.verbose) # find what to do if param.orientation == '': todo = 'get_orientation' else: todo = 'set_orientation' # check if orientation is correct if check_orientation_input(): sct.printv('\nERROR in '+os.path.basename(__file__)+': orientation is not recognized. Use one of the following orientation: '+param.list_of_correct_orientation+'\n', 1, 'error') sys.exit(2) # display input parameters sct.printv('\nInput parameters:', param.verbose) sct.printv(' data ..................'+param.fname_data, param.verbose) # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) if param.fname_out == '': # path_out, file_out, ext_out = '', file_data+'_'+param.orientation, ext_data fname_out = path_data+file_data+'_'+param.orientation+ext_data else: fname_out = param.fname_out # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, param.verbose) # Copying input data to tmp folder and convert to nii # NB: cannot use c3d here because c3d cannot convert 4D data. sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) sct.run('cp '+param.fname_data+' '+path_tmp+'data'+ext_data, param.verbose) # go to tmp folder os.chdir(path_tmp) # convert to nii format sct.run('fslchfiletype NIFTI data', param.verbose) # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension('data.nii') sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz)+ ' x ' + str(nt), param.verbose) # if 4d, loop across the data if nt == 1: if todo == 'set_orientation': # set orientation sct.printv('\nChange orientation...', param.verbose) set_orientation('data.nii', param.orientation, 'data_orient.nii') elif todo == 'get_orientation': # get orientation sct.printv('\nGet orientation...', param.verbose) sct.printv(get_orientation('data.nii'), 1) else: # split along T dimension sct.printv('\nSplit along T dimension...', param.verbose) sct.run(fsloutput+'fslsplit data data_T', param.verbose) if todo == 'set_orientation': # set orientation sct.printv('\nChange orientation...', param.verbose) for it in range(nt): file_data_split = 'data_T'+str(it).zfill(4)+'.nii' file_data_split_orient = 'data_orient_T'+str(it).zfill(4)+'.nii' set_orientation(file_data_split, param.orientation, file_data_split_orient) # Merge files back sct.printv('\nMerge file back...', param.verbose) cmd = fsloutput+'fslmerge -t data_orient' for it in range(nt): file_data_split_orient = 'data_orient_T'+str(it).zfill(4)+'.nii' cmd = cmd+' '+file_data_split_orient sct.run(cmd, param.verbose) elif todo == 'get_orientation': sct.printv('\nGet orientation...', param.verbose) sct.printv(get_orientation('data_T0000.nii'), 1) # come back to parent folder os.chdir('..') # Generate output files if todo == 'set_orientation': sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp+'data_orient.nii', fname_out) # Remove temporary files if param.remove_tmp_files == 1: sct.printv('\nRemove temporary files...', param.verbose) sct.run('rm -rf '+path_tmp, param.verbose) # to view results if todo == 'set_orientation': sct.printv('\nDone! To view results, type:', param.verbose) sct.printv('fslview '+fname_out+' &', param.verbose, 'code') print
def main(): # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') print path_sct # Initialization fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI fname_data = '' fname_bvecs = '' fname_schedule = path_sct+'/flirtsch/schedule_TxTy.sch' interp = param.interp remove_temp_files = param.remove_temp_files verbose = param.verbose start_time = time.time() # Parameters for debug mode if param.debug: fname_data = path_sct+'/testing/data/errsm_23/dmri/dmri.nii.gz' fname_bvecs = path_sct+'/testing/data/errsm_23/dmri/bvecs.txt' interp = 'trilinear' remove_temp_files = 0 verbose = 1 # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:],'hb:i:v:s:') except getopt.GetoptError: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ("-b"): fname_bvecs = arg elif opt in ("-i"): fname_data = arg elif opt in ('-s'): interp = str(arg) elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_data == '' or fname_bvecs == '': usage() # check existence of input files sct.check_file_exist(fname_data) sct.check_file_exist(fname_bvecs) # print arguments print '\nCheck parameters:' print '.. DWI data: '+fname_data print '.. bvecs file: '+fname_bvecs print '' # Get full path fname_data = os.path.abspath(fname_data) fname_bvecs = os.path.abspath(fname_bvecs) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(fname_data) # create temporary folder path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.run('mkdir '+path_tmp) # go to tmp folder os.chdir(path_tmp) # Get size of data print '\nGet dimensions data...' nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(fname_data) print '.. '+str(nx)+' x '+str(ny)+' x '+str(nz)+' x '+str(nt) # Open bvecs file print '\nOpen bvecs file...' bvecs = [] with open(fname_bvecs) as f: for line in f: bvecs_new = map(float, line.split()) bvecs.append(bvecs_new) # Check if bvecs file is nx3 if not len(bvecs[0][:]) == 3: print '.. WARNING: bvecs file is 3xn instead of nx3. Consider using sct_dmri_transpose_bvecs.' print 'Transpose bvecs...' # transpose bvecs bvecs = zip(*bvecs) # Identify b=0 and DW images print '\nIdentify b=0 and DW images...' index_b0 = [] index_dwi = [] for it in xrange(0,nt): if math.sqrt(math.fsum([i**2 for i in bvecs[it]])) < 0.01: index_b0.append(it) else: index_dwi.append(it) n_b0 = len(index_b0) n_dwi = len(index_dwi) print '.. Index of b=0:'+str(index_b0) print '.. Index of DWI:'+str(index_dwi) #TODO: check if number of bvecs and nt match # Split into T dimension print '\nSplit along T dimension...' #cmd = fsloutput+'fslsplit tmp.data tmp.data_splitT' status, output = sct.run(fsloutput+'fslsplit '+fname_data+' tmp.data_splitT') # retrieve output names status, output = sct.run('ls tmp.data_splitT*.*') file_data_split = output.split() # Remove .nii extension file_data_split = [file_data_split[i].replace('.nii','') for i in xrange (0,len(file_data_split))] # Merge b=0 images print '\nMerge b=0...' file_b0 = 'tmp.b0' cmd = fsloutput+'fslmerge -t '+file_b0 for it in xrange(0,n_b0): cmd += ' '+file_data_split[index_b0[it]] #print('>> '+cmd) status, output = sct.run(cmd) # Merge DWI images print '\nMerge DWI...' file_dwi = 'tmp.dwi' cmd = fsloutput+'fslmerge -t '+file_dwi for it in xrange(0,n_dwi): cmd += ' '+file_data_split[index_dwi[it]] status, output = sct.run(cmd) # Average b=0 images print '\nAverage b=0...' file_b0_mean = 'tmp.b0_mean' cmd = fsloutput+'fslmaths '+file_b0+' -Tmean '+file_b0_mean status, output = sct.run(cmd) # Average DWI images print '\nAverage DWI...' file_dwi_mean = 'tmp.dwi_mean' cmd = fsloutput+'fslmaths '+file_dwi+' -Tmean '+file_dwi_mean status, output = sct.run(cmd) # REGISTER DWI TO THE MEAN DWI --> output transfo Tdwi # --------------------------------------------------------------------------------------- # loop across DWI data print '\nRegister DWI data to '+file_dwi_mean+'...' for it in xrange(0,n_dwi): # estimate transformation matrix file_target = file_dwi_mean file_mat = 'tmp.mat_'+str(index_dwi[it]).zfill(4) cmd = fsloutput+'flirt -in '+file_data_split[index_dwi[it]]+' -ref '+file_target+' -omat '+file_mat+' -cost normcorr -schedule '+fname_schedule+' -interp trilinear -out '+file_data_split[index_dwi[it]]+'_moco' status, output = sct.run(cmd) # Merge corrected DWI images print '\nMerge corrected DWI...' file_dwi = 'tmp.dwi_moco' cmd = fsloutput+'fslmerge -t '+file_dwi for it in xrange(0,n_dwi): cmd += ' '+file_data_split[index_dwi[it]]+'_moco' status, output = sct.run(cmd) # Average corrected DWI print '\nAverage corrected DWI...' file_dwi_mean = 'tmp.dwi_moco_mean' cmd = fsloutput+'fslmaths '+file_dwi+' -Tmean '+file_dwi_mean status, output = sct.run(cmd) # REGISTER B=0 DATA TO THE FIRST B=0 --> output transfo Tb0 # --------------------------------------------------------------------------------------- print '\nRegister b=0 data to the first b=0...' for it in xrange(0,n_b0): # estimate transformation matrix file_target = file_data_split[int(index_b0[0])] file_mat = 'tmp.mat_'+str(index_b0[it]).zfill(4) cmd = fsloutput+'flirt -in '+file_data_split[index_b0[it]]+' -ref '+file_target+' -omat '+file_mat+' -cost normcorr -forcescaling -2D -out '+file_data_split[index_b0[it]]+'_moco' status, output = sct.run(cmd) # Merge corrected b=0 images print '\nMerge corrected b=0...' cmd = fsloutput+'fslmerge -t tmp.b0_moco' for it in xrange(0,n_b0): cmd += ' '+file_data_split[index_b0[it]]+'_moco' status, output = sct.run(cmd) # Average corrected b=0 print '\nAverage corrected b=0...' cmd = fsloutput+'fslmaths tmp.b0_moco -Tmean tmp.b0_moco_mean' status, output = sct.run(cmd) # REGISTER MEAN DWI TO THE MEAN B=0 --> output transfo Tdwi2b0 # --------------------------------------------------------------------------------------- print '\nRegister mean DWI to the mean b=0...' cmd = fsloutput+'flirt -in tmp.dwi_moco_mean -ref tmp.b0_moco_mean -omat tmp.mat_dwi2b0 -cost mutualinfo -forcescaling -dof 12 -2D -out tmp.dwi_mean_moco_reg2b0' status, output = sct.run(cmd) # COMBINE TRANSFORMATIONS # --------------------------------------------------------------------------------------- print '\nCombine all transformations...' # USE FSL convert_xfm: convert_xfm -omat AtoC.mat -concat BtoC.mat AtoB.mat # For DWI print '\n.. For DWI:' for it in xrange(0,n_dwi): cmd = 'convert_xfm -omat tmp.mat_final_'+str(index_dwi[it]).zfill(4)+' -concat tmp.mat_dwi2b0 tmp.mat_'+str(index_dwi[it]).zfill(4) status, output = sct.run(cmd) # For b=0 (don't concat because there is just one mat file -- just rename it) print '\n.. For b=0:' for it in xrange(0,n_b0): cmd = 'cp tmp.mat_'+str(index_b0[it]).zfill(4)+' tmp.mat_final_'+str(index_b0[it]).zfill(4) status, output = sct.run(cmd) # APPLY TRANSFORMATIONS # --------------------------------------------------------------------------------------- ## Split original data into T dimension #print '\nSplit original data along T dimension...' #cmd = fsloutput+'fslsplit '+fname_data+' tmp.data_raw_splitT' #print('>> '+cmd) #status, output = commands.getstatusoutput(cmd) #print '\nApply transformations to original data...' #for it in xrange(0,nt): # cmd = fsloutput+'flirt -in tmp.data_raw_splitT'+str(it).zfill(4)+' -ref tmp.data_raw_splitT'+index_b0[0].zfill(4)+' -applyxfm -init tmp.mat_final_'+str(it).zfill(4)+' -out tmp.data_raw_splitT'+str(it).zfill(4)+'_moco' # print('>> '+cmd) # status, output = commands.getstatusoutput(cmd) # ## Merge corrected data #print '\nMerge corrected data...' #cmd = fsloutput+'fslmerge -t tmp.data_raw_moco' #for it in xrange(0,it): # cmd += ' tmp.data_raw_splitT'+str(it).zfill(4)+'_moco' #print('>> '+cmd) #status, output = commands.getstatusoutput(cmd) print '\nApply transformations...' for it in xrange(0,nt): # -paddingsize 3 prevents from having missing slices at the edge cmd = fsloutput+'flirt -in tmp.data_splitT'+str(it).zfill(4)+' -ref tmp.data_splitT'+str(index_b0[0]).zfill(4)+' -applyxfm -init tmp.mat_final_'+str(it).zfill(4)+' -out tmp.data_splitT'+str(it).zfill(4)+'_moco -paddingsize 3'+' -interp '+interp status, output = sct.run(cmd) # Merge corrected data print '\nMerge all corrected data...' cmd = fsloutput+'fslmerge -t tmp.data_moco' for it in xrange(0,nt): cmd += ' tmp.data_splitT'+str(it).zfill(4)+'_moco' status, output = sct.run(cmd) # Merge corrected DWI images print '\nMerge corrected DWI...' cmd = fsloutput+'fslmerge -t tmp.dwi_moco' for it in xrange(0,n_dwi): cmd += ' tmp.data_splitT'+str(index_dwi[it]).zfill(4)+'_moco' status, output = sct.run(cmd) # Average corrected DWI print '\nAverage corrected DWI...' cmd = fsloutput+'fslmaths tmp.dwi_moco -Tmean tmp.dwi_moco_mean' status, output = sct.run(cmd) # Merge corrected b=0 images print '\nMerge corrected b=0...' cmd = fsloutput+'fslmerge -t tmp.b0_moco' for it in xrange(0,n_b0): cmd += ' tmp.data_splitT'+str(index_b0[it]).zfill(4)+'_moco' status, output = sct.run(cmd) # Average corrected b=0 print '\nAverage corrected b=0...' cmd = fsloutput+'fslmaths tmp.b0_moco -Tmean tmp.b0_moco_mean' status, output = sct.run(cmd) # Generate output files print('\nGenerate output files...') sct.generate_output_file('tmp.data_moco.nii',path_data,file_data+'_moco',ext_data) sct.generate_output_file('tmp.dwi_moco_mean.nii',path_data,'dwi_moco_mean',ext_data) sct.generate_output_file('tmp.b0_moco_mean.nii',path_data,'b0_moco_mean',ext_data) # come back to parent folder os.chdir('..') # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm -rf '+path_tmp) # display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s' # to view results print '\nTo view results, type:' print 'fslview '+file_data+' '+file_data+'_moco &\n'
def main(args=None): # Initialization # fname_anat = '' # fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) param = Param() # remove_temp_files = param.remove_temp_files # verbose = param.verbose start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-param' in arguments: param.update(arguments['-param']) if '-r' in arguments: remove_temp_files = int(arguments['-r']) if '-v' in arguments: verbose = int(arguments['-v']) # Display arguments sct.printv('\nCheck input arguments...') sct.printv(' Volume to smooth .................. ' + fname_anat) sct.printv(' Centerline ........................ ' + fname_centerline) sct.printv(' Sigma (mm) ........................ ' + str(sigma)) sct.printv(' Verbose ........................... ' + str(verbose)) # Check that input is 3D: from spinalcordtoolbox.image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv( 'WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i ' + fname_anat + ' -split t -o ' + fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname( fname_centerline) path_tmp = sct.tmp_create(basename="smooth_spinalcord", verbose=verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.copy(fname_anat, os.path.join(path_tmp, "anat" + ext_anat)) sct.copy(fname_centerline, os.path.join(path_tmp, "centerline" + ext_centerline)) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # convert to nii format convert('anat' + ext_anat, 'anat.nii') convert('centerline' + ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI sct.printv('\nOrient input volume to RPI orientation...') fname_anat_rpi = msct_image.Image("anat.nii") \ .change_orientation("RPI", generate_path=True) \ .save() \ .absolutepath # Change orientation of the input image into RPI sct.printv('\nOrient centerline to RPI orientation...') fname_centerline_rpi = msct_image.Image("centerline.nii") \ .change_orientation("RPI", generate_path=True) \ .save() \ .absolutepath # Straighten the spinal cord # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) cache_sig = sct.cache_signature( input_files=[fname_anat_rpi, fname_centerline_rpi], input_params={"x": "spline"}, ) cachefile = os.path.join(curdir, "straightening.cache") if sct.cache_valid(cachefile, cache_sig) and os.path.isfile( os.path.join( curdir, 'warp_curve2straight.nii.gz')) and os.path.isfile( os.path.join( curdir, 'warp_straight2curve.nii.gz')) and os.path.isfile( os.path.join(curdir, 'straight_ref.nii.gz')): # if they exist, copy them into current folder sct.printv('Reusing existing warping field which seems to be valid', verbose, 'warning') sct.copy(os.path.join(curdir, 'warp_curve2straight.nii.gz'), 'warp_curve2straight.nii.gz') sct.copy(os.path.join(curdir, 'warp_straight2curve.nii.gz'), 'warp_straight2curve.nii.gz') sct.copy(os.path.join(curdir, 'straight_ref.nii.gz'), 'straight_ref.nii.gz') # apply straightening sct.run([ 'sct_apply_transfo', '-i', fname_anat_rpi, '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', 'anat_rpi_straight.nii', '-x', 'spline' ], verbose) else: sct.run([ 'sct_straighten_spinalcord', '-i', fname_anat_rpi, '-o', 'anat_rpi_straight.nii', '-s', fname_centerline_rpi, '-x', 'spline', '-param', 'algo_fitting=' + param.algo_fitting ], verbose) sct.cache_save(cachefile, cache_sig) # Smooth the straightened image along z sct.printv('\nSmooth the straightened image along z...') sct.run([ 'sct_maths', '-i', 'anat_rpi_straight.nii', '-smooth', '0,0,' + str(sigma), '-o', 'anat_rpi_straight_smooth.nii' ], verbose) # Apply the reversed warping field to get back the curved spinal cord sct.printv( '\nApply the reversed warping field to get back the curved spinal cord...' ) sct.run([ 'sct_apply_transfo', '-i', 'anat_rpi_straight_smooth.nii', '-o', 'anat_rpi_straight_smooth_curved.nii', '-d', 'anat.nii', '-w', 'warp_straight2curve.nii.gz', '-x', 'spline' ], verbose) # replace zeroed voxels by original image (issue #937) sct.printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.save('anat_rpi_straight_smooth_curved_nonzero.nii') # come back os.chdir(curdir) # Generate output file sct.printv('\nGenerate output file...') sct.generate_output_file( os.path.join(path_tmp, "anat_rpi_straight_smooth_curved_nonzero.nii"), file_anat + '_smooth' + ext_anat) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...') sct.rmtree(path_tmp) # Display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's\n') sct.display_viewer_syntax([file_anat, file_anat + '_smooth'], verbose=verbose)
def main(args=None): if args is None: args = sys.argv[1:] # initialize parameters param = Param() # Initialization fname_output = '' path_out = '' fname_src_seg = '' fname_dest_seg = '' fname_src_label = '' fname_dest_label = '' generate_warpinv = 1 start_time = time.time() # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # get default registration parameters # step1 = Paramreg(step='1', type='im', algo='syn', metric='MI', iter='5', shrink='1', smooth='0', gradStep='0.5') step0 = Paramreg(step='0', type='im', algo='syn', metric='MI', iter='0', shrink='1', smooth='0', gradStep='0.5', slicewise='0', dof='Tx_Ty_Tz_Rx_Ry_Rz') # only used to put src into dest space step1 = Paramreg(step='1', type='im') paramreg = ParamregMultiStep([step0, step1]) parser = get_parser(paramreg=paramreg) arguments = parser.parse(args) # get arguments fname_src = arguments['-i'] fname_dest = arguments['-d'] if '-iseg' in arguments: fname_src_seg = arguments['-iseg'] if '-dseg' in arguments: fname_dest_seg = arguments['-dseg'] if '-ilabel' in arguments: fname_src_label = arguments['-ilabel'] if '-dlabel' in arguments: fname_dest_label = arguments['-dlabel'] if '-o' in arguments: fname_output = arguments['-o'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-owarp' in arguments: fname_output_warp = arguments['-owarp'] else: fname_output_warp = '' if '-initwarp' in arguments: fname_initwarp = os.path.abspath(arguments['-initwarp']) else: fname_initwarp = '' if '-initwarpinv' in arguments: fname_initwarpinv = os.path.abspath(arguments['-initwarpinv']) else: fname_initwarpinv = '' if '-m' in arguments: fname_mask = arguments['-m'] else: fname_mask = '' padding = arguments['-z'] if "-param" in arguments: paramreg_user = arguments['-param'] # update registration parameters for paramStep in paramreg_user: paramreg.addStep(paramStep) identity = int(arguments['-identity']) interp = arguments['-x'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) # print arguments print '\nInput parameters:' print ' Source .............. '+fname_src print ' Destination ......... '+fname_dest print ' Init transfo ........ '+fname_initwarp print ' Mask ................ '+fname_mask print ' Output name ......... '+fname_output # print ' Algorithm ........... '+paramreg.algo # print ' Number of iterations '+paramreg.iter # print ' Metric .............. '+paramreg.metric print ' Remove temp files ... '+str(remove_temp_files) print ' Verbose ............. '+str(verbose) # update param param.verbose = verbose param.padding = padding param.fname_mask = fname_mask param.remove_temp_files = remove_temp_files # Get if input is 3D sct.printv('\nCheck if input data are 3D...', verbose) sct.check_if_3d(fname_src) sct.check_if_3d(fname_dest) # Check if user selected type=seg, but did not input segmentation data if 'paramreg_user' in locals(): if True in ['type=seg' in paramreg_user[i] for i in range(len(paramreg_user))]: if fname_src_seg == '' or fname_dest_seg == '': sct.printv('\nERROR: if you select type=seg you must specify -iseg and -dseg flags.\n', 1, 'error') # Extract path, file and extension path_src, file_src, ext_src = sct.extract_fname(fname_src) path_dest, file_dest, ext_dest = sct.extract_fname(fname_dest) # check if source and destination images have the same name (related to issue #373) # If so, change names to avoid conflict of result files and warns the user suffix_src, suffix_dest = '_reg', '_reg' if file_src == file_dest: suffix_src, suffix_dest = '_src_reg', '_dest_reg' # define output folder and file name if fname_output == '': path_out = '' if not path_out else path_out # output in user's current directory file_out = file_src + suffix_src file_out_inv = file_dest + suffix_dest ext_out = ext_src else: path, file_out, ext_out = sct.extract_fname(fname_output) path_out = path if not path_out else path_out file_out_inv = file_out + '_inv' # create QC folder sct.create_folder(param.path_qc) # create temporary folder path_tmp = sct.tmp_create() # copy files to temporary folder from sct_convert import convert sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) convert(fname_src, path_tmp+'src.nii') convert(fname_dest, path_tmp+'dest.nii') if fname_src_seg: convert(fname_src_seg, path_tmp+'src_seg.nii') convert(fname_dest_seg, path_tmp+'dest_seg.nii') if fname_src_label: convert(fname_src_label, path_tmp+'src_label.nii') convert(fname_dest_label, path_tmp+'dest_label.nii') if fname_mask != '': convert(fname_mask, path_tmp+'mask.nii.gz') # go to tmp folder os.chdir(path_tmp) # reorient destination to RPI sct.run('sct_image -i dest.nii -setorient RPI -o dest_RPI.nii') if fname_dest_seg: sct.run('sct_image -i dest_seg.nii -setorient RPI -o dest_seg_RPI.nii') if fname_dest_label: sct.run('sct_image -i dest_label.nii -setorient RPI -o dest_label_RPI.nii') if identity: # overwrite paramreg and only do one identity transformation step0 = Paramreg(step='0', type='im', algo='syn', metric='MI', iter='0', shrink='1', smooth='0', gradStep='0.5') paramreg = ParamregMultiStep([step0]) # Put source into destination space using header (no estimation -- purely based on header) # TODO: Check if necessary to do that # TODO: use that as step=0 # sct.printv('\nPut source into destination space using header...', verbose) # sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[dest_pad.nii,src.nii,1,16] -c 0 -f 1 -s 0 -o [regAffine,src_regAffine.nii] -n BSpline[3]', verbose) # if segmentation, also do it for seg # initialize list of warping fields warp_forward = [] warp_inverse = [] # initial warping is specified, update list of warping fields and skip step=0 if fname_initwarp: sct.printv('\nSkip step=0 and replace with initial transformations: ', param.verbose) sct.printv(' '+fname_initwarp, param.verbose) # sct.run('cp '+fname_initwarp+' warp_forward_0.nii.gz', verbose) warp_forward = [fname_initwarp] start_step = 1 if fname_initwarpinv: warp_inverse = [fname_initwarpinv] else: sct.printv('\nWARNING: No initial inverse warping field was specified, therefore the inverse warping field will NOT be generated.', param.verbose, 'warning') generate_warpinv = 0 else: start_step = 0 # loop across registration steps for i_step in range(start_step, len(paramreg.steps)): sct.printv('\n--\nESTIMATE TRANSFORMATION FOR STEP #'+str(i_step), param.verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = 'src.nii' dest = 'dest_RPI.nii' interp_step = 'spline' elif paramreg.steps[str(i_step)].type == 'seg': src = 'src_seg.nii' dest = 'dest_seg_RPI.nii' interp_step = 'nn' elif paramreg.steps[str(i_step)].type == 'label': src = 'src_label.nii' dest = 'dest_label_RPI.nii' interp_step = 'nn' else: # src = dest = interp_step = None sct.printv('ERROR: Wrong image type.', 1, 'error') # if step>0, apply warp_forward_concat to the src image to be used if i_step > 0: sct.printv('\nApply transformation from previous step', param.verbose) sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose) src = sct.add_suffix(src, '_reg') # register src --> dest warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.insert(0, warp_inverse_out) # Concatenate transformations sct.printv('\nConcatenate transformations...', verbose) sct.run('sct_concat_transfo -w '+','.join(warp_forward)+' -d dest.nii -o warp_src2dest.nii.gz', verbose) sct.run('sct_concat_transfo -w '+','.join(warp_inverse)+' -d dest.nii -o warp_dest2src.nii.gz', verbose) # Apply warping field to src data sct.printv('\nApply transfo source --> dest...', verbose) sct.run('sct_apply_transfo -i src.nii -o src_reg.nii -d dest.nii -w warp_src2dest.nii.gz -x '+interp, verbose) sct.printv('\nApply transfo dest --> source...', verbose) sct.run('sct_apply_transfo -i dest.nii -o dest_reg.nii -d src.nii -w warp_dest2src.nii.gz -x '+interp, verbose) # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) # generate: src_reg fname_src2dest = sct.generate_output_file(path_tmp+'src_reg.nii', path_out+file_out+ext_out, verbose) # generate: forward warping field if fname_output_warp == '': fname_output_warp = path_out+'warp_'+file_src+'2'+file_dest+'.nii.gz' sct.generate_output_file(path_tmp+'warp_src2dest.nii.gz', fname_output_warp, verbose) if generate_warpinv: # generate: dest_reg fname_dest2src = sct.generate_output_file(path_tmp+'dest_reg.nii', path_out+file_out_inv+ext_dest, verbose) # generate: inverse warping field sct.generate_output_file(path_tmp+'warp_dest2src.nii.gz', path_out+'warp_'+file_dest+'2'+file_src+'.nii.gz', verbose) # Delete temporary files if remove_temp_files: sct.printv('\nRemove temporary files...', verbose) sct.run('rm -rf '+path_tmp, verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s', verbose) sct.printv('\nTo view results, type:', verbose) sct.printv('fslview '+fname_dest+' '+fname_src2dest+' &', verbose, 'info') if generate_warpinv: sct.printv('fslview '+fname_src+' '+fname_dest2src+' &\n', verbose, 'info')
def main(): # Initialization fname_data = '' suffix_out = '_crop' remove_temp_files = param.remove_temp_files verbose = param.verbose fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI remove_temp_files = param.remove_temp_files # Parameters for debug mode if param.debug: print '\n*** WARNING: DEBUG MODE ON ***\n' fname_data = path_sct+'/testing/data/errsm_23/t2/t2.nii.gz' remove_temp_files = 0 else: # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:],'hi:r:v:') except getopt.GetoptError: usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_data = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ('-v'): verbose = int(arg) # display usage if a mandatory argument is not provided if fname_data == '': usage() # Check file existence sct.printv('\nCheck file existence...', verbose) sct.check_file_exist(fname_data, verbose) # Get dimensions of data sct.printv('\nGet dimensions of data...', verbose) nx, ny, nz, nt, px, py, pz, pt = Image(fname_data).dim sct.printv('.. '+str(nx)+' x '+str(ny)+' x '+str(nz), verbose) # check if 4D data if not nt == 1: sct.printv('\nERROR in '+os.path.basename(__file__)+': Data should be 3D.\n', 1, 'error') sys.exit(2) # print arguments print '\nCheck parameters:' print ' data ................... '+fname_data print # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(fname_data) path_out, file_out, ext_out = '', file_data+suffix_out, ext_data # create temporary folder path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S")+'/' sct.run('mkdir '+path_tmp) # copy files into tmp folder sct.run('isct_c3d '+fname_data+' -o '+path_tmp+'data.nii') # go to tmp folder os.chdir(path_tmp) # change orientation sct.printv('\nChange orientation to RPI...', verbose) set_orientation('data.nii', 'RPI', 'data_rpi.nii') # get image of medial slab sct.printv('\nGet image of medial slab...', verbose) image_array = nibabel.load('data_rpi.nii').get_data() nx, ny, nz = image_array.shape scipy.misc.imsave('image.jpg', image_array[math.floor(nx/2), :, :]) # Display the image sct.printv('\nDisplay image and get cropping region...', verbose) fig = plt.figure() # fig = plt.gcf() # ax = plt.gca() ax = fig.add_subplot(111) img = mpimg.imread("image.jpg") implot = ax.imshow(img.T) implot.set_cmap('gray') plt.gca().invert_yaxis() # mouse callback ax.set_title('Left click on the top and bottom of your cropping field.\n Right click to remove last point.\n Close window when your done.') line, = ax.plot([], [], 'ro') # empty line cropping_coordinates = LineBuilder(line) plt.show() # disconnect callback # fig.canvas.mpl_disconnect(line) # check if user clicked two times if len(cropping_coordinates.xs) != 2: sct.printv('\nERROR: You have to select two points. Exit program.\n', 1, 'error') sys.exit(2) # convert coordinates to integer zcrop = [int(i) for i in cropping_coordinates.ys] # sort coordinates zcrop.sort() # crop image sct.printv('\nCrop image...', verbose) nii = Image('data_rpi.nii') data_crop = nii.data[:, :, zcrop[0]:zcrop[1]] nii.data = data_crop nii.setFileName('data_rpi_crop.nii') nii.save() # come back to parent folder os.chdir('..') sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp+'data_rpi_crop.nii', path_out+file_out+ext_out) # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp) # to view results print '\nDone! To view results, type:' print 'fslview '+path_out+file_out+ext_out+' &' print
def validation(self, fname_manual_gmseg, fname_sc_seg): path_manual_gmseg, file_manual_gmseg, ext_manual_gmseg = sct.extract_fname( fname_manual_gmseg) path_sc_seg, file_sc_seg, ext_sc_seg = sct.extract_fname(fname_sc_seg) # Create tmp folder and copy files in it tmp_dir = sct.tmp_create() sct.run('cp ' + fname_manual_gmseg + ' ' + tmp_dir + file_manual_gmseg + ext_manual_gmseg) sct.run('cp ' + fname_sc_seg + ' ' + tmp_dir + file_sc_seg + ext_sc_seg) sct.run('cp ' + self.param.output_folder + self.fname_warp_template2gm + ' ' + tmp_dir + self.fname_warp_template2gm) os.chdir(tmp_dir) sct.run('sct_warp_template -d ' + fname_manual_gmseg + ' -w ' + self.fname_warp_template2gm + ' -qc 0 -a 0') if 'MNI-Poly-AMU_GM.nii.gz' in os.listdir('label/template/'): im_new_template_gm = Image('label/template/MNI-Poly-AMU_GM.nii.gz') im_new_template_wm = Image('label/template/MNI-Poly-AMU_WM.nii.gz') else: im_new_template_gm = Image('label/template/PAM50_gm.nii.gz') im_new_template_wm = Image('label/template/PAM50_wm.nii.gz') im_new_template_gm = thr_im(im_new_template_gm, self.param.thr, self.param.thr) im_new_template_wm = thr_im(im_new_template_wm, self.param.thr, self.param.thr) self.im_template_gm = thr_im(self.im_template_gm, self.param.thr, self.param.thr) self.im_template_wm = thr_im(self.im_template_wm, self.param.thr, self.param.thr) fname_new_template_gm = 'new_template_gm.nii.gz' im_new_template_gm.setFileName(fname_new_template_gm) im_new_template_gm.save() fname_new_template_wm = 'new_template_wm.nii.gz' im_new_template_wm.setFileName(fname_new_template_wm) im_new_template_wm.save() fname_old_template_wm = 'old_template_wm.nii.gz' self.im_template_wm.setFileName(fname_old_template_wm) self.im_template_wm.save() fname_old_template_gm = 'old_template_gm.nii.gz' self.im_template_gm.setFileName(fname_old_template_gm) self.im_template_gm.save() fname_manual_wmseg = 'target_manual_wmseg.nii.gz' sct.run('sct_maths -i ' + file_sc_seg + ext_sc_seg + ' -sub ' + file_manual_gmseg + ext_manual_gmseg + ' -o ' + fname_manual_wmseg) # Compute Hausdorff distance status, output_old_hd = sct.run('sct_compute_hausdorff_distance -i ' + fname_old_template_gm + ' -r ' + file_manual_gmseg + ext_manual_gmseg + ' -t 1 -v 1') status, output_new_hd = sct.run('sct_compute_hausdorff_distance -i ' + fname_new_template_gm + ' -r ' + file_manual_gmseg + ext_manual_gmseg + ' -t 1 -v 1') hd_name = 'hd_md_multilabel_reg.txt' hd_fic = open(hd_name, 'w') hd_fic.write( 'The "diff" columns are comparisons between regular template registration and corrected template registration according to SC internal structure\n' 'Diff = metric_regular_reg - metric_corrected_reg\n') hd_fic.write('#Slice, HD, HD diff, MD, MD diff\n') no_ref_slices = [] init_hd = "Hausdorff's distance - First relative Hausdorff's distance median - Second relative Hausdorff's distance median(all in mm)\n" old_gm_hd = output_old_hd[output_old_hd.find(init_hd) + len(init_hd):].split('\n') new_gm_hd = output_new_hd[output_new_hd.find(init_hd) + len(init_hd):].split('\n') for i in range(len(old_gm_hd) - 3): # last two lines are informations i_old, val_old = old_gm_hd[i].split(':') i_new, val_new = new_gm_hd[i].split(':') i_old = int(i_old[-2:]) i_new = int(i_new[-2:]) assert i == i_old == i_new, 'ERROR: when comparing Hausdorff distances, slice numbers differs.' hd_old, med1_old, med2_old = val_old.split('-') hd_new, med1_new, med2_new = val_new.split('-') if float(hd_old) == 0.0: no_ref_slices.append(i) hd_fic.write(str(i) + ', NO MANUAL SEGMENTATION\n') else: md_new = max(float(med1_new), float(med2_new)) md_old = max(float(med1_old), float(med2_old)) hd_fic.write( str(i) + ', ' + hd_new + ', ' + str(float(hd_old) - float(hd_new)) + ', ' + str(md_new) + ', ' + str(md_old - md_new) + '\n') hd_fic.close() # Compute Dice coefficient # --- DC old template try: status_old_gm, output_old_gm = sct.run( 'sct_dice_coefficient -i ' + file_manual_gmseg + ext_manual_gmseg + ' -d ' + fname_old_template_gm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: corrected_manual_gmseg = file_manual_gmseg + '_in_old_template_space' + ext_manual_gmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_old_template_gm + ',' + file_manual_gmseg + ext_manual_gmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_gmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_gmseg+' -thr 0.1 -o '+corrected_manual_gmseg) sct.run('sct_maths -i ' + corrected_manual_gmseg + ' -bin 0.1 -o ' + corrected_manual_gmseg) status_old_gm, output_old_gm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_gmseg + ' -d ' + fname_old_template_gm + ' -2d-slices 2', error_exit='warning') try: status_old_wm, output_old_wm = sct.run( 'sct_dice_coefficient -i ' + fname_manual_wmseg + ' -d ' + fname_old_template_wm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: path_manual_wmseg, file_manual_wmseg, ext_manual_wmseg = sct.extract_fname( fname_manual_wmseg) corrected_manual_wmseg = file_manual_wmseg + '_in_old_template_space' + ext_manual_wmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_old_template_wm + ',' + fname_manual_wmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_wmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_wmseg+' -thr 0.1 -o '+corrected_manual_wmseg) sct.run('sct_maths -i ' + corrected_manual_wmseg + ' -bin 0.1 -o ' + corrected_manual_wmseg) status_old_wm, output_old_wm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_wmseg + ' -d ' + fname_old_template_wm + ' -2d-slices 2', error_exit='warning') # --- DC new template try: status_new_gm, output_new_gm = sct.run( 'sct_dice_coefficient -i ' + file_manual_gmseg + ext_manual_gmseg + ' -d ' + fname_new_template_gm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: corrected_manual_gmseg = file_manual_gmseg + '_in_new_template_space' + ext_manual_gmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_new_template_gm + ',' + file_manual_gmseg + ext_manual_gmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_gmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_gmseg+' -thr 0.1 -o '+corrected_manual_gmseg) sct.run('sct_maths -i ' + corrected_manual_gmseg + ' -bin 0.1 -o ' + corrected_manual_gmseg) status_new_gm, output_new_gm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_gmseg + ' -d ' + fname_new_template_gm + ' -2d-slices 2', error_exit='warning') try: status_new_wm, output_new_wm = sct.run( 'sct_dice_coefficient -i ' + fname_manual_wmseg + ' -d ' + fname_new_template_wm + ' -2d-slices 2', error_exit='warning', raise_exception=True) except Exception: # put the result and the reference in the same space using a registration with ANTs with no iteration: path_manual_wmseg, file_manual_wmseg, ext_manual_wmseg = sct.extract_fname( fname_manual_wmseg) corrected_manual_wmseg = file_manual_wmseg + '_in_new_template_space' + ext_manual_wmseg sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[' + fname_new_template_wm + ',' + fname_manual_wmseg + ',1,16] -o [reg_ref_to_res,' + corrected_manual_wmseg + '] -n BSpline[3] -c 0 -f 1 -s 0') # sct.run('sct_maths -i '+corrected_manual_wmseg+' -thr 0.1 -o '+corrected_manual_wmseg) sct.run('sct_maths -i ' + corrected_manual_wmseg + ' -bin 0.1 -o ' + corrected_manual_wmseg) status_new_wm, output_new_wm = sct.run( 'sct_dice_coefficient -i ' + corrected_manual_wmseg + ' -d ' + fname_new_template_wm + ' -2d-slices 2', error_exit='warning') dice_name = 'dice_multilabel_reg.txt' dice_fic = open(dice_name, 'w') dice_fic.write( 'The "diff" columns are comparisons between regular template registration and corrected template registration according to SC internal structure\n' 'Diff = metric_corrected_reg - metric_regular_reg\n') dice_fic.write('#Slice, WM DC, WM diff, GM DC, GM diff\n') init_dc = '2D Dice coefficient by slice:\n' old_gm_dc = output_old_gm[output_old_gm.find(init_dc) + len(init_dc):].split('\n') old_wm_dc = output_old_wm[output_old_wm.find(init_dc) + len(init_dc):].split('\n') new_gm_dc = output_new_gm[output_new_gm.find(init_dc) + len(init_dc):].split('\n') new_wm_dc = output_new_wm[output_new_wm.find(init_dc) + len(init_dc):].split('\n') for i in range(len(old_gm_dc)): if i not in no_ref_slices: i_new_gm, val_new_gm = new_gm_dc[i].split(' ') i_new_wm, val_new_wm = new_wm_dc[i].split(' ') i_old_gm, val_old_gm = old_gm_dc[i].split(' ') i_old_wm, val_old_wm = old_wm_dc[i].split(' ') assert i == int(i_new_gm) == int(i_new_wm) == int( i_old_gm ) == int( i_old_wm ), 'ERROR: when comparing Dice coefficients, slice numbers differs.' dice_fic.write( str(i) + ', ' + val_new_wm + ', ' + str(float(val_new_wm) - float(val_old_wm)) + ', ' + val_new_gm + ', ' + str(float(val_new_gm) - float(val_old_gm)) + '\n') else: dice_fic.write(str(i) + ', NO MANUAL SEGMENTATION\n') dice_fic.close() os.chdir('..') sct.generate_output_file(tmp_dir + hd_name, self.param.output_folder + hd_name) sct.generate_output_file(tmp_dir + dice_name, self.param.output_folder + dice_name) if self.param.remove_tmp: sct.run('rm -rf ' + tmp_dir, error_exit='warning')
def main(): # Initialization fname_anat = '' fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) remove_temp_files = param.remove_temp_files verbose = param.verbose start_time = time.time() # Check input param try: opts, args = getopt.getopt(sys.argv[1:], 'hi:c:r:s:v:') except getopt.GetoptError as err: print str(err) usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-c'): fname_centerline = arg elif opt in ('-i'): fname_anat = arg elif opt in ('-r'): remove_temp_files = arg elif opt in ('-s'): sigma = arg elif opt in ('-v'): verbose = int(arg) # Display usage if a mandatory argument is not provided if fname_anat == '' or fname_centerline == '': usage() # Display arguments print '\nCheck input arguments...' print ' Volume to smooth .................. ' + fname_anat print ' Centerline ........................ ' + fname_centerline print ' FWHM .............................. '+str(sigma) print ' Verbose ........................... '+str(verbose) # Check existence of input files print('\nCheck existence of input files...') sct.check_file_exist(fname_anat) sct.check_file_exist(fname_centerline) # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname(fname_centerline) # create temporary folder print('\nCreate temporary folder...') path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.run('mkdir '+path_tmp) # copy files to temporary folder print('\nCopy files...') sct.run('c3d '+fname_anat+' -o '+path_tmp+'/anat.nii') sct.run('c3d '+fname_centerline+' -o '+path_tmp+'/centerline.nii') # go to tmp folder os.chdir(path_tmp) # Change orientation of the input image into RPI print '\nOrient input volume to RPI orientation...' sct.run('sct_orientation -i anat.nii -o anat_rpi.nii -orientation RPI') # Change orientation of the input image into RPI print '\nOrient centerline to RPI orientation...' sct.run('sct_orientation -i centerline.nii -o centerline_rpi.nii -orientation RPI') # Straighten the spinal cord print '\nStraighten the spinal cord...' sct.run('sct_straighten_spinalcord.py -i anat_rpi.nii -c centerline_rpi.nii -w spline -v '+str(verbose)) # Smooth the straightened image along z print '\nSmooth the straightened image along z...' sct.run('c3d anat_rpi_straight.nii -smooth 0x0x'+str(sigma)+'vox -o anat_rpi_straight_smooth.nii') # Apply the reversed warping field to get back the curved spinal cord print '\nApply the reversed warping field to get back the curved spinal cord (assuming a 3D image)...' sct.run('WarpImageMultiTransform 3 anat_rpi_straight_smooth.nii anat_rpi_straight_smooth_curved.nii -R anat.nii --use-BSpline warp_straight2curve.nii.gz') # come back to parent folder os.chdir('..') # Generate output file print '\nGenerate output file...' sct.generate_output_file(path_tmp+'/anat_rpi_straight_smooth_curved.nii','',file_anat+'_smooth',ext_anat) # Remove temporary files if remove_temp_files == 1: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp) #Display elapsed time elapsed_time = time.time() - start_time print '\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s\n' # to view results print 'To view results, type:' print 'fslview '+file_anat+' '+file_anat+'_smooth &\n'
def register(self): # accentuate separation WM/GM self.im_gm = thr_im(self.im_gm, 0.01, self.param.thr) self.im_wm = thr_im(self.im_wm, 0.01, self.param.thr) self.im_template_gm = thr_im(self.im_template_gm, 0.01, self.param.thr) self.im_template_wm = thr_im(self.im_template_wm, 0.01, self.param.thr) # create multilabel images: # copy GM images to keep header information im_automatic_ml = self.im_gm.copy() im_template_ml = self.im_template_gm.copy() # create multi-label segmentation with GM*200 + WM*100 (100 and 200 encoded in self.param.gap) im_automatic_ml.data = self.param.gap[ 1] * self.im_gm.data + self.param.gap[0] * self.im_wm.data im_template_ml.data = self.param.gap[ 1] * self.im_template_gm.data + self.param.gap[ 0] * self.im_template_wm.data # set new names fname_automatic_ml = 'multilabel_automatic_seg.nii.gz' fname_template_ml = 'multilabel_template_seg.nii.gz' im_automatic_ml.setFileName(fname_automatic_ml) im_template_ml.setFileName(fname_template_ml) # Create temporary folder and put files in it tmp_dir = sct.tmp_create() path_gm, file_gm, ext_gm = sct.extract_fname(fname_gm) path_warp_template2target, file_warp_template2target, ext_warp_template2target = sct.extract_fname( self.fname_warp_template2target) convert(fname_gm, tmp_dir + file_gm + ext_gm) convert(fname_warp_template, tmp_dir + file_warp_template2target + ext_warp_template2target, squeeze_data=0) if self.fname_warp_target2template is not None: path_warp_target2template, file_warp_target2template, ext_warp_target2template = sct.extract_fname( self.fname_warp_target2template) convert(self.fname_warp_target2template, tmp_dir + file_warp_target2template + ext_warp_target2template, squeeze_data=0) os.chdir(tmp_dir) # save images im_automatic_ml.save() im_template_ml.save() # apply template2image warping field if self.apply_warp_template == 1: fname_template_ml_new = sct.add_suffix(fname_template_ml, '_r') sct.run('sct_apply_transfo -i ' + fname_template_ml + ' -d ' + fname_automatic_ml + ' -w ' + file_warp_template2target + ext_warp_template2target + ' -o ' + fname_template_ml_new) fname_template_ml = fname_template_ml_new nx, ny, nz, nt, px, py, pz, pt = im_automatic_ml.dim size_mask = int(22.5 / px) fname_mask = 'square_mask.nii.gz' sct.run('sct_create_mask -i ' + fname_automatic_ml + ' -p centerline,' + fname_automatic_ml + ' -f box -size ' + str(size_mask) + ' -o ' + fname_mask) fname_automatic_ml, xi, xf, yi, yf, zi, zf = crop_im( fname_automatic_ml, fname_mask) fname_template_ml, xi, xf, yi, yf, zi, zf = crop_im( fname_template_ml, fname_mask) # fname_automatic_ml_smooth = sct.add_suffix(fname_automatic_ml, '_smooth') # sct.run('sct_maths -i '+fname_automatic_ml+' -smooth '+str(self.param.smooth)+','+str(self.param.smooth)+',0 -o '+fname_automatic_ml_smooth) # fname_automatic_ml = fname_automatic_ml_smooth path_automatic_ml, file_automatic_ml, ext_automatic_ml = sct.extract_fname( fname_automatic_ml) path_template_ml, file_template_ml, ext_template_ml = sct.extract_fname( fname_template_ml) # Register multilabel images together cmd_reg = 'sct_register_multimodal -i ' + fname_template_ml + ' -d ' + fname_automatic_ml + ' -param ' + self.param.param_reg if 'centermass' in self.param.param_reg: fname_template_ml_seg = sct.add_suffix(fname_template_ml, '_bin') sct.run('sct_maths -i ' + fname_template_ml + ' -bin 0 -o ' + fname_template_ml_seg) fname_automatic_ml_seg = sct.add_suffix(fname_automatic_ml, '_bin') # sct.run('sct_maths -i '+fname_automatic_ml+' -thr 50 -o '+fname_automatic_ml_seg) sct.run('sct_maths -i ' + fname_automatic_ml + ' -bin 50 -o ' + fname_automatic_ml_seg) cmd_reg += ' -iseg ' + fname_template_ml_seg + ' -dseg ' + fname_automatic_ml_seg sct.run(cmd_reg) fname_warp_multilabel_template2auto = 'warp_' + file_template_ml + '2' + file_automatic_ml + '.nii.gz' fname_warp_multilabel_auto2template = 'warp_' + file_automatic_ml + '2' + file_template_ml + '.nii.gz' self.fname_warp_template2gm = sct.extract_fname( self.fname_warp_template2target )[1] + '_reg_gm' + sct.extract_fname( self.fname_warp_template2target)[2] # fname_warp_multilabel_template2auto = pad_im(fname_warp_multilabel_template2auto, nx, ny, nz, xi, xf, yi, yf, zi, zf) # fname_warp_multilabel_auto2template = pad_im(fname_warp_multilabel_auto2template, nx, ny, nz, xi, xf, yi, yf, zi, zf) sct.run('sct_concat_transfo -w ' + file_warp_template2target + ext_warp_template2target + ',' + fname_warp_multilabel_template2auto + ' -d ' + file_gm + ext_gm + ' -o ' + self.fname_warp_template2gm) if self.fname_warp_target2template is not None: if self.fname_template_dest is None: path_script = os.path.dirname(__file__) path_sct = os.path.dirname(path_script) if self.template == 'MNI-Poly-AMU': self.fname_template_dest = path_sct + '/data/MNI-Poly-AMU/template/MNI-Poly-AMU_T2.nii.gz' elif self.template == 'PAM50': self.fname_template_dest = path_sct + '/data/PAM50/template/PAM50_t2.nii.gz' self.fname_warp_gm2template = sct.extract_fname( self.fname_warp_target2template )[1] + '_reg_gm' + sct.extract_fname( self.fname_warp_target2template)[2] sct.run('sct_concat_transfo -w ' + fname_warp_multilabel_auto2template + ',' + file_warp_target2template + ext_warp_target2template + ' -d ' + self.fname_template_dest + ' -o ' + self.fname_warp_gm2template) os.chdir('..') # sct.generate_output_file(tmp_dir+fname_warp_multilabel_template2auto, self.param.output_folder+'warp_template_multilabel2automatic_seg_multilabel.nii.gz') # sct.generate_output_file(tmp_dir+fname_warp_multilabel_auto2template, self.param.output_folder+'warp_automatic_seg_multilabel2template_multilabel.nii.gz') sct.generate_output_file( tmp_dir + self.fname_warp_template2gm, self.param.output_folder + self.fname_warp_template2gm) if self.fname_warp_target2template is not None: sct.generate_output_file( tmp_dir + self.fname_warp_gm2template, self.param.output_folder + self.fname_warp_gm2template) if self.param.qc: fname_grid_warped = visualize_warp( tmp_dir + fname_warp_multilabel_template2auto, rm_tmp=self.param.remove_tmp) path_grid_warped, file_grid_warped, ext_grid_warped = sct.extract_fname( fname_grid_warped) sct.generate_output_file( fname_grid_warped, self.param.output_folder + file_grid_warped + ext_grid_warped) if self.param.remove_tmp: sct.run('rm -rf ' + tmp_dir, error_exit='warning')
def create_mask(): fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI # parse argument for method method_type = param.process[0] # check method val if not method_type == 'center': method_val = param.process[1] # check existence of input files if method_type == 'centerline': sct.check_file_exist(method_val, param.verbose) # Extract path/file/extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) # Get output folder and file name if param.fname_out == '': param.fname_out = param.file_prefix+file_data+ext_data # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.tmp_create(param.verbose) # )sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) # sct.run('mkdir '+path_tmp, param.verbose) sct.printv('\nCheck orientation...', param.verbose) orientation_input = get_orientation(Image(param.fname_data)) sct.printv('.. '+orientation_input, param.verbose) reorient_coordinates = False # copy input data to tmp folder convert(param.fname_data, path_tmp+'data.nii') if method_type == 'centerline': convert(method_val, path_tmp+'centerline.nii.gz') if method_type == 'point': convert(method_val, path_tmp+'point.nii.gz') # go to tmp folder os.chdir(path_tmp) # reorient to RPI sct.printv('\nReorient to RPI...', param.verbose) # if not orientation_input == 'RPI': sct.run('sct_image -i data.nii -o data_RPI.nii -setorient RPI -v 0', verbose=False) if method_type == 'centerline': sct.run('sct_image -i centerline.nii.gz -o centerline_RPI.nii.gz -setorient RPI -v 0', verbose=False) if method_type == 'point': sct.run('sct_image -i point.nii.gz -o point_RPI.nii.gz -setorient RPI -v 0', verbose=False) # # if method_type == 'centerline': # orientation_centerline = get_orientation_3d(method_val, filename=True) # if not orientation_centerline == 'RPI': # sct.run('sct_image -i ' + method_val + ' -o ' + path_tmp + 'centerline.nii.gz' + ' -setorient RPI -v 0', verbose=False) # else: # convert(method_val, path_tmp+'centerline.nii.gz') # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image('data_RPI.nii').dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz)+ ' x ' + str(nt), param.verbose) # in case user input 4d data if nt != 1: sct.printv('WARNING in '+os.path.basename(__file__)+': Input image is 4d but output mask will 3D.', param.verbose, 'warning') # extract first volume to have 3d reference nii = Image('data_RPI.nii') data3d = nii.data[:,:,:,0] nii.data = data3d nii.save() if method_type == 'coord': # parse to get coordinate coord = map(int, method_val.split('x')) if method_type == 'point': # get file name fname_point = method_val # extract coordinate of point sct.printv('\nExtract coordinate of point...', param.verbose) # TODO: change this way to remove dependence to sct.run. ProcessLabels.display_voxel returns list of coordinates status, output = sct.run('sct_label_utils -i point_RPI.nii.gz -display', param.verbose) # parse to get coordinate coord = output[output.find('Position=')+10:-17].split(',') if method_type == 'center': # set coordinate at center of FOV coord = round(float(nx)/2), round(float(ny)/2) if method_type == 'centerline': # get name of centerline from user argument fname_centerline = 'centerline_RPI.nii.gz' else: # generate volume with line along Z at coordinates 'coord' sct.printv('\nCreate line...', param.verbose) fname_centerline = create_line('data_RPI.nii', coord, nz) # create mask sct.printv('\nCreate mask...', param.verbose) centerline = nibabel.load(fname_centerline) # open centerline hdr = centerline.get_header() # get header hdr.set_data_dtype('uint8') # set imagetype to uint8 spacing = hdr.structarr['pixdim'] data_centerline = centerline.get_data() # get centerline z_centerline_not_null = [iz for iz in range(0, nz, 1) if data_centerline[:, :, iz].any()] # get center of mass of the centerline cx = [0] * nz cy = [0] * nz for iz in range(0, nz, 1): if iz in z_centerline_not_null: cx[iz], cy[iz] = ndimage.measurements.center_of_mass(numpy.array(data_centerline[:, :, iz])) # create 2d masks file_mask = 'data_mask' for iz in range(nz): if iz not in z_centerline_not_null: # write an empty nifty volume img = nibabel.Nifti1Image(data_centerline[:, :, iz], None, hdr) nibabel.save(img, (file_mask + str(iz) + '.nii')) else: center = numpy.array([cx[iz], cy[iz]]) mask2d = create_mask2d(center, param.shape, param.size, nx, ny, even=param.even, spacing=spacing) # Write NIFTI volumes img = nibabel.Nifti1Image(mask2d, None, hdr) nibabel.save(img, (file_mask+str(iz)+'.nii')) # merge along Z # cmd = 'fslmerge -z mask ' # CHANGE THAT CAN IMPACT SPEED: # related to issue #755, we cannot open more than 256 files at one time. # to solve this issue, we do not open more than 100 files ''' im_list = [] im_temp = [] for iz in range(nz_not_null): if iz != 0 and iz % 100 == 0: im_temp.append(concat_data(im_list, 2)) im_list = [Image(file_mask + str(iz) + '.nii')] else: im_list.append(Image(file_mask+str(iz)+'.nii')) if im_temp: im_temp.append(concat_data(im_list, 2)) im_out = concat_data(im_temp, 2, no_expand=True) else: im_out = concat_data(im_list, 2) ''' fname_list = [file_mask + str(iz) + '.nii' for iz in range(nz)] im_out = concat_data(fname_list, dim=2) im_out.setFileName('mask_RPI.nii.gz') im_out.save() # reorient if necessary # if not orientation_input == 'RPI': sct.run('sct_image -i mask_RPI.nii.gz -o mask.nii.gz -setorient ' + orientation_input, param.verbose) # copy header input --> mask im_dat = Image('data.nii') im_mask = Image('mask.nii.gz') im_mask = copy_header(im_dat, im_mask) im_mask.save() # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp+'mask.nii.gz', param.fname_out) # Remove temporary files if param.remove_tmp_files == 1: sct.printv('\nRemove temporary files...', param.verbose) sct.run('rm -rf '+path_tmp, param.verbose, error_exit='warning') # to view results sct.printv('\nDone! To view results, type:', param.verbose) sct.printv('fslview '+param.fname_data+' '+param.fname_out+' -l Red -t 0.5 &', param.verbose, 'info') print
def main(): # Initialization fname_anat = '' fname_point = '' slice_gap = param.gap remove_tmp_files = param.remove_tmp_files gaussian_kernel = param.gaussian_kernel start_time = time.time() # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') path_sct = sct.slash_at_the_end(path_sct, 1) # Parameters for debug mode if param.debug == 1: sct.printv('\n*** WARNING: DEBUG MODE ON ***\n\t\t\tCurrent working directory: '+os.getcwd(), 'warning') status, path_sct_testing_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') fname_anat = path_sct_testing_data+'/t2/t2.nii.gz' fname_point = path_sct_testing_data+'/t2/t2_centerline_init.nii.gz' slice_gap = 5 else: # Check input param try: opts, args = getopt.getopt(sys.argv[1:],'hi:p:g:r:k:') except getopt.GetoptError as err: print str(err) usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_anat = arg elif opt in ('-p'): fname_point = arg elif opt in ('-g'): slice_gap = int(arg) elif opt in ('-r'): remove_tmp_files = int(arg) elif opt in ('-k'): gaussian_kernel = int(arg) # display usage if a mandatory argument is not provided if fname_anat == '' or fname_point == '': usage() # check existence of input files sct.check_file_exist(fname_anat) sct.check_file_exist(fname_point) # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_point, file_point, ext_point = sct.extract_fname(fname_point) # extract path of schedule file # TODO: include schedule file in sct # TODO: check existence of schedule file file_schedule = path_sct + param.schedule_file # Get input image orientation input_image_orientation = get_orientation(fname_anat) # Display arguments print '\nCheck input arguments...' print ' Anatomical image: '+fname_anat print ' Orientation: '+input_image_orientation print ' Point in spinal cord: '+fname_point print ' Slice gap: '+str(slice_gap) print ' Gaussian kernel: '+str(gaussian_kernel) print ' Degree of polynomial: '+str(param.deg_poly) # create temporary folder print('\nCreate temporary folder...') path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.create_folder(path_tmp) print '\nCopy input data...' sct.run('cp '+fname_anat+ ' '+path_tmp+'/tmp.anat'+ext_anat) sct.run('cp '+fname_point+ ' '+path_tmp+'/tmp.point'+ext_point) # go to temporary folder os.chdir(path_tmp) # convert to nii sct.run('fslchfiletype NIFTI tmp.anat') sct.run('fslchfiletype NIFTI tmp.point') # Reorient input anatomical volume into RL PA IS orientation print '\nReorient input volume to RL PA IS orientation...' #sct.run(sct.fsloutput + 'fslswapdim tmp.anat RL PA IS tmp.anat_orient') set_orientation('tmp.anat.nii', 'RPI', 'tmp.anat_orient.nii') # Reorient binary point into RL PA IS orientation print '\nReorient binary point into RL PA IS orientation...' sct.run(sct.fsloutput + 'fslswapdim tmp.point RL PA IS tmp.point_orient') set_orientation('tmp.point.nii', 'RPI', 'tmp.point_orient') # Get image dimensions print '\nGet image dimensions...' nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension('tmp.anat_orient') print '.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz) print '.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm' # Split input volume print '\nSplit input volume...' sct.run(sct.fsloutput + 'fslsplit tmp.anat_orient tmp.anat_orient_z -z') file_anat_split = ['tmp.anat_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] # Get the coordinates of the input point print '\nGet the coordinates of the input point...' file = nibabel.load('tmp.point_orient.nii') data = file.get_data() x_init, y_init, z_init = (data > 0).nonzero() x_init = x_init[0] y_init = y_init[0] z_init = z_init[0] print '('+str(x_init)+', '+str(y_init)+', '+str(z_init)+')' # Extract the slice corresponding to z=z_init print '\nExtract the slice corresponding to z='+str(z_init)+'...' file_point_split = ['tmp.point_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] sct.run(sct.fsloutput+'fslroi tmp.point_orient '+file_point_split[z_init]+' 0 -1 0 -1 '+str(z_init)+' 1') # Create gaussian mask from point print '\nCreate gaussian mask from point...' file_mask_split = ['tmp.mask_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] sct.run(sct.fsloutput+'fslmaths '+file_point_split[z_init]+' -s '+str(gaussian_kernel)+' '+file_mask_split[z_init]) # Obtain max value from mask print '\nFind maximum value from mask...' file = nibabel.load(file_mask_split[z_init]+'.nii') data = file.get_data() max_value_mask = numpy.max(data) print '..'+str(max_value_mask) # Normalize mask beween 0 and 1 print '\nNormalize mask beween 0 and 1...' sct.run(sct.fsloutput+'fslmaths '+file_mask_split[z_init]+' -div '+str(max_value_mask)+' '+file_mask_split[z_init]) ## Take the square of the mask #print '\nCalculate the square of the mask...' #sct.run(sct.fsloutput+'fslmaths '+file_mask_split[z_init]+' -mul '+file_mask_split[z_init]+' '+file_mask_split[z_init]) # initialize variables file_mat = ['tmp.mat_z'+str(z).zfill(4) for z in range(0,nz,1)] file_mat_inv = ['tmp.mat_inv_z'+str(z).zfill(4) for z in range(0,nz,1)] file_mat_inv_cumul = ['tmp.mat_inv_cumul_z'+str(z).zfill(4) for z in range(0,nz,1)] # create identity matrix for initial transformation matrix fid = open(file_mat_inv_cumul[z_init], 'w') fid.write('%i %i %i %i\n' %(1, 0, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # initialize centerline: give value corresponding to initial point x_centerline = [x_init] y_centerline = [y_init] z_centerline = [z_init] warning_count = 0 # go up (1), then down (2) in reference to the binary point for iUpDown in range(1, 3): if iUpDown == 1: # z increases slice_gap_signed = slice_gap elif iUpDown == 2: # z decreases slice_gap_signed = -slice_gap # reverse centerline (because values will be appended at the end) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # initialization before looping z_dest = z_init # point given by user z_src = z_dest + slice_gap_signed # continue looping if 0 < z < nz while 0 <= z_src and z_src <= nz-1: # print current z: print 'z='+str(z_src)+':' # estimate transformation sct.run(fsloutput+'flirt -in '+file_anat_split[z_src]+' -ref '+file_anat_split[z_dest]+' -schedule '+file_schedule+ ' -verbose 0 -omat '+file_mat[z_src]+' -cost normcorr -forcescaling -inweight '+file_mask_split[z_dest]+' -refweight '+file_mask_split[z_dest]) # display transfo status, output = sct.run('cat '+file_mat[z_src]) print output # check if transformation is bigger than 1.5x slice_gap tx = float(output.split()[3]) ty = float(output.split()[7]) norm_txy = numpy.linalg.norm([tx, ty],ord=2) if norm_txy > 1.5*slice_gap: print 'WARNING: Transformation is too large --> using previous one.' warning_count = warning_count + 1 # if previous transformation exists, replace current one with previous one if os.path.isfile(file_mat[z_dest]): sct.run('cp '+file_mat[z_dest]+' '+file_mat[z_src]) # estimate inverse transformation matrix sct.run('convert_xfm -omat '+file_mat_inv[z_src]+' -inverse '+file_mat[z_src]) # compute cumulative transformation sct.run('convert_xfm -omat '+file_mat_inv_cumul[z_src]+' -concat '+file_mat_inv[z_src]+' '+file_mat_inv_cumul[z_dest]) # apply inverse cumulative transformation to initial gaussian mask (to put it in src space) sct.run(fsloutput+'flirt -in '+file_mask_split[z_init]+' -ref '+file_mask_split[z_init]+' -applyxfm -init '+file_mat_inv_cumul[z_src]+' -out '+file_mask_split[z_src]) # open inverse cumulative transformation file and generate centerline fid = open(file_mat_inv_cumul[z_src]) mat = fid.read().split() x_centerline.append(x_init + float(mat[3])) y_centerline.append(y_init + float(mat[7])) z_centerline.append(z_src) #z_index = z_index+1 # define new z_dest (target slice) and new z_src (moving slice) z_dest = z_dest + slice_gap_signed z_src = z_src + slice_gap_signed # Reconstruct centerline # ==================================================================================================== # reverse back centerline (because it's been reversed once, so now all values are in the right order) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # fit centerline in the Z-X plane using polynomial function print '\nFit centerline in the Z-X plane using polynomial function...' coeffsx = numpy.polyfit(z_centerline, x_centerline, deg=param.deg_poly) polyx = numpy.poly1d(coeffsx) x_centerline_fit = numpy.polyval(polyx, z_centerline) # calculate RMSE rmse = numpy.linalg.norm(x_centerline_fit-x_centerline)/numpy.sqrt( len(x_centerline) ) # calculate max absolute error max_abs = numpy.max( numpy.abs(x_centerline_fit-x_centerline) ) print '.. RMSE (in mm): '+str(rmse*px) print '.. Maximum absolute error (in mm): '+str(max_abs*px) # fit centerline in the Z-Y plane using polynomial function print '\nFit centerline in the Z-Y plane using polynomial function...' coeffsy = numpy.polyfit(z_centerline, y_centerline, deg=param.deg_poly) polyy = numpy.poly1d(coeffsy) y_centerline_fit = numpy.polyval(polyy, z_centerline) # calculate RMSE rmse = numpy.linalg.norm(y_centerline_fit-y_centerline)/numpy.sqrt( len(y_centerline) ) # calculate max absolute error max_abs = numpy.max( numpy.abs(y_centerline_fit-y_centerline) ) print '.. RMSE (in mm): '+str(rmse*py) print '.. Maximum absolute error (in mm): '+str(max_abs*py) # display if param.debug == 1: import matplotlib.pyplot as plt plt.figure() plt.plot(z_centerline,x_centerline,'.',z_centerline,x_centerline_fit,'r') plt.legend(['Data','Polynomial Fit']) plt.title('Z-X plane polynomial interpolation') plt.show() plt.figure() plt.plot(z_centerline,y_centerline,'.',z_centerline,y_centerline_fit,'r') plt.legend(['Data','Polynomial Fit']) plt.title('Z-Y plane polynomial interpolation') plt.show() # generate full range z-values for centerline z_centerline_full = [iz for iz in range(0, nz, 1)] # calculate X and Y values for the full centerline x_centerline_fit_full = numpy.polyval(polyx, z_centerline_full) y_centerline_fit_full = numpy.polyval(polyy, z_centerline_full) # Generate fitted transformation matrices and write centerline coordinates in text file print '\nGenerate fitted transformation matrices and write centerline coordinates in text file...' file_mat_inv_cumul_fit = ['tmp.mat_inv_cumul_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] file_mat_cumul_fit = ['tmp.mat_cumul_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] fid_centerline = open('tmp.centerline_coordinates.txt', 'w') for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, x_centerline_fit_full[iz]-x_init) ) fid.write('%i %i %i %f\n' %(0, 1, 0, y_centerline_fit_full[iz]-y_init) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # compute forward cumulative fitted transformation matrix sct.run('convert_xfm -omat '+file_mat_cumul_fit[iz]+' -inverse '+file_mat_inv_cumul_fit[iz]) # write centerline coordinates in x, y, z format fid_centerline.write('%f %f %f\n' %(x_centerline_fit_full[iz], y_centerline_fit_full[iz], z_centerline_full[iz]) ) fid_centerline.close() # Prepare output data # ==================================================================================================== # write centerline as text file for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, x_centerline_fit_full[iz]-x_init) ) fid.write('%i %i %i %f\n' %(0, 1, 0, y_centerline_fit_full[iz]-y_init) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # write polynomial coefficients numpy.savetxt('tmp.centerline_polycoeffs_x.txt',coeffsx) numpy.savetxt('tmp.centerline_polycoeffs_y.txt',coeffsy) # apply transformations to data print '\nApply fitted transformation matrices...' file_anat_split_fit = ['tmp.anat_orient_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] file_mask_split_fit = ['tmp.mask_orient_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] file_point_split_fit = ['tmp.point_orient_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] for iz in range(0, nz, 1): # forward cumulative transformation to data sct.run(fsloutput+'flirt -in '+file_anat_split[iz]+' -ref '+file_anat_split[iz]+' -applyxfm -init '+file_mat_cumul_fit[iz]+' -out '+file_anat_split_fit[iz]) # inverse cumulative transformation to mask sct.run(fsloutput+'flirt -in '+file_mask_split[z_init]+' -ref '+file_mask_split[z_init]+' -applyxfm -init '+file_mat_inv_cumul_fit[iz]+' -out '+file_mask_split_fit[iz]) # inverse cumulative transformation to point sct.run(fsloutput+'flirt -in '+file_point_split[z_init]+' -ref '+file_point_split[z_init]+' -applyxfm -init '+file_mat_inv_cumul_fit[iz]+' -out '+file_point_split_fit[iz]+' -interp nearestneighbour') # Merge into 4D volume print '\nMerge into 4D volume...' sct.run(fsloutput+'fslmerge -z tmp.anat_orient_fit tmp.anat_orient_fit_z*') sct.run(fsloutput+'fslmerge -z tmp.mask_orient_fit tmp.mask_orient_fit_z*') sct.run(fsloutput+'fslmerge -z tmp.point_orient_fit tmp.point_orient_fit_z*') # Copy header geometry from input data print '\nCopy header geometry from input data...' sct.run(fsloutput+'fslcpgeom tmp.anat_orient.nii tmp.anat_orient_fit.nii ') sct.run(fsloutput+'fslcpgeom tmp.anat_orient.nii tmp.mask_orient_fit.nii ') sct.run(fsloutput+'fslcpgeom tmp.anat_orient.nii tmp.point_orient_fit.nii ') # Reorient outputs into the initial orientation of the input image print '\nReorient the centerline into the initial orientation of the input image...' set_orientation('tmp.point_orient_fit.nii', input_image_orientation, 'tmp.point_orient_fit.nii') set_orientation('tmp.mask_orient_fit.nii', input_image_orientation, 'tmp.mask_orient_fit.nii') # Generate output file (in current folder) print '\nGenerate output file (in current folder)...' os.chdir('..') # come back to parent folder #sct.generate_output_file('tmp.centerline_polycoeffs_x.txt','./','centerline_polycoeffs_x','.txt') #sct.generate_output_file('tmp.centerline_polycoeffs_y.txt','./','centerline_polycoeffs_y','.txt') #sct.generate_output_file('tmp.centerline_coordinates.txt','./','centerline_coordinates','.txt') #sct.generate_output_file('tmp.anat_orient.nii','./',file_anat+'_rpi',ext_anat) #sct.generate_output_file('tmp.anat_orient_fit.nii', file_anat+'_rpi_align'+ext_anat) #sct.generate_output_file('tmp.mask_orient_fit.nii', file_anat+'_mask'+ext_anat) fname_output_centerline = sct.generate_output_file(path_tmp+'/tmp.point_orient_fit.nii', file_anat+'_centerline'+ext_anat) # Delete temporary files if remove_tmp_files == 1: print '\nRemove temporary files...' sct.run('rm -rf '+path_tmp) # print number of warnings print '\nNumber of warnings: '+str(warning_count)+' (if >10, you should probably reduce the gap and/or increase the kernel size' # display elapsed time elapsed_time = time.time() - start_time print '\nFinished! \n\tGenerated file: '+fname_output_centerline+'\n\tElapsed time: '+str(int(round(elapsed_time)))+'s\n'
def main(args=None): if not args: args = sys.argv[1:] # initialize parameters param = Param() # call main function parser = get_parser() arguments = parser.parse(args) fname_data = arguments['-i'] fname_bvecs = arguments['-bvec'] average = arguments['-a'] verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level remove_temp_files = int(arguments['-r']) path_out = arguments['-ofolder'] if '-bval' in arguments: fname_bvals = arguments['-bval'] else: fname_bvals = '' if '-bvalmin' in arguments: param.bval_min = arguments['-bvalmin'] # Initialization start_time = time.time() # sct.printv(arguments) sct.printv('\nInput parameters:', verbose) sct.printv(' input file ............' + fname_data, verbose) sct.printv(' bvecs file ............' + fname_bvecs, verbose) sct.printv(' bvals file ............' + fname_bvals, verbose) sct.printv(' average ...............' + str(average), verbose) # Get full path fname_data = os.path.abspath(fname_data) fname_bvecs = os.path.abspath(fname_bvecs) if fname_bvals: fname_bvals = os.path.abspath(fname_bvals) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(fname_data) # create temporary folder path_tmp = sct.tmp_create(basename="dmri_separate", verbose=verbose) # copy files into tmp folder and convert to nifti sct.printv('\nCopy files into temporary folder...', verbose) ext = '.nii' dmri_name = 'dmri' b0_name = file_data + '_b0' b0_mean_name = b0_name + '_mean' dwi_name = file_data + '_dwi' dwi_mean_name = dwi_name + '_mean' if not convert(fname_data, os.path.join(path_tmp, dmri_name + ext)): sct.printv('ERROR in convert.', 1, 'error') sct.copy(fname_bvecs, os.path.join(path_tmp, "bvecs"), verbose=verbose) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # Get size of data im_dmri = Image(dmri_name + ext) sct.printv('\nGet dimensions data...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_dmri.dim sct.printv('.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), verbose) # Identify b=0 and DWI images sct.printv(fname_bvals) index_b0, index_dwi, nb_b0, nb_dwi = identify_b0(fname_bvecs, fname_bvals, param.bval_min, verbose) # Split into T dimension sct.printv('\nSplit along T dimension...', verbose) im_dmri_split_list = split_data(im_dmri, 3) for im_d in im_dmri_split_list: im_d.save() # Merge b=0 images sct.printv('\nMerge b=0...', verbose) from sct_image import concat_data l = [] for it in range(nb_b0): l.append(dmri_name + '_T' + str(index_b0[it]).zfill(4) + ext) im_out = concat_data(l, 3).save(b0_name + ext) # Average b=0 images if average: sct.printv('\nAverage b=0...', verbose) sct.run(['sct_maths', '-i', b0_name + ext, '-o', b0_mean_name + ext, '-mean', 't'], verbose) # Merge DWI l = [] for it in range(nb_dwi): l.append(dmri_name + '_T' + str(index_dwi[it]).zfill(4) + ext) im_out = concat_data(l, 3).save(dwi_name + ext) # Average DWI images if average: sct.printv('\nAverage DWI...', verbose) sct.run(['sct_maths', '-i', dwi_name + ext, '-o', dwi_mean_name + ext, '-mean', 't'], verbose) # come back os.chdir(curdir) # Generate output files fname_b0 = os.path.abspath(os.path.join(path_out, b0_name + ext_data)) fname_dwi = os.path.abspath(os.path.join(path_out, dwi_name + ext_data)) fname_b0_mean = os.path.abspath(os.path.join(path_out, b0_mean_name + ext_data)) fname_dwi_mean = os.path.abspath(os.path.join(path_out, dwi_mean_name + ext_data)) sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(os.path.join(path_tmp, b0_name + ext), fname_b0, verbose) sct.generate_output_file(os.path.join(path_tmp, dwi_name + ext), fname_dwi, verbose) if average: sct.generate_output_file(os.path.join(path_tmp, b0_mean_name + ext), fname_b0_mean, verbose) sct.generate_output_file(os.path.join(path_tmp, dwi_mean_name + ext), fname_dwi_mean, verbose) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', verbose) return fname_b0, fname_b0_mean, fname_dwi, fname_dwi_mean
def main(args=None): # initialization start_time = time.time() param = Param() # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_tmp_files = int(arguments['-r']) if '-v' in arguments: param.verbose = int(arguments['-v']) sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............' + param.fname_data, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir ' + path_tmp, param.verbose) # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, path_tmp + 'fmri.nii') # go to tmp folder os.chdir(path_tmp) # run moco fmri_moco(param) # come back to parent folder os.chdir('..') # Generate output files path_out = sct.slash_at_the_end(path_out, 1) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) if os.path.isfile(path_tmp + 'fmri' + param.suffix + '.nii'): sct.printv(path_tmp + 'fmri' + param.suffix + '.nii') sct.printv(path_out + file_data + param.suffix + ext_data) sct.generate_output_file(path_tmp + 'fmri' + param.suffix + '.nii', path_out + file_data + param.suffix + ext_data, param.verbose) sct.generate_output_file( path_tmp + 'fmri' + param.suffix + '_mean.nii', path_out + file_data + param.suffix + '_mean' + ext_data, param.verbose) # Delete temporary files if param.remove_tmp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.run('rm -rf ' + path_tmp, param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's', param.verbose) # To view results sct.printv('\nTo view results, type:', param.verbose) sct.printv( 'fslview -m ortho,ortho ' + param.path_out + file_data + param.suffix + ' ' + file_data + ' &\n', param.verbose, 'info')
def main(args=None): if args is None: args = sys.argv[1:] # initialize parameters param = Param() # Initialization fname_output = '' path_out = '' fname_src_seg = '' fname_dest_seg = '' fname_src_label = '' fname_dest_label = '' generate_warpinv = 1 start_time = time.time() # get path of the toolbox path_sct = os.environ.get("SCT_DIR", os.path.dirname(os.path.dirname(__file__))) # get default registration parameters # step1 = Paramreg(step='1', type='im', algo='syn', metric='MI', iter='5', shrink='1', smooth='0', gradStep='0.5') step0 = Paramreg( step='0', type='im', algo='syn', metric='MI', iter='0', shrink='1', smooth='0', gradStep='0.5', slicewise='0', dof='Tx_Ty_Tz_Rx_Ry_Rz') # only used to put src into dest space step1 = Paramreg(step='1', type='im') paramreg = ParamregMultiStep([step0, step1]) parser = get_parser(paramreg=paramreg) arguments = parser.parse(args) # get arguments fname_src = arguments['-i'] fname_dest = arguments['-d'] if '-iseg' in arguments: fname_src_seg = arguments['-iseg'] if '-dseg' in arguments: fname_dest_seg = arguments['-dseg'] if '-ilabel' in arguments: fname_src_label = arguments['-ilabel'] if '-dlabel' in arguments: fname_dest_label = arguments['-dlabel'] if '-o' in arguments: fname_output = arguments['-o'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-owarp' in arguments: fname_output_warp = arguments['-owarp'] else: fname_output_warp = '' if '-initwarp' in arguments: fname_initwarp = os.path.abspath(arguments['-initwarp']) else: fname_initwarp = '' if '-initwarpinv' in arguments: fname_initwarpinv = os.path.abspath(arguments['-initwarpinv']) else: fname_initwarpinv = '' if '-m' in arguments: fname_mask = arguments['-m'] else: fname_mask = '' padding = arguments['-z'] if "-param" in arguments: paramreg_user = arguments['-param'] # update registration parameters for paramStep in paramreg_user: paramreg.addStep(paramStep) path_qc = arguments.get("-qc", None) identity = int(arguments['-identity']) interp = arguments['-x'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) # sct.printv(arguments) sct.printv('\nInput parameters:') sct.printv(' Source .............. ' + fname_src) sct.printv(' Destination ......... ' + fname_dest) sct.printv(' Init transfo ........ ' + fname_initwarp) sct.printv(' Mask ................ ' + fname_mask) sct.printv(' Output name ......... ' + fname_output) # sct.printv(' Algorithm ........... '+paramreg.algo) # sct.printv(' Number of iterations '+paramreg.iter) # sct.printv(' Metric .............. '+paramreg.metric) sct.printv(' Remove temp files ... ' + str(remove_temp_files)) sct.printv(' Verbose ............. ' + str(verbose)) # update param param.verbose = verbose param.padding = padding param.fname_mask = fname_mask param.remove_temp_files = remove_temp_files # Get if input is 3D sct.printv('\nCheck if input data are 3D...', verbose) sct.check_if_3d(fname_src) sct.check_if_3d(fname_dest) # Check if user selected type=seg, but did not input segmentation data if 'paramreg_user' in locals(): if True in [ 'type=seg' in paramreg_user[i] for i in range(len(paramreg_user)) ]: if fname_src_seg == '' or fname_dest_seg == '': sct.printv( '\nERROR: if you select type=seg you must specify -iseg and -dseg flags.\n', 1, 'error') # Extract path, file and extension path_src, file_src, ext_src = sct.extract_fname(fname_src) path_dest, file_dest, ext_dest = sct.extract_fname(fname_dest) # check if source and destination images have the same name (related to issue #373) # If so, change names to avoid conflict of result files and warns the user suffix_src, suffix_dest = '_reg', '_reg' if file_src == file_dest: suffix_src, suffix_dest = '_src_reg', '_dest_reg' # define output folder and file name if fname_output == '': path_out = '' if not path_out else path_out # output in user's current directory file_out = file_src + suffix_src file_out_inv = file_dest + suffix_dest ext_out = ext_src else: path, file_out, ext_out = sct.extract_fname(fname_output) path_out = path if not path_out else path_out file_out_inv = file_out + '_inv' # create temporary folder path_tmp = sct.tmp_create() sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) Image(fname_src).save(os.path.join(path_tmp, "src.nii")) Image(fname_dest).save(os.path.join(path_tmp, "dest.nii")) if fname_src_seg: Image(fname_src_seg).save(os.path.join(path_tmp, "src_seg.nii")) if fname_dest_seg: Image(fname_dest_seg).save(os.path.join(path_tmp, "dest_seg.nii")) if fname_src_label: Image(fname_src_label).save(os.path.join(path_tmp, "src_label.nii")) Image(fname_dest_label).save(os.path.join(path_tmp, "dest_label.nii")) if fname_mask != '': Image(fname_mask).save(os.path.join(path_tmp, "mask.nii.gz")) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # reorient destination to RPI Image('dest.nii').change_orientation("RPI").save('dest_RPI.nii') if fname_dest_seg: Image('dest_seg.nii').change_orientation("RPI").save( 'dest_seg_RPI.nii') if fname_dest_label: Image('dest_label.nii').change_orientation("RPI").save( 'dest_label_RPI.nii') if identity: # overwrite paramreg and only do one identity transformation step0 = Paramreg(step='0', type='im', algo='syn', metric='MI', iter='0', shrink='1', smooth='0', gradStep='0.5') paramreg = ParamregMultiStep([step0]) # Put source into destination space using header (no estimation -- purely based on header) # TODO: Check if necessary to do that # TODO: use that as step=0 # sct.printv('\nPut source into destination space using header...', verbose) # sct.run('isct_antsRegistration -d 3 -t Translation[0] -m MI[dest_pad.nii,src.nii,1,16] -c 0 -f 1 -s 0 -o # [regAffine,src_regAffine.nii] -n BSpline[3]', verbose) # if segmentation, also do it for seg # initialize list of warping fields warp_forward = [] warp_inverse = [] # initial warping is specified, update list of warping fields and skip step=0 if fname_initwarp: sct.printv('\nSkip step=0 and replace with initial transformations: ', param.verbose) sct.printv(' ' + fname_initwarp, param.verbose) # sct.copy(fname_initwarp, 'warp_forward_0.nii.gz') warp_forward = [fname_initwarp] start_step = 1 if fname_initwarpinv: warp_inverse = [fname_initwarpinv] else: sct.printv( '\nWARNING: No initial inverse warping field was specified, therefore the inverse warping field ' 'will NOT be generated.', param.verbose, 'warning') generate_warpinv = 0 else: start_step = 0 # loop across registration steps for i_step in range(start_step, len(paramreg.steps)): sct.printv('\n--\nESTIMATE TRANSFORMATION FOR STEP #' + str(i_step), param.verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = 'src.nii' dest = 'dest_RPI.nii' interp_step = 'spline' elif paramreg.steps[str(i_step)].type == 'seg': src = 'src_seg.nii' dest = 'dest_seg_RPI.nii' interp_step = 'nn' elif paramreg.steps[str(i_step)].type == 'label': src = 'src_label.nii' dest = 'dest_label_RPI.nii' interp_step = 'nn' else: # src = dest = interp_step = None sct.printv('ERROR: Wrong image type.', 1, 'error') # if step>0, apply warp_forward_concat to the src image to be used if i_step > 0: sct.printv('\nApply transformation from previous step', param.verbose) sct.run([ 'sct_apply_transfo', '-i', src, '-d', dest, '-w', ','.join(warp_forward), '-o', sct.add_suffix(src, '_reg'), '-x', interp_step ], verbose) src = sct.add_suffix(src, '_reg') # register src --> dest warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.insert(0, warp_inverse_out) # Concatenate transformations sct.printv('\nConcatenate transformations...', verbose) sct.run([ 'sct_concat_transfo', '-w', ','.join(warp_forward), '-d', 'dest.nii', '-o', 'warp_src2dest.nii.gz' ], verbose) sct.run([ 'sct_concat_transfo', '-w', ','.join(warp_inverse), '-d', 'src.nii', '-o', 'warp_dest2src.nii.gz' ], verbose) # Apply warping field to src data sct.printv('\nApply transfo source --> dest...', verbose) sct.run([ 'sct_apply_transfo', '-i', 'src.nii', '-o', 'src_reg.nii', '-d', 'dest.nii', '-w', 'warp_src2dest.nii.gz', '-x', interp ], verbose) sct.printv('\nApply transfo dest --> source...', verbose) sct.run([ 'sct_apply_transfo', '-i', 'dest.nii', '-o', 'dest_reg.nii', '-d', 'src.nii', '-w', 'warp_dest2src.nii.gz', '-x', interp ], verbose) # come back os.chdir(curdir) # Generate output files sct.printv('\nGenerate output files...', verbose) # generate: src_reg fname_src2dest = sct.generate_output_file( os.path.join(path_tmp, "src_reg.nii"), os.path.join(path_out, file_out + ext_out), verbose) # generate: forward warping field if fname_output_warp == '': fname_output_warp = os.path.join( path_out, 'warp_' + file_src + '2' + file_dest + '.nii.gz') sct.generate_output_file(os.path.join(path_tmp, "warp_src2dest.nii.gz"), fname_output_warp, verbose) if generate_warpinv: # generate: dest_reg fname_dest2src = sct.generate_output_file( os.path.join(path_tmp, "dest_reg.nii"), os.path.join(path_out, file_out_inv + ext_dest), verbose) # generate: inverse warping field sct.generate_output_file( os.path.join(path_tmp, "warp_dest2src.nii.gz"), os.path.join(path_out, 'warp_' + file_dest + '2' + file_src + '.nii.gz'), verbose) # Delete temporary files if remove_temp_files: sct.printv('\nRemove temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', verbose) if path_qc is not None: if fname_dest_seg: generate_qc(fname_src2dest, fname_in2=fname_dest, fname_seg=fname_dest_seg, args=args, path_qc=os.path.abspath(path_qc), process='sct_register_multimodal') else: sct.printv( 'WARNING: Cannot generate QC because it requires destination segmentation.', 1, 'warning') if generate_warpinv: sct.display_viewer_syntax([fname_src, fname_dest2src], verbose=verbose) sct.display_viewer_syntax([fname_dest, fname_src2dest], verbose=verbose)
def main(args=None): # initializations param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) fname_data = arguments['-i'] fname_seg = arguments['-s'] if '-l' in arguments: fname_landmarks = arguments['-l'] label_type = 'body' elif '-ldisc' in arguments: fname_landmarks = arguments['-ldisc'] label_type = 'disc' else: sct.printv('ERROR: Labels should be provided.', 1, 'error') if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = '' param.path_qc = arguments.get("-qc", None) path_template = arguments['-t'] contrast_template = arguments['-c'] ref = arguments['-ref'] param.remove_temp_files = int(arguments.get('-r')) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level param.verbose = verbose # TODO: not clean, unify verbose or param.verbose in code, but not both param_centerline = ParamCenterline( algo_fitting=arguments['-centerline-algo'], smooth=arguments['-centerline-smooth']) # registration parameters if '-param' in arguments: # reset parameters but keep step=0 (might be overwritten if user specified step=0) paramreg = ParamregMultiStep([step0]) if ref == 'subject': paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz' # add user parameters for paramStep in arguments['-param']: paramreg.addStep(paramStep) else: paramreg = ParamregMultiStep([step0, step1, step2]) # if ref=subject, initialize registration using different affine parameters if ref == 'subject': paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz' # initialize other parameters zsubsample = param.zsubsample # retrieve template file names file_template_vertebral_labeling = get_file_label(os.path.join(path_template, 'template'), 'vertebral labeling') file_template = get_file_label(os.path.join(path_template, 'template'), contrast_template.upper() + '-weighted template') file_template_seg = get_file_label(os.path.join(path_template, 'template'), 'spinal cord') # start timer start_time = time.time() # get fname of the template + template objects fname_template = os.path.join(path_template, 'template', file_template) fname_template_vertebral_labeling = os.path.join(path_template, 'template', file_template_vertebral_labeling) fname_template_seg = os.path.join(path_template, 'template', file_template_seg) fname_template_disc_labeling = os.path.join(path_template, 'template', 'PAM50_label_disc.nii.gz') # check file existence # TODO: no need to do that! sct.printv('\nCheck template files...') sct.check_file_exist(fname_template, verbose) sct.check_file_exist(fname_template_vertebral_labeling, verbose) sct.check_file_exist(fname_template_seg, verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) # sct.printv(arguments) sct.printv('\nCheck parameters:', verbose) sct.printv(' Data: ' + fname_data, verbose) sct.printv(' Landmarks: ' + fname_landmarks, verbose) sct.printv(' Segmentation: ' + fname_seg, verbose) sct.printv(' Path template: ' + path_template, verbose) sct.printv(' Remove temp files: ' + str(param.remove_temp_files), verbose) # check input labels labels = check_labels(fname_landmarks, label_type=label_type) vertebral_alignment = False if len(labels) > 2 and label_type == 'disc': vertebral_alignment = True path_tmp = sct.tmp_create(basename="register_to_template", verbose=verbose) # set temporary file names ftmp_data = 'data.nii' ftmp_seg = 'seg.nii.gz' ftmp_label = 'label.nii.gz' ftmp_template = 'template.nii' ftmp_template_seg = 'template_seg.nii.gz' ftmp_template_label = 'template_label.nii.gz' # copy files to temporary folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) Image(fname_data).save(os.path.join(path_tmp, ftmp_data)) Image(fname_seg).save(os.path.join(path_tmp, ftmp_seg)) Image(fname_landmarks).save(os.path.join(path_tmp, ftmp_label)) Image(fname_template).save(os.path.join(path_tmp, ftmp_template)) Image(fname_template_seg).save(os.path.join(path_tmp, ftmp_template_seg)) Image(fname_template_vertebral_labeling).save(os.path.join(path_tmp, ftmp_template_label)) if label_type == 'disc': Image(fname_template_disc_labeling).save(os.path.join(path_tmp, ftmp_template_label)) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # Generate labels from template vertebral labeling if label_type == 'body': sct.printv('\nGenerate labels from template vertebral labeling', verbose) ftmp_template_label_, ftmp_template_label = ftmp_template_label, sct.add_suffix(ftmp_template_label, "_body") sct_label_utils.main(args=['-i', ftmp_template_label_, '-vert-body', '0', '-o', ftmp_template_label]) # check if provided labels are available in the template sct.printv('\nCheck if provided labels are available in the template', verbose) image_label_template = Image(ftmp_template_label) labels_template = image_label_template.getNonZeroCoordinates(sorting='value') if labels[-1].value > labels_template[-1].value: sct.printv('ERROR: Wrong landmarks input. Labels must have correspondence in template space. \nLabel max ' 'provided: ' + str(labels[-1].value) + '\nLabel max from template: ' + str(labels_template[-1].value), verbose, 'error') # if only one label is present, force affine transformation to be Tx,Ty,Tz only (no scaling) if len(labels) == 1: paramreg.steps['0'].dof = 'Tx_Ty_Tz' sct.printv('WARNING: Only one label is present. Forcing initial transformation to: ' + paramreg.steps['0'].dof, 1, 'warning') # Project labels onto the spinal cord centerline because later, an affine transformation is estimated between the # template's labels (centered in the cord) and the subject's labels (assumed to be centered in the cord). # If labels are not centered, mis-registration errors are observed (see issue #1826) ftmp_label = project_labels_on_spinalcord(ftmp_label, ftmp_seg, param_centerline) # binarize segmentation (in case it has values below 0 caused by manual editing) sct.printv('\nBinarize segmentation', verbose) ftmp_seg_, ftmp_seg = ftmp_seg, sct.add_suffix(ftmp_seg, "_bin") sct_maths.main(['-i', ftmp_seg_, '-bin', '0.5', '-o', ftmp_seg]) # Switch between modes: subject->template or template->subject if ref == 'template': # resample data to 1mm isotropic sct.printv('\nResample data to 1mm isotropic...', verbose) resample_file(ftmp_data, add_suffix(ftmp_data, '_1mm'), '1.0x1.0x1.0', 'mm', 'linear', verbose) ftmp_data = add_suffix(ftmp_data, '_1mm') resample_file(ftmp_seg, add_suffix(ftmp_seg, '_1mm'), '1.0x1.0x1.0', 'mm', 'linear', verbose) ftmp_seg = add_suffix(ftmp_seg, '_1mm') # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling # with nearest neighbour can make them disappear. resample_labels(ftmp_label, ftmp_data, add_suffix(ftmp_label, '_1mm')) ftmp_label = add_suffix(ftmp_label, '_1mm') # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) ftmp_data = Image(ftmp_data).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_seg = Image(ftmp_seg).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_label = Image(ftmp_label).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_crop') if vertebral_alignment: # cropping the segmentation based on the label coverage to ensure good registration with vertebral alignment # See https://github.com/neuropoly/spinalcordtoolbox/pull/1669 for details image_labels = Image(ftmp_label) coordinates_labels = image_labels.getNonZeroCoordinates(sorting='z') nx, ny, nz, nt, px, py, pz, pt = image_labels.dim offset_crop = 10.0 * pz # cropping the image 10 mm above and below the highest and lowest label cropping_slices = [coordinates_labels[0].z - offset_crop, coordinates_labels[-1].z + offset_crop] # make sure that the cropping slices do not extend outside of the slice range (issue #1811) if cropping_slices[0] < 0: cropping_slices[0] = 0 if cropping_slices[1] > nz: cropping_slices[1] = nz msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, np.int32(np.round(cropping_slices))),))).save(ftmp_seg) else: # if we do not align the vertebral levels, we crop the segmentation from top to bottom im_seg_rpi = Image(ftmp_seg_) bottom = 0 for data in msct_image.SlicerOneAxis(im_seg_rpi, "IS"): if (data != 0).any(): break bottom += 1 top = im_seg_rpi.data.shape[2] for data in msct_image.SlicerOneAxis(im_seg_rpi, "SI"): if (data != 0).any(): break top -= 1 msct_image.spatial_crop(im_seg_rpi, dict(((2, (bottom, top)),))).save(ftmp_seg) # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) fn_warp_curve2straight = os.path.join(curdir, "warp_curve2straight.nii.gz") fn_warp_straight2curve = os.path.join(curdir, "warp_straight2curve.nii.gz") fn_straight_ref = os.path.join(curdir, "straight_ref.nii.gz") cache_input_files=[ftmp_seg] if vertebral_alignment: cache_input_files += [ ftmp_template_seg, ftmp_label, ftmp_template_label, ] cache_sig = sct.cache_signature( input_files=cache_input_files, ) cachefile = os.path.join(curdir, "straightening.cache") if sct.cache_valid(cachefile, cache_sig) and os.path.isfile(fn_warp_curve2straight) and os.path.isfile(fn_warp_straight2curve) and os.path.isfile(fn_straight_ref): sct.printv('Reusing existing warping field which seems to be valid', verbose, 'warning') sct.copy(fn_warp_curve2straight, 'warp_curve2straight.nii.gz') sct.copy(fn_warp_straight2curve, 'warp_straight2curve.nii.gz') sct.copy(fn_straight_ref, 'straight_ref.nii.gz') # apply straightening sct_apply_transfo.main(args=[ '-i', ftmp_seg, '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', add_suffix(ftmp_seg, '_straight')]) else: from spinalcordtoolbox.straightening import SpinalCordStraightener sc_straight = SpinalCordStraightener(ftmp_seg, ftmp_seg) sc_straight.param_centerline = param_centerline sc_straight.output_filename = add_suffix(ftmp_seg, '_straight') sc_straight.path_output = './' sc_straight.qc = '0' sc_straight.remove_temp_files = param.remove_temp_files sc_straight.verbose = verbose if vertebral_alignment: sc_straight.centerline_reference_filename = ftmp_template_seg sc_straight.use_straight_reference = True sc_straight.discs_input_filename = ftmp_label sc_straight.discs_ref_filename = ftmp_template_label sc_straight.straighten() sct.cache_save(cachefile, cache_sig) # N.B. DO NOT UPDATE VARIABLE ftmp_seg BECAUSE TEMPORARY USED LATER # re-define warping field using non-cropped space (to avoid issue #367) sct_concat_transfo.main(args=[ '-w', 'warp_straight2curve.nii.gz', '-d', ftmp_data, '-o', 'warp_straight2curve.nii.gz']) if vertebral_alignment: sct.copy('warp_curve2straight.nii.gz', 'warp_curve2straightAffine.nii.gz') else: # Label preparation: # -------------------------------------------------------------------------------- # Remove unused label on template. Keep only label present in the input label image sct.printv('\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run(['sct_label_utils', '-i', ftmp_template_label, '-o', ftmp_template_label, '-remove-reference', ftmp_label]) # Dilating the input label so they can be straighten without losing them sct.printv('\nDilating input labels using 3vox ball radius') sct_maths.main(['-i', ftmp_label, '-dilate', '3', '-o', add_suffix(ftmp_label, '_dilate')]) ftmp_label = add_suffix(ftmp_label, '_dilate') # Apply straightening to labels sct.printv('\nApply straightening to labels...', verbose) sct_apply_transfo.main(args=[ '-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) try: register_landmarks(ftmp_label, ftmp_template_label, paramreg.steps['0'].dof, fname_affine='straight2templateAffine.txt', verbose=verbose) except RuntimeError: raise('Input labels do not seem to be at the right place. Please check the position of the labels. ' 'See documentation for more details: https://www.slideshare.net/neuropoly/sct-course-20190121/42') # Concatenate transformations: curve --> straight --> affine sct.printv('\nConcatenate transformations: curve --> straight --> affine...', verbose) sct_concat_transfo.main(args=[ '-w', ['warp_curve2straight.nii.gz', 'straight2templateAffine.txt'], '-d', 'template.nii', '-o', 'warp_curve2straightAffine.nii.gz']) # Apply transformation sct.printv('\nApply transformation...', verbose) sct_apply_transfo.main(args=[ '-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_apply_transfo.main(args=[ '-i', ftmp_seg, '-o', add_suffix(ftmp_seg, '_straightAffine'), '-d', ftmp_template, '-w', 'warp_curve2straightAffine.nii.gz', '-x', 'linear']) ftmp_seg = add_suffix(ftmp_seg, '_straightAffine') """ # Benjamin: Issue from Allan Martin, about the z=0 slice that is screwed up, caused by the affine transform. # Solution found: remove slices below and above landmarks to avoid rotation effects points_straight = [] for coord in landmark_template: points_straight.append(coord.z) min_point, max_point = int(np.round(np.min(points_straight))), int(np.round(np.max(points_straight))) ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_black') msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, (min_point,max_point)),))).save(ftmp_seg) """ # open segmentation im = Image(ftmp_seg) im_new = msct_image.empty_like(im) # binarize im_new.data = im.data > 0.5 # find min-max of anat2template (for subsequent cropping) zmin_template, zmax_template = msct_image.find_zmin_zmax(im_new, threshold=0.5) # save binarized segmentation im_new.save(add_suffix(ftmp_seg, '_bin')) # unused? # crop template in z-direction (for faster processing) # TODO: refactor to use python module instead of doing i/o sct.printv('\nCrop data in template space (for faster processing)...', verbose) ftmp_template_, ftmp_template = ftmp_template, add_suffix(ftmp_template, '_crop') msct_image.spatial_crop(Image(ftmp_template_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_template) ftmp_template_seg_, ftmp_template_seg = ftmp_template_seg, add_suffix(ftmp_template_seg, '_crop') msct_image.spatial_crop(Image(ftmp_template_seg_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_template_seg) ftmp_data_, ftmp_data = ftmp_data, add_suffix(ftmp_data, '_crop') msct_image.spatial_crop(Image(ftmp_data_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_data) ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_crop') msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_seg) # sub-sample in z-direction # TODO: refactor to use python module instead of doing i/o sct.printv('\nSub-sample in z-direction (for faster processing)...', verbose) sct.run(['sct_resample', '-i', ftmp_template, '-o', add_suffix(ftmp_template, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_template = add_suffix(ftmp_template, '_sub') sct.run(['sct_resample', '-i', ftmp_template_seg, '-o', add_suffix(ftmp_template_seg, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_template_seg = add_suffix(ftmp_template_seg, '_sub') sct.run(['sct_resample', '-i', ftmp_data, '-o', add_suffix(ftmp_data, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_data = add_suffix(ftmp_data, '_sub') sct.run(['sct_resample', '-i', ftmp_seg, '-o', add_suffix(ftmp_seg, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_seg = add_suffix(ftmp_seg, '_sub') # Registration straight spinal cord to template sct.printv('\nRegister straight spinal cord to template...', verbose) # loop across registration steps warp_forward = [] warp_inverse = [] for i_step in range(1, len(paramreg.steps)): sct.printv('\nEstimate transformation for step #' + str(i_step) + '...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = ftmp_data dest = ftmp_template interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = ftmp_seg dest = ftmp_template_seg interp_step = 'nn' else: sct.printv('ERROR: Wrong image type.', 1, 'error') if paramreg.steps[str(i_step)].algo == 'centermassrot' and paramreg.steps[str(i_step)].rot_method == 'hog': src_seg = ftmp_seg dest_seg = ftmp_template_seg # if step>1, apply warp_forward_concat to the src image to be used if i_step > 1: # apply transformation from previous step, to use as new src for registration sct_apply_transfo.main(args=[ '-i', src, '-d', dest, '-w', warp_forward, '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step]) src = add_suffix(src, '_regStep' + str(i_step - 1)) if paramreg.steps[str(i_step)].algo == 'centermassrot' and paramreg.steps[str(i_step)].rot_method == 'hog': # also apply transformation to the seg sct_apply_transfo.main(args=[ '-i', src_seg, '-d', dest_seg, '-w', warp_forward, '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step]) src_seg = add_suffix(src_seg, '_regStep' + str(i_step - 1)) # register src --> dest # TODO: display param for debugging if paramreg.steps[str(i_step)].algo == 'centermassrot' and paramreg.steps[str(i_step)].rot_method == 'hog': # im_seg case warp_forward_out, warp_inverse_out = register([src, src_seg], [dest, dest_seg], paramreg, param, str(i_step)) else: 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: anat --> template sct.printv('\nConcatenate transformations: anat --> template...', verbose) warp_forward.insert(0, 'warp_curve2straightAffine.nii.gz') sct_concat_transfo.main(args=[ '-w', warp_forward, '-d', 'template.nii', '-o', 'warp_anat2template.nii.gz']) # Concatenate transformations: template --> anat sct.printv('\nConcatenate transformations: template --> anat...', verbose) warp_inverse.reverse() if vertebral_alignment: warp_inverse.append('warp_straight2curve.nii.gz') sct_concat_transfo.main(args=[ '-w', warp_inverse, '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz']) else: warp_inverse.append('straight2templateAffine.txt') warp_inverse.append('warp_straight2curve.nii.gz') sct_concat_transfo.main(args=[ '-w', warp_inverse, '-winv', ['straight2templateAffine.txt'], '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz']) # register template->subject elif ref == 'subject': # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) ftmp_data = Image(ftmp_data).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_seg = Image(ftmp_seg).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_label = Image(ftmp_label).change_orientation("RPI", generate_path=True).save().absolutepath # Remove unused label on template. Keep only label present in the input label image sct.printv('\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run(['sct_label_utils', '-i', ftmp_template_label, '-o', ftmp_template_label, '-remove-reference', ftmp_label]) # Add one label because at least 3 orthogonal labels are required to estimate an affine transformation. This # new label is added at the level of the upper most label (lowest value), at 1cm to the right. for i_file in [ftmp_label, ftmp_template_label]: im_label = Image(i_file) coord_label = im_label.getCoordinatesAveragedByValue() # N.B. landmarks are sorted by value # Create new label from copy import deepcopy new_label = deepcopy(coord_label[0]) # move it 5mm to the left (orientation is RAS) nx, ny, nz, nt, px, py, pz, pt = im_label.dim new_label.x = np.round(coord_label[0].x + 5.0 / px) # assign value 99 new_label.value = 99 # Add to existing image im_label.data[int(new_label.x), int(new_label.y), int(new_label.z)] = new_label.value # Overwrite label file # im_label.absolutepath = 'label_rpi_modif.nii.gz' im_label.save() # Bring template to subject space using landmark-based transformation sct.printv('\nEstimate transformation for step #0...', verbose) warp_forward = ['template2subjectAffine.txt'] warp_inverse = ['template2subjectAffine.txt'] try: register_landmarks(ftmp_template_label, ftmp_label, paramreg.steps['0'].dof, fname_affine=warp_forward[0], verbose=verbose, path_qc="./") except Exception: sct.printv('ERROR: input labels do not seem to be at the right place. Please check the position of the labels. See documentation for more details: https://www.slideshare.net/neuropoly/sct-course-20190121/42', 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_apply_transfo.main(args=[ '-i', src, '-d', dest, '-w', warp_forward, '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step]) 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_concat_transfo.main(args=[ '-w', warp_forward, '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz']) sct.printv('\nConcatenate transformations: subject --> template...', verbose) sct_concat_transfo.main(args=[ '-w', warp_inverse, '-winv', ['template2subjectAffine.txt'], '-d', 'template.nii', '-o', 'warp_anat2template.nii.gz']) # Apply warping fields to anat and template sct.run(['sct_apply_transfo', '-i', 'template.nii', '-o', 'template2anat.nii.gz', '-d', 'data.nii', '-w', 'warp_template2anat.nii.gz', '-crop', '1'], verbose) sct.run(['sct_apply_transfo', '-i', 'data.nii', '-o', 'anat2template.nii.gz', '-d', 'template.nii', '-w', 'warp_anat2template.nii.gz', '-crop', '1'], verbose) # come back os.chdir(curdir) # Generate output files sct.printv('\nGenerate output files...', verbose) fname_template2anat = os.path.join(path_output, 'template2anat' + ext_data) fname_anat2template = os.path.join(path_output, 'anat2template' + ext_data) sct.generate_output_file(os.path.join(path_tmp, "warp_template2anat.nii.gz"), os.path.join(path_output, "warp_template2anat.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "warp_anat2template.nii.gz"), os.path.join(path_output, "warp_anat2template.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "template2anat.nii.gz"), fname_template2anat, verbose) sct.generate_output_file(os.path.join(path_tmp, "anat2template.nii.gz"), fname_anat2template, verbose) if ref == 'template': # copy straightening files in case subsequent SCT functions need them sct.generate_output_file(os.path.join(path_tmp, "warp_curve2straight.nii.gz"), os.path.join(path_output, "warp_curve2straight.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "warp_straight2curve.nii.gz"), os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "straight_ref.nii.gz"), os.path.join(path_output, "straight_ref.nii.gz"), verbose) # Delete temporary files if param.remove_temp_files: sct.printv('\nDelete temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', verbose) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) if param.path_qc is not None: generate_qc(fname_data, fname_in2=fname_template2anat, fname_seg=fname_seg, args=args, path_qc=os.path.abspath(param.path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_register_to_template') sct.display_viewer_syntax([fname_data, fname_template2anat], verbose=verbose) sct.display_viewer_syntax([fname_template, fname_anat2template], verbose=verbose)
def main(): # Initialization fname_anat = '' fname_centerline = '' centerline_fitting = 'polynome' remove_temp_files = param.remove_temp_files interp = param.interp degree_poly = param.deg_poly # extract path of the script path_script = os.path.dirname(__file__)+'/' # Parameters for debug mode if param.debug == 1: print '\n*** WARNING: DEBUG MODE ON ***\n' status, path_sct_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') fname_anat = path_sct_data+'/t2/t2.nii.gz' fname_centerline = path_sct_data+'/t2/t2_seg.nii.gz' else: # Check input param try: opts, args = getopt.getopt(sys.argv[1:],'hi:c:r:d:f:s:') except getopt.GetoptError as err: print str(err) usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_anat = arg elif opt in ('-c'): fname_centerline = arg elif opt in ('-r'): remove_temp_files = int(arg) elif opt in ('-d'): degree_poly = int(arg) elif opt in ('-f'): centerline_fitting = str(arg) elif opt in ('-s'): interp = str(arg) # display usage if a mandatory argument is not provided if fname_anat == '' or fname_centerline == '': usage() # check existence of input files sct.check_file_exist(fname_anat) sct.check_file_exist(fname_centerline) # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) # Display arguments print '\nCheck input arguments...' print ' Input volume ...................... '+fname_anat print ' Centerline ........................ '+fname_centerline print '' # Get input image orientation input_image_orientation = get_orientation(fname_anat) # Reorient input data into RL PA IS orientation set_orientation(fname_anat, 'RPI', 'tmp.anat_orient.nii') set_orientation(fname_centerline, 'RPI', 'tmp.centerline_orient.nii') # Open centerline #========================================================================================== print '\nGet dimensions of input centerline...' nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension('tmp.centerline_orient.nii') print '.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz) print '.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm' print '\nOpen centerline volume...' file = nibabel.load('tmp.centerline_orient.nii') data = file.get_data() X, Y, Z = (data>0).nonzero() min_z_index, max_z_index = min(Z), max(Z) # loop across z and associate x,y coordinate with the point having maximum intensity x_centerline = [0 for iz in range(min_z_index, max_z_index+1, 1)] y_centerline = [0 for iz in range(min_z_index, max_z_index+1, 1)] z_centerline = [iz for iz in range(min_z_index, max_z_index+1, 1)] # Two possible scenario: # 1. the centerline is probabilistic: each slices contains voxels with the probability of containing the centerline [0:...:1] # We only take the maximum value of the image to aproximate the centerline. # 2. The centerline/segmentation image contains many pixels per slice with values {0,1}. # We take all the points and approximate the centerline on all these points. X, Y, Z = ((data<1)*(data>0)).nonzero() # X is empty if binary image if (len(X) > 0): # Scenario 1 for iz in range(min_z_index, max_z_index+1, 1): x_centerline[iz-min_z_index], y_centerline[iz-min_z_index] = numpy.unravel_index(data[:,:,iz].argmax(), data[:,:,iz].shape) else: # Scenario 2 for iz in range(min_z_index, max_z_index+1, 1): x_seg, y_seg = (data[:,:,iz]>0).nonzero() if len(x_seg) > 0: x_centerline[iz-min_z_index] = numpy.mean(x_seg) y_centerline[iz-min_z_index] = numpy.mean(y_seg) # TODO: find a way to do the previous loop with this, which is more neat: # [numpy.unravel_index(data[:,:,iz].argmax(), data[:,:,iz].shape) for iz in range(0,nz,1)] # clear variable del data # Fit the centerline points with the kind of curve given as argument of the script and return the new smoothed coordinates if centerline_fitting == 'splines': try: x_centerline_fit, y_centerline_fit = b_spline_centerline(x_centerline,y_centerline,z_centerline) except ValueError: print "splines fitting doesn't work, trying with polynomial fitting...\n" x_centerline_fit, y_centerline_fit = polynome_centerline(x_centerline,y_centerline,z_centerline) elif centerline_fitting == 'polynome': x_centerline_fit, y_centerline_fit = polynome_centerline(x_centerline,y_centerline,z_centerline) #========================================================================================== # Split input volume print '\nSplit input volume...' sct.run(sct.fsloutput + 'fslsplit tmp.anat_orient.nii tmp.anat_z -z') file_anat_split = ['tmp.anat_z'+str(z).zfill(4) for z in range(0,nz,1)] # initialize variables file_mat_inv_cumul = ['tmp.mat_inv_cumul_z'+str(z).zfill(4) for z in range(0,nz,1)] z_init = min_z_index displacement_max_z_index = x_centerline_fit[z_init-min_z_index]-x_centerline_fit[max_z_index-min_z_index] # write centerline as text file print '\nGenerate fitted transformation matrices...' file_mat_inv_cumul_fit = ['tmp.mat_inv_cumul_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] for iz in range(min_z_index, max_z_index+1, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') if (x_centerline[iz-min_z_index] == 0 and y_centerline[iz-min_z_index] == 0): displacement = 0 else: displacement = x_centerline_fit[z_init-min_z_index]-x_centerline_fit[iz-min_z_index] fid.write('%i %i %i %f\n' %(1, 0, 0, displacement) ) fid.write('%i %i %i %f\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # we complete the displacement matrix in z direction for iz in range(0, min_z_index, 1): fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, 0) ) fid.write('%i %i %i %f\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() for iz in range(max_z_index+1, nz, 1): fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, displacement_max_z_index) ) fid.write('%i %i %i %f\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # apply transformations to data print '\nApply fitted transformation matrices...' file_anat_split_fit = ['tmp.anat_orient_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] for iz in range(0, nz, 1): # forward cumulative transformation to data sct.run(fsloutput+'flirt -in '+file_anat_split[iz]+' -ref '+file_anat_split[iz]+' -applyxfm -init '+file_mat_inv_cumul_fit[iz]+' -out '+file_anat_split_fit[iz]+' -interp '+interp) # Merge into 4D volume print '\nMerge into 4D volume...' sct.run(fsloutput+'fslmerge -z tmp.anat_orient_fit tmp.anat_orient_fit_z*') # Reorient data as it was before print '\nReorient data back into native orientation...' set_orientation('tmp.anat_orient_fit.nii', input_image_orientation, 'tmp.anat_orient_fit_reorient.nii') # Generate output file (in current folder) print '\nGenerate output file (in current folder)...' sct.generate_output_file('tmp.anat_orient_fit_reorient.nii', file_anat+'_flatten'+ext_anat) # Delete temporary files if remove_temp_files == 1: print '\nDelete temporary files...' sct.run('rm -rf tmp.*') # to view results print '\nDone! To view results, type:' print 'fslview '+file_anat+ext_anat+' '+file_anat+'_flatten'+ext_anat+' &\n'
def main(args=None): """ Main function :param args: :return: """ # get parser args if args is None: args = None if sys.argv[1:] else ['--help'] else: # flatten the list of input arguments because -w and -winv carry a nested list lst = [] for line in args: lst.append(line) if isinstance(line, str) else lst.extend(line) args = lst parser = get_parser() arguments = parser.parse_args(args=args) # Initialization fname_warp_final = '' # concatenated transformations fname_dest = arguments.d fname_warp_list = arguments.w warpinv_filename = arguments.winv if arguments.o is not None: fname_warp_final = arguments.o verbose = arguments.v sct.init_sct(log_level=verbose, update=True) # Update log level # Parse list of warping fields sct.printv('\nParse list of warping fields...', verbose) use_inverse = [] fname_warp_list_invert = [] # list_warp = list_warp.replace(' ', '') # remove spaces # list_warp = list_warp.split(",") # parse with comma for idx_warp, path_warp in enumerate(fname_warp_list): # Check if this transformation should be inverted if path_warp in warpinv_filename: use_inverse.append('-i') # list_warp[idx_warp] = path_warp[1:] # remove '-' fname_warp_list_invert += [[ use_inverse[idx_warp], fname_warp_list[idx_warp] ]] else: use_inverse.append('') fname_warp_list_invert += [[path_warp]] path_warp = fname_warp_list[idx_warp] if path_warp.endswith((".nii", ".nii.gz")) \ and Image(fname_warp_list[idx_warp]).header.get_intent()[0] != 'vector': raise ValueError("Displacement field in {} is invalid: should be encoded" \ " in a 5D file with vector intent code" \ " (see https://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1.h" \ .format(path_warp)) # need to check if last warping field is an affine transfo isLastAffine = False path_fname, file_fname, ext_fname = sct.extract_fname( fname_warp_list_invert[-1][-1]) if ext_fname in ['.txt', '.mat']: isLastAffine = True # check if destination file is 3d if not sct.check_if_3d(fname_dest): sct.printv('ERROR: Destination data must be 3d') # Here we take the inverse of the warp list, because sct_WarpImageMultiTransform concatenates in the reverse order fname_warp_list_invert.reverse() fname_warp_list_invert = functools.reduce(lambda x, y: x + y, fname_warp_list_invert) # Check file existence sct.printv('\nCheck file existence...', verbose) sct.check_file_exist(fname_dest, verbose) for i in range(len(fname_warp_list)): sct.check_file_exist(fname_warp_list[i], verbose) # Get output folder and file name if fname_warp_final == '': path_out, file_out, ext_out = sct.extract_fname(param.fname_warp_final) else: path_out, file_out, ext_out = sct.extract_fname(fname_warp_final) # Check dimension of destination data (cf. issue #1419, #1429) im_dest = Image(fname_dest) if im_dest.dim[2] == 1: dimensionality = '2' else: dimensionality = '3' cmd = [ 'isct_ComposeMultiTransform', dimensionality, 'warp_final' + ext_out, '-R', fname_dest ] + fname_warp_list_invert status, output = sct.run(cmd, verbose=verbose, is_sct_binary=True) # check if output was generated if not os.path.isfile('warp_final' + ext_out): sct.printv('ERROR: Warping field was not generated.\n' + output, 1, 'error') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file('warp_final' + ext_out, os.path.join(path_out, file_out + ext_out))
def main(args=None): # Initialization param = Param() start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-param' in arguments: param.update(arguments['-param']) if '-r' in arguments: remove_temp_files = int(arguments['-r']) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level # Display arguments sct.printv('\nCheck input arguments...') sct.printv(' Volume to smooth .................. ' + fname_anat) sct.printv(' Centerline ........................ ' + fname_centerline) sct.printv(' Sigma (mm) ........................ ' + str(sigma)) sct.printv(' Verbose ........................... ' + str(verbose)) # Check that input is 3D: from spinalcordtoolbox.image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv('WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i ' + fname_anat + ' -split t -o ' + fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname(fname_centerline) path_tmp = sct.tmp_create(basename="smooth_spinalcord", verbose=verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.copy(fname_anat, os.path.join(path_tmp, "anat" + ext_anat)) sct.copy(fname_centerline, os.path.join(path_tmp, "centerline" + ext_centerline)) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # convert to nii format convert('anat' + ext_anat, 'anat.nii') convert('centerline' + ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI sct.printv('\nOrient input volume to RPI orientation...') fname_anat_rpi = msct_image.Image("anat.nii") \ .change_orientation("RPI", generate_path=True) \ .save() \ .absolutepath # Change orientation of the input image into RPI sct.printv('\nOrient centerline to RPI orientation...') fname_centerline_rpi = msct_image.Image("centerline.nii") \ .change_orientation("RPI", generate_path=True) \ .save() \ .absolutepath # Straighten the spinal cord # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) cache_sig = sct.cache_signature(input_files=[fname_anat_rpi, fname_centerline_rpi], input_params={"x": "spline"}) cachefile = os.path.join(curdir, "straightening.cache") if sct.cache_valid(cachefile, cache_sig) and os.path.isfile(os.path.join(curdir, 'warp_curve2straight.nii.gz')) and os.path.isfile(os.path.join(curdir, 'warp_straight2curve.nii.gz')) and os.path.isfile(os.path.join(curdir, 'straight_ref.nii.gz')): # if they exist, copy them into current folder sct.printv('Reusing existing warping field which seems to be valid', verbose, 'warning') sct.copy(os.path.join(curdir, 'warp_curve2straight.nii.gz'), 'warp_curve2straight.nii.gz') sct.copy(os.path.join(curdir, 'warp_straight2curve.nii.gz'), 'warp_straight2curve.nii.gz') sct.copy(os.path.join(curdir, 'straight_ref.nii.gz'), 'straight_ref.nii.gz') # apply straightening sct.run(['sct_apply_transfo', '-i', fname_anat_rpi, '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', 'anat_rpi_straight.nii', '-x', 'spline'], verbose) else: sct.run(['sct_straighten_spinalcord', '-i', fname_anat_rpi, '-o', 'anat_rpi_straight.nii', '-s', fname_centerline_rpi, '-x', 'spline', '-param', 'algo_fitting='+param.algo_fitting], verbose) sct.cache_save(cachefile, cache_sig) # move warping fields locally (to use caching next time) sct.copy('warp_curve2straight.nii.gz', os.path.join(curdir, 'warp_curve2straight.nii.gz')) sct.copy('warp_straight2curve.nii.gz', os.path.join(curdir, 'warp_straight2curve.nii.gz')) # Smooth the straightened image along z sct.printv('\nSmooth the straightened image...') sigma_smooth = ",".join([str(i) for i in sigma]) sct_maths.main(args=['-i', 'anat_rpi_straight.nii', '-smooth', sigma_smooth, '-o', 'anat_rpi_straight_smooth.nii', '-v', '0']) # Apply the reversed warping field to get back the curved spinal cord sct.printv('\nApply the reversed warping field to get back the curved spinal cord...') sct.run(['sct_apply_transfo', '-i', 'anat_rpi_straight_smooth.nii', '-o', 'anat_rpi_straight_smooth_curved.nii', '-d', 'anat.nii', '-w', 'warp_straight2curve.nii.gz', '-x', 'spline'], verbose) # replace zeroed voxels by original image (issue #937) sct.printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.save('anat_rpi_straight_smooth_curved_nonzero.nii') # come back os.chdir(curdir) # Generate output file sct.printv('\nGenerate output file...') sct.generate_output_file(os.path.join(path_tmp, "anat_rpi_straight_smooth_curved_nonzero.nii"), file_anat + '_smooth' + ext_anat) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...') sct.rmtree(path_tmp) # Display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's\n') sct.display_viewer_syntax([file_anat, file_anat + '_smooth'], verbose=verbose)
def main(args=None): # Initialization # fname_anat = '' # fname_centerline = '' sigma = 3 # default value of the standard deviation for the Gaussian smoothing (in terms of number of voxels) # remove_temp_files = param.remove_temp_files # verbose = param.verbose start_time = time.time() parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_anat = arguments['-i'] fname_centerline = arguments['-s'] if '-smooth' in arguments: sigma = arguments['-smooth'] if '-r' in arguments: remove_temp_files = int(arguments['-r']) if '-v' in arguments: verbose = int(arguments['-v']) # Display arguments sct.printv('\nCheck input arguments...') sct.printv(' Volume to smooth .................. ' + fname_anat) sct.printv(' Centerline ........................ ' + fname_centerline) sct.printv(' Sigma (mm) ........................ ' + str(sigma)) sct.printv(' Verbose ........................... ' + str(verbose)) # Check that input is 3D: from msct_image import Image nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: sct.printv( 'WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i ' + fname_anat + ' -split t -o ' + fname_anat, verbose, 'warning') sct.printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = sct.extract_fname( fname_centerline) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir ' + path_tmp, verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('cp ' + fname_anat + ' ' + path_tmp + 'anat' + ext_anat, verbose) sct.run( 'cp ' + fname_centerline + ' ' + path_tmp + 'centerline' + ext_centerline, verbose) # go to tmp folder os.chdir(path_tmp) # convert to nii format convert('anat' + ext_anat, 'anat.nii') convert('centerline' + ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI sct.printv('\nOrient input volume to RPI orientation...') fname_anat_rpi = set_orientation('anat.nii', 'RPI', filename=True) move(fname_anat_rpi, 'anat_rpi.nii') # Change orientation of the input image into RPI sct.printv('\nOrient centerline to RPI orientation...') fname_centerline_rpi = set_orientation('centerline.nii', 'RPI', filename=True) move(fname_centerline_rpi, 'centerline_rpi.nii') # Straighten the spinal cord # 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 anat_rpi.nii -w warp_curve2straight.nii.gz -d straight_ref.nii.gz -o anat_rpi_straight.nii -x spline', verbose) else: sct.run( 'sct_straighten_spinalcord -i anat_rpi.nii -s centerline_rpi.nii -qc 0 -x spline', verbose) # Smooth the straightened image along z sct.printv('\nSmooth the straightened image along z...') sct.run( 'sct_maths -i anat_rpi_straight.nii -smooth 0,0,' + str(sigma) + ' -o anat_rpi_straight_smooth.nii', verbose) # Apply the reversed warping field to get back the curved spinal cord sct.printv( '\nApply the reversed warping field to get back the curved spinal cord...' ) sct.run( 'sct_apply_transfo -i anat_rpi_straight_smooth.nii -o anat_rpi_straight_smooth_curved.nii -d anat.nii -w warp_straight2curve.nii.gz -x spline', verbose) # replace zeroed voxels by original image (issue #937) sct.printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.setFileName('anat_rpi_straight_smooth_curved_nonzero.nii') nii_smooth.save() # come back to parent folder os.chdir('..') # Generate output file sct.printv('\nGenerate output file...') sct.generate_output_file( path_tmp + '/anat_rpi_straight_smooth_curved_nonzero.nii', file_anat + '_smooth' + ext_anat) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...') sct.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\n') # to view results sct.printv('Done! To view results, type:', verbose) sct.printv('fslview ' + file_anat + ' ' + file_anat + '_smooth &\n', verbose, 'info')