zea.beamform.pixelgrid¶

Pixel grid calculation for ultrasound beamforming.

Functions

aliasing_limits(sound_speed, ...[, ...])

Largest pixel pitch the beamforming grid can use without spatial aliasing.

cartesian_pixel_grid(xlims, zlims[, ylims, ...])

Generate a Cartesian pixel grid.

check_for_aliasing(xlims, zlims, ...[, ...])

Warn if the grid's pixel pitch exceeds aliasing_limits().

polar_pixel_grid(polar_limits, zlims, ...[, ...])

Generate a polar grid.

radial_pixel_grid(rlims, dr, oris, dirs[, ...])

Generate a focused pixel grid based on input parameters.

scanline_aligned_apodization(n_tx, ...)

Compounding apodization mask that isolates each pixel's owning transmit.

scanline_pixel_grid(transmit_origins, ...[, ...])

Pixel grid for scanline beamforming: one column of pixels per transmit.

transmit_direction_bounds(focus_distances, ...)

Bounding box of the ray directions of each transmit.

zea.beamform.pixelgrid.aliasing_limits(sound_speed, center_frequency, f_number, focus_distances, probe_geometry, tx_apodizations, polar_angles, transmit_origins, demodulation_frequency=None, demodulated=False, bandwidth=None, azimuth_angles=None, element_width=None, element_height=None)[source]¶

Largest pixel pitch the beamforming grid can use without spatial aliasing.

A transmit in direction t and a receive in direction r at frequency f give spatial frequency k = f * (t + r) / c in the image. Sweeping f over the band, r over the receive cone (set by f_number) and t over the transmit rays (see transmit_direction_bounds()) gives the image’s k-space support, as in the k-space description of pulse-echo imaging by Walker and Trahey. The limits follow from its bounding box:

  • Lateral: dx <= 1 / (k_x_max - k_x_min), about lambda_min * F / 2 for equal transmit and receive apertures. Elevational dy likewise.

  • Axial, RF: dz <= 1 / (2 * k_z_max), about lambda_min / 4.

  • Axial, IQ: dz <= 1 / (k_z_max - k_z_min), about c / (2 B) on axis.

Given an element size, element directivity (-6 dB, see DIRECTIVITY_6DB) limits how oblique a path can be and still carry energy. Plane waves are not limited this way, as their direction is fixed by the steering.

For a curved array the receive cone is measured from each element’s tilted normal (as in zea.beamform.beamformer.fnumber_mask()), so it is widened by the largest tilt. Directivity is then not applied, since it too is relative to each element’s normal.

Reference

W. F. Walker and G. E. Trahey, “The Application of k-Space in Pulse Echo Ultrasound,” IEEE Transactions on Ultrasonics, Ferroelectrics, and Frequency Control, vol. 45, no. 3, pp. 541–558, 1998. doi.org/10.1109/58.677599

Parameters:
  • sound_speed (float) – Speed of sound in m/s.

  • center_frequency (float | ndarray) – Transmit center frequency in Hz, scalar or per transmit.

  • f_number (float) – Receive f-number; 0 means the full aperture.

  • focus_distances – Transmit geometry, see transmit_direction_bounds().

  • probe_geometry – Transmit geometry, see transmit_direction_bounds().

  • tx_apodizations – Transmit geometry, see transmit_direction_bounds().

  • polar_angles – Transmit geometry, see transmit_direction_bounds().

  • transmit_origins – Transmit geometry, see transmit_direction_bounds().

  • azimuth_angles – Transmit geometry, see transmit_direction_bounds().

  • demodulation_frequency (float | ndarray | None) – Demodulation frequency in Hz. None or 0 falls back to center_frequency.

  • demodulated (bool) – Whether the data reaching the grid is baseband IQ.

  • bandwidth (float | None) – Bandwidth of the data in Hz. None means unknown, in which case the RF axial limit is used.

  • element_width (float | None) – Element width in m. None disables the lateral directivity limit.

  • element_height (float | None) – Element height in m. None falls back to element_width.

Returns:

dx_max, dy_max and dz_max in metres, plus the quantities behind them for reporting: lambda_min, transmit_direction ((lo, hi) of the x, y and z of the transmit directions), sin_theta_r, sin_theta_directivity (along x and y), axial_basis (a readable explanation) and kx_range / ky_range / kz_range (the (lowest, highest) spatial frequency of the image along each axis, in cycles per metre).

Return type:

dict

zea.beamform.pixelgrid.cartesian_pixel_grid(xlims, zlims, ylims=(0.0, 0.0), grid_size_x=None, grid_size_y=None, grid_size_z=None, dx=None, dy=None, dz=None)[source]¶

Generate a Cartesian pixel grid.

