From 37f57e0fee0dd417e84387000f3c80c6c2907e9f Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 8 Aug 2024 09:52:51 +0200 Subject: [PATCH 01/11] Adding a slider, changing the way we use matplotlib --- .../matplotlib_data_visualisation.py | 653 ++++++++++-------- 1 file changed, 381 insertions(+), 272 deletions(-) diff --git a/simpa/visualisation/matplotlib_data_visualisation.py b/simpa/visualisation/matplotlib_data_visualisation.py index 29c5e299..82ed4a53 100644 --- a/simpa/visualisation/matplotlib_data_visualisation.py +++ b/simpa/visualisation/matplotlib_data_visualisation.py @@ -4,6 +4,7 @@ from simpa.io_handling import load_hdf5 import matplotlib.pyplot as plt +from matplotlib.widgets import Button, Slider import matplotlib as mpl import numpy as np from simpa.utils import SegmentationClasses, Tags @@ -11,276 +12,384 @@ from simpa.utils.settings import Settings from simpa.utils import get_data_field_from_simpa_output from simpa.log import Logger - - -def visualise_data(wavelength: int = None, - path_to_hdf5_file: str = None, - settings: Settings = None, - path_manager: PathManager = None, - show_absorption=False, - show_scattering=False, - show_anisotropy=False, - show_speed_of_sound=False, - show_tissue_density=False, - show_fluence=False, - show_initial_pressure=False, - show_time_series_data=False, - show_reconstructed_data=False, - show_segmentation_map=False, - show_oxygenation=False, - show_blood_volume_fraction=False, - show_linear_unmixing_sO2=False, - show_diffuse_reflectance=False, - log_scale=False, - show_xz_only=False, - save_path=None): - - if settings is not None and Tags.WAVELENGTHS in settings: - if wavelength is None or wavelength not in settings[Tags.WAVELENGTHS]: - wavelength = settings[Tags.WAVELENGTHS][0] - - if settings is not None and Tags.WAVELENGTH in settings: - wavelength = settings[Tags.WAVELENGTH] - - if path_to_hdf5_file is None and (settings is None or path_manager is None): - raise ValueError("Either the path_to_hdf5_file or the given settings and path_manager must not be None!") - - if path_to_hdf5_file is None: - path_to_hdf5_file = path_manager.get_hdf5_file_save_path() + "/" + settings[Tags.VOLUME_NAME] + ".hdf5" - - logger = Logger() - file = load_hdf5(path_to_hdf5_file) - - fluence = None - initial_pressure = None - time_series_data = None - reconstructed_data = None - oxygenation = None - blood_volume_fraction = None - linear_unmixing_sO2 = None - diffuse_reflectance = None - diffuse_reflectance_position = None - - absorption = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) - scattering = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_SCATTERING_PER_CM, wavelength) - anisotropy = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_ANISOTROPY, wavelength) - segmentation_map = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_SEGMENTATION) - speed_of_sound = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_SPEED_OF_SOUND) - density = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_DENSITY) - - if show_fluence: - try: - fluence = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_FLUENCE, wavelength) - except KeyError as e: - logger.critical("The key " + str(Tags.DATA_FIELD_FLUENCE) + " was not in the simpa output.") - show_fluence = False - fluence = None - - if show_diffuse_reflectance: - try: - diffuse_reflectance = get_data_field_from_simpa_output(file, - Tags.DATA_FIELD_DIFFUSE_REFLECTANCE, - wavelength) - diffuse_reflectance_position = get_data_field_from_simpa_output(file, - Tags.DATA_FIELD_DIFFUSE_REFLECTANCE_POS, - wavelength) - except KeyError as e: - logger.critical("The key " + str(Tags.DATA_FIELD_FLUENCE) + " was not in the simpa output.") - show_fluence = False - fluence = None - - if show_initial_pressure: - try: - initial_pressure = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength) - except KeyError as e: - logger.critical("The key " + str(Tags.DATA_FIELD_INITIAL_PRESSURE) + " was not in the simpa output.") - show_initial_pressure = False - initial_pressure = None - - if show_time_series_data: - try: - time_series_data = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_TIME_SERIES_DATA, wavelength) - except KeyError as e: - logger.critical("The key " + str(Tags.DATA_FIELD_TIME_SERIES_DATA) + " was not in the simpa output.") - show_time_series_data = False - time_series_data = None - - if show_reconstructed_data: - try: - reconstructed_data = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_RECONSTRUCTED_DATA, wavelength) - except KeyError as e: - logger.critical("The key " + str(Tags.DATA_FIELD_RECONSTRUCTED_DATA) + " was not in the simpa output.") - show_reconstructed_data = False - reconstructed_data = None - - if show_oxygenation: - try: - oxygenation = get_data_field_from_simpa_output(file, Tags.DATA_FIELD_OXYGENATION, wavelength) - except KeyError as e: - logger.critical("The key " + str(Tags.DATA_FIELD_OXYGENATION) + " was not in the simpa output.") - show_oxygenation = False - oxygenation = None - - if show_blood_volume_fraction: - try: - blood_volume_fraction = get_data_field_from_simpa_output( - file, Tags.DATA_FIELD_BLOOD_VOLUME_FRACTION, wavelength) - except KeyError as e: - logger.critical("The key " + str(Tags.DATA_FIELD_BLOOD_VOLUME_FRACTION) + " was not in the simpa output.") - show_blood_volume_fraction = False - blood_volume_fraction = None - - if show_linear_unmixing_sO2: - try: - linear_unmixing_output = get_data_field_from_simpa_output(file, Tags.LINEAR_UNMIXING_RESULT) - linear_unmixing_sO2 = linear_unmixing_output["sO2"] - except KeyError as e: - logger.critical("The key " + str(Tags.LINEAR_UNMIXING_RESULT) + " was not in the simpa output or blood " - "oxygen saturation was not computed.") - show_linear_unmixing_sO2 = False - linear_unmixing_sO2 = None - - cmap_label_names, cmap_label_values, cmap = get_segmentation_colormap() - - data_to_show = [] - data_item_names = [] - cmaps = [] - logscales = [] - - if diffuse_reflectance is not None and show_diffuse_reflectance: - fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) - plt.title("Diffuse reflectance") - ax.scatter(diffuse_reflectance_position[:, 0], - diffuse_reflectance_position[:, 1], - diffuse_reflectance_position[:, 2], - c=diffuse_reflectance, - cmap='RdBu', - antialiased=False) - ax.set_box_aspect((2, 1, 1)) - plt.show() - - if absorption is not None and show_absorption: - data_to_show.append(absorption) - data_item_names.append("Absorption Coefficient") - cmaps.append("gray") - logscales.append(True and log_scale) - if scattering is not None and show_scattering: - data_to_show.append(scattering) - data_item_names.append("Scattering Coefficient") - cmaps.append("gray") - logscales.append(True and log_scale) - if anisotropy is not None and show_anisotropy: - data_to_show.append(anisotropy) - data_item_names.append("Anisotropy") - cmaps.append("gray") - logscales.append(True and log_scale) - if speed_of_sound is not None and show_speed_of_sound: - data_to_show.append(speed_of_sound) - data_item_names.append("Speed of Sound") - cmaps.append("gray") - logscales.append(True and log_scale) - if density is not None and show_tissue_density: - data_to_show.append(density) - data_item_names.append("Density") - cmaps.append("gray") - logscales.append(True and log_scale) - if fluence is not None and show_fluence: - data_to_show.append(fluence) - data_item_names.append("Fluence") - cmaps.append("viridis") - logscales.append(True and log_scale) - if initial_pressure is not None and show_initial_pressure: - data_to_show.append(initial_pressure) - data_item_names.append("Initial Pressure") - cmaps.append("viridis") - logscales.append(True and log_scale) - if time_series_data is not None and show_time_series_data: - data_to_show.append(time_series_data) - data_item_names.append("Time Series Data") - cmaps.append("gray") - logscales.append(False and log_scale) - if reconstructed_data is not None and show_reconstructed_data: - data_to_show.append(reconstructed_data) - data_item_names.append("Reconstruction") - cmaps.append("viridis") - logscales.append(True and log_scale) - if oxygenation is not None and show_oxygenation: - data_to_show.append(oxygenation) - data_item_names.append("Oxygenation") - cmaps.append("viridis") - logscales.append(False and log_scale) - if blood_volume_fraction is not None and show_blood_volume_fraction: - data_to_show.append(blood_volume_fraction) - data_item_names.append("Blood Volume Fraction") - cmaps.append("viridis") - logscales.append(False and log_scale) - if linear_unmixing_sO2 is not None and show_linear_unmixing_sO2: - data_to_show.append(linear_unmixing_sO2) - data_item_names.append("Linear Unmixed Oxygenation") - cmaps.append("viridis") - logscales.append(False and log_scale) - if segmentation_map is not None and show_segmentation_map: - data_to_show.append(segmentation_map) - data_item_names.append("Segmentation Map") - cmaps.append(cmap) - logscales.append(False) - - if show_xz_only: - num_rows = 1 - else: - num_rows = 2 - - plt.figure(figsize=(len(data_to_show)*4, num_rows*3.5)) - for i in range(len(data_to_show)): - - plt.subplot(num_rows, len(data_to_show), i+1) - plt.title(data_item_names[i]) - if len(np.shape(data_to_show[i])) > 2: - pos = int(np.shape(data_to_show[i])[1] / 2) - 1 - data = data_to_show[i][:, pos, :].T - plt.imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) +from abc import ABC + + +class visualise_data(): + """ + + """ + + def __init__(self, + wavelengths: list = None, + path_to_hdf5_file: str = None, + settings: Settings = None, + path_manager: PathManager = None, + show_absorption=False, + show_scattering=False, + show_anisotropy=False, + show_speed_of_sound=False, + show_tissue_density=False, + show_fluence=False, + show_initial_pressure=False, + show_time_series_data=False, + show_reconstructed_data=False, + show_segmentation_map=False, + show_oxygenation=False, + show_blood_volume_fraction=False, + show_linear_unmixing_sO2=False, + show_diffuse_reflectance=False, + log_scale=False, + show_xz_only=False, + save_path=None, + add_wavelengths_slider: bool = True, + wavelength_step: bool = 10): + super().__init__() + + if path_to_hdf5_file is None and (settings is None or path_manager is None): + raise ValueError("Either the path_to_hdf5_file or the given settings and path_manager must not be None!") + + if path_to_hdf5_file is None: + path_to_hdf5_file = path_manager.get_hdf5_file_save_path() + "/" + settings[Tags.VOLUME_NAME] + ".hdf5" + + if settings is not None and Tags.WAVELENGTHS in settings: + if wavelengths is None or wavelengths not in settings[Tags.WAVELENGTHS]: + wavelengths = settings[Tags.WAVELENGTHS] + + self.show_absorption = show_absorption + self.show_scattering = show_scattering + self.show_anisotropy = show_anisotropy + self.show_speed_of_sound = show_speed_of_sound + self.show_tissue_density = show_tissue_density + self.show_fluence = show_fluence + self.show_initial_pressure = show_initial_pressure + self.show_time_series_data = show_time_series_data + self.show_reconstructed_data = show_reconstructed_data + self.show_segmentation_map = show_segmentation_map + self.show_oxygenation = show_oxygenation + self.show_blood_volume_fraction = show_blood_volume_fraction + self.show_linear_unmixing_sO2 = show_linear_unmixing_sO2 + self.show_diffuse_reflectance = show_diffuse_reflectance + + num_cols = 0 + for attribute in dir(self): + if not attribute.startswith('_') and getattr(self, attribute) is True: + num_cols += 1 + + self.show_xz_only = show_xz_only + self.path_to_hdf5_file = load_hdf5(path_to_hdf5_file) + self.log_scale = log_scale + self.save_path = save_path + self.logger = Logger() + + if self.show_xz_only: + num_rows = 1 + else: + num_rows = 2 + self.fig, self.axes = plt.subplots(num_rows, num_cols, figsize=(num_cols*4, num_rows*3.5)) + + self.plot_data_for_wavelength(wavelengths[0]) + plt.tight_layout() + + if len(wavelengths) != 0 and add_wavelengths_slider: + + # Make a vertically oriented slider to control the amplitude + ax_wavelength = self.fig.add_axes([0.1, 0.25, 0.0225, 0.63]) + wavelength_slider = Slider( + ax=ax_wavelength, + label="Wavelength (nm)", + valmin=np.min(wavelengths), + valmax=np.max(wavelengths), + valinit=np.min(wavelengths), + valstep=wavelength_step, + orientation="vertical" + ) + + # The function to be called anytime a slider's value changes + def update(val): + self.plot_data_for_wavelength(val, colour_bar=False) + self.fig.canvas.draw_idle() + + # register the update function with each slider + wavelength_slider.on_changed(update) + + plt.show() + + if save_path is not None: + plt.savefig(save_path, dpi=500) else: - data = data_to_show[i][:, :].T - plt.imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) - plt.colorbar() - - if not show_xz_only: - plt.subplot(num_rows, len(data_to_show), i + 1 + len(data_to_show)) - plt.title(data_item_names[i]) - if len(np.shape(data_to_show[i])) > 2: - pos = int(np.shape(data_to_show[i])[0] / 2) - data = data_to_show[i][pos, :, :].T - plt.imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) - else: - data = data_to_show[i][:, :].T - plt.imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) - plt.colorbar() - - plt.tight_layout() - if save_path is not None: - plt.savefig(save_path, dpi=500) - else: - plt.show() - plt.close() - - -def get_segmentation_colormap(): - values = [] - names = [] - - for string in SegmentationClasses.__dict__: - if string[0:2] != "__": - values.append(SegmentationClasses.__dict__[string]) - names.append(string) - - values = np.asarray(values) - names = np.asarray(names) - sort_indexes = np.argsort(values) - values = values[sort_indexes] - names = names[sort_indexes] - - colors = [list(np.random.random(3)) for _ in range(len(names))] - cmap = mpl.colors.LinearSegmentedColormap.from_list( - 'Custom cmap', colors, len(names)) - - return names, values, cmap + plt.show() + plt.close() + + def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = True): + + fluence = None + initial_pressure = None + time_series_data = None + reconstructed_data = None + oxygenation = None + blood_volume_fraction = None + linear_unmixing_sO2 = None + diffuse_reflectance = None + diffuse_reflectance_position = None + + absorption = get_data_field_from_simpa_output( + self.path_to_hdf5_file, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) + scattering = get_data_field_from_simpa_output( + self.path_to_hdf5_file, Tags.DATA_FIELD_SCATTERING_PER_CM, wavelength) + anisotropy = get_data_field_from_simpa_output(self.path_to_hdf5_file, Tags.DATA_FIELD_ANISOTROPY, wavelength) + segmentation_map = get_data_field_from_simpa_output(self.path_to_hdf5_file, Tags.DATA_FIELD_SEGMENTATION) + speed_of_sound = get_data_field_from_simpa_output(self.path_to_hdf5_file, Tags.DATA_FIELD_SPEED_OF_SOUND) + density = get_data_field_from_simpa_output(self.path_to_hdf5_file, Tags.DATA_FIELD_DENSITY) + + if self.show_fluence: + try: + fluence = get_data_field_from_simpa_output(self.path_to_hdf5_file, Tags.DATA_FIELD_FLUENCE, wavelength) + except KeyError as e: + self.logger.critical("The key " + str(Tags.DATA_FIELD_FLUENCE) + " was not in the simpa output.") + show_fluence = False + fluence = None + + if self.show_diffuse_reflectance: + try: + diffuse_reflectance = get_data_field_from_simpa_output(self.path_to_hdf5_file, + Tags.DATA_FIELD_DIFFUSE_REFLECTANCE, + wavelength) + diffuse_reflectance_position = get_data_field_from_simpa_output(self.path_to_hdf5_file, + Tags.DATA_FIELD_DIFFUSE_REFLECTANCE_POS, + wavelength) + except KeyError as e: + self.logger.critical("The key " + str(Tags.DATA_FIELD_FLUENCE) + " was not in the simpa output.") + show_fluence = False + fluence = None + + if self.show_initial_pressure: + try: + initial_pressure = get_data_field_from_simpa_output( + self.path_to_hdf5_file, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength) + except KeyError as e: + self.logger.critical("The key " + str(Tags.DATA_FIELD_INITIAL_PRESSURE) + + " was not in the simpa output.") + show_initial_pressure = False + initial_pressure = None + + if self.show_time_series_data: + try: + time_series_data = get_data_field_from_simpa_output( + self.path_to_hdf5_file, Tags.DATA_FIELD_TIME_SERIES_DATA, wavelength) + except KeyError as e: + self.logger.critical("The key " + str(Tags.DATA_FIELD_TIME_SERIES_DATA) + + " was not in the simpa output.") + show_time_series_data = False + time_series_data = None + + if self.show_reconstructed_data: + try: + reconstructed_data = get_data_field_from_simpa_output( + self.path_to_hdf5_file, Tags.DATA_FIELD_RECONSTRUCTED_DATA, wavelength) + except KeyError as e: + self.logger.critical("The key " + str(Tags.DATA_FIELD_RECONSTRUCTED_DATA) + + " was not in the simpa output.") + show_reconstructed_data = False + reconstructed_data = None + + if self.show_oxygenation: + try: + oxygenation = get_data_field_from_simpa_output( + self.path_to_hdf5_file, Tags.DATA_FIELD_OXYGENATION, wavelength) + except KeyError as e: + self.logger.critical("The key " + str(Tags.DATA_FIELD_OXYGENATION) + " was not in the simpa output.") + show_oxygenation = False + oxygenation = None + + if self.show_blood_volume_fraction: + try: + blood_volume_fraction = get_data_field_from_simpa_output( + self.path_to_hdf5_file, Tags.DATA_FIELD_BLOOD_VOLUME_FRACTION, wavelength) + except KeyError as e: + self.logger.critical("The key " + str(Tags.DATA_FIELD_BLOOD_VOLUME_FRACTION) + + " was not in the simpa output.") + show_blood_volume_fraction = False + blood_volume_fraction = None + + if self.show_linear_unmixing_sO2: + try: + linear_unmixing_output = get_data_field_from_simpa_output( + self.path_to_hdf5_file, Tags.LINEAR_UNMIXING_RESULT) + linear_unmixing_sO2 = linear_unmixing_output["sO2"] + except KeyError as e: + self.logger.critical("The key " + str(Tags.LINEAR_UNMIXING_RESULT) + " was not in the simpa output or blood " + "oxygen saturation was not computed.") + show_linear_unmixing_sO2 = False + linear_unmixing_sO2 = None + + cmap_label_names, cmap_label_values, cmap = self.get_segmentation_colormap() + + data_to_show = [] + data_item_names = [] + cmaps = [] + logscales = [] + + if diffuse_reflectance is not None and self.show_diffuse_reflectance: + fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) + plt.set_title("Diffuse reflectance") + ax.scatter(diffuse_reflectance_position[:, 0], + diffuse_reflectance_position[:, 1], + diffuse_reflectance_position[:, 2], + c=diffuse_reflectance, + cmap='RdBu', + antialiased=False) + ax.set_box_aspect((2, 1, 1)) + plt.show() + + if absorption is not None and self.show_absorption: + data_to_show.append(absorption) + data_item_names.append("Absorption Coefficient") + cmaps.append("gray") + logscales.append(True and self.log_scale) + if scattering is not None and self.show_scattering: + data_to_show.append(scattering) + data_item_names.append("Scattering Coefficient") + cmaps.append("gray") + logscales.append(True and self.log_scale) + if anisotropy is not None and self.show_anisotropy: + data_to_show.append(anisotropy) + data_item_names.append("Anisotropy") + cmaps.append("gray") + logscales.append(True and self.log_scale) + if speed_of_sound is not None and self.show_speed_of_sound: + data_to_show.append(speed_of_sound) + data_item_names.append("Speed of Sound") + cmaps.append("gray") + logscales.append(True and self.log_scale) + if density is not None and self.show_tissue_density: + data_to_show.append(density) + data_item_names.append("Density") + cmaps.append("gray") + logscales.append(True and self.log_scale) + if fluence is not None and self.show_fluence: + data_to_show.append(fluence) + data_item_names.append("Fluence") + cmaps.append("viridis") + logscales.append(True and self.log_scale) + if initial_pressure is not None and self.show_initial_pressure: + data_to_show.append(initial_pressure) + data_item_names.append("Initial Pressure") + cmaps.append("viridis") + logscales.append(True and self.log_scale) + if time_series_data is not None and self.show_time_series_data: + data_to_show.append(time_series_data) + data_item_names.append("Time Series Data") + cmaps.append("gray") + logscales.append(False and self.log_scale) + if reconstructed_data is not None and self.show_reconstructed_data: + data_to_show.append(reconstructed_data) + data_item_names.append("Reconstruction") + cmaps.append("viridis") + logscales.append(True and self.log_scale) + if oxygenation is not None and self.show_oxygenation: + data_to_show.append(oxygenation) + data_item_names.append("Oxygenation") + cmaps.append("viridis") + logscales.append(False and self.log_scale) + if blood_volume_fraction is not None and self.show_blood_volume_fraction: + data_to_show.append(blood_volume_fraction) + data_item_names.append("Blood Volume Fraction") + cmaps.append("viridis") + logscales.append(False and self.log_scale) + if linear_unmixing_sO2 is not None and self.show_linear_unmixing_sO2: + data_to_show.append(linear_unmixing_sO2) + data_item_names.append("Linear Unmixed Oxygenation") + cmaps.append("viridis") + logscales.append(False and self.log_scale) + if segmentation_map is not None and self.show_segmentation_map: + data_to_show.append(segmentation_map) + data_item_names.append("Segmentation Map") + cmaps.append(cmap) + logscales.append(False) + + for i in range(len(data_to_show)): + + if self.show_xz_only: + self.axes[i].clear() + self.axes[i].set_title(data_item_names[i]) + if len(np.shape(data_to_show[i])) > 2: + pos = int(np.shape(data_to_show[i])[1] / 2) - 1 + data = data_to_show[i][:, pos, :].T + img = self.axes[i].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + else: + data = data_to_show[i][:, :].T + img = self.axes[i].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + if colour_bar: + plt.colorbar(img, ax=self.axes[i]) + + elif len(data_to_show) == 1 and not self.show_xz_only: + self.axes[0].clear() + self.axes[0].set_title(data_item_names) + if len(np.shape(data_to_show[i])) > 2: + pos = int(np.shape(data_to_show[i])[1] / 2) - 1 + data = data_to_show[i][:, pos, :].T + img = self.axes[0].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + else: + data = data_to_show[i][:, :].T + img = self.axes[0].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + if colour_bar: + plt.colorbar(img, ax=self.axes[0]) + + self.axes[1].clear() + self.axes[1].set_title(data_item_names) + if len(np.shape(data_to_show[i])) > 2: + pos = int(np.shape(data_to_show[i])[0] / 2) + data = data_to_show[i][pos, :, :].T + img = self.axes[1].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + else: + data = data_to_show[i][:, :].T + img = self.axes[1].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + if colour_bar: + plt.colorbar(img, ax=self.axes[1]) + + elif not self.show_xz_only: + self.axes[0][i].clear() + self.axes[0][i].set_title(data_item_names[i]) + if len(np.shape(data_to_show[i])) > 2: + pos = int(np.shape(data_to_show[i])[1] / 2) - 1 + data = data_to_show[i][:, pos, :].T + img = self.axes[0, i].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + else: + data = data_to_show[i][:, :].T + img = self.axes[0, i].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + if colour_bar: + plt.colorbar(img, ax=self.axes[0][i]) + + self.axes[1][i].clear() + self.axes[1][i].set_title(data_item_names[i]) + if len(np.shape(data_to_show[i])) > 2: + pos = int(np.shape(data_to_show[i])[0] / 2) + data = data_to_show[i][pos, :, :].T + img = self.axes[1][i].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + else: + data = data_to_show[i][:, :].T + img = self.axes[1][i].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) + if colour_bar: + plt.colorbar(img, ax=self.axes[1][i]) + + def get_segmentation_colormap(self): + values = [] + names = [] + + for string in SegmentationClasses.__dict__: + if string[0:2] != "__": + values.append(SegmentationClasses.__dict__[string]) + names.append(string) + + values = np.asarray(values) + names = np.asarray(names) + sort_indexes = np.argsort(values) + values = values[sort_indexes] + names = names[sort_indexes] + + colors = [list(np.random.random(3)) for _ in range(len(names))] + cmap = mpl.colors.LinearSegmentedColormap.from_list( + 'Custom cmap', colors, len(names)) + + return names, values, cmap From 07a1370cca04736d9887110cbdb3a74aba7283de Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 8 Aug 2024 12:30:24 +0200 Subject: [PATCH 02/11] Adding docs and improving functionality --- .../matplotlib_data_visualisation.py | 97 ++++++++++++++++--- simpa_examples/linear_unmixing.py | 4 +- 2 files changed, 87 insertions(+), 14 deletions(-) diff --git a/simpa/visualisation/matplotlib_data_visualisation.py b/simpa/visualisation/matplotlib_data_visualisation.py index 82ed4a53..53d261e5 100644 --- a/simpa/visualisation/matplotlib_data_visualisation.py +++ b/simpa/visualisation/matplotlib_data_visualisation.py @@ -15,9 +15,32 @@ from abc import ABC -class visualise_data(): +class VisualiseData: """ - + A class to visualize data stored in an HDF5 file. It provides options to display various + physical properties such as absorption, scattering, anisotropy, speed of sound, tissue density, + fluence, initial pressure, time series data, reconstructed data, segmentation maps, + oxygenation, blood volume fraction, and linear unmixing results. + + The class supports visualizing data in 2D (xz-plane and zy-plane), and includes features such as + logarithmic scaling, saving visualizations, and adding a slider for wavelength selection. + + Attributes + ---------- + fig : matplotlib.figure.Figure + Figure object for plotting. + axes : numpy.ndarray + Array of axes for subplots. + logger : Logger + Logger object for handling logging. + + Methods + ------- + plot_data_for_wavelength(wavelength, colour_bar=True): + Plots data for a specific wavelength. + + get_segmentation_colormap(): + Returns the colormap for the segmentation map. """ def __init__(self, @@ -42,8 +65,34 @@ def __init__(self, log_scale=False, show_xz_only=False, save_path=None, - add_wavelengths_slider: bool = True, - wavelength_step: bool = 10): + add_wavelengths_slider: bool = True): + """ + :param wavelengths: A list of wavelengths to be visualized. + :param path_to_hdf5_file: Path to the HDF5 file containing the data. If not provided, it is constructed from settings and path_manager. + :param settings: An instance of Settings containing configuration parameters, including the volume name and wavelengths. + :param path_manager: An instance of PathManager used to manage file paths. It is used to generate the path to the HDF5 file if not provided. + :param show_absorption: If True, display the absorption coefficient. + :param show_scattering: If True, display the scattering coefficient. + :param show_anisotropy: If True, display the anisotropy. + :param show_speed_of_sound: If True, display the speed of sound. + :param show_tissue_density: If True, display the tissue density. + :param show_fluence: If True, display the fluence. + :param show_initial_pressure: If True, display the initial pressure. + :param show_time_series_data: If True, display the time series data. + :param show_reconstructed_data: If True, display the reconstructed data. + :param show_segmentation_map: If True, display the segmentation map. + :param show_oxygenation: If True, display the oxygenation. + :param show_blood_volume_fraction: If True, display the blood volume fraction. + :param show_linear_unmixing_sO2: If True, display the linear unmixing of oxygen saturation (sO2). + :param show_diffuse_reflectance: If True, display the diffuse reflectance. + :param log_scale: If True, use a logarithmic scale for the data. + :param show_xz_only: If True, display only the xz-plane. + :param save_path: Path to save the visualization. If None, the visualization will be displayed instead of saved. + :param add_wavelengths_slider: If True, add a slider for wavelength selection to the visualization. + + :raises ValueError: If both path_to_hdf5_file and the combination of settings and path_manager are None. + """ + super().__init__() if path_to_hdf5_file is None and (settings is None or path_manager is None): @@ -53,8 +102,7 @@ def __init__(self, path_to_hdf5_file = path_manager.get_hdf5_file_save_path() + "/" + settings[Tags.VOLUME_NAME] + ".hdf5" if settings is not None and Tags.WAVELENGTHS in settings: - if wavelengths is None or wavelengths not in settings[Tags.WAVELENGTHS]: - wavelengths = settings[Tags.WAVELENGTHS] + wavelengths = settings[Tags.WAVELENGTHS] self.show_absorption = show_absorption self.show_scattering = show_scattering @@ -92,8 +140,11 @@ def __init__(self, plt.tight_layout() if len(wavelengths) != 0 and add_wavelengths_slider: + # find the step in the wavelengths + wavelength_step = wavelengths[1] - wavelengths[0] - # Make a vertically oriented slider to control the amplitude + # Make a slider to control the wavelength + self.fig.subplots_adjust(left=0.25) ax_wavelength = self.fig.add_axes([0.1, 0.25, 0.0225, 0.63]) wavelength_slider = Slider( ax=ax_wavelength, @@ -105,12 +156,12 @@ def __init__(self, orientation="vertical" ) - # The function to be called anytime a slider's value changes + # Function to update the value with the slider def update(val): self.plot_data_for_wavelength(val, colour_bar=False) self.fig.canvas.draw_idle() - # register the update function with each slider + # update on changed slider value wavelength_slider.on_changed(update) plt.show() @@ -122,6 +173,15 @@ def update(val): plt.close() def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = True): + """ + Plots the data for a specific wavelength. The method handles various types of data, + including absorption, scattering, fluence, initial pressure, etc. + + :param wavelength: The wavelength for which data should be plotted. + :type wavelength: int + :param colour_bar: Whether to display the color bar. + :type colour_bar: bool + """ fluence = None initial_pressure = None @@ -223,7 +283,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr show_linear_unmixing_sO2 = False linear_unmixing_sO2 = None - cmap_label_names, cmap_label_values, cmap = self.get_segmentation_colormap() + cmap_label_names, cmap_label_values, cmap = self.get_segmentation_colourmap() data_to_show = [] data_item_names = [] @@ -232,7 +292,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr if diffuse_reflectance is not None and self.show_diffuse_reflectance: fig, ax = plt.subplots(subplot_kw={"projection": "3d"}) - plt.set_title("Diffuse reflectance") + plt.title("Diffuse reflectance") ax.scatter(diffuse_reflectance_position[:, 0], diffuse_reflectance_position[:, 1], diffuse_reflectance_position[:, 2], @@ -373,7 +433,20 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr if colour_bar: plt.colorbar(img, ax=self.axes[1][i]) - def get_segmentation_colormap(self): + def get_segmentation_colourmap(self): + """ + Generates a colormap for segmentation classes. + + This method creates a custom colormap based on the segmentation classes defined in the + `SegmentationClasses` class. Each class is assigned a random color. + + :return: A tuple containing the names of the segmentation classes, their corresponding values, and the colormap. + :rtype: tuple (np.ndarray, np.ndarray, mpl.colors.LinearSegmentedColormap) + - names: An array of the segmentation class names. + - values: An array of the segmentation class values. + - cmap: A matplotlib colormap object created from random colors. + """ + values = [] names = [] diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index e1b78f7f..4db349fc 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -173,7 +173,7 @@ def create_example_tissue(): # Visualize linear unmixing result if visualise: visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelength=WAVELENGTHS[0], + wavelengths=WAVELENGTHS, show_initial_pressure=True, show_oxygenation=True, show_linear_unmixing_sO2=True) @@ -181,7 +181,7 @@ def create_example_tissue(): if __name__ == "__main__": parser = ArgumentParser(description='Run the linear unmixing example') - parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--spacing", default=0.2, type=float, help='the voxel spacing in mm') parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') config = parser.parse_args() From c6fdcedbf89fc102fcd880da5374e51fcb8ba0ef Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 8 Aug 2024 13:00:17 +0200 Subject: [PATCH 03/11] updating examples and such --- simpa/__init__.py | 4 ++-- .../matplotlib_data_visualisation.py | 2 +- simpa_examples/linear_unmixing.py | 12 +++++----- simpa_examples/minimal_optical_simulation.py | 19 ++++++++------- ...minimal_optical_simulation_uniform_cube.py | 12 +++++----- simpa_examples/msot_invision_simulation.py | 12 +++++----- .../optical_and_acoustic_simulation.py | 18 +++++++------- .../perform_image_reconstruction.py | 8 +++---- simpa_examples/segmentation_loader.py | 12 +++++----- .../SimulationWithMSOTInvision.py | 16 ++++++------- .../ReproduceDISMeasurements.py | 16 ++++++------- .../volume_creation/SegmentationLoader.py | 24 +++++++++---------- 12 files changed, 78 insertions(+), 77 deletions(-) diff --git a/simpa/__init__.py b/simpa/__init__.py index b336a76f..7640aff0 100644 --- a/simpa/__init__.py +++ b/simpa/__init__.py @@ -10,7 +10,7 @@ __version__ = version("simpa") except PackageNotFoundError: __version__ = "unknown version" - + from .core.simulation_modules.volume_creation_module.volume_creation_module_model_based_adapter import \ ModelBasedVolumeCreationAdapter from .core.simulation_modules.volume_creation_module.volume_creation_module_segmentation_based_adapter import \ @@ -56,7 +56,7 @@ from .io_handling.zenodo_download import download_from_zenodo from .io_handling.ipasc import export_to_ipasc -from .visualisation.matplotlib_data_visualisation import visualise_data +from .visualisation.matplotlib_data_visualisation import VisualiseData from .visualisation.matplotlib_device_visualisation import visualise_device from .utils.quality_assurance.data_sanity_testing import assert_equal_shapes diff --git a/simpa/visualisation/matplotlib_data_visualisation.py b/simpa/visualisation/matplotlib_data_visualisation.py index 53d261e5..a95ec744 100644 --- a/simpa/visualisation/matplotlib_data_visualisation.py +++ b/simpa/visualisation/matplotlib_data_visualisation.py @@ -139,7 +139,7 @@ def __init__(self, self.plot_data_for_wavelength(wavelengths[0]) plt.tight_layout() - if len(wavelengths) != 0 and add_wavelengths_slider: + if len(wavelengths) != 1 and add_wavelengths_slider: # find the step in the wavelengths wavelength_step = wavelengths[1] - wavelengths[0] diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index 4db349fc..18734257 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -8,7 +8,7 @@ import simpa as sp from simpa import Tags -from simpa.visualisation.matplotlib_data_visualisation import visualise_data +from simpa.visualisation.matplotlib_data_visualisation import VisualiseData from simpa.utils.profiling import profile # FIXME temporary workaround for newest Intel architectures @@ -172,11 +172,11 @@ def create_example_tissue(): # Visualize linear unmixing result if visualise: - visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelengths=WAVELENGTHS, - show_initial_pressure=True, - show_oxygenation=True, - show_linear_unmixing_sO2=True) + VisualiseData(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + wavelengths=WAVELENGTHS, + show_initial_pressure=True, + show_oxygenation=True, + show_linear_unmixing_sO2=True) if __name__ == "__main__": diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index c3646f22..3e75d4b3 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -154,18 +154,19 @@ def __init__(self): sp.simulate(pipeline, settings, device) - if Tags.WAVELENGTH in settings: - WAVELENGTH = settings[Tags.WAVELENGTH] + if Tags.WAVELENGTHS in settings: + WAVELENGTHS = settings[Tags.WAVELENGTHS] + else: - WAVELENGTH = 700 + WAVELENGTHS = [798] if visualise: - sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelength=WAVELENGTH, - show_initial_pressure=True, - show_absorption=True, - show_diffuse_reflectance=SAVE_REFLECTANCE, - log_scale=True) + sp.VisualiseData(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + wavelengths=WAVELENGTHS, + show_initial_pressure=True, + show_absorption=True, + show_diffuse_reflectance=SAVE_REFLECTANCE, + log_scale=True) if __name__ == "__main__": diff --git a/simpa_examples/minimal_optical_simulation_uniform_cube.py b/simpa_examples/minimal_optical_simulation_uniform_cube.py index 675addd5..f74fe6f8 100644 --- a/simpa_examples/minimal_optical_simulation_uniform_cube.py +++ b/simpa_examples/minimal_optical_simulation_uniform_cube.py @@ -98,12 +98,12 @@ def create_example_tissue(): sp.simulate(pipeline, settings, device) if visualise: - sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelength=settings[Tags.WAVELENGTH], - show_initial_pressure=True, - show_absorption=True, - show_diffuse_reflectance=SAVE_REFLECTANCE, - log_scale=True) + sp.VisualiseData(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + wavelengths=settings[Tags.WAVELENGTHS], + show_initial_pressure=True, + show_absorption=True, + show_diffuse_reflectance=SAVE_REFLECTANCE, + log_scale=True) if __name__ == "__main__": diff --git a/simpa_examples/msot_invision_simulation.py b/simpa_examples/msot_invision_simulation.py index 210daf4b..381ae2e7 100644 --- a/simpa_examples/msot_invision_simulation.py +++ b/simpa_examples/msot_invision_simulation.py @@ -200,12 +200,12 @@ def get_settings(): sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) if visualise: - sp.visualise_data(settings=settings, - path_manager=path_manager, - show_absorption=True, - show_initial_pressure=True, - show_reconstructed_data=True, - show_xz_only=True) + sp.VisualiseData(settings=settings, + path_manager=path_manager, + show_absorption=True, + show_initial_pressure=True, + show_reconstructed_data=True, + show_xz_only=True) if __name__ == "__main__": diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 2346d921..3cb33a23 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -199,18 +199,18 @@ def create_example_tissue(): sp.simulate(SIMULATION_PIPELINE, settings, device) if Tags.WAVELENGTH in settings: - WAVELENGTH = settings[Tags.WAVELENGTH] + WAVELENGTHS = settings[Tags.WAVELENGTHS] else: - WAVELENGTH = 700 + WAVELENGTHS = [700] if visualise: - sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], - wavelength=WAVELENGTH, - show_time_series_data=True, - show_initial_pressure=True, - show_reconstructed_data=True, - log_scale=False, - show_xz_only=False) + sp.VisualiseData(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], + wavelengths=WAVELENGTHS, + show_time_series_data=True, + show_initial_pressure=True, + show_reconstructed_data=True, + log_scale=False, + show_xz_only=False) if __name__ == "__main__": diff --git a/simpa_examples/perform_image_reconstruction.py b/simpa_examples/perform_image_reconstruction.py index 2d1fa9b2..003e4b7f 100644 --- a/simpa_examples/perform_image_reconstruction.py +++ b/simpa_examples/perform_image_reconstruction.py @@ -50,10 +50,10 @@ def run_perform_image_reconstruction(SPACING: Union[int, float] = 0.5, path_mana reconstructed_image = np.squeeze(reconstructed_image) if visualise: - sp.visualise_data(path_to_hdf5_file=PATH, - wavelength=settings[Tags.WAVELENGTH], - show_reconstructed_data=True, - show_xz_only=True) + sp.VisualiseData(path_to_hdf5_file=PATH, + wavelengths=settings[Tags.WAVELENGTHS], + show_reconstructed_data=True, + show_xz_only=True) if __name__ == "__main__": diff --git a/simpa_examples/segmentation_loader.py b/simpa_examples/segmentation_loader.py index 8114ce0f..23aff12a 100644 --- a/simpa_examples/segmentation_loader.py +++ b/simpa_examples/segmentation_loader.py @@ -92,15 +92,15 @@ def segmentation_class_mapping(): sp.simulate(pipeline, settings, sp.RSOMExplorerP50(element_spacing_mm=1.0)) if Tags.WAVELENGTH in settings: - WAVELENGTH = settings[Tags.WAVELENGTH] + WAVELENGTHS = settings[Tags.WAVELENGTHS] else: - WAVELENGTH = 700 + WAVELENGTHS = [700] if visualise: - sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", - wavelength=WAVELENGTH, - show_initial_pressure=True, - show_segmentation_map=True) + sp.VisualiseData(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", + wavelength=WAVELENGTHS, + show_initial_pressure=True, + show_segmentation_map=True) if __name__ == "__main__": diff --git a/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py b/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py index 397d2da5..845b9906 100644 --- a/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py +++ b/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py @@ -9,7 +9,7 @@ from simpa.utils.libraries.molecule_library import Molecule, MolecularCompositionGenerator from simpa.utils.libraries.spectrum_library import AbsorptionSpectrumLibrary, AnisotropySpectrumLibrary, \ ScatteringSpectrumLibrary -from simpa.visualisation.matplotlib_data_visualisation import visualise_data +from simpa.visualisation.matplotlib_data_visualisation import VisualiseData import numpy as np from simpa.utils.path_manager import PathManager from simpa import DelayAndSumAdapter, MCXAdapter, KWaveAdapter, ModelBasedVolumeCreationAdapter, FieldOfViewCropping @@ -197,13 +197,13 @@ def tear_down(self): os.remove(self.settings[Tags.SIMPA_OUTPUT_PATH]) def visualise_result(self, show_figure_on_screen=True, save_path=None): - visualise_data(settings=self.settings, - path_manager=self.path_manager, - show_absorption=True, - show_initial_pressure=True, - show_reconstructed_data=True, - show_xz_only=True, - save_path=save_path + "InvisionSimulationTest.png") + VisualiseData(settings=self.settings, + path_manager=self.path_manager, + show_absorption=True, + show_initial_pressure=True, + show_reconstructed_data=True, + show_xz_only=True, + save_path=save_path + "InvisionSimulationTest.png") if __name__ == "__main__": diff --git a/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py b/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py index dc464bec..9de22052 100644 --- a/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py +++ b/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py @@ -23,7 +23,7 @@ from simpa.io_handling import load_data_field from simpa.core.device_digital_twins import * import numpy as np -from simpa.visualisation.matplotlib_data_visualisation import visualise_data +from simpa.visualisation.matplotlib_data_visualisation import VisualiseData from simpa import ModelBasedVolumeCreationAdapter, MCXAdapter from simpa_tests.manual_tests.test_with_experimental_measurements.utils import read_reference_spectra, read_rxt_file from simpa_tests.manual_tests import ManualIntegrationTestClass @@ -227,13 +227,13 @@ def visualise_result(self, show_figure_on_screen=True, save_path=None): if save_path is None: save_path = "" - visualise_data(path_to_hdf5_file=self.path_manager.get_hdf5_file_save_path() + "/" + self.VOLUME_NAME + ".hdf5", - wavelength=800, - show_segmentation_map=False, - show_absorption=True, - show_fluence=True, - log_scale=True, - save_path=save_path + "DIS_measurement_simulation_a.png") + VisualiseData(path_to_hdf5_file=self.path_manager.get_hdf5_file_save_path() + "/" + self.VOLUME_NAME + ".hdf5", + wavelengths=[800], + show_segmentation_map=False, + show_absorption=True, + show_fluence=True, + log_scale=True, + save_path=save_path + "DIS_measurement_simulation_a.png") measured_transmittance = np.asarray([self.transmittance_spectrum.get_value_for_wavelength(wl) for wl in self.settings[Tags.WAVELENGTHS]]) diff --git a/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py b/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py index 780d92a7..aac5a1d9 100644 --- a/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py +++ b/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py @@ -92,18 +92,18 @@ def visualise_result(self, show_figure_on_screen=True, save_path=None): else: save_path = save_path + "SegmentationLoaderExample.png" - sp.visualise_data(path_to_hdf5_file=self.path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", - wavelength=700, - show_initial_pressure=True, - show_segmentation_map=True, - show_absorption=True, - show_fluence=True, - show_tissue_density=True, - show_speed_of_sound=True, - show_anisotropy=True, - show_scattering=True, - save_path=save_path, - log_scale=False) + sp.VisualiseData(path_to_hdf5_file=self.path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", + wavelengths=[700], + show_initial_pressure=True, + show_segmentation_map=True, + show_absorption=True, + show_fluence=True, + show_tissue_density=True, + show_speed_of_sound=True, + show_anisotropy=True, + show_scattering=True, + save_path=save_path, + log_scale=False) if __name__ == "__main__": From 2b9d88d6cfbab4c1df1e9afa9fdd880a737e7260 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 8 Aug 2024 13:11:27 +0200 Subject: [PATCH 04/11] remove old imports --- simpa/visualisation/matplotlib_data_visualisation.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/simpa/visualisation/matplotlib_data_visualisation.py b/simpa/visualisation/matplotlib_data_visualisation.py index a95ec744..614aabf5 100644 --- a/simpa/visualisation/matplotlib_data_visualisation.py +++ b/simpa/visualisation/matplotlib_data_visualisation.py @@ -4,7 +4,7 @@ from simpa.io_handling import load_hdf5 import matplotlib.pyplot as plt -from matplotlib.widgets import Button, Slider +from matplotlib.widgets import Slider import matplotlib as mpl import numpy as np from simpa.utils import SegmentationClasses, Tags @@ -12,7 +12,6 @@ from simpa.utils.settings import Settings from simpa.utils import get_data_field_from_simpa_output from simpa.log import Logger -from abc import ABC class VisualiseData: From dffb04ca4c13fc2a5c49b00373c6222f10bbb125 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 8 Aug 2024 13:21:26 +0200 Subject: [PATCH 05/11] fix wavelength vs wavelengths problem --- simpa_examples/optical_and_acoustic_simulation.py | 2 +- simpa_examples/segmentation_loader.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 3cb33a23..f4c19eb3 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -198,7 +198,7 @@ def create_example_tissue(): sp.simulate(SIMULATION_PIPELINE, settings, device) - if Tags.WAVELENGTH in settings: + if Tags.WAVELENGTHS in settings: WAVELENGTHS = settings[Tags.WAVELENGTHS] else: WAVELENGTHS = [700] diff --git a/simpa_examples/segmentation_loader.py b/simpa_examples/segmentation_loader.py index 23aff12a..d75806fd 100644 --- a/simpa_examples/segmentation_loader.py +++ b/simpa_examples/segmentation_loader.py @@ -91,14 +91,14 @@ def segmentation_class_mapping(): sp.simulate(pipeline, settings, sp.RSOMExplorerP50(element_spacing_mm=1.0)) - if Tags.WAVELENGTH in settings: + if Tags.WAVELENGTHS in settings: WAVELENGTHS = settings[Tags.WAVELENGTHS] else: WAVELENGTHS = [700] if visualise: sp.VisualiseData(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", - wavelength=WAVELENGTHS, + wavelengths=WAVELENGTHS, show_initial_pressure=True, show_segmentation_map=True) From 33c900dcbc124d38162f9834aade82e266fb3cb5 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Mon, 12 Aug 2024 10:00:27 +0200 Subject: [PATCH 06/11] Fix missing self. error and adding type hints --- .../matplotlib_data_visualisation.py | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/simpa/visualisation/matplotlib_data_visualisation.py b/simpa/visualisation/matplotlib_data_visualisation.py index 614aabf5..9571293d 100644 --- a/simpa/visualisation/matplotlib_data_visualisation.py +++ b/simpa/visualisation/matplotlib_data_visualisation.py @@ -47,23 +47,23 @@ def __init__(self, path_to_hdf5_file: str = None, settings: Settings = None, path_manager: PathManager = None, - show_absorption=False, - show_scattering=False, - show_anisotropy=False, - show_speed_of_sound=False, - show_tissue_density=False, - show_fluence=False, - show_initial_pressure=False, - show_time_series_data=False, - show_reconstructed_data=False, - show_segmentation_map=False, - show_oxygenation=False, - show_blood_volume_fraction=False, - show_linear_unmixing_sO2=False, - show_diffuse_reflectance=False, - log_scale=False, - show_xz_only=False, - save_path=None, + show_absorption: bool = False, + show_scattering: bool = False, + show_anisotropy: bool = False, + show_speed_of_sound: bool = False, + show_tissue_density: bool = False, + show_fluence: bool = False, + show_initial_pressure: bool = False, + show_time_series_data: bool = False, + show_reconstructed_data: bool = False, + show_segmentation_map: bool = False, + show_oxygenation: bool = False, + show_blood_volume_fraction: bool = False, + show_linear_unmixing_sO2: bool = False, + show_diffuse_reflectance: bool = False, + log_scale: bool = False, + show_xz_only: bool = False, + save_path: bool = None, add_wavelengths_slider: bool = True): """ :param wavelengths: A list of wavelengths to be visualized. @@ -206,7 +206,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr fluence = get_data_field_from_simpa_output(self.path_to_hdf5_file, Tags.DATA_FIELD_FLUENCE, wavelength) except KeyError as e: self.logger.critical("The key " + str(Tags.DATA_FIELD_FLUENCE) + " was not in the simpa output.") - show_fluence = False + self.show_fluence = False fluence = None if self.show_diffuse_reflectance: @@ -219,7 +219,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr wavelength) except KeyError as e: self.logger.critical("The key " + str(Tags.DATA_FIELD_FLUENCE) + " was not in the simpa output.") - show_fluence = False + self.show_fluence = False fluence = None if self.show_initial_pressure: @@ -229,7 +229,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr except KeyError as e: self.logger.critical("The key " + str(Tags.DATA_FIELD_INITIAL_PRESSURE) + " was not in the simpa output.") - show_initial_pressure = False + self.show_initial_pressure = False initial_pressure = None if self.show_time_series_data: @@ -239,7 +239,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr except KeyError as e: self.logger.critical("The key " + str(Tags.DATA_FIELD_TIME_SERIES_DATA) + " was not in the simpa output.") - show_time_series_data = False + self.show_time_series_data = False time_series_data = None if self.show_reconstructed_data: @@ -249,7 +249,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr except KeyError as e: self.logger.critical("The key " + str(Tags.DATA_FIELD_RECONSTRUCTED_DATA) + " was not in the simpa output.") - show_reconstructed_data = False + self.show_reconstructed_data = False reconstructed_data = None if self.show_oxygenation: @@ -258,7 +258,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr self.path_to_hdf5_file, Tags.DATA_FIELD_OXYGENATION, wavelength) except KeyError as e: self.logger.critical("The key " + str(Tags.DATA_FIELD_OXYGENATION) + " was not in the simpa output.") - show_oxygenation = False + self.show_oxygenation = False oxygenation = None if self.show_blood_volume_fraction: @@ -268,7 +268,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr except KeyError as e: self.logger.critical("The key " + str(Tags.DATA_FIELD_BLOOD_VOLUME_FRACTION) + " was not in the simpa output.") - show_blood_volume_fraction = False + self.show_blood_volume_fraction = False blood_volume_fraction = None if self.show_linear_unmixing_sO2: @@ -279,7 +279,7 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr except KeyError as e: self.logger.critical("The key " + str(Tags.LINEAR_UNMIXING_RESULT) + " was not in the simpa output or blood " "oxygen saturation was not computed.") - show_linear_unmixing_sO2 = False + self.show_linear_unmixing_sO2 = False linear_unmixing_sO2 = None cmap_label_names, cmap_label_values, cmap = self.get_segmentation_colourmap() From a6c930ae153297a0d0458e75a9e4c400e13d9c15 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Mon, 12 Aug 2024 14:51:32 +0200 Subject: [PATCH 07/11] return visualise_data to being a function --- meat_heterogeneities/csac.py | 17 ++ simpa/__init__.py | 2 +- .../matplotlib_data_visualisation.py | 185 +++++++++++++----- simpa_examples/linear_unmixing.py | 14 +- simpa_examples/minimal_optical_simulation.py | 18 +- ...minimal_optical_simulation_uniform_cube.py | 12 +- simpa_examples/msot_invision_simulation.py | 12 +- .../optical_and_acoustic_simulation.py | 21 +- .../perform_image_reconstruction.py | 8 +- simpa_examples/segmentation_loader.py | 14 +- .../SimulationWithMSOTInvision.py | 16 +- .../ReproduceDISMeasurements.py | 16 +- .../volume_creation/SegmentationLoader.py | 24 +-- 13 files changed, 238 insertions(+), 121 deletions(-) create mode 100644 meat_heterogeneities/csac.py diff --git a/meat_heterogeneities/csac.py b/meat_heterogeneities/csac.py new file mode 100644 index 00000000..00a49e78 --- /dev/null +++ b/meat_heterogeneities/csac.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import matplotlib.pyplot as plt + +import simpa as sp +import nrrd +file = "/home/f762e/Workspace/data/Simulated_data/meat_2/both_exponential_51_0.6_0_0.1_1400_1640_961_1178.hdf5" +sp.VisualiseData(path_to_hdf5_file=file, show_initial_pressure=True, + show_blood_volume_fraction=True, show_reconstructed_data=True, wavelengths=[700, 800], log_scale=True) + +nrrd_file = "/home/f762e/Workspace/data/Real_data/meat_measurements/meat_study_pa/Scan_50_pa.nrrd" +data, header = nrrd.read(nrrd_file) + +plt.imshow(data[:, :, 0, 10]) +plt.show() diff --git a/simpa/__init__.py b/simpa/__init__.py index 7640aff0..c25f2ebb 100644 --- a/simpa/__init__.py +++ b/simpa/__init__.py @@ -56,7 +56,7 @@ from .io_handling.zenodo_download import download_from_zenodo from .io_handling.ipasc import export_to_ipasc -from .visualisation.matplotlib_data_visualisation import VisualiseData +from .visualisation.matplotlib_data_visualisation import visualise_data from .visualisation.matplotlib_device_visualisation import visualise_device from .utils.quality_assurance.data_sanity_testing import assert_equal_shapes diff --git a/simpa/visualisation/matplotlib_data_visualisation.py b/simpa/visualisation/matplotlib_data_visualisation.py index 9571293d..6e6d042d 100644 --- a/simpa/visualisation/matplotlib_data_visualisation.py +++ b/simpa/visualisation/matplotlib_data_visualisation.py @@ -43,7 +43,7 @@ class VisualiseData: """ def __init__(self, - wavelengths: list = None, + wavelength: int | float = None, path_to_hdf5_file: str = None, settings: Settings = None, path_manager: PathManager = None, @@ -62,11 +62,9 @@ def __init__(self, show_linear_unmixing_sO2: bool = False, show_diffuse_reflectance: bool = False, log_scale: bool = False, - show_xz_only: bool = False, - save_path: bool = None, - add_wavelengths_slider: bool = True): + show_xz_only: bool = False): """ - :param wavelengths: A list of wavelengths to be visualized. + :param wavelength: The wavelength to be initially visualized. :param path_to_hdf5_file: Path to the HDF5 file containing the data. If not provided, it is constructed from settings and path_manager. :param settings: An instance of Settings containing configuration parameters, including the volume name and wavelengths. :param path_manager: An instance of PathManager used to manage file paths. It is used to generate the path to the HDF5 file if not provided. @@ -94,15 +92,20 @@ def __init__(self, super().__init__() + if settings is not None and Tags.WAVELENGTHS in settings: + if wavelength is None or wavelength not in settings[Tags.WAVELENGTHS]: + wavelength = settings[Tags.WAVELENGTHS][0] + + if settings is not None and Tags.WAVELENGTH in settings: + wavelength = settings[Tags.WAVELENGTH] + if path_to_hdf5_file is None and (settings is None or path_manager is None): - raise ValueError("Either the path_to_hdf5_file or the given settings and path_manager must not be None!") + raise ValueError( + "Either the path_to_hdf5_file or the given settings and path_manager must not be None!") if path_to_hdf5_file is None: path_to_hdf5_file = path_manager.get_hdf5_file_save_path() + "/" + settings[Tags.VOLUME_NAME] + ".hdf5" - if settings is not None and Tags.WAVELENGTHS in settings: - wavelengths = settings[Tags.WAVELENGTHS] - self.show_absorption = show_absorption self.show_scattering = show_scattering self.show_anisotropy = show_anisotropy @@ -126,7 +129,6 @@ def __init__(self, self.show_xz_only = show_xz_only self.path_to_hdf5_file = load_hdf5(path_to_hdf5_file) self.log_scale = log_scale - self.save_path = save_path self.logger = Logger() if self.show_xz_only: @@ -135,51 +137,16 @@ def __init__(self, num_rows = 2 self.fig, self.axes = plt.subplots(num_rows, num_cols, figsize=(num_cols*4, num_rows*3.5)) - self.plot_data_for_wavelength(wavelengths[0]) + self.plot_data_for_wavelength(wavelength) plt.tight_layout() - if len(wavelengths) != 1 and add_wavelengths_slider: - # find the step in the wavelengths - wavelength_step = wavelengths[1] - wavelengths[0] - - # Make a slider to control the wavelength - self.fig.subplots_adjust(left=0.25) - ax_wavelength = self.fig.add_axes([0.1, 0.25, 0.0225, 0.63]) - wavelength_slider = Slider( - ax=ax_wavelength, - label="Wavelength (nm)", - valmin=np.min(wavelengths), - valmax=np.max(wavelengths), - valinit=np.min(wavelengths), - valstep=wavelength_step, - orientation="vertical" - ) - - # Function to update the value with the slider - def update(val): - self.plot_data_for_wavelength(val, colour_bar=False) - self.fig.canvas.draw_idle() - - # update on changed slider value - wavelength_slider.on_changed(update) - - plt.show() - - if save_path is not None: - plt.savefig(save_path, dpi=500) - else: - plt.show() - plt.close() - def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = True): """ Plots the data for a specific wavelength. The method handles various types of data, including absorption, scattering, fluence, initial pressure, etc. :param wavelength: The wavelength for which data should be plotted. - :type wavelength: int :param colour_bar: Whether to display the color bar. - :type colour_bar: bool """ fluence = None @@ -465,3 +432,129 @@ def get_segmentation_colourmap(self): 'Custom cmap', colors, len(names)) return names, values, cmap + + def add_wavelength_slider(self, wavelengths, settings): + """ + Add a slider for the wavelengths + :param settings: The settings object that contains the wavelength values. + :param wavelengths: The wavelengths the slider can use + """ + + if settings is not None and Tags.WAVELENGTH in settings: + wavelengths = settings[Tags.WAVELENGTHS] + # find the step in the wavelengths + wavelength_step = wavelengths[1] - wavelengths[0] + + # Make a slider to control the wavelength + self.fig.subplots_adjust(left=0.25) + ax_wavelength = self.fig.add_axes([0.1, 0.25, 0.0225, 0.63]) + wavelength_slider = Slider( + ax=ax_wavelength, + label="Wavelength (nm)", + valmin=np.min(wavelengths), + valmax=np.max(wavelengths), + valinit=np.min(wavelengths), + valstep=wavelength_step, + orientation="vertical" + ) + + # Function to update the value with the slider + def update(val): + self.plot_data_for_wavelength(val, colour_bar=False) + self.fig.canvas.draw_idle() + + # update on changed slider value + wavelength_slider.on_changed(update) + plt.show() + + def save_fig(self, save_path): + """ + Save the figure to a file. + :param save_path: Path to save the visualization. If None, the visualization will be displayed instead of saved. + """ + plt.savefig(save_path, dpi=500) + + def show_fig(self): + """ + Show the plot + """ + plt.show() + + +def visualise_data(wavelength: int | float = None, + wavelengths: list = None, + path_to_hdf5_file: str = None, + settings: Settings = None, + path_manager: PathManager = None, + show_absorption: bool = False, + show_scattering: bool = False, + show_anisotropy: bool = False, + show_speed_of_sound: bool = False, + show_tissue_density: bool = False, + show_fluence: bool = False, + show_initial_pressure: bool = False, + show_time_series_data: bool = False, + show_reconstructed_data: bool = False, + show_segmentation_map: bool = False, + show_oxygenation: bool = False, + show_blood_volume_fraction: bool = False, + show_linear_unmixing_sO2: bool = False, + show_diffuse_reflectance: bool = False, + log_scale: bool = False, + show_xz_only: bool = False, + save_path: str = None, + add_wavelengths_slider: bool = False): + """ + + :param wavelength: The wavelength to be initially visualized. + :param wavelengths: The wavelengths to be visualized in the slider. + :param path_to_hdf5_file: Path to the HDF5 file containing the data. If not provided, it is constructed from settings and path_manager. + :param settings: An instance of Settings containing configuration parameters, including the volume name and wavelengths. + :param path_manager: An instance of PathManager used to manage file paths. It is used to generate the path to the HDF5 file if not provided. + :param show_absorption: If True, display the absorption coefficient. + :param show_scattering: If True, display the scattering coefficient. + :param show_anisotropy: If True, display the anisotropy. + :param show_speed_of_sound: If True, display the speed of sound. + :param show_tissue_density: If True, display the tissue density. + :param show_fluence: If True, display the fluence. + :param show_initial_pressure: If True, display the initial pressure. + :param show_time_series_data: If True, display the time series data. + :param show_reconstructed_data: If True, display the reconstructed data. + :param show_segmentation_map: If True, display the segmentation map. + :param show_oxygenation: If True, display the oxygenation. + :param show_blood_volume_fraction: If True, display the blood volume fraction. + :param show_linear_unmixing_sO2: If True, display the linear unmixing of oxygen saturation (sO2). + :param show_diffuse_reflectance: If True, display the diffuse reflectance. + :param log_scale: If True, use a logarithmic scale for the data. + :param show_xz_only: If True, display only the xz-plane. + :param save_path: Path to save the visualization. If None, the visualization will be displayed instead of saved. + :param add_wavelengths_slider: If True, add a slider for wavelength selection to the visualization. + """ + plot = VisualiseData(wavelength, + path_to_hdf5_file, + settings, + path_manager, + show_absorption, + show_scattering, + show_anisotropy, + show_speed_of_sound, + show_tissue_density, + show_fluence, + show_initial_pressure, + show_time_series_data, + show_reconstructed_data, + show_segmentation_map, + show_oxygenation, + show_blood_volume_fraction, + show_linear_unmixing_sO2, + show_diffuse_reflectance, + log_scale, + show_xz_only) + + if add_wavelengths_slider: + plot.add_wavelength_slider(wavelengths, settings) + + if save_path is not None: + plot.save_fig(save_path) + else: + plot.show_fig() diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index 18734257..6dc696a2 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -8,7 +8,7 @@ import simpa as sp from simpa import Tags -from simpa.visualisation.matplotlib_data_visualisation import VisualiseData +from simpa.visualisation.matplotlib_data_visualisation import visualise_data from simpa.utils.profiling import profile # FIXME temporary workaround for newest Intel architectures @@ -172,11 +172,13 @@ def create_example_tissue(): # Visualize linear unmixing result if visualise: - VisualiseData(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelengths=WAVELENGTHS, - show_initial_pressure=True, - show_oxygenation=True, - show_linear_unmixing_sO2=True) + visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + wavelength=WAVELENGTHS[0], + wavelengths=WAVELENGTHS, + show_initial_pressure=True, + show_oxygenation=True, + show_linear_unmixing_sO2=True, + add_wavelengths_slider=True) if __name__ == "__main__": diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index 3e75d4b3..fba69ba5 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -155,18 +155,18 @@ def __init__(self): sp.simulate(pipeline, settings, device) if Tags.WAVELENGTHS in settings: - WAVELENGTHS = settings[Tags.WAVELENGTHS] - + WAVELENGTH = settings[Tags.WAVELENGTH] else: - WAVELENGTHS = [798] + WAVELENGTH = 700 if visualise: - sp.VisualiseData(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelengths=WAVELENGTHS, - show_initial_pressure=True, - show_absorption=True, - show_diffuse_reflectance=SAVE_REFLECTANCE, - log_scale=True) + sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + wavelength=700, + wavelengths=WAVELENGTH, + show_initial_pressure=True, + show_absorption=True, + show_diffuse_reflectance=SAVE_REFLECTANCE, + log_scale=True) if __name__ == "__main__": diff --git a/simpa_examples/minimal_optical_simulation_uniform_cube.py b/simpa_examples/minimal_optical_simulation_uniform_cube.py index f74fe6f8..675addd5 100644 --- a/simpa_examples/minimal_optical_simulation_uniform_cube.py +++ b/simpa_examples/minimal_optical_simulation_uniform_cube.py @@ -98,12 +98,12 @@ def create_example_tissue(): sp.simulate(pipeline, settings, device) if visualise: - sp.VisualiseData(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelengths=settings[Tags.WAVELENGTHS], - show_initial_pressure=True, - show_absorption=True, - show_diffuse_reflectance=SAVE_REFLECTANCE, - log_scale=True) + sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + wavelength=settings[Tags.WAVELENGTH], + show_initial_pressure=True, + show_absorption=True, + show_diffuse_reflectance=SAVE_REFLECTANCE, + log_scale=True) if __name__ == "__main__": diff --git a/simpa_examples/msot_invision_simulation.py b/simpa_examples/msot_invision_simulation.py index 381ae2e7..210daf4b 100644 --- a/simpa_examples/msot_invision_simulation.py +++ b/simpa_examples/msot_invision_simulation.py @@ -200,12 +200,12 @@ def get_settings(): sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) if visualise: - sp.VisualiseData(settings=settings, - path_manager=path_manager, - show_absorption=True, - show_initial_pressure=True, - show_reconstructed_data=True, - show_xz_only=True) + sp.visualise_data(settings=settings, + path_manager=path_manager, + show_absorption=True, + show_initial_pressure=True, + show_reconstructed_data=True, + show_xz_only=True) if __name__ == "__main__": diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index f4c19eb3..1fea01bd 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -198,19 +198,24 @@ def create_example_tissue(): sp.simulate(SIMULATION_PIPELINE, settings, device) + if Tags.WAVELENGTH in settings: + WAVELENGTH = settings[Tags.WAVELENGTH] + else: + WAVELENGTH = 700 + if Tags.WAVELENGTHS in settings: WAVELENGTHS = settings[Tags.WAVELENGTHS] else: - WAVELENGTHS = [700] + WAVELENGTHS = [700, 800] if visualise: - sp.VisualiseData(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], - wavelengths=WAVELENGTHS, - show_time_series_data=True, - show_initial_pressure=True, - show_reconstructed_data=True, - log_scale=False, - show_xz_only=False) + sp.visualise_data(wavelength=700, path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], + wavelengths=[700, 800], + show_time_series_data=True, + show_initial_pressure=True, + show_reconstructed_data=True, + log_scale=False, + show_xz_only=False, add_wavelengths_slider=True) if __name__ == "__main__": diff --git a/simpa_examples/perform_image_reconstruction.py b/simpa_examples/perform_image_reconstruction.py index 003e4b7f..2d1fa9b2 100644 --- a/simpa_examples/perform_image_reconstruction.py +++ b/simpa_examples/perform_image_reconstruction.py @@ -50,10 +50,10 @@ def run_perform_image_reconstruction(SPACING: Union[int, float] = 0.5, path_mana reconstructed_image = np.squeeze(reconstructed_image) if visualise: - sp.VisualiseData(path_to_hdf5_file=PATH, - wavelengths=settings[Tags.WAVELENGTHS], - show_reconstructed_data=True, - show_xz_only=True) + sp.visualise_data(path_to_hdf5_file=PATH, + wavelength=settings[Tags.WAVELENGTH], + show_reconstructed_data=True, + show_xz_only=True) if __name__ == "__main__": diff --git a/simpa_examples/segmentation_loader.py b/simpa_examples/segmentation_loader.py index d75806fd..8114ce0f 100644 --- a/simpa_examples/segmentation_loader.py +++ b/simpa_examples/segmentation_loader.py @@ -91,16 +91,16 @@ def segmentation_class_mapping(): sp.simulate(pipeline, settings, sp.RSOMExplorerP50(element_spacing_mm=1.0)) - if Tags.WAVELENGTHS in settings: - WAVELENGTHS = settings[Tags.WAVELENGTHS] + if Tags.WAVELENGTH in settings: + WAVELENGTH = settings[Tags.WAVELENGTH] else: - WAVELENGTHS = [700] + WAVELENGTH = 700 if visualise: - sp.VisualiseData(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", - wavelengths=WAVELENGTHS, - show_initial_pressure=True, - show_segmentation_map=True) + sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", + wavelength=WAVELENGTH, + show_initial_pressure=True, + show_segmentation_map=True) if __name__ == "__main__": diff --git a/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py b/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py index 845b9906..397d2da5 100644 --- a/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py +++ b/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py @@ -9,7 +9,7 @@ from simpa.utils.libraries.molecule_library import Molecule, MolecularCompositionGenerator from simpa.utils.libraries.spectrum_library import AbsorptionSpectrumLibrary, AnisotropySpectrumLibrary, \ ScatteringSpectrumLibrary -from simpa.visualisation.matplotlib_data_visualisation import VisualiseData +from simpa.visualisation.matplotlib_data_visualisation import visualise_data import numpy as np from simpa.utils.path_manager import PathManager from simpa import DelayAndSumAdapter, MCXAdapter, KWaveAdapter, ModelBasedVolumeCreationAdapter, FieldOfViewCropping @@ -197,13 +197,13 @@ def tear_down(self): os.remove(self.settings[Tags.SIMPA_OUTPUT_PATH]) def visualise_result(self, show_figure_on_screen=True, save_path=None): - VisualiseData(settings=self.settings, - path_manager=self.path_manager, - show_absorption=True, - show_initial_pressure=True, - show_reconstructed_data=True, - show_xz_only=True, - save_path=save_path + "InvisionSimulationTest.png") + visualise_data(settings=self.settings, + path_manager=self.path_manager, + show_absorption=True, + show_initial_pressure=True, + show_reconstructed_data=True, + show_xz_only=True, + save_path=save_path + "InvisionSimulationTest.png") if __name__ == "__main__": diff --git a/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py b/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py index 9de22052..dc464bec 100644 --- a/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py +++ b/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py @@ -23,7 +23,7 @@ from simpa.io_handling import load_data_field from simpa.core.device_digital_twins import * import numpy as np -from simpa.visualisation.matplotlib_data_visualisation import VisualiseData +from simpa.visualisation.matplotlib_data_visualisation import visualise_data from simpa import ModelBasedVolumeCreationAdapter, MCXAdapter from simpa_tests.manual_tests.test_with_experimental_measurements.utils import read_reference_spectra, read_rxt_file from simpa_tests.manual_tests import ManualIntegrationTestClass @@ -227,13 +227,13 @@ def visualise_result(self, show_figure_on_screen=True, save_path=None): if save_path is None: save_path = "" - VisualiseData(path_to_hdf5_file=self.path_manager.get_hdf5_file_save_path() + "/" + self.VOLUME_NAME + ".hdf5", - wavelengths=[800], - show_segmentation_map=False, - show_absorption=True, - show_fluence=True, - log_scale=True, - save_path=save_path + "DIS_measurement_simulation_a.png") + visualise_data(path_to_hdf5_file=self.path_manager.get_hdf5_file_save_path() + "/" + self.VOLUME_NAME + ".hdf5", + wavelength=800, + show_segmentation_map=False, + show_absorption=True, + show_fluence=True, + log_scale=True, + save_path=save_path + "DIS_measurement_simulation_a.png") measured_transmittance = np.asarray([self.transmittance_spectrum.get_value_for_wavelength(wl) for wl in self.settings[Tags.WAVELENGTHS]]) diff --git a/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py b/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py index aac5a1d9..780d92a7 100644 --- a/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py +++ b/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py @@ -92,18 +92,18 @@ def visualise_result(self, show_figure_on_screen=True, save_path=None): else: save_path = save_path + "SegmentationLoaderExample.png" - sp.VisualiseData(path_to_hdf5_file=self.path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", - wavelengths=[700], - show_initial_pressure=True, - show_segmentation_map=True, - show_absorption=True, - show_fluence=True, - show_tissue_density=True, - show_speed_of_sound=True, - show_anisotropy=True, - show_scattering=True, - save_path=save_path, - log_scale=False) + sp.visualise_data(path_to_hdf5_file=self.path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", + wavelength=700, + show_initial_pressure=True, + show_segmentation_map=True, + show_absorption=True, + show_fluence=True, + show_tissue_density=True, + show_speed_of_sound=True, + show_anisotropy=True, + show_scattering=True, + save_path=save_path, + log_scale=False) if __name__ == "__main__": From 8f3f9b65ad522a6b36cc6a64475de8f740ddb28c Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Mon, 12 Aug 2024 14:53:22 +0200 Subject: [PATCH 08/11] remove accidental add --- meat_heterogeneities/csac.py | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 meat_heterogeneities/csac.py diff --git a/meat_heterogeneities/csac.py b/meat_heterogeneities/csac.py deleted file mode 100644 index 00a49e78..00000000 --- a/meat_heterogeneities/csac.py +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ -# SPDX-FileCopyrightText: 2021 Janek Groehl -# SPDX-License-Identifier: MIT - -import matplotlib.pyplot as plt - -import simpa as sp -import nrrd -file = "/home/f762e/Workspace/data/Simulated_data/meat_2/both_exponential_51_0.6_0_0.1_1400_1640_961_1178.hdf5" -sp.VisualiseData(path_to_hdf5_file=file, show_initial_pressure=True, - show_blood_volume_fraction=True, show_reconstructed_data=True, wavelengths=[700, 800], log_scale=True) - -nrrd_file = "/home/f762e/Workspace/data/Real_data/meat_measurements/meat_study_pa/Scan_50_pa.nrrd" -data, header = nrrd.read(nrrd_file) - -plt.imshow(data[:, :, 0, 10]) -plt.show() From 19a8dfbccd344d577b3682dbb65cbc042ebf1978 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Mon, 12 Aug 2024 14:56:15 +0200 Subject: [PATCH 09/11] return examples to earlier version --- simpa_examples/linear_unmixing.py | 2 +- simpa_examples/minimal_optical_simulation.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index 6dc696a2..cf07377c 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -183,7 +183,7 @@ def create_example_tissue(): if __name__ == "__main__": parser = ArgumentParser(description='Run the linear unmixing example') - parser.add_argument("--spacing", default=0.2, type=float, help='the voxel spacing in mm') + parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') config = parser.parse_args() diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index fba69ba5..c3646f22 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -154,15 +154,14 @@ def __init__(self): sp.simulate(pipeline, settings, device) - if Tags.WAVELENGTHS in settings: + if Tags.WAVELENGTH in settings: WAVELENGTH = settings[Tags.WAVELENGTH] else: WAVELENGTH = 700 if visualise: sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelength=700, - wavelengths=WAVELENGTH, + wavelength=WAVELENGTH, show_initial_pressure=True, show_absorption=True, show_diffuse_reflectance=SAVE_REFLECTANCE, From 658e46484ecc94bfdf4ddc185418512f463b7a50 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 15 Aug 2024 09:44:20 +0200 Subject: [PATCH 10/11] reverting to Union --- ...vice_digital_twins.detection_geometries.rst | 6 ++++++ ...e_digital_twins.illumination_geometries.rst | 6 ++++++ ...pa.core.device_digital_twins.pa_devices.rst | 6 ++++++ .../source/simpa.core.device_digital_twins.rst | 5 +++++ ...ore.processing_components.multispectral.rst | 6 ++++++ .../simpa.core.processing_components.rst | 5 +++++ docs/source/simpa.core.rst | 6 ++++++ ...imulation_modules.reconstruction_module.rst | 18 ++++++++++++------ docs/source/simpa.core.simulation_modules.rst | 9 +++++++-- ...mulation_modules.volume_creation_module.rst | 10 ++++++++-- .../matplotlib_data_visualisation.py | 5 +++-- 11 files changed, 70 insertions(+), 12 deletions(-) diff --git a/docs/source/simpa.core.device_digital_twins.detection_geometries.rst b/docs/source/simpa.core.device_digital_twins.detection_geometries.rst index 3eb9caa8..413d22fa 100644 --- a/docs/source/simpa.core.device_digital_twins.detection_geometries.rst +++ b/docs/source/simpa.core.device_digital_twins.detection_geometries.rst @@ -12,6 +12,12 @@ detection\_geometries :show-inheritance: +.. automodule:: simpa.core.device_digital_twins.detection_geometries.detection_geometry_base + :members: + :undoc-members: + :show-inheritance: + + .. automodule:: simpa.core.device_digital_twins.detection_geometries.linear_array :members: :undoc-members: diff --git a/docs/source/simpa.core.device_digital_twins.illumination_geometries.rst b/docs/source/simpa.core.device_digital_twins.illumination_geometries.rst index eb29ecbd..b398a489 100644 --- a/docs/source/simpa.core.device_digital_twins.illumination_geometries.rst +++ b/docs/source/simpa.core.device_digital_twins.illumination_geometries.rst @@ -18,6 +18,12 @@ illumination\_geometries :show-inheritance: +.. automodule:: simpa.core.device_digital_twins.illumination_geometries.illumination_geometry_base + :members: + :undoc-members: + :show-inheritance: + + .. automodule:: simpa.core.device_digital_twins.illumination_geometries.ithera_msot_acuity_illumination :members: :undoc-members: diff --git a/docs/source/simpa.core.device_digital_twins.pa_devices.rst b/docs/source/simpa.core.device_digital_twins.pa_devices.rst index dd796554..462b9051 100644 --- a/docs/source/simpa.core.device_digital_twins.pa_devices.rst +++ b/docs/source/simpa.core.device_digital_twins.pa_devices.rst @@ -22,3 +22,9 @@ pa\_devices :members: :undoc-members: :show-inheritance: + + +.. automodule:: simpa.core.device_digital_twins.pa_devices.photoacoustic_device + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/simpa.core.device_digital_twins.rst b/docs/source/simpa.core.device_digital_twins.rst index 5b25dcc9..a8aeb366 100644 --- a/docs/source/simpa.core.device_digital_twins.rst +++ b/docs/source/simpa.core.device_digital_twins.rst @@ -12,3 +12,8 @@ device\_digital\_twins simpa.core.device_digital_twins.detection_geometries simpa.core.device_digital_twins.illumination_geometries simpa.core.device_digital_twins.pa_devices + +.. automodule:: simpa.core.device_digital_twins.digital_device_twin_base + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/simpa.core.processing_components.multispectral.rst b/docs/source/simpa.core.processing_components.multispectral.rst index ef6f10e1..9a2e368b 100644 --- a/docs/source/simpa.core.processing_components.multispectral.rst +++ b/docs/source/simpa.core.processing_components.multispectral.rst @@ -10,3 +10,9 @@ multispectral :members: :undoc-members: :show-inheritance: + + +.. automodule:: simpa.core.processing_components.multispectral.multispectral_processing_algorithm + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/simpa.core.processing_components.rst b/docs/source/simpa.core.processing_components.rst index 7780b57d..52a88285 100644 --- a/docs/source/simpa.core.processing_components.rst +++ b/docs/source/simpa.core.processing_components.rst @@ -11,3 +11,8 @@ processing\_components simpa.core.processing_components.monospectral simpa.core.processing_components.multispectral + +.. automodule:: simpa.core.processing_components.processing_component_base + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/simpa.core.rst b/docs/source/simpa.core.rst index e86ea34b..78a560aa 100644 --- a/docs/source/simpa.core.rst +++ b/docs/source/simpa.core.rst @@ -13,6 +13,12 @@ core simpa.core.processing_components simpa.core.simulation_modules +.. automodule:: simpa.core.pipeline_element_base + :members: + :undoc-members: + :show-inheritance: + + .. automodule:: simpa.core.simulation :members: :undoc-members: diff --git a/docs/source/simpa.core.simulation_modules.reconstruction_module.rst b/docs/source/simpa.core.simulation_modules.reconstruction_module.rst index f14dd281..a875b39f 100644 --- a/docs/source/simpa.core.simulation_modules.reconstruction_module.rst +++ b/docs/source/simpa.core.simulation_modules.reconstruction_module.rst @@ -6,37 +6,43 @@ reconstruction\_module :undoc-members: :show-inheritance: -.. automodule:: simpa.core.simulation_modules.reconstruction_module.reconstruction_module_delay_and_sum_adapter +.. automodule:: simpa.core.simulation_modules.reconstruction_module.delay_and_sum_adapter :members: :undoc-members: :show-inheritance: -.. automodule:: simpa.core.simulation_modules.reconstruction_module.reconstruction_module_delay_multiply_and_sum_adapter +.. automodule:: simpa.core.simulation_modules.reconstruction_module.delay_multiply_and_sum_adapter :members: :undoc-members: :show-inheritance: -.. automodule:: simpa.core.simulation_modules.reconstruction_module.reconstruction_module_signed_delay_multiply_and_sum_adapter +.. automodule:: simpa.core.simulation_modules.reconstruction_module.reconstruction_adapter_base :members: :undoc-members: :show-inheritance: -.. automodule:: simpa.core.simulation_modules.reconstruction_module.reconstruction_module_test_adapter +.. automodule:: simpa.core.simulation_modules.reconstruction_module.reconstruction_test_adapter :members: :undoc-members: :show-inheritance: -.. automodule:: simpa.core.simulation_modules.reconstruction_module.reconstruction_module_time_reversal_adapter +.. automodule:: simpa.core.simulation_modules.reconstruction_module.reconstruction_utils :members: :undoc-members: :show-inheritance: -.. automodule:: simpa.core.simulation_modules.reconstruction_module.reconstruction_utils +.. automodule:: simpa.core.simulation_modules.reconstruction_module.signed_delay_multiply_and_sum_adapter + :members: + :undoc-members: + :show-inheritance: + + +.. automodule:: simpa.core.simulation_modules.reconstruction_module.time_reversal_adapter :members: :undoc-members: :show-inheritance: diff --git a/docs/source/simpa.core.simulation_modules.rst b/docs/source/simpa.core.simulation_modules.rst index 6ffa2e19..f2f2e85e 100644 --- a/docs/source/simpa.core.simulation_modules.rst +++ b/docs/source/simpa.core.simulation_modules.rst @@ -9,7 +9,12 @@ simulation\_modules .. toctree:: :maxdepth: 4 - simpa.core.simulation_modules.acoustic_forward_module - simpa.core.simulation_modules.optical_simulation_module + simpa.core.simulation_modules.acoustic_module + simpa.core.simulation_modules.optical_module simpa.core.simulation_modules.reconstruction_module simpa.core.simulation_modules.volume_creation_module + +.. automodule:: simpa.core.simulation_modules.simulation_module_base + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/simpa.core.simulation_modules.volume_creation_module.rst b/docs/source/simpa.core.simulation_modules.volume_creation_module.rst index 8fda1e0b..acf284c9 100644 --- a/docs/source/simpa.core.simulation_modules.volume_creation_module.rst +++ b/docs/source/simpa.core.simulation_modules.volume_creation_module.rst @@ -6,13 +6,19 @@ volume\_creation\_module :undoc-members: :show-inheritance: -.. automodule:: simpa.core.simulation_modules.volume_creation_module.volume_creation_module_model_based_adapter +.. automodule:: simpa.core.simulation_modules.volume_creation_module.model_based_adapter :members: :undoc-members: :show-inheritance: -.. automodule:: simpa.core.simulation_modules.volume_creation_module.volume_creation_module_segmentation_based_adapter +.. automodule:: simpa.core.simulation_modules.volume_creation_module.segmentation_based_adapter + :members: + :undoc-members: + :show-inheritance: + + +.. automodule:: simpa.core.simulation_modules.volume_creation_module.volume_creation_adapter_base :members: :undoc-members: :show-inheritance: diff --git a/simpa/visualisation/matplotlib_data_visualisation.py b/simpa/visualisation/matplotlib_data_visualisation.py index 6e6d042d..84d4021a 100644 --- a/simpa/visualisation/matplotlib_data_visualisation.py +++ b/simpa/visualisation/matplotlib_data_visualisation.py @@ -12,6 +12,7 @@ from simpa.utils.settings import Settings from simpa.utils import get_data_field_from_simpa_output from simpa.log import Logger +from typing import Union class VisualiseData: @@ -43,7 +44,7 @@ class VisualiseData: """ def __init__(self, - wavelength: int | float = None, + wavelength: Union[int, float] = None, path_to_hdf5_file: str = None, settings: Settings = None, path_manager: PathManager = None, @@ -481,7 +482,7 @@ def show_fig(self): plt.show() -def visualise_data(wavelength: int | float = None, +def visualise_data(wavelength: Union[int, float] = None, wavelengths: list = None, path_to_hdf5_file: str = None, settings: Settings = None, From 82a860416b9c5e6fe3e645733f2b293fbf6874bd Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 16 Aug 2024 15:30:29 +0200 Subject: [PATCH 11/11] ensure it works with one plot, adding some docs --- .../matplotlib_data_visualisation.py | 77 +++++++------------ .../optical_and_acoustic_simulation.py | 5 +- 2 files changed, 31 insertions(+), 51 deletions(-) diff --git a/simpa/visualisation/matplotlib_data_visualisation.py b/simpa/visualisation/matplotlib_data_visualisation.py index 84d4021a..dadf3daf 100644 --- a/simpa/visualisation/matplotlib_data_visualisation.py +++ b/simpa/visualisation/matplotlib_data_visualisation.py @@ -46,8 +46,6 @@ class VisualiseData: def __init__(self, wavelength: Union[int, float] = None, path_to_hdf5_file: str = None, - settings: Settings = None, - path_manager: PathManager = None, show_absorption: bool = False, show_scattering: bool = False, show_anisotropy: bool = False, @@ -66,9 +64,7 @@ def __init__(self, show_xz_only: bool = False): """ :param wavelength: The wavelength to be initially visualized. - :param path_to_hdf5_file: Path to the HDF5 file containing the data. If not provided, it is constructed from settings and path_manager. - :param settings: An instance of Settings containing configuration parameters, including the volume name and wavelengths. - :param path_manager: An instance of PathManager used to manage file paths. It is used to generate the path to the HDF5 file if not provided. + :param path_to_hdf5_file: Path to the HDF5 file containing the data. :param show_absorption: If True, display the absorption coefficient. :param show_scattering: If True, display the scattering coefficient. :param show_anisotropy: If True, display the anisotropy. @@ -85,28 +81,12 @@ def __init__(self, :param show_diffuse_reflectance: If True, display the diffuse reflectance. :param log_scale: If True, use a logarithmic scale for the data. :param show_xz_only: If True, display only the xz-plane. - :param save_path: Path to save the visualization. If None, the visualization will be displayed instead of saved. - :param add_wavelengths_slider: If True, add a slider for wavelength selection to the visualization. :raises ValueError: If both path_to_hdf5_file and the combination of settings and path_manager are None. """ super().__init__() - if settings is not None and Tags.WAVELENGTHS in settings: - if wavelength is None or wavelength not in settings[Tags.WAVELENGTHS]: - wavelength = settings[Tags.WAVELENGTHS][0] - - if settings is not None and Tags.WAVELENGTH in settings: - wavelength = settings[Tags.WAVELENGTH] - - if path_to_hdf5_file is None and (settings is None or path_manager is None): - raise ValueError( - "Either the path_to_hdf5_file or the given settings and path_manager must not be None!") - - if path_to_hdf5_file is None: - path_to_hdf5_file = path_manager.get_hdf5_file_save_path() + "/" + settings[Tags.VOLUME_NAME] + ".hdf5" - self.show_absorption = show_absorption self.show_scattering = show_scattering self.show_anisotropy = show_anisotropy @@ -335,6 +315,9 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr cmaps.append(cmap) logscales.append(False) + if len(data_to_show) == 1: + self.axes = [self.axes] + for i in range(len(data_to_show)): if self.show_xz_only: @@ -350,31 +333,6 @@ def plot_data_for_wavelength(self, wavelength: int = None, colour_bar: bool = Tr if colour_bar: plt.colorbar(img, ax=self.axes[i]) - elif len(data_to_show) == 1 and not self.show_xz_only: - self.axes[0].clear() - self.axes[0].set_title(data_item_names) - if len(np.shape(data_to_show[i])) > 2: - pos = int(np.shape(data_to_show[i])[1] / 2) - 1 - data = data_to_show[i][:, pos, :].T - img = self.axes[0].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) - else: - data = data_to_show[i][:, :].T - img = self.axes[0].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) - if colour_bar: - plt.colorbar(img, ax=self.axes[0]) - - self.axes[1].clear() - self.axes[1].set_title(data_item_names) - if len(np.shape(data_to_show[i])) > 2: - pos = int(np.shape(data_to_show[i])[0] / 2) - data = data_to_show[i][pos, :, :].T - img = self.axes[1].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) - else: - data = data_to_show[i][:, :].T - img = self.axes[1].imshow(np.log10(data) if logscales[i] else data, cmap=cmaps[i]) - if colour_bar: - plt.colorbar(img, ax=self.axes[1]) - elif not self.show_xz_only: self.axes[0][i].clear() self.axes[0][i].set_title(data_item_names[i]) @@ -506,6 +464,13 @@ def visualise_data(wavelength: Union[int, float] = None, save_path: str = None, add_wavelengths_slider: bool = False): """ + A function to visualize simpa output data. It provides options to display various + physical properties such as absorption, scattering, anisotropy, speed of sound, tissue density, + fluence, initial pressure, time series data, reconstructed data, segmentation maps, + oxygenation, blood volume fraction, and linear unmixing results. + + The class supports visualizing data in 2D (xz-plane and zy-plane), and includes features such as + logarithmic scaling, saving visualizations, and adding a slider for wavelength selection. :param wavelength: The wavelength to be initially visualized. :param wavelengths: The wavelengths to be visualized in the slider. @@ -531,10 +496,26 @@ def visualise_data(wavelength: Union[int, float] = None, :param save_path: Path to save the visualization. If None, the visualization will be displayed instead of saved. :param add_wavelengths_slider: If True, add a slider for wavelength selection to the visualization. """ + + if settings is not None and Tags.WAVELENGTHS in settings: + wavelengths = settings[Tags.WAVELENGTHS] + if wavelength is None or wavelength not in settings[Tags.WAVELENGTHS]: + wavelength = settings[Tags.WAVELENGTHS][0] + elif wavelengths is not None and wavelength is None: + wavelength = wavelengths[0] + + if settings is not None and Tags.WAVELENGTH in settings: + wavelength = settings[Tags.WAVELENGTH] + + if path_to_hdf5_file is None and (settings is None or path_manager is None): + raise ValueError( + "Either the path_to_hdf5_file or the given settings and path_manager must not be None!") + + if path_to_hdf5_file is None: + path_to_hdf5_file = path_manager.get_hdf5_file_save_path() + "/" + settings[Tags.VOLUME_NAME] + ".hdf5" + plot = VisualiseData(wavelength, path_to_hdf5_file, - settings, - path_manager, show_absorption, show_scattering, show_anisotropy, diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 425b1037..16cda056 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -208,10 +208,9 @@ def create_example_tissue(): WAVELENGTHS = [700, 800] if visualise: - sp.visualise_data(wavelength=700, path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], - wavelengths=[700, 800], + sp.visualise_data(wavelength=WAVELENGTH, path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], + wavelengths=WAVELENGTHS, show_time_series_data=True, - show_initial_pressure=True, show_reconstructed_data=True, log_scale=False, show_xz_only=False, add_wavelengths_slider=True)