Source code for simpa.core.device_digital_twins.detection_geometries.curved_array

# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ
# SPDX-FileCopyrightText: 2021 Janek Groehl
# SPDX-License-Identifier: MIT
import numpy as np

from simpa.core.device_digital_twins import DetectionGeometryBase
from simpa.utils import Tags


[docs]class CurvedArrayDetectionGeometry(DetectionGeometryBase): """ This class represents a digital twin of a ultrasound detection device with a curved detection geometry. The origin for this device is the center (focus) of the curved array. """ def __init__(self, pitch_mm=0.5, radius_mm=40, number_detector_elements=256, detector_element_width_mm=0.24, detector_element_length_mm=13, center_frequency_hz=3.96e6, bandwidth_percent=55, sampling_frequency_mhz=40, angular_origin_offset=np.pi, device_position_mm=None, field_of_view_extent_mm=None): """ :param pitch_mm: In-plane distance between the beginning of one detector element to the next detector element. :param radius_mm: :param number_detector_elements: :param detector_element_width_mm: :param detector_element_length_mm: :param center_frequency_hz: :param bandwidth_percent: :param sampling_frequency_mhz: :param angular_origin_offset: :param device_position_mm: Center (focus) of the curved array. """ super(CurvedArrayDetectionGeometry, self).__init__( number_detector_elements=number_detector_elements, detector_element_width_mm=detector_element_width_mm, detector_element_length_mm=detector_element_length_mm, center_frequency_hz=center_frequency_hz, bandwidth_percent=bandwidth_percent, sampling_frequency_mhz=sampling_frequency_mhz, device_position_mm=device_position_mm) self.pitch_mm = pitch_mm self.radius_mm = radius_mm self.angular_origin_offset = angular_origin_offset detector_positions = self.get_detector_element_positions_base_mm() min_x_coordinate = np.min(detector_positions[:, 0]) max_x_coordinate = np.max(detector_positions[:, 0]) self.probe_width_mm = max_x_coordinate - min_x_coordinate min_z_coordinate = np.min(detector_positions[:, 2]) max_z_coordinate = np.max(detector_positions[:, 2]) self.probe_height_mm = max_z_coordinate - min_z_coordinate if field_of_view_extent_mm is None: self.field_of_view_extent_mm = np.asarray([-self.probe_width_mm/2, self.probe_width_mm/2, 0, 0, 0, 100]) else: self.field_of_view_extent_mm = field_of_view_extent_mm
[docs] def check_settings_prerequisites(self, global_settings) -> bool: if global_settings[Tags.DIM_VOLUME_Z_MM] < (self.probe_height_mm + global_settings[Tags.SPACING_MM]): self.logger.error("Volume z dimension is too small to encompass the device in simulation!" "Must be at least {} mm but was {} mm" .format((self.probe_height_mm + global_settings[Tags.SPACING_MM]), global_settings[Tags.DIM_VOLUME_Z_MM])) return False if global_settings[Tags.DIM_VOLUME_X_MM] < (self.probe_width_mm + global_settings[Tags.SPACING_MM]): self.logger.error("Volume x dimension is too small to encompass MSOT device in simulation!" "Must be at least {} mm but was {} mm" .format(self.probe_width_mm + global_settings[Tags.SPACING_MM], global_settings[Tags.DIM_VOLUME_X_MM])) return False return True
[docs] def update_settings_for_use_of_model_based_volume_creator(self, global_settings): pass
[docs] def get_detector_element_positions_base_mm(self) -> np.ndarray: pitch_angle = self.pitch_mm / self.radius_mm detector_radius = self.radius_mm detector_positions = np.zeros((self.number_detector_elements, 3)) det_elements = np.arange(-int(self.number_detector_elements / 2) + 0.5, int(self.number_detector_elements / 2) + 0.5) detector_positions[:, 0] = np.sin(pitch_angle * det_elements - self.angular_origin_offset) * detector_radius detector_positions[:, 2] = np.cos(pitch_angle * det_elements - self.angular_origin_offset) * detector_radius return detector_positions
[docs] def get_detector_element_orientations(self) -> np.ndarray: detector_positions = self.get_detector_element_positions_base_mm() detector_orientations = np.subtract(0, detector_positions) norm = np.linalg.norm(detector_orientations, axis=-1) for dim in range(3): detector_orientations[:, dim] = detector_orientations[:, dim] / norm return detector_orientations
[docs] def serialize(self) -> dict: serialized_device = self.__dict__ return {"CurvedArrayDetectionGeometry": serialized_device}
[docs] @staticmethod def deserialize(dictionary_to_deserialize): deserialized_device = CurvedArrayDetectionGeometry() for key, value in dictionary_to_deserialize.items(): deserialized_device.__dict__[key] = value return deserialized_device