Behaviour:
  • If ylims has zero extent (abs(ymax - ymin) < eps) the function returns a 2D grid with shape (nz, nx, 3) that contains (x, y=0, z) per-pixel (y omitted as a dimension).

  • If ylims has non-zero extent the function returns a 3D grid with shape (nz, nx, ny, 3) containing (x, y, z) per-voxel.

Parameters:
  • xlims (tuple) – [xmin, xmax]

  • ylims (tuple) – [ymin, ymax] — if ymax == ymin (within tol) treated as “no y extent”

  • zlims (tuple) – [zmin, zmax]

  • grid_size_x (int) – number of samples along each axis. For 2D (no y extent) only grid_size_x and grid_size_z are required if using sizes.

  • grid_size_y (int) – number of samples along each axis. For 2D (no y extent) only grid_size_x and grid_size_z are required if using sizes.

  • grid_size_z (int) – number of samples along each axis. For 2D (no y extent) only grid_size_x and grid_size_z are required if using sizes.

  • dx (float) – spacings along axes. For 2D, only dx and dz are required if using spacings.

  • dy (float) – spacings along axes. For 2D, only dx and dz are required if using spacings.

  • dz (float) – spacings along axes. For 2D, only dx and dz are required if using spacings.

Returns:

  • 2D: shape (nz, nx, 3) with per-pixel [x, y, z] (y will be zeros)

  • 3D: shape (nz, nx, ny, 3) with per-voxel [x, y, z]

Return type:

np.ndarray

zea.beamform.pixelgrid.check_for_aliasing(xlims, zlims, grid_size_x, grid_size_z, sound_speed, center_frequency, f_number, focus_distances, probe_geometry, tx_apodizations, polar_angles, transmit_origins, demodulation_frequency=None, demodulated=False, bandwidth=None, azimuth_angles=None, ylims=(0.0, 0.0), grid_size_y=1, element_width=None, element_height=None)[source]¶

Warn if the grid’s pixel pitch exceeds aliasing_limits().

Only valid for regular cartesian grids (2D or 3D). Arguments not listed below are forwarded to aliasing_limits().

Parameters:
  • xlims – Lateral and axial extent (min, max) of the grid in metres.

  • zlims – Lateral and axial extent (min, max) of the grid in metres.

  • grid_size_x (int) – Number of pixels along x and z, with pixels on both limits as in cartesian_pixel_grid(). An axis with a single pixel is not checked.

  • grid_size_z (int) – Number of pixels along x and z, with pixels on both limits as in cartesian_pixel_grid(). An axis with a single pixel is not checked.

  • ylims – Elevational extent and number of pixels. The default is a 2D grid.

  • grid_size_y (int) – Elevational extent and number of pixels. The default is a 2D grid.

Returns:

The messages emitted; empty when the grid is adequate.

Return type:

list

zea.beamform.pixelgrid.polar_pixel_grid(polar_limits, zlims, num_radial_pixels, num_polar_pixels, distance_to_apex=None, *, grid_apex=(0.0, 0.0, 0.0), polar_angles=None)[source]¶

Generate a polar grid.

Uses radial_pixel_grid but based on parameters that are present in the scan class. Currently only 2D grids (no elevation steering) are supported.

Parameters:
  • polar_limits (tuple) – Polar limits of pixel grid ([polar_min, polar_max])

  • zlims (tuple) – Depth limits of pixel grid ([zmin, zmax]), measured from the transducer surface (z = 0) just like for a Cartesian grid.

  • num_radial_pixels (int) – Number of depth pixels.

  • num_polar_pixels (int) – Number of polar pixels.

  • grid_apex (array-like, optional) – The point (x, y, z) [m] the rays fan out from, on the central axis at or behind the array (see zea.Parameters.grid_apex). Radii are measured from it, so the rows sit at zlims - grid_apex[2]. Defaults to the middle of the array.

  • polar_angles (array-like, optional) – Explicit angle of each column [rad], for example one per transmit for a scanline grid. Overrides polar_limits and num_polar_pixels when given.

  • distance_to_apex (float, optional) – Deprecated, use grid_apex=(0, 0, -distance_to_apex).

Returns:

Pixel grid of size (num_radial_pixels, num_polar_pixels, 3) in Cartesian coordinates (x, y, z)

Return type:

grid (np.ndarray)

zea.beamform.pixelgrid.radial_pixel_grid(rlims, dr, oris, dirs, num_radial_pixels=None)[source]¶

Generate a focused pixel grid based on input parameters.

To accommodate the multitude of ways of defining a focused transmit grid, we define pixel “rays” or “lines” according to their origins (oris) and directions (dirs). The position along the ray is defined by its limits (rlims) and spacing (dr).

