Example #1
0
    def __call__(self, x, y):
        if np.shape(x) != np.shape(y):
            raise ValueError("x and y required to be the same shape")

        result = self._interpolation.ev(np.ravel(x), np.ravel(y))
        result.shape = x.shape

        return result
Example #2
0
def dose_inside_cube(x_dose, y_dose, z_dose, dose, cube):
    """Find the dose just within the given cube.
    """
    cube_definition = cubify(cube)
    print(cube_definition)
    vertices = cube_vertices(cube_definition)
    bounding_box = get_bounding_box(vertices)

    x_outside = (x_dose < bounding_box[1][0]) | (x_dose > bounding_box[1][1])
    y_outside = (y_dose < bounding_box[0][0]) | (y_dose > bounding_box[0][1])
    z_outside = (z_dose < bounding_box[2][0]) | (z_dose > bounding_box[2][1])

    xx, yy, zz = np.meshgrid(
        x_dose[np.invert(x_outside)],
        y_dose[np.invert(y_outside)],
        z_dose[np.invert(z_outside)],
    )

    where_x = np.where(np.invert(x_outside))[0]
    where_y = np.where(np.invert(y_outside))[0]
    where_z = np.where(np.invert(z_outside))[0]

    bounded_dose = dose[
        where_y[0] : where_y[-1] + 1,
        where_x[0] : where_x[-1] + 1,
        where_z[0] : where_z[-1] + 1,
    ]

    points_to_test = np.array(
        [
            [y, x, z, d]
            for y, x, z, d in zip(
                np.ravel(yy), np.ravel(xx), np.ravel(zz), np.ravel(bounded_dose)
            )
        ]
    )

    inside_cube = [
        test_if_in_cube(point_test, cube_definition)
        for point_test in points_to_test[:, 0:3]
    ]

    points_inside_cube = points_to_test[inside_cube, :]

    ax = plot_cube(cube_definition)
    ax.scatter(
        points_inside_cube[:, 1],
        points_inside_cube[:, 0],
        points_inside_cube[:, 2],
        c=points_inside_cube[:, 3],
        alpha=0.4,
    )

    return ax
Example #3
0
def get_interpolated_dose(coords_grid, dose_interpolation):
    coords_grid_ij_indexing = np.array([
        np.ravel(coords_grid[:, :, 1]),
        np.ravel(coords_grid[:, :, 0]),
        np.ravel(coords_grid[:, :, 2]),
    ]).T

    interpolated_dose = dose_interpolation(coords_grid_ij_indexing)
    coords_dim = np.shape(coords_grid)
    interpolated_dose = np.reshape(interpolated_dose,
                                   (coords_dim[0], coords_dim[1]))

    return interpolated_dose
Example #4
0
def apply_transform(xx, yy, transform):
    xx = np.array(xx, copy=False)
    yy = np.array(yy, copy=False)

    xx_flat = np.ravel(xx)
    transformed = transform @ np.vstack(
        [xx_flat, np.ravel(yy), np.ones_like(xx_flat)])

    xx_transformed = transformed[0]
    yy_transformed = transformed[1]

    xx_transformed.shape = xx.shape
    yy_transformed.shape = yy.shape

    return xx_transformed, yy_transformed
Example #5
0
def define_rotation_field_points_at_origin(edge_lengths, penumbra):
    x_half_range = edge_lengths[0] / 2 + penumbra / 2
    y_half_range = edge_lengths[1] / 2 + penumbra / 2

    num_x = np.ceil(x_half_range * 2 * 8) + 1
    num_y = np.ceil(y_half_range * 2 * 8) + 1

    x = np.linspace(-x_half_range, x_half_range, int(num_x))
    y = np.linspace(-y_half_range, y_half_range, int(num_y))

    xx, yy = np.meshgrid(x, y)
    xx_flat = np.ravel(xx)
    yy_flat = np.ravel(yy)

    inside = np.logical_and((np.abs(xx_flat) < x_half_range),
                            (np.abs(yy_flat) < y_half_range))

    xx_flat = xx_flat[np.invert(inside)]
    yy_flat = yy_flat[np.invert(inside)]

    return xx_flat, yy_flat
