def mlc_dd2dcm(mlc): mlc = np.array(mlc, copy=False) dicom_mlc_format = [] for control_point in mlc: concatenated = np.hstack( [-control_point[-1::-1, 1], control_point[-1::-1, 0]]) dicom_mlc_format.append(concatenated.astype(str).tolist()) return dicom_mlc_format
def pcolormesh_grid(x, y, grid_resolution=None): if grid_resolution is None: diffs = np.hstack([np.diff(x), np.diff(y)]) assert np.all(np.abs(diffs - diffs[0]) < 10 ** -12) grid_resolution = diffs[0] new_x = np.concatenate([x - grid_resolution / 2, [x[-1] + grid_resolution / 2]]) new_y = np.concatenate([y - grid_resolution / 2, [y[-1] + grid_resolution / 2]]) return new_x, new_y
def calculate_coordinates_shell_3d(distance, distance_step_size): """Create points along the surface of a sphere (a shell) where no gap between points is larger than the defined distance_step_size""" number_of_rows = np.ceil(np.pi * distance / distance_step_size).astype(int) + 1 elevation = np.linspace(0, np.pi, number_of_rows) row_radii = distance * np.sin(elevation) row_circumference = 2 * np.pi * row_radii amount_in_row = np.ceil(row_circumference / distance_step_size).astype(int) + 1 x_coords = [] y_coords = [] z_coords = [] for i, phi in enumerate(elevation): azimuth = np.linspace(0, 2 * np.pi, amount_in_row[i] + 1)[:-1:] x_coords.append(distance * np.sin(phi) * np.cos(azimuth)) y_coords.append(distance * np.sin(phi) * np.sin(azimuth)) z_coords.append(distance * np.cos(phi) * np.ones_like(azimuth)) return (np.hstack(x_coords), np.hstack(y_coords), np.hstack(z_coords))
def format_coords_for_dicom(all_merged): dicom_format_coords_by_z = {} for z, merged in all_merged.items(): coords = get_coords_from_polygon_or_multipolygon(merged) new_contour_data = [] for coord in coords: stacked_coords = np.hstack( list(zip(coord[0], coord[1], z * np.ones_like(coord[1])))) stacked_coords = np.round(stacked_coords, 1) stacked_coords = stacked_coords.tolist() new_contour_data.append(stacked_coords) dicom_format_coords_by_z[z] = new_contour_data return dicom_format_coords_by_z
def find_relevant_control_points(mu): """Returns that control points that had an MU difference either side. """ mu_diff = np.diff(mu) no_change = mu_diff == 0 try: start = no_change[0] end = no_change[-1] except IndexError: all_true = np.empty_like(mu).astype(bool) all_true.fill(True) return all_true no_change_before = no_change[0:-1] no_change_after = no_change[1::] no_change_before_and_after = no_change_before & no_change_after irrelevant_control_point = np.hstack( [start, no_change_before_and_after, end]) relevant_control_points = np.invert(irrelevant_control_point) return relevant_control_points
def load_mephysto(filepath, output_to_file=False, output_directory=None, sort=True): """Input the filepath of a mephysto .mcc file and return the data of the scans in four lists, distance, relative_dose, scan_curvetype, and scan_depth. Each respective element in these lists corresponds to an individual scan. """ # Open the file and store the contents in file_contents with open(filepath) as file_pointer: file_contents = np.array(file_pointer.readlines()) # Use the functions defined within mccread.py to pull the desired data distance, relative_dose = pull_mephysto_data(file_contents) scan_curvetype = pull_mephysto_item("SCAN_CURVETYPE", file_contents) scan_depth = pull_mephysto_number("SCAN_DEPTH", file_contents) # Convert python lists into numpy arrays for easier use distance = np.array(distance, dtype=object) relative_dose = np.array(relative_dose, dtype=object) scan_curvetype = np.array(scan_curvetype) scan_depth = np.array(scan_depth) # If the user requests to sort the data (which is default) the loaded # mephysto files are organised so that PDDs are first, then inplane # profiles, then crossplane profiles. if sort: # Find the references for where the scan type is the relevant type # and then use the "hstack" function to join the references together. sort_ref = np.hstack([ np.where(scan_curvetype == "PDD")[0], # reference of PDDs np.where(scan_curvetype == "INPLANE_PROFILE")[0], # inplane ref np.where(scan_curvetype == "CROSSPLANE_PROFILE")[0], # crossplane ]) # Confirm that the length of sort_ref is the same as scan_curvetype. # This will be false if there exists an unexpected scan_curvetype. assert len(sort_ref) == len(scan_curvetype) # Apply the sorting reference to each of the relevant variables. distance = distance[sort_ref] relative_dose = relative_dose[sort_ref] scan_curvetype = scan_curvetype[sort_ref] scan_depth = scan_depth[sort_ref] # Output csv's if "output_to_file" is True if output_to_file: # If user didn't define an output_directory use a default one if output_directory is None: # Define output directory as a mephysto folder filepath_directory = os.path.dirname(filepath) filename = os.path.splitext(os.path.basename(filepath))[0] output_directory = os.path.join(filepath_directory, filename) # If the output directory does not exist create it if not os.path.exists(output_directory): os.makedirs(output_directory) # Call the file_output function within csvoutput.py file_output(output_directory, distance, relative_dose, scan_curvetype, scan_depth) return distance, relative_dose, scan_curvetype, scan_depth