Parameters:
  • rlims (tuple) – Radial limits of pixel grid ([rmin, rmax]) with respect to each ray origin. Both ends are included when dr divides the range evenly, matching cartesian_pixel_grid()’s treatment of zlims.

  • dr (float) – Pixel spacing in radius

  • oris (np.ndarray) – Origin of each ray in Cartesian coordinates (x, y, z) with shape (nrays, 3)

  • dirs (np.ndarray) – Direction of each ray [rad], shape (nrays, 2): the polar angle atan(x / z) in column 0 and the elevation angle atan(y / z) in column 1 (currently ignored).

  • num_radial_pixels (int, optional) – Number of samples along each ray. Derived from rlims and dr when not given.

Returns:

Pixel grid of size (nr, nrays, 3) in

Cartesian coordinates (x, y, z), with nr being the number of radial pixels.

Return type:

grid (np.ndarray)

zea.beamform.pixelgrid.scanline_aligned_apodization(n_tx, num_depth_pixels)[source]¶

Compounding apodization mask that isolates each pixel’s owning transmit.

For a grid built by scanline_pixel_grid() (shape (num_depth_pixels, n_tx, 3), flattened row-major to (n_pix, 3)), pixel (i, n) at flat index i * n_tx + n belongs to transmit n. This returns the corresponding one-hot weight (1 for the owning transmit, 0 for every other transmit) to feed to zea.ops.AlignedApodization, turning the regular pixel-based DAS pipeline into scanline (line-by-line) beamforming.

Parameters:
  • n_tx (int) – Number of transmits (grid columns).

  • num_depth_pixels (int) – Number of depth samples per line (grid rows).

Returns:

Apodization weights of shape (num_depth_pixels * n_tx, n_tx).

Return type:

np.ndarray

zea.beamform.pixelgrid.scanline_pixel_grid(transmit_origins, focus_distances, polar_angles, zlims, num_depth_pixels, azimuth_angles=None, grid_type='cartesian')[source]¶

Pixel grid for scanline beamforming: one column of pixels per transmit.

Scanline (line-by-line) imaging is a special case of pixel-based DAS beamforming where each transmit is beamformed to a single column of pixels. This builds that grid in the same (grid_size_z, grid_size_x, 3) layout as cartesian_pixel_grid() (one column n per transmit n, num_depth_pixels rows), so it can be beamformed by the regular pixel-based Beamform pipeline. Pair with scanline_aligned_apodization() (fed to AlignedApodization) to zero out every transmit except the one whose column each pixel belongs to.

Parameters:
  • transmit_origins (np.ndarray) – Beam origins (n_tx, 3) in meters.

  • focus_distances (np.ndarray) – Focus distances (n_tx,) in meters.

  • polar_angles (np.ndarray) – Steering angles (n_tx,) in radians.

  • zlims (tuple) – Depth range (z_min, z_max) in meters.

  • num_depth_pixels (int) – Number of samples along each line.

  • azimuth_angles (np.ndarray, optional) – Azimuth angles (n_tx,). Defaults to zeros.

  • grid_type (str) – "cartesian" for a vertical column at each beam’s lateral focus position (linear-scan geometry), matching cartesian_pixel_grid(). "polar" for a steered ray from each transmit’s own origin (sector / phased-array geometry), matching polar_pixel_grid(). Defaults to "cartesian".

Returns:

Pixel positions of shape (num_depth_pixels, n_tx, 3) in Cartesian (x, y, z) coordinates (meters); column n is the beam line of transmit n.

Return type:

np.ndarray

zea.beamform.pixelgrid.transmit_direction_bounds(focus_distances, probe_geometry, tx_apodizations, polar_angles, transmit_origins, azimuth_angles=None)[source]¶

Bounding box of the ray directions of each transmit.

For focused and diverging transmits, each active element (non-zero tx_apodizations) sends a ray through the focal point or virtual source, see _focused_ray_direction_bounds(). A plane wave travels in one direction, its steering direction, so its box is a single point.

Parameters:
  • focus_distances – Focus distance per transmit, shape (n_tx,). Negative for diverging waves; see zea.beamform.geometry.is_plane_wave() for plane waves.

  • probe_geometry – Element positions, shape (n_el, 3).

  • tx_apodizations – Transmit apodization per transmit, shape (n_tx, n_el).

  • polar_angles – Steering angle per transmit in radians, shape (n_tx,). None means unsteered.

  • transmit_origins – Origin of each transmit, shape (n_tx, 3).

  • azimuth_angles – Steering azimuth per transmit in radians, shape (n_tx,). None means steering in the x-z plane.

Returns:

(bounds, plane). bounds has shape (n_tx, 3, 2): per transmit, one (lo, hi) row per axis x, y, z of the unit ray directions. plane marks the plane waves. Transmits without active elements or with a NaN focus are skipped. (None, None) if no usable transmit remains.

Return type:

tuple[ndarray, ndarray] | tuple[None, None]