def apply_negative(column): result = np.ones_like(column).astype(np.float64) * np.nan negative_values = column.values > 2**15 result[negative_values] = column[negative_values] - 2**16 result[np.invert(negative_values)] = column[np.invert(negative_values)] if np.any(np.isnan(result)): raise Exception("Not all column values were converted") return result
def assert_array_agreement(unique_logfile_gantry_angles, mosaiq_gantry_angles, allowed_deviation): difference_matrix = np.abs(unique_logfile_gantry_angles[:, None] - mosaiq_gantry_angles[None, :]) agreement_matrix = difference_matrix <= allowed_deviation row_agreement = np.any(agreement_matrix, axis=1) at_least_one_agreement = np.all(row_agreement) assert at_least_one_agreement, ( "There is a logfile gantry angle that deviates by more than {} degrees" " from the Mosaiq control points. Unsure how to handle this.\n\n" "Logfile: {}\nMosaiq: {}\nDifference Matrix:\n{}\n" "Agreement Matrix:\n{}".format( allowed_deviation, unique_logfile_gantry_angles, mosaiq_gantry_angles, difference_matrix, agreement_matrix, ))
def convert_numbers_to_string(name, lookup, column): dtype = np.array([item for _, item in lookup.items()]).dtype result = np.empty_like(column).astype(dtype) result[:] = "" for i, item in lookup.items(): result[column.values == int(i)] = item if np.any(result == ""): print(lookup) print(np.where(result == "")) print(column[result == ""].values) unconverted_entries = np.unique(column[result == ""]) raise Exception( "The conversion lookup list for converting {} is incomplete. " "The following data numbers were not converted:\n" "{}\n" "Please update the trf2csv conversion script to include these " "in its definitions.".format(name, unconverted_entries)) return result
def field_centre_and_rotation_refining( field, edge_lengths, penumbra, initial_centre, fixed_rotation=None, niter=10, pylinac_tol=0.2, ): if fixed_rotation is None: check_aspect_ratio(edge_lengths) predicted_rotation = optimise_rotation(field, initial_centre, edge_lengths, penumbra) else: predicted_rotation = fixed_rotation predicted_centre = optimise_centre(field, initial_centre, edge_lengths, penumbra, predicted_rotation) for _ in range(niter): if fixed_rotation is None: previous_rotation = predicted_rotation predicted_rotation = optimise_rotation(field, predicted_centre, edge_lengths, penumbra) try: check_rotation_close(edge_lengths, previous_rotation, predicted_rotation) break except ValueError: pass previous_centre = predicted_centre predicted_centre = optimise_centre(field, predicted_centre, edge_lengths, penumbra, predicted_rotation) try: check_centre_close(previous_centre, predicted_centre) break except ValueError: pass if fixed_rotation is None: verification_rotation = optimise_rotation(field, predicted_centre, edge_lengths, penumbra) check_rotation_close(edge_lengths, verification_rotation, predicted_rotation) if not pylinac_tol is None: try: pylinac = run_wlutz( field, edge_lengths, penumbra, predicted_centre, predicted_rotation, find_bb=False, ) except ValueError as e: raise ValueError( "After finding the field centre during comparison to Pylinac the pylinac " f"code raised the following error:\n {e}") pylinac_2_2_6_out_of_tol = np.any( np.abs( np.array(pylinac["v2.2.6"]["field_centre"]) - predicted_centre) > pylinac_tol) pylinac_2_2_7_out_of_tol = np.any( np.abs( np.array(pylinac["v2.2.7"]["field_centre"]) - predicted_centre) > pylinac_tol) if pylinac_2_2_6_out_of_tol or pylinac_2_2_7_out_of_tol: raise PylinacComparisonDeviation( "The determined field centre deviates from pylinac more " "than the defined tolerance") centre = predicted_centre.tolist() return centre, predicted_rotation
def optimise_bb_centre( field: imginterp.Field, bb_diameter, edge_lengths, penumbra, field_centre, field_rotation, pylinac_tol=0.2, debug=True, ): centralised_field = utilities.create_centralised_field( field, field_centre, field_rotation) to_minimise_edge_agreement = create_bb_to_minimise(centralised_field, bb_diameter) bb_bounds = define_bb_bounds(bb_diameter, edge_lengths, penumbra) bb_centre_in_centralised_field = bb_basinhopping( to_minimise_edge_agreement, bb_bounds) if check_if_at_bounds(bb_centre_in_centralised_field, bb_bounds): raise ValueError("BB found at bounds, likely incorrect") bb_centre = utilities.transform_point(bb_centre_in_centralised_field, field_centre, field_rotation) verification_repeat = bb_basinhopping(to_minimise_edge_agreement, bb_bounds) repeat_agreement = np.abs(verification_repeat - bb_centre_in_centralised_field) if np.any(repeat_agreement > BB_REPEAT_TOL): bb_repeated = utilities.transform_point(verification_repeat, field_centre, field_rotation) if debug: reporting.image_analysis_figure( field.x, field.y, field.img, bb_centre, field_centre, field_rotation, bb_diameter, edge_lengths, penumbra, ) plt.title("First iteration") reporting.image_analysis_figure( field.x, field.y, field.img, bb_repeated, field_centre, field_rotation, bb_diameter, edge_lengths, penumbra, ) plt.title("Second iteration") plt.show() raise ValueError("BB centre not able to be consistently determined\n" f" First iteration: {bb_centre}\n" f" Second iteration: {bb_repeated}") if not pylinac_tol is None: try: pylinac_result = pylinac.run_wlutz( field, edge_lengths, penumbra, field_centre, field_rotation, find_bb=True, pylinac_versions=("v2.2.6", ), ) except ValueError: raise ValueError( "While comparing result to PyLinac an error was raised") # warnings.simplefilter("always", UserWarning) # warnings.warn( # "This iteration has not been checked against pylinac. " # "When attempting to run pylinac instead an error was " # f"raised. Pylinac raised the following error:\n\n{e}\n" # ) # pylinac = {} try: pylinac_2_2_6_out_of_tol = np.any( np.abs( np.array(pylinac_result["v2.2.6"]["bb_centre"]) - bb_centre) > pylinac_tol) if pylinac_2_2_6_out_of_tol: raise pylinac.PylinacComparisonDeviation( "The determined bb centre deviates from pylinac more " "than the defined tolerance") except KeyError: pass return bb_centre
def check_if_at_bounds(bb_centre, bb_bounds): x_at_bounds = np.any(np.array(bb_centre[0]) == np.array(bb_bounds[0])) y_at_bounds = np.any(np.array(bb_centre[1]) == np.array(bb_bounds[1])) any_at_bounds = x_at_bounds or y_at_bounds return any_at_bounds
def gamma_loop(options: GammaInternalFixedOptions): still_searching_for_gamma = np.full_like(options.flat_dose_reference, True, dtype=bool) current_gamma = np.inf * np.ones(( len(options.flat_dose_reference), len(options.dose_percent_threshold), len(options.distance_mm_threshold), )) distance_step_size = np.min( options.distance_mm_threshold) / options.interp_fraction to_be_checked = options.reference_points_to_calc & still_searching_for_gamma distance = 0.0 force_search_distances = np.sort(options.distance_mm_threshold) while distance <= options.maximum_test_distance: if not options.quiet: sys.stdout.write( "\rCurrent distance: {0:.2f} mm | " "Number of reference points remaining: {1}".format( distance, np.sum(to_be_checked))) min_relative_dose_difference = calculate_min_dose_difference( options, distance, to_be_checked, distance_step_size) current_gamma, still_searching_for_gamma_all = multi_thresholds_gamma_calc( options, current_gamma, min_relative_dose_difference, distance, to_be_checked, ) still_searching_for_gamma = np.any(np.any( still_searching_for_gamma_all, axis=-1), axis=-1) to_be_checked = options.reference_points_to_calc & still_searching_for_gamma if np.sum(to_be_checked) == 0: break relevant_distances = options.distance_mm_threshold[np.any( np.any( options.reference_points_to_calc[:, None, None] & still_searching_for_gamma_all, axis=0, ), axis=0, )] distance_step_size = np.min( relevant_distances) / options.interp_fraction distance_step_size = np.max([ distance / options.interp_fraction / options.max_gamma, distance_step_size ]) distance += distance_step_size if len(force_search_distances) != 0: if distance >= force_search_distances[0]: distance = force_search_distances[0] force_search_distances = np.delete(force_search_distances, 0) return current_gamma