Example #6
0
def calculate_min_dose_difference(options, distance, to_be_checked,
                                  distance_step_size):
    """Determine the minimum dose difference.

    Calculated for a given distance from each reference point.
    """

    min_relative_dose_difference = np.nan * np.ones_like(
        options.flat_dose_reference[to_be_checked])

    num_dimensions = np.shape(options.flat_mesh_axes_reference)[0]

    coordinates_at_distance_shell = pymedphys._utilities.createshells.calculate_coordinates_shell(  # pylint: disable = protected-access
        distance, num_dimensions, distance_step_size)

    num_points_in_shell = np.shape(coordinates_at_distance_shell)[1]

    estimated_ram_needed = (np.uint64(num_points_in_shell) *
                            np.uint64(np.count_nonzero(to_be_checked)) *
                            np.uint64(32) * np.uint64(num_dimensions) *
                            np.uint64(2))

    num_slices = np.floor(
        estimated_ram_needed / options.ram_available).astype(int) + 1

    if not options.quiet:
        sys.stdout.write(
            " | Points tested per reference point: {} | RAM split count: {}".
            format(num_points_in_shell, num_slices))
        sys.stdout.flush()

    all_checks = np.where(np.ravel(to_be_checked))[0]
    index = np.arange(len(all_checks))
    sliced = np.array_split(index, num_slices)

    sorted_sliced = [np.sort(current_slice) for current_slice in sliced]

    for current_slice in sorted_sliced:
        to_be_checked_sliced = np.full_like(to_be_checked, False, dtype=bool)
        to_be_checked_sliced[  # pylint: disable=unsupported-assignment-operation
            all_checks[current_slice]] = True

        assert np.all(to_be_checked[to_be_checked_sliced])

        axes_reference_to_be_checked = options.flat_mesh_axes_reference[:,
                                                                        to_be_checked_sliced]

        evaluation_dose = interpolate_evaluation_dose_at_distance(
            options.evaluation_interpolation,
            axes_reference_to_be_checked,
            coordinates_at_distance_shell,
        )

        if options.local_gamma:
            with np.errstate(divide="ignore"):
                relative_dose_difference = (
                    evaluation_dose -
                    options.flat_dose_reference[to_be_checked_sliced][None, :]
                ) / (
                    options.flat_dose_reference[to_be_checked_sliced][None, :])
        else:
            relative_dose_difference = (
                evaluation_dose -
                options.flat_dose_reference[to_be_checked_sliced][None, :]
            ) / options.global_normalisation

        min_relative_dose_difference[current_slice] = np.min(
            np.abs(relative_dose_difference), axis=0)

    return min_relative_dose_difference
Example #7
0
    def from_user_inputs(
        cls,
        axes_reference,
        dose_reference,
        axes_evaluation,
        dose_evaluation,
        dose_percent_threshold,
        distance_mm_threshold,
        lower_percent_dose_cutoff=20,
        interp_fraction=10,
        max_gamma=None,
        local_gamma=False,
        global_normalisation=None,
        skip_once_passed=False,
        random_subset=None,
        ram_available=None,
        quiet=False,
    ):
        if max_gamma is None:
            max_gamma = np.inf

        axes_reference, axes_evaluation = run_input_checks(
            axes_reference, dose_reference, axes_evaluation, dose_evaluation)

        dose_percent_threshold = expand_dims_to_1d(dose_percent_threshold)
        distance_mm_threshold = expand_dims_to_1d(distance_mm_threshold)

        if global_normalisation is None:
            global_normalisation = np.max(dose_reference)

        lower_dose_cutoff = lower_percent_dose_cutoff / 100 * global_normalisation

        maximum_test_distance = np.max(distance_mm_threshold) * max_gamma

        evaluation_interpolation = scipy.interpolate.RegularGridInterpolator(
            axes_evaluation,
            np.array(dose_evaluation),
            bounds_error=False,
            fill_value=np.inf,
        )

        dose_reference = np.array(dose_reference)
        reference_dose_above_threshold = dose_reference >= lower_dose_cutoff

        mesh_axes_reference = np.meshgrid(*axes_reference, indexing="ij")
        flat_mesh_axes_reference = np.array(
            [np.ravel(item) for item in mesh_axes_reference])

        reference_points_to_calc = reference_dose_above_threshold
        reference_points_to_calc = np.ravel(reference_points_to_calc)

        if random_subset is not None:
            to_calc_index = np.where(reference_points_to_calc)[0]

            np.random.shuffle(to_calc_index)
            random_subset_to_calc = np.full_like(reference_points_to_calc,
                                                 False,
                                                 dtype=bool)
            random_subset_to_calc[  # pylint: disable=unsupported-assignment-operation
                to_calc_index[0:random_subset]] = True

            reference_points_to_calc = random_subset_to_calc

        flat_dose_reference = np.ravel(dose_reference)

        return cls(
            flat_mesh_axes_reference,
            flat_dose_reference,
            reference_points_to_calc,
            dose_percent_threshold,
            distance_mm_threshold,
            evaluation_interpolation,
            interp_fraction,
            max_gamma,
            lower_dose_cutoff,
            maximum_test_distance,
            global_normalisation,
            local_gamma,
            skip_once_passed,
            ram_available,
            quiet,
        )
