def get_contours_from_mask(x_grid, y_grid, mask): fig, ax = plt.subplots() with warnings.catch_warnings(): warnings.simplefilter("ignore", UserWarning) cs = ax.contour(x_grid, y_grid, mask, [0]) contours = [path.vertices for path in cs.collections[0].get_paths()] plt.close(fig) return contours
def multi_slice_viewer(volume, dx, dy): # remove_keymap_conflicts({'j', 'k'}) fig, ax = plt.subplots() ax.volume = volume ax.index = volume.shape[2] // 2 print(ax.index) extent = (0, 0 + (volume.shape[1] * dx), 0, 0 + (volume.shape[0] * dy)) ax.imshow(volume[:, :, ax.index], extent=extent) ax.set_xlabel("x distance [mm]") ax.set_ylabel("y distance [mm]") ax.set_title("item=" + str(ax.index)) fig.suptitle("Axial view", fontsize=16) fig.canvas.mpl_connect("key_press_event", process_key_axial)
def merge_view_horz(volume, dx, dy): junctions = [] # creating merged volume merge_vol = np.zeros((volume.shape[0], volume.shape[1])) # creating vector for processing along cols (one row) amplitude = np.zeros( (volume.shape[0], volume.shape[2])) # 1 if it is vertical 0 if the bars are horizontal y = np.linspace(0, 0 + (volume.shape[0] * dy), volume.shape[0], endpoint=False) # definition of the distance axis # x = np.arange(0,) #definition of the distance axis # merging the two images together ampl_resamp = np.zeros(((volume.shape[0]) * 10, volume.shape[2])) # amp_peak = np.zeros((volume.shape[0]) * 10) for item in tqdm(range(0, volume.shape[2])): merge_vol = merge_vol + volume[:, :, item] amplitude[:, item] = volume[:, int(volume.shape[1] / 2), item] ampl_resamp[:, item] = signal.resample( amplitude[:, item], int(len(amplitude)) * 10) # resampling the amplitude vector # amp_peak = amp_peak + ampl_resamp[:, item] / volume.shape[2] fig, ax = plt.subplots(nrows=2, squeeze=True, figsize=(6, 8)) extent = (0, 0 + (volume.shape[1] * dx), 0, 0 + (volume.shape[0] * dy)) ax[0].imshow(merge_vol, extent=extent, aspect="auto") ax[0].set_xlabel("x distance [mm]") ax[0].set_ylabel("y distance [mm]") ax[1].plot(y, amplitude, label="Amplitude profile") ax[1].set_ylabel("amplitude") ax[1].set_xlabel("y distance [mm]") ax[1].legend() fig.suptitle("Merged volume", fontsize=16) # peaks, peak_type, peak_figs = peak_find(ampl_resamp, dy) peaks, peak_type, peak_figs = pf.peak_find(ampl_resamp, dy) # junction_figs = minimize_junction_Y(ampl_resamp, peaks, peak_type, dy / 10) junction_figs = minY.minimize_junction_Y(ampl_resamp, peaks, peak_type, dy / 10) junctions.append(junction_figs) return fig, peak_figs, junctions
def plot(self, show_peaks: bool = True): """Plot the profile. Parameters ---------- show_peaks : bool Whether to plot the peak locations as well. Will not show if a peak search has not yet been done. """ _, ax = plt.subplots() ax.plot(self.values) if show_peaks: peaks_x = [peak.idx for peak in self.peaks] peaks_y = [peak.value for peak in self.peaks] ax.plot(peaks_x, peaks_y, "go")
def plot2axes( self, axes=None, edgecolor: str = "black", fill: bool = False, plot_peaks: bool = True, ): """Add 2 circles to the axes: one at the maximum and minimum radius of the ROI. See Also -------- :meth:`~pylinac.core.profile.CircleProfile.plot2axes` : Further parameter info. """ if axes is None: _, axes = plt.subplots() axes.imshow(self.image_array) axes.add_patch( matplotlib.patches.Circle( (self.center.x, self.center.y), edgecolor=edgecolor, radius=self.radius * (1 + self.width_ratio), fill=fill, ) ) axes.add_patch( matplotlib.patches.Circle( (self.center.x, self.center.y), edgecolor=edgecolor, radius=self.radius * (1 - self.width_ratio), fill=fill, ) ) if plot_peaks: x_locs = [peak.x for peak in self.peaks] y_locs = [peak.y for peak in self.peaks] axes.autoscale(enable=False) axes.scatter(x_locs, y_locs, s=20, marker="x", c=edgecolor)
def plot2axes( # pylint: disable = arguments-differ self, axes=None, edgecolor: str = "black", fill: bool = False, plot_peaks: bool = True, ): """Plot the circle to an axes. Parameters ---------- axes : matplotlib.Axes, None The axes to plot on. If None, will create a new figure of the image array. edgecolor : str Color of the Circle; must be a valid matplotlib color. fill : bool Whether to fill the circle. matplotlib keyword. plot_peaks : bool If True, plots the found peaks as well. """ if axes is None: _, axes = plt.subplots() axes.imshow(self.image_array) axes.add_patch( matplotlib.patches.Circle( (self.center.x, self.center.y), edgecolor=edgecolor, radius=self.radius, fill=fill, ) ) if plot_peaks: x_locs = [peak.x for peak in self.peaks] y_locs = [peak.y for peak in self.peaks] axes.autoscale(enable=False) axes.scatter(x_locs, y_locs, s=40, marker="x", c=edgecolor)
def plot_and_save_results( reference_mudensity, evaluation_mudensity, gamma, gamma_options, png_record_directory, header_text="", footer_text="", ): reference_filepath = png_record_directory.joinpath("reference.png") evaluation_filepath = png_record_directory.joinpath("evaluation.png") diff_filepath = png_record_directory.joinpath("diff.png") gamma_filepath = png_record_directory.joinpath("gamma.png") diff = evaluation_mudensity - reference_mudensity imageio.imwrite(reference_filepath, reference_mudensity) imageio.imwrite(evaluation_filepath, evaluation_mudensity) imageio.imwrite(diff_filepath, diff) imageio.imwrite(gamma_filepath, gamma) largest_mu_density = np.max( [np.max(evaluation_mudensity), np.max(reference_mudensity)]) largest_diff = np.max(np.abs(diff)) widths = [1, 1] heights = [0.5, 1, 1, 1, 0.4] gs_kw = dict(width_ratios=widths, height_ratios=heights) fig, axs = plt.subplots(5, 2, figsize=(10, 16), gridspec_kw=gs_kw) gs = axs[0, 0].get_gridspec() for ax in axs[0, 0:]: ax.remove() for ax in axs[1, 0:]: ax.remove() for ax in axs[4, 0:]: ax.remove() ax_header = fig.add_subplot(gs[0, :]) ax_hist = fig.add_subplot(gs[1, :]) ax_footer = fig.add_subplot(gs[4, :]) ax_header.axis("off") ax_footer.axis("off") ax_header.text(0, 0, header_text, ha="left", wrap=True, fontsize=21) ax_footer.text(0, 1, footer_text, ha="left", va="top", wrap=True, fontsize=6) plt.sca(axs[2, 0]) pymedphys.mudensity.display(GRID, reference_mudensity, vmin=0, vmax=largest_mu_density) axs[2, 0].set_title("Reference MU Density") plt.sca(axs[2, 1]) pymedphys.mudensity.display(GRID, evaluation_mudensity, vmin=0, vmax=largest_mu_density) axs[2, 1].set_title("Evaluation MU Density") plt.sca(axs[3, 0]) pymedphys.mudensity.display(GRID, diff, cmap="seismic", vmin=-largest_diff, vmax=largest_diff) plt.title("Evaluation - Reference") plt.sca(axs[3, 1]) pymedphys.mudensity.display(GRID, gamma, cmap="coolwarm", vmin=0, vmax=2) plt.title("Local Gamma | " f"{gamma_options['dose_percent_threshold']}%/" f"{gamma_options['distance_mm_threshold']}mm") plt.sca(ax_hist) plot_gamma_hist( gamma, gamma_options["dose_percent_threshold"], gamma_options["distance_mm_threshold"], ) return fig
def merge_view_filtrot(volume, dx, dy): volume_resort = np.copy( volume ) # this will hold the resorted volume 0 to 3 clockwise junctions_comb = [] peaks_figs_comb = [] # we need to create 4 matches # 0,1,2,3 will be tagged top left, top right, bottom right, bottom left for i in range(0, int(np.shape(volume)[2])): diag_stack = [ 0, 0, 0, 0, ] # we will sum along one direction whichever is biggest will tag the file for j in range(0, int(min([np.shape(volume)[0], np.shape(volume)[1]]) / 2)): # print('j=',j,int(np.shape(volume)[0] / 2)+j, int(np.shape(volume)[1] / 2)+j) diag_stack[0] = ( diag_stack[0] + volume[ int(np.shape(volume)[0] / 2) - j, int(np.shape(volume)[1] / 2) - j, i, ] ) diag_stack[1] = ( diag_stack[1] + volume[ int(np.shape(volume)[0] / 2) - j, int(np.shape(volume)[1] / 2) + j, i, ] ) diag_stack[2] = ( diag_stack[2] + volume[ int(np.shape(volume)[0] / 2) + j, int(np.shape(volume)[1] / 2) + j, i, ] ) diag_stack[3] = ( diag_stack[3] + volume[ int(np.shape(volume)[0] / 2) + j, int(np.shape(volume)[1] / 2) - j, i, ] ) volume_resort[:, :, np.argmax(diag_stack)] = volume[:, :, i] # creating merged volumes merge_vol = np.zeros((volume_resort.shape[0], volume_resort.shape[1])) # creating vector for processing (1 horizontal & 1 vertical) amplitude_horz = np.zeros( (volume_resort.shape[1], volume_resort.shape[2]) ) # 1 if it is vertical 0 if the bars are horizontal amplitude_vert = np.zeros((volume_resort.shape[0], volume_resort.shape[2])) # y = np.linspace(0, 0 + (volume_resort.shape[0] * dy), volume_resort.shape[0], # endpoint=False) # definition of the distance axis # x = np.linspace(0, 0 + (volume_resort.shape[1] * dy), volume_resort.shape[1], # endpoint=False) # definition of the distance axis ampl_resamp_y1 = np.zeros( ((volume_resort.shape[0]) * 10, int(volume_resort.shape[2] / 2)) ) ampl_resamp_y2 = np.zeros( ((volume_resort.shape[0]) * 10, int(volume_resort.shape[2] / 2)) ) ampl_resamp_x1 = np.zeros( ((volume_resort.shape[1]) * 10, int(volume_resort.shape[2] / 2)) ) ampl_resamp_x2 = np.zeros( ((volume_resort.shape[1]) * 10, int(volume_resort.shape[2] / 2)) ) amplitude_horz[:, 0] = volume_resort[ int(volume_resort.shape[0] / 3.25), :, 0 ] # for profile 1 amplitude_horz[:, 1] = volume_resort[ int(volume_resort.shape[0] / 3.25), :, 1 ] # for profile 1 amplitude_horz[:, 3] = volume_resort[ int(volume_resort.shape[0]) - int(volume_resort.shape[0] / 3.25), :, 2 ] # the numbers here are reversed because we are going to slide the second graph (the overlay) to minimize the error #for profile 2 amplitude_horz[:, 2] = volume_resort[ int(volume_resort.shape[0]) - int(volume_resort.shape[0] / 3.25), :, 3 ] amplitude_vert[:, 0] = volume_resort[ :, int(volume_resort.shape[1]) - int(volume_resort.shape[1] / 2.8), 1 ] # the numbers here are reversed because we are going to slide the second graph (the overlay) to minimize the error #for profile 3 amplitude_vert[:, 1] = volume_resort[ :, int(volume_resort.shape[1]) - int(volume_resort.shape[1] / 2.8), 2 ] amplitude_vert[:, 3] = volume_resort[ :, int(volume_resort.shape[1] / 2.8), 3 ] # for profile 4 amplitude_vert[:, 2] = volume_resort[:, int(volume_resort.shape[1] / 2.8), 0] plt.figure() for item in tqdm(range(0, int(volume.shape[2] / 2))): merge_vol = merge_vol + volume[:, :, item] data_samp = amplitude_vert[:, item] ampl_resamp_y1[:, item] = signal.resample( data_samp, int(np.shape(amplitude_vert)[0]) * 10 ) data_samp = amplitude_horz[:, item] ampl_resamp_x1[:, item] = signal.resample( data_samp, int(np.shape(amplitude_horz)[0]) * 10 ) for item in tqdm(range(int(volume.shape[2] / 2), volume.shape[2])): merge_vol = merge_vol + volume[:, :, item] data_samp = amplitude_vert[:, item] ampl_resamp_y2[:, item - int(volume.shape[2] / 2)] = signal.resample( data_samp, int(np.shape(amplitude_vert)[0]) * 10 ) data_samp = amplitude_horz[:, item] ampl_resamp_x2[:, item - int(volume.shape[2] / 2)] = signal.resample( data_samp, int(np.shape(amplitude_horz)[0]) * 10 ) fig, ax = plt.subplots(ncols=1, nrows=1, squeeze=True, figsize=(6, 8)) extent = (0, 0 + (volume.shape[1] * dx), 0, 0 + (volume.shape[0] * dy)) ax.imshow(merge_vol, extent=extent, aspect="auto") ax.set_aspect("equal", "box") ax.set_xlabel("x distance [mm]") ax.set_ylabel("y distance [mm]") fig.suptitle("Merged volume", fontsize=16) ax.hlines(dy * int(volume_resort.shape[0] / 3.25), 0, dx * volume_resort.shape[1]) ax.text( dx * int(volume_resort.shape[1] / 2.25), dy * int(volume_resort.shape[0] / 3), "Profile 2", ) ax.hlines( dy * int(volume_resort.shape[0]) - dy * int(volume_resort.shape[0] / 3.25), 0, dx * volume_resort.shape[1], ) ax.text( dx * int(volume_resort.shape[1] / 2.25), dy * int(volume_resort.shape[0]) - dy * int(volume_resort.shape[0] / 3.5), "Profile 1", ) ax.vlines(dx * int(volume_resort.shape[1] / 2.8), 0, dy * volume_resort.shape[0]) ax.text( dx * int(volume_resort.shape[1] / 3.1), dy * int(volume_resort.shape[0] / 1.8), "Profile 4", rotation=90, ) ax.vlines( dx * int(volume_resort.shape[1]) - dx * int(volume_resort.shape[1] / 2.8), 0, dy * volume_resort.shape[0], ) ax.text( dx * int(volume_resort.shape[1]) - dx * int(volume_resort.shape[1] / 2.9), dy * int(volume_resort.shape[0] / 1.8), "Profile 3", rotation=90, ) # plt.show() peaks, peak_type, peak_figs = pffr.peak_find_fieldrot( ampl_resamp_x1, dx, "Profile 1" ) junction_figs = minFR.minimize_junction_fieldrot( ampl_resamp_x1, peaks, peak_type, dx / 10, "Profile 1" ) peaks_figs_comb.append(peak_figs) junctions_comb.append(junction_figs) peaks, peak_type, peak_figs = pffr.peak_find_fieldrot( ampl_resamp_x2, dx, "Profile 2" ) junction_figs = minFR.minimize_junction_fieldrot( ampl_resamp_x2, peaks, peak_type, dx / 10, "Profile 2" ) peaks_figs_comb.append(peak_figs) junctions_comb.append(junction_figs) peaks, peak_type, peak_figs = pffr.peak_find_fieldrot( ampl_resamp_y1, dy, "Profile 3" ) junction_figs = minFR.minimize_junction_fieldrot( ampl_resamp_y1, peaks, peak_type, dy / 10, "Profile 3" ) peaks_figs_comb.append(peak_figs) junctions_comb.append(junction_figs) peaks, peak_type, peak_figs = pffr.peak_find_fieldrot( ampl_resamp_y2, dy, "Profile 4" ) junction_figs = minFR.minimize_junction_fieldrot( ampl_resamp_y2, peaks, peak_type, dy / 10, "Profile 4" ) peaks_figs_comb.append(peak_figs) junctions_comb.append(junction_figs) return fig, peaks_figs_comb, junctions_comb
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