LION.operators.ct.ct_utils

Functions

from_HU_to_density(img)

Converts an image in Hounsfield Units into (approximate) mass density in g/cm^3, linearly: air (-1000 HU) -> 0.0012, water (0 HU) -> 1.0137, 500 HU -> 1.52.

from_HU_to_material_id(img)

Converts an image in Hounsfield units into a material index: 0 air (< -950), 1 lung [-950, -750), 2 bronchi [-750, -150), 3 fat [-150, 0), 4 muscle [0, 150), 5 bone marrow [150, 300), 6 bone (>= 300).

from_HU_to_mu(img[, mass_attenuation, ...])

Linear attenuation coefficient mu = density * (mu/rho), per length unit (default 1/mm, as LION geometries are in mm), with mass_attenuation the mass attenuation coefficient mu/rho in cm^2/g (e.g. WATER_MASS_ATTENUATION[80], from the NIST tables cited there: water-equivalent, monochromatic).

from_HU_to_normal(img)

Converts image in Hounsfield Units (air-> -1000, bone->500) into a [0-1] image.

sinogram_add_noise(proj[, I0, sigma, ...])

Adds realistic noise to sinograms (see _sinogram_add_noise), with or without gradients.


LION.operators.ct.ct_utils.from_HU_to_density(img)[source]

Converts an image in Hounsfield Units into (approximate) mass density in g/cm^3, linearly: air (-1000 HU) -> 0.0012, water (0 HU) -> 1.0137, 500 HU -> 1.52. Real tissue is closer to bilinear (bone is denser per HU); this is the conversion LION uses for its simulated datasets. Commercial scanners use a piecewise linear function. STIR has scanner-specific bilinear conversions from HU to attenuation (for PET/SPECT attenuation correction, so at those photon energies): https://raw.githubusercontent.com/UCL/STIR/85cc1940c297b1749cf44a9fba937d7cefdccd47/src/utilities/share/ct_slopes.json

LION.operators.ct.ct_utils.from_HU_to_material_id(img)[source]

Converts an image in Hounsfield units into a material index: 0 air (< -950), 1 lung [-950, -750), 2 bronchi [-750, -150), 3 fat [-150, 0), 4 muscle [0, 150), 5 bone marrow [150, 300), 6 bone (>= 300). Returns a new uint8 array/tensor; the input is not modified. May require some image filtering preprocessing

LION.operators.ct.ct_utils.from_HU_to_mu(img, mass_attenuation=None, length_unit_cm=0.1)[source]

Linear attenuation coefficient mu = density * (mu/rho), per length unit (default 1/mm, as LION geometries are in mm), with mass_attenuation the mass attenuation coefficient mu/rho in cm^2/g (e.g. WATER_MASS_ATTENUATION[80], from the NIST tables cited there: water-equivalent, monochromatic).

Without mass_attenuation it returns the density (g/cm^3) of from_HU_to_density, as it did before October 2026 (when its docstring called that the attenuation).

LION.operators.ct.ct_utils.from_HU_to_normal(img)[source]

Converts image in Hounsfield Units (air-> -1000, bone->500) into a [0-1] image. Comercial scanners use a piecewise linear function. Check STIR for real values. (https://raw.githubusercontent.com/UCL/STIR/85cc1940c297b1749cf44a9fba937d7cefdccd47/src/utilities/share/ct_slopes.json)

LION.operators.ct.ct_utils.sinogram_add_noise(proj, I0=1000, sigma=5, sigma_blur=0.3015, ks_value=3, flat_field=None, dark_field=None, enable_gradients=False, model='physical', detector='energy_integrating', mass_attenuation=0.1837, length_unit_cm=0.1, min_counts=1.0)[source]

Adds realistic noise to sinograms (see _sinogram_add_noise), with or without gradients.

model says how the line integrals proj become attenuation, i.e. what I0 means:

  • "physical" (default): proj are line integrals of a density image (g/cm^3, e.g. from from_HU_to_density) in length_unit_cm (0.1: mm, as LION geometries), and the transmission is exp(-mass_attenuation * proj * length_unit_cm), with mass_attenuation the mu/rho of water in cm^2/g (default DEFAULT_MASS_ATTENUATION, water at 80 keV; monochromatic, water-equivalent). I0 is the number of photons per detector pixel in vacuum, as in XCIST, and larger objects get noisier data, as in a scanner.

  • "relative": proj is divided by the maximum of each sample, so the most attenuating ray always keeps e^-1 of I0, whatever the object and its units. LION’s model before October 2026 (mass_attenuation unused).

The returned sinogram has the units of proj in both cases. The physical model only makes sense for density line integrals in the given length unit (as LION’s simulated experiments produce, see ct_experiments); for sinograms in other units, use model="relative".

detector:

  • "energy_integrating" (default; most clinical scanners): Poisson photon counts, detector cross-talk (a Gaussian blur of std sigma_blur pixels along the detector) and Gaussian electronic noise of std sigma photons, as in XCIST.

  • "photon_counting": the Poisson photon counts themselves. The energy threshold rejects electronic noise, so sigma and sigma_blur are not used, and the counts are integers >= 0. Charge sharing, pulse pile-up and energy resolution are not modelled.

min_counts: the detector signal (photons plus electronic noise, the dark field subtracted) can be 0 or negative when a ray gets (almost) no photons, and the log needs a positive value. Signals below min_counts (in photons; default 1) are set to it, so a starved ray reads as at most log(I0 / min_counts) of attenuation. None: LION’s old rule, signals <= 0 become 1e-6, i.e. log(I0) + 13.8, far beyond any real attenuation, which turns starved rays into strong streaks. It only matters when rays are starved, so in practice only with the physical model. A photon-counting detector’s signal is never negative, but it can be 0.

LION’s noise before October 2026 was the relative model with three errors, since fixed and not kept as an option (Ander, 25 Oct 2026): detector cross-talk blurred across neighbouring angles as well as detectors, the attenuation scaled by the maximum of the whole batch, and the 1e-6 rule.