Python API
Signatures and docstrings of the public functions and classes in qoraholo at main (e6aba69), read from the source without importing it. The guides on this site say how the pieces fit together; this page is for looking up an argument.
qoraholo
Holotomography (optical diffraction tomography) reconstruction. Arrays are numpy, centre-origin, square with even sides. Functions that work on images accept a single 2D image or a 3D stack (count, n, n) and return the same rank. Lengths are micrometres, spatial frequencies cycles per micrometre. ``backend`` is ``"cpu"`` or ``"cuda"``.
find_sideband
python
def find_sideband(hologram, dc_exclusion: float=0.0) -> SidebandPeak of the +1 sideband (x > 0 half plane) of the first hologram, in pixels from DC.
extract_fields
python
def extract_fields(
holograms,
center: tuple[float, float] | None = None,
radius: float = 0.0,
dc_exclusion: float = 0.0,
output_size: int = 0,
backend: str = 'cpu',
)Off-axis hologram(s) to complex field(s). ``center`` is the sideband centre (y, x) in pixels from DC; detected when omitted. For multi-angle data pass the reference carrier so the illumination tilt stays in the field. ``radius`` defaults to |center| / 3, ``output_size`` to the hologram side.
field_ratio
python
def field_ratio(sample, background)sample / background; background is one field or one per sample field.
unwrap
python
def unwrap(phase, backend: str='cpu')Least-squares unwrapping (Ghiglia & Romero 1994), congruent to the input mod 2 pi.
rytov
python
def rytov(ratio, backend: str='cpu')log|r| + i unwrap(arg r), with the mean phase on the image border in (-pi, pi].
reconstruct
python
def reconstruct(
fields,
illuminations,
geometry: Geometry,
nz: int = 0,
iterations: int = 0,
solver: str = 'gp',
gp_transform: str = 'r2c',
tv_weight: float = 0.01,
tv_inner: int = 10,
mapping: str = 'gather',
backend: str = 'cpu',
return_filled: bool = False,
)Rytov fields (count, n, n) to a refractive-index volume (nz, n, n). ``illuminations`` is (count, 2) lateral frequencies (fy, fx) of the incident waves. ``iterations`` runs a missing-cone solver: ``solver="gp"`` is Gerchberg-Papoulis with n >= n_medium, on half spectra (``gp_transform="r2c"``, real part only) or full complex spectra (``"c2c"``); ``solver="tv"`` is FISTA with a total-variation term of weight ``tv_weight`` on n^2 - n_medium^2 and ``tv_inner`` dual iterations per step. With ``return_filled`` the fraction of k-space voxels that received data is returned as well.
Pipeline
python
class Pipeline:
def __init__(
self,
count: int,
hologram_size: int,
center: tuple[float, float],
illuminations,
geometry: Geometry,
radius: float = 0.0,
output_size: int = 0,
nz: int = 0,
iterations: int = 0,
solver: str = 'gp',
gp_transform: str = 'r2c',
tv_weight: float = 0.01,
tv_inner: int = 10,
mapping: str = 'gather',
backend: str = 'cpu',
upload_chunks: int = 1,
)Holograms to refractive index with plans and buffers allocated once. On CUDA the background fields stay on the device and each ``run`` does one upload of the sample holograms and one download of the volume. ``center`` is the reference carrier (y, x) in pixels from DC, as for ``extract_fields``; the other arguments are those of ``extract_fields`` and ``reconstruct``. ``upload_chunks`` splits the CUDA upload so field extraction overlaps it.
Pipeline.set_background
python
def set_background(self, backgrounds) -> NoneBackground holograms (count, N, N); their fields are extracted once and kept.
Pipeline.run
python
def run(self, holograms)Sample holograms (count, N, N) to the index volume (nz, n, n).
Pipeline.filled
python
@property
def filled(self) -> floatFraction of k-space voxels that received data in the last run.
Pipeline.timings
python
@property
def timings(self) -> dict[str, float]Milliseconds per stage of the last run; on CUDA, device event intervals.
Pipeline.setup_ms
python
@property
def setup_ms(self) -> floatPipeline.device_bytes
python
@property
def device_bytes(self) -> intcircular_scan
python
def circular_scan(count: int, na_illumination: float, geometry: Geometry)Normal incidence, then count - 1 directions evenly spaced on a circle: (count, 2).
render_phantom
python
def render_phantom(spheres, geometry: Geometry, nz: int, n: int)Refractive index of the spheres at voxel centres, (nz, n, n).
forward_fields
python
def forward_fields(
spheres,
illuminations,
geometry: Geometry,
n: int,
model: str = 'rytov',
)Field ratios u / u_in at z = 0, (count, n, n), band-limited to the detection NA. ``"born"`` and ``"rytov"`` place the analytic sphere spectrum on each Ewald cap; ``"exact"`` solves the scalar Helmholtz equation for one sphere by partial waves (multiple scattering included).
bpm_forward
python
def bpm_forward(
ri,
illuminations,
geometry: Geometry,
obliquity: bool = True,
backend: str = 'cpu',
)Refractive index (nz, n, n) to field ratios (count, n, n) by multi-slice beam propagation. One slice per voxel: a thin phase screen, then angular-spectrum propagation in the medium. The output is refocused to z = 0 and band-limited to the detection NA like ``forward_fields``. ``obliquity`` weights the scattered part of each slice by the obliquity of the scattered wave, which makes the weak-object limit the first Born approximation at every illumination angle.
bpm_reconstruct
python
def bpm_reconstruct(
fields,
illuminations,
geometry: Geometry,
initial,
iterations: int = 20,
batch: int = 0,
step: float = 1.0,
tv_weight: float = 0.0,
tv_inner: int = 10,
obliquity: bool = True,
backend: str = 'cpu',
return_loss: bool = False,
)Field ratios (count, n, n) to refractive index by gradient descent on the BPM. ``initial`` (nz, n, n) is the starting volume, e.g. a Rytov reconstruction. Each iteration back-propagates the residual for the gradient, then applies n >= n_medium and, with ``tv_weight > 0``, a TV proximal step. ``batch=0`` uses all illuminations per step with FISTA momentum; otherwise one iteration is a pass over batches of that many illuminations. ``step`` multiplies the automatic step 1 / L. With ``return_loss`` the data term of every iteration is returned as well.
synthesize_holograms
python
def synthesize_holograms(
ratio,
illuminations,
geometry: Geometry,
carrier: tuple[float, float] = (5.0, 5.0),
reference_amplitude: float = 1.0,
noise: float = 0.0,
seed: int = 1,
)|R + u_in * ratio|^2 + Gaussian noise, with R = a exp(-2 pi i carrier . r).
qoraholo.born2d
First Born scattering in two dimensions (x, z) as a linear operator with its exact adjoint.
A plane wave travelling along +z with lateral frequency ``fx`` (cycles/µm) illuminates a
scattering potential V(z, x) = k0² (n(z, x)² − n_m²), k0 = 2π/λ, sampled at pixel centres of a
centre-origin (nz, n) grid. Under the first Born approximation the scattered field on the line
z = 0 through the grid centre, divided by the incident field, has the spectrum
R(μ') = i / (4π ν_z) · V̂(μ', ν_z − ν_in,z), μ = μ' + fx, ν_z = sqrt((n_m/λ)² − μ²)
where V̂(K) = ∫ V(r) exp(−2πi K·r) d²r. This is the Fourier diffraction theorem with the 2D
Green's function (i/4) H0⁽¹⁾(k_m r), written for field ratios u_s / u_in refocused to z = 0
and band-limited to the detection NA, the convention of ``bpm_forward`` and
``forward_fields`` (μ' is the frequency of the ratio, μ that of the scattered field).
The operator is discretised exactly as written: V̂ by the midpoint rule over the object
pixels at the off-grid frequencies K (no interpolation onto a grid), the ratio by the
inverse transform sampled at the detector pixels. Both are explicit sums, so ``adjoint`` is
the conjugate transpose of ``forward`` to rounding error.
Validity: the first Born approximation needs the phase the object adds to be small,
|δn| · k0 · L ≪ 1 for an object of size L along the beam (``born_phase``). It leaves out
multiple scattering; ``bpm_forward`` keeps forward multiple scattering.potential
python
def potential(ri, n_medium: float, wavelength: float) -> np.ndarrayScattering potential k0² (n² − n_m²) in µm⁻² from a refractive index map.
born_phase
python
def born_phase(delta_n: float, size: float, wavelength: float) -> float|δn| k0 L: the phase an object of index contrast δn and thickness L adds (radians).
Born2D
python
@dataclass(frozen=True)
class Born2D:
wavelength: float
n_medium: float
na: float
pixel: float
n: int
nz: int
illuminations: tuple[float, ...]First Born forward model for a centre-origin (nz, n) potential and ``count`` plane waves. wavelength: vacuum wavelength, µm. pixel: object pixel and detector sample spacing, µm. illuminations: lateral frequency fx of each plane wave, cycles/µm, |fx| < n_medium/λ.
Born2D.x
python
@property
def x(self) -> np.ndarrayLateral sample positions, µm, x = 0 at index n // 2.
Born2D.z
python
@property
def z(self) -> np.ndarrayAxial sample positions, µm, z = 0 at index nz // 2.
Born2D.mu
python
@property
def mu(self) -> np.ndarrayFrequencies of the field ratio, cycles/µm, centre-origin.
Born2D.matrix
python
def matrix(self) -> np.ndarray``forward`` as an explicit (count · n, nz · n) matrix, for small problems.
Born2D.forward
python
def forward(self, v) -> np.ndarrayPotential (nz, n), µm⁻² -> scattered field ratios u_s / u_in at z = 0, (count, n).
Born2D.adjoint
python
def adjoint(self, fields) -> np.ndarrayConjugate transpose of ``forward``: (count, n) -> (nz, n).