def get_shape_features_3D(mask, metadata=None): mask = sitk.GetArrayFromImage(mask) # Pre-allocation perimeter = list() convexity = list() area = list() rad_dist_avg = list() rad_dist_std = list() roughness_avg = list() roughness_std = list() cvar = list() prax = list() evar = list() solidity = list() compactness = list() rad_dist = list() rad_dist_norm = list() roughness = list() # Now calculate some of the edge shape features # TODO: Adapt to allow for multiple slices at once labeledImage = label(mask, connectivity=3) for nblob in range(1, np.max(labeledImage)): # Iterate over all slices blobimage = np.zeros(mask.shape) blobimage[mask == nblob] = 1 blobimage = blobimage.astype(np.uint8) if np.sum(blobimage) <= 3: # Only 1 or 2 points in volume, which means it's not really a # volume, therefore we ignore it. continue blobimage = sitk.GetImageFromArray(blobimage) boundary_points = cf.get_smooth_contour(blobimage, N_min_smooth, N_max_smooth) if boundary_points.shape[0] <= 3: # Only 1 or 2 points in volume, which means it's not really a # volume, therefore we ignore it. continue rad_dist_i, rad_dist_norm_i = compute_radial_distance( boundary_points) rad_dist.append(rad_dist_i) rad_dist_norm.append(rad_dist_norm_i) perimeter.append(compute_perimeter(boundary_points)) area.append(compute_area(boundary_points)) compactness.append(compute_compactness(boundary_points)) roughness_i, roughness_avg_temp = compute_roughness( boundary_points, rad_dist_i) roughness_avg.append(roughness_avg_temp) roughness.append(roughness_i) cvar.append(compute_cvar(boundary_points)) prax.append(compute_prax(boundary_points)) evar.append(compute_evar(boundary_points)) # TODO: Move computing convexity into esf convex_hull = cf.convex_hull(blobimage) convexity.append(compute_perimeter(convex_hull) / perimeter[-1]) solidity.append(compute_area(convex_hull)/area[-1]) rad_dist_avg.append(np.mean(np.asarray(rad_dist_i))) rad_dist_std.append(np.std(np.asarray(rad_dist_i))) roughness_std.append(np.std(np.asarray(roughness_i))) compactness_avg = np.mean(compactness) compactness_std = np.std(compactness) compactness_std = np.std(compactness) compactness_std = np.std(compactness) convexity_avg = np.mean(convexity) convexity_std = np.std(convexity) rad_dist_avg = np.mean(rad_dist_avg) rad_dist_std = np.mean(rad_dist_std) roughness_avg = np.mean(roughness_avg) roughness_std = np.mean(roughness_std) cvar_avg = np.mean(cvar) cvar_std = np.std(cvar) prax_avg = np.mean(prax) prax_std = np.std(prax) evar_avg = np.mean(evar) evar_std = np.std(evar) solidity_avg = np.mean(solidity) solidity_std = np.std(solidity) shape_labels = ['sf_compactness_avg', 'sf_compactness_std', 'sf_rad_dist_avg', 'sf_rad_dist_std', 'sf_roughness_avg', 'sf_roughness_std', 'sf_convexity_avg', 'sf_convexity_std', 'sf_cvar_avg', 'sf_cvar_std', 'sf_prax_avg', 'sf_prax_std', 'sf_evar_avg', 'sf_evar_std', 'sf_solidity_avg', 'sf_solidity_std'] shape_features = [compactness_avg, compactness_std, rad_dist_avg, rad_dist_std, roughness_avg, roughness_std, convexity_avg, convexity_std, cvar_avg, cvar_std, prax_avg, prax_std, evar_avg, evar_std, solidity_avg, solidity_std] if metadata is not None: if (0x18, 0x50) in metadata.keys(): # import dicom as pydicom # metadata = pydicom.read_file(metadata) pixel_spacing = metadata[0x28, 0x30].value slice_thickness = int(metadata[0x18, 0x50].value) voxel_volume = pixel_spacing[0] * pixel_spacing[1] * slice_thickness volume = np.sum(mask) * voxel_volume shape_labels.append('sf_volume') shape_features.append(volume) return shape_features, shape_labels
def get_shape_features(mask, metadata=None): # CONSTANTS N_min_smooth = 10 N_max_smooth = 40 N_mask_slices = mask.GetSize()[2] # Pre-allocation perimeter = np.zeros([N_mask_slices, 1]) convexity = np.zeros([N_mask_slices, 1]) area = np.zeros([N_mask_slices, 1]) rad_dist_avg = np.zeros([N_mask_slices, 1]) rad_dist_std = np.zeros([N_mask_slices, 1]) roughness_avg = np.zeros([N_mask_slices, 1]) roughness_std = np.zeros([N_mask_slices, 1]) cvar = np.zeros([N_mask_slices, 1]) prax = np.zeros([N_mask_slices, 1]) evar = np.zeros([N_mask_slices, 1]) solidity = np.zeros([N_mask_slices, 1]) compactness = np.zeros([N_mask_slices, 1]) rad_dist = list() rad_dist_norm = list() roughness = list() # Now calculate some of the edge shape features # TODO: Adapt to allow for multiple slices at once for i_slice in range(0, N_mask_slices): if np.sum(mask[:, :, i_slice]) != 0: boundary_points = cf.get_smooth_contour(mask[:, :, i_slice], N_min_smooth, N_max_smooth) else: # No points in volume, therefore we ignore it. continue if boundary_points.shape[0] <= 3: # Only 1 or 2 points in volume, which means it's not really a # volume, therefore we ignore it. continue rad_dist_i, rad_dist_norm_i = compute_radial_distance( boundary_points) rad_dist.append(rad_dist_i) rad_dist_norm.append(rad_dist_norm_i) perimeter[i_slice] = compute_perimeter(boundary_points) area[i_slice] = compute_area(boundary_points) compactness[i_slice] = compute_compactness(boundary_points) roughness_i, roughness_avg[i_slice] = compute_roughness( boundary_points, rad_dist_i) roughness.append(roughness_i) cvar[i_slice] = compute_cvar(boundary_points) prax[i_slice] = compute_prax(boundary_points) evar[i_slice] = compute_evar(boundary_points) # TODO: Move computing convexity into esf convex_hull = cf.convex_hull(mask[:, :, i_slice]) convexity[i_slice] = compute_perimeter(convex_hull)\ / perimeter[i_slice] solidity[i_slice] = compute_area(convex_hull)/area[i_slice] rad_dist_avg[i_slice] = np.mean(np.asarray(rad_dist_i)) rad_dist_std[i_slice] = np.std(np.asarray(rad_dist_i)) roughness_std[i_slice] = np.std(np.asarray(roughness_i)) compactness_avg = np.mean(compactness) compactness_std = np.std(compactness) convexity_avg = np.mean(convexity) convexity_std = np.std(convexity) rad_dist_avg = np.mean(rad_dist_avg) rad_dist_std = np.mean(rad_dist_std) roughness_avg = np.mean(roughness_avg) roughness_std = np.mean(roughness_std) cvar_avg = np.mean(cvar) cvar_std = np.std(cvar) prax_avg = np.mean(prax) prax_std = np.std(prax) evar_avg = np.mean(evar) evar_std = np.std(evar) solidity_avg = np.mean(solidity) solidity_std = np.std(solidity) panda_labels = ['sf_compactness_avg', 'sf_compactness_std', 'sf_rad_dist_avg', 'sf_rad_dist_std', 'sf_roughness_avg', 'sf_roughness_std', 'sf_convexity_avg', 'sf_convexity_std', 'sf_cvar_avg', 'sf_cvar_std', 'sf_prax_avg', 'sf_prax_std', 'sf_evar_avg', 'sf_evar_std', 'sf_solidity_avg', 'sf_solidity_std'] shape_features = [compactness_avg, compactness_std, rad_dist_avg, rad_dist_std, roughness_avg, roughness_std, convexity_avg, convexity_std, cvar_avg, cvar_std, prax_avg, prax_std, evar_avg, evar_std, solidity_avg, solidity_std] if metadata is not None: # import dicom as pydicom # metadata = pydicom.read_file(metadata) pixel_spacing = metadata[0x28, 0x30].value slice_thickness = int(metadata[0x18, 0x50].value) voxel_volume = pixel_spacing[0] * pixel_spacing[1] * slice_thickness volume = np.sum(mask) * voxel_volume panda_labels.append('volume') shape_features.append(volume) pandas_dict = dict(zip(panda_labels, shape_features)) shape_dict = dict() shape_dict['all'] = pd.Series(pandas_dict) shape_features = pd.Series(shape_dict) return shape_features