Example #8
0
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
Example #9
0
def image_analysis_figure(
    x,
    y,
    img,
    bb_centre,
    field_centre,
    field_rotation,
    bb_diameter,
    edge_lengths,
    penumbra,
    units="(mm)",
):
    field = imginterp.create_interpolated_field(x, y, img)

    x_half_bound = edge_lengths[0] / 2 + penumbra * 3
    y_half_bound = edge_lengths[1] / 2 + penumbra * 3

    x_axis = np.linspace(-x_half_bound, x_half_bound, 200)
    y_axis = np.linspace(-y_half_bound, y_half_bound, 200)

    field_transform = interppoints.translate_and_rotate_transform(
        field_centre, field_rotation)
    x_field_interp, y_field_interp = createaxis.transform_axis(
        x_axis, y_axis, field_transform)

    if bb_centre is not None:
        bb_transform = interppoints.translate_and_rotate_transform(
            bb_centre, 0)
        x_bb_interp, y_bb_interp = createaxis.transform_axis(
            x_axis, y_axis, bb_transform)
    else:
        x_bb_interp, y_bb_interp = None, None

    fig, axs = plt.subplots(ncols=2, nrows=4, figsize=(12, 15))
    gs = axs[0, 0].get_gridspec()
    for ax in np.ravel(axs[0:2, 0:2]):
        ax.remove()

    ax_big = fig.add_subplot(gs[0:2, 0:2])

    axs[0, 0] = ax_big

    pixel_value_label = "Scaled image pixel value"

    image_with_overlays(
        fig,
        ax_big,
        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=units,
    )

    profile_flip_plot(axs[2, 0], x_axis, field(*x_field_interp))
    axs[2, 0].set_xlim([
        edge_lengths[0] / 2 - penumbra * 2, edge_lengths[0] / 2 + penumbra * 2
    ])
    axs[2, 0].set_title("Flipped profile about field centre [field x-axis]")
    axs[2, 0].set_xlabel(f"Distance from field centre {units}")
    axs[2, 0].set_ylabel(pixel_value_label)

    profile_flip_plot(axs[2, 1], y_axis, field(*y_field_interp))
    axs[2, 1].set_xlim([
        edge_lengths[1] / 2 - penumbra * 2, edge_lengths[1] / 2 + penumbra * 2
    ])
    axs[2, 1].set_title("Flipped profile about field centre [field y-axis]")
    axs[2, 1].set_xlabel(f"Distance from field centre {units}")
    axs[2, 1].set_ylabel(pixel_value_label)

    if bb_centre is not None:
        x_mask = (x_axis >= -bb_diameter / 2 - penumbra) & (
            x_axis <= bb_diameter / 2 + penumbra)
        profile_flip_plot(axs[3, 0], x_axis[x_mask],
                          field(*x_bb_interp)[x_mask])
        axs[3, 0].set_xlim(
            [-bb_diameter / 2 - penumbra, bb_diameter / 2 + penumbra])
        axs[3, 0].set_title("Flipped profile about BB centre [panel x-axis]")
        axs[3, 0].set_xlabel(f"Displacement from BB centre {units}")
        axs[3, 0].set_ylabel(pixel_value_label)

        y_mask = (y_axis >= -bb_diameter / 2 - penumbra) & (
            y_axis <= bb_diameter / 2 + penumbra)
        profile_flip_plot(axs[3, 1], y_axis[y_mask],
                          field(*y_bb_interp)[y_mask])
        axs[3, 1].set_xlim(
            [-bb_diameter / 2 - penumbra, bb_diameter / 2 + penumbra])
        axs[3, 1].set_title("Flipped profile about BB centre [panel y-axis]")
        axs[3, 1].set_xlabel(f"Displacement from BB centre {units}")
        axs[3, 1].set_ylabel(pixel_value_label)

    plt.tight_layout()

    return fig, axs