def gamma_filter_brute_force(axes_reference, dose_reference, axes_evaluation, dose_evaluation, distance_mm_threshold, dose_threshold, lower_dose_cutoff=0, **_): xx_ref, yy_ref, zz_ref = np.meshgrid(*axes_reference, indexing="ij") gamma_array = np.ones_like(dose_evaluation).astype(np.float) * np.nan mesh_index = np.meshgrid( *[np.arange(len(coord_eval)) for coord_eval in axes_evaluation]) eval_index = np.reshape(np.array(mesh_index), (3, -1)) run_index = np.arange(np.shape(eval_index)[1]) np.random.shuffle(run_index) sys.stdout.write(" ") for counter, point_index in enumerate(run_index): i, j, k = eval_index[:, point_index] eval_x = axes_evaluation[0][i] eval_y = axes_evaluation[1][j] eval_z = axes_evaluation[2][k] if dose_evaluation[i, j, k] < lower_dose_cutoff: continue distance = np.sqrt((xx_ref - eval_x)**2 + (yy_ref - eval_y)**2 + (zz_ref - eval_z)**2) dose_diff = dose_evaluation[i, j, k] - dose_reference gamma = np.min( np.sqrt((dose_diff / dose_threshold)**2 + (distance / distance_mm_threshold)**2)) gamma_array[i, j, k] = gamma if counter // 30 == counter / 30: percent_pass = str( np.round(calculate_pass_rate(gamma_array), decimals=1)) sys.stdout.write( "\rPercent Pass: {0}% | Percent Complete: {1:.2f}%".format( percent_pass, counter / np.shape(eval_index)[1] * 100)) sys.stdout.flush() return calculate_pass_rate(gamma_array)
def convert2_ratio_perim_area(width, length): """Convert width and length data into ratio of perimeter to area.""" perimeter = (np.pi / 2 * (3 * (width + length) - np.sqrt( (3 * width + length) * (3 * length + width)))) area = np.pi / 4 * width * length return perimeter / area
def create_bb_attenuation_func(diameter, penumbra, max_attenuation): dx = diameter / 100 radius = diameter / 2 image_half_width = penumbra * 2 + radius x = np.arange(-image_half_width, image_half_width + dx, dx) xx, yy = np.meshgrid(x, x) with warnings.catch_warnings(): warnings.simplefilter("ignore") z = np.sqrt(radius**2 - xx**2 - yy**2) / radius z[np.isnan(z)] = 0 sig = profiles.scaled_penumbra_sig() * penumbra sig_pixel = sig / dx filtered = scipy.ndimage.gaussian_filter(z, sig_pixel) interp = scipy.interpolate.RegularGridInterpolator((x, x), filtered, bounds_error=False, fill_value=None) def attenuation(x, y): return 1 - interp((x, y)) * max_attenuation return attenuation
def multi_thresholds_gamma_calc( options: GammaInternalFixedOptions, current_gamma, min_relative_dose_difference, distance, to_be_checked, ): gamma_at_distance = np.sqrt( (min_relative_dose_difference[:, None, None] / (options.dose_percent_threshold[None, :, None] / 100))**2 + (distance / options.distance_mm_threshold[None, None, :])**2) current_gamma[to_be_checked, :, :] = np.min( np.concatenate( [ gamma_at_distance[None, :, :, :], current_gamma[None, to_be_checked, :, :], ], axis=0, ), axis=0, ) still_searching_for_gamma = current_gamma > ( distance / options.distance_mm_threshold[None, None, :]) if options.skip_once_passed: still_searching_for_gamma = still_searching_for_gamma & (current_gamma >= 1) return current_gamma, still_searching_for_gamma
def distance_to(self, point): """Calculate the minimum distance from the line to a point. Equations are from here: http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html #14 Parameters ---------- point : Point, iterable The point to calculate distance to. """ point = Point(point).as_array() lp1 = self.point1.as_array() lp2 = self.point2.as_array() numerator = np.sqrt( np.sum(np.power(np.cross((lp2 - lp1), (lp1 - point)), 2))) denominator = np.sqrt(np.sum(np.power(lp2 - lp1, 2))) return numerator / denominator
def distance_to(self, thing: Union["Point", "Circle"]): """Calculate the distance to the given point. Parameters ---------- thing : Circle, Point, 2 element iterable The other thing to calculate distance to. """ if isinstance(thing, Circle): return abs( np.sqrt((self.x - thing.center.x)**2 + (self.y - thing.center.y)**2) - thing.radius) p = Point(thing) return math.sqrt((self.x - p.x)**2 + (self.y - p.y)**2 + (self.z - p.z)**2)
def scaled_penumbra_sig(profile_shoulder_edge=0.8): sig = 1 / (2 * np.sqrt(2) * scipy.special.erfinv(profile_shoulder_edge * 2 - 1)) return sig
def gaussian_cdf(x, mu=0, sig=1): x = np.array(x, copy=False) return 0.5 * (1 + scipy.special.erf((x - mu) / (sig * np.sqrt(2))))
def gamma_filter_numpy(axes_reference, dose_reference, axes_evaluation, dose_evaluation, distance_mm_threshold, dose_threshold, lower_dose_cutoff=0, **_): coord_diffs = [ coord_ref[:, None] - coord_eval[None, :] for coord_ref, coord_eval in zip(axes_reference, axes_evaluation) ] all_in_vicinity = [ np.where(np.abs(diff) < distance_mm_threshold) for diff in coord_diffs ] ref_coord_points = create_point_combination( [in_vicinity[0] for in_vicinity in all_in_vicinity]) eval_coord_points = create_point_combination( [in_vicinity[1] for in_vicinity in all_in_vicinity]) distances = np.sqrt( np.sum( [ coord_diff[ref_points, eval_points]**2 for ref_points, eval_points, coord_diff in zip( ref_coord_points, eval_coord_points, coord_diffs) ], axis=0, )) within_distance_threshold = distances < distance_mm_threshold distances = distances[within_distance_threshold] ref_coord_points = ref_coord_points[:, within_distance_threshold] eval_coord_points = eval_coord_points[:, within_distance_threshold] dose_diff = ( dose_evaluation[eval_coord_points[0, :], eval_coord_points[1, :], eval_coord_points[2, :]] - dose_reference[ref_coord_points[0, :], ref_coord_points[1, :], ref_coord_points[2, :]]) gamma = np.sqrt((dose_diff / dose_threshold)**2 + (distances / distance_mm_threshold)**2) gamma_pass = gamma < 1 eval_pass = eval_coord_points[:, gamma_pass] ravel_index = convert_to_ravel_index(eval_pass) gamma_pass_array = np.zeros_like(dose_evaluation).astype(np.bool) gamma_pass_array = np.ravel(gamma_pass_array) dose_above_cut_off = np.ravel(dose_evaluation) > lower_dose_cutoff gamma_pass_array[ravel_index] = True gamma_pass_percentage = np.mean(gamma_pass_array[dose_above_cut_off]) * 100 return gamma_pass_percentage
def image_with_overlays( fig, ax, x, y, img, field_transform, bb_centre, field_centre, bb_diameter, edge_lengths, x_field_interp, y_field_interp, x_bb_interp, y_bb_interp, pixel_value_label, units="(mm)", ): rect_crosshair_dx = [ -edge_lengths[0] / 2, edge_lengths[0], -edge_lengths[0], edge_lengths[0], ] rect_crosshair_dy = [ -edge_lengths[1] / 2, edge_lengths[1], 0, -edge_lengths[1] ] rect_dx = [-edge_lengths[0] / 2, 0, edge_lengths[0], 0, -edge_lengths[0]] rect_dy = [-edge_lengths[1] / 2, edge_lengths[1], 0, -edge_lengths[1], 0] c = ax.contourf(x, y, img, 100) fig.colorbar(c, ax=ax, label=pixel_value_label) ax.plot(*draw_by_diff(rect_dx, rect_dy, field_transform), "k", lw=3) ax.plot(*draw_by_diff(rect_crosshair_dx, rect_crosshair_dy, field_transform), "k", lw=1) ax.plot(x_field_interp[0], x_field_interp[1], "k", lw=0.5, alpha=0.3) ax.plot(y_field_interp[0], y_field_interp[1], "k", lw=0.5, alpha=0.3) if bb_centre is not None: bb_radius = bb_diameter / 2 bb_crosshair = np.array([-bb_radius, bb_radius]) t = np.linspace(0, 2 * np.pi) circle_x_origin = bb_diameter / 2 * np.sin(t) circle_y_origin = bb_diameter / 2 * np.cos(t) circle_x = circle_x_origin + bb_centre[0] circle_y = circle_y_origin + bb_centre[1] ax.plot([bb_centre[0]] * 2, bb_crosshair + bb_centre[1], "k", lw=1) ax.plot(bb_crosshair + bb_centre[0], [bb_centre[1]] * 2, "k", lw=1) ax.plot(circle_x, circle_y, "k", lw=3) ax.plot(x_bb_interp[0], x_bb_interp[1], "k", lw=0.5, alpha=0.3) ax.plot(y_bb_interp[0], y_bb_interp[1], "k", lw=0.5, alpha=0.3) ax.plot( [field_centre[0], bb_centre[0]], [field_centre[1], bb_centre[1]], c="C3", lw=3, ) ax.axis("equal") long_edge = np.sqrt(np.sum((np.array(edge_lengths))**2)) long_edge_fraction = long_edge * 0.6 ax.set_xlim([ field_centre[0] - long_edge_fraction, field_centre[0] + long_edge_fraction ]) ax.set_ylim([ field_centre[1] - long_edge_fraction, field_centre[1] + long_edge_fraction ]) ax.set_xlabel(f"iView panel absolute x-pos {units}") ax.set_ylabel(f"iView panel absolute y-pos {units}")