Source code for patato.recon.numpy_backprojection.recon

#  Copyright (c) Thomas Else 2023-25.
#  License: MIT

from typing import Sequence

import numpy as np
from .. import ReconstructionAlgorithm

# Add a loading bar
from tqdm.auto import tqdm


[docs] class SlowBackprojection(ReconstructionAlgorithm): """Slow example backprojection."""
[docs] def reconstruct( self, time_series: np.ndarray, fs: float, geometry: np.ndarray, n_pixels: Sequence[int], field_of_view: Sequence[float], speed_of_sound: float, **kwargs, ) -> np.ndarray: """ Parameters ---------- time_series : array_like Photoacoustic time series data in a numpy array. Shape: (..., n_detectors, n_time_samples) fs : float Time series sampling frequency (Hz). geometry : array_like The detector geometry. Shape: (n_detectors, 3) n_pixels : tuple of int Tuple of length 3, (nx, ny, nz) field_of_view : tuple of float Tuple of length 3, (lx, ly, lz) - the size of the reconstruction volume. speed_of_sound : float Speed of sound (m/s). kwargs Extra parameters (optional), useful for advanced algorithms (e.g. multi speed of sound etc.). Returns ------- array_like The reconstructed image. """ print("Running batch of delay and sum reconstruction code.") # Get useful parameters: dl = speed_of_sound / fs # Reshape frames so that we can loop through to reconstruct original_shape = time_series.shape[:-2] frames = int(np.prod(original_shape)) signal = time_series.reshape((frames,) + time_series.shape[-2:]) xs, ys, zs = [ np.linspace(-field_of_view[i] / 2, field_of_view[i] / 2, n_pixels[i]) if n_pixels[i] != 1 else np.array([0.0]) for i in range(3) ] Z, Y, X = np.meshgrid(zs, ys, xs, indexing="ij") # Note that the reconstructions are stored in memory in the order z, y, x (i.e. the x axis is the fastest # changing in memory) output = np.zeros((frames,) + tuple(n_pixels)[::-1]) for n_frame in tqdm(range(frames), desc="Looping through frames", position=0): for n_detector in tqdm( range(signal.shape[-2]), desc="Looping through detectors", position=1, leave=False, ): detx, dety, detz = geometry[n_detector] d = ( np.sqrt((detx - X) ** 2 + (dety - Y) ** 2 + (detz - Z) ** 2) / dl ).astype(np.int32) output[n_frame] += signal[n_frame, n_detector, d] return output.reshape(original_shape + tuple(n_pixels)[::-1])
[docs] @staticmethod def get_algorithm_name() -> str: """ Returns ------- str Algorithm name. """ return "Slow Backprojection"