Source code for mbirjax.preprocess.pymbir

import warnings
import numpy as np
import jax.numpy as jnp
import h5py


[docs] def compute_sino_and_params(filename, bh_correction=True): """ Load ORNL sinogram data from an HDF5 file and prepare all required parameters for cone‐beam reconstruction. This function performs the following steps in one call: 1. Extracts geometry parameters and defaults via `create_proj_params_dict_ornl`. 2. Reads the raw sinogram data via `load_projection_data_ornl`. 3. Optionally applies beam hardening correction to the sinogram via `apply_bh_correction`. Args: filename (str): Path to the ORNL HDF5 file containing projection data and geometry attributes. bh_correction (bool, optional): If True, apply beam hardening correction using the file’s stored parameters. Defaults to True. Returns: tuple: sino (numpy.ndarray): sinogram data of shape (num_views, num_det_rows, num_det_channels). cone_beam_params (dict): Dictionary of mandatory parameters for mbirjax.ConeBeamModel, including: - "sinogram_shape" - "angles" - "source_detector_dist" - "source_iso_dist" optional_params (dict): Additional model settings for set_params(), including: - "delta_det_channel" - "delta_det_row" - "delta_voxel" - "det_channel_offset" - "det_row_offset" Example: .. code-block:: python sino, cone_beam_params, optional_params = compute_sino_and_params("scan.h5", bh_correction=True) ct_model = mbirjax.ConeBeamModel(**cone_beam_params) ct_model.set_params(**optional_params) recon, recon_dict = ct_model.recon(sino, weights=weights) """ import mbirjax.preprocess as mjp with h5py.File(filename, 'r') as h5_file: cone_beam_params, optional_params, det_rotation = create_proj_params_dict_ornl(h5_file) sinogram = load_projection_data_ornl(h5_file) if bh_correction: BHCN_params = h5_file.attrs['BHC_params'] sinogram = apply_bh_correction(sinogram, BHCN_params) if np.abs(det_rotation) > 1e-6: # Correct the sinogram for detector rotation sinogram = mjp.correct_det_rotation(sinogram, det_rotation=det_rotation) warnings.warn('TODO: Verify the direction of sinogram rotation.') return sinogram, cone_beam_params, optional_params
def create_proj_params_dict_ornl(h5_file): """ Build cone‐beam geometry dictionaries from an open ORNL HDF5 file. This function reads the HDF5 attributes and computes all distances and offsets normalized to detector‐pixel units, and converts angles to radians. Args: h5_file (h5py.File): Opened NSI HDF5 file with required attributes: - 'distance unit', 'det_pixel_size', 'iso_det_dist', 'src_iso_dist' - 'voxel_size', 'det_column_offset', 'det_row_offset' - 'angle unit', 'angles', 'det_angle' - Dataset group 'projection' with at least one entry Returns: tuple: cone_beam_params (dict): Required parameters for ConeBeamModel constructor: - "sinogram_shape" (tuple): shape of the raw projection array. - "angles" (ndarray): projection angles in radians. - "source_detector_dist" (float): source to detector distance (in pixel units). - "source_iso_dist" (float): source to isocenter distance (in pixel units). optional_params (dict): Additional reconstruction settings for `set_params()`: - "delta_det_channel" (float) - "delta_det_row" (float) - "delta_voxel" (float) - "det_channel_offset" (float) - "det_row_offset" (float) """ # Unit conversion to 'ALU' pixel units if necessary det_pixel_size = h5_file.attrs['det_pixel_size'] iso_det_dist = h5_file.attrs['iso_det_dist'] / det_pixel_size source_iso_dist = h5_file.attrs['src_iso_dist'] / det_pixel_size source_detector_dist = source_iso_dist + iso_det_dist delta_voxel = h5_file.attrs['voxel_size'] / det_pixel_size det_channel_offset = -h5_file.attrs['det_column_offset'] / det_pixel_size det_row_offset = -h5_file.attrs['det_row_offset'] / det_pixel_size delta_det_channel = 1.0 delta_det_row = 1.0 # Angle conversion to radians if h5_file.attrs['angle unit'] == 'radian': angles = h5_file.attrs['angles'] det_rotation = h5_file.attrs['det_angle'] else: angles = h5_file.attrs['angles'] * np.pi / 180 det_rotation = h5_file.attrs['det_angle'] * np.pi / 180 sinogram_shape = h5_file['projection']['NegativeLogNorm_Proj'].shape cone_beam_params = dict() cone_beam_params["sinogram_shape"] = sinogram_shape cone_beam_params["angles"] = angles cone_beam_params["source_detector_dist"] = source_detector_dist cone_beam_params["source_iso_dist"] = source_iso_dist optional_params = dict() optional_params["delta_det_channel"] = delta_det_channel optional_params["delta_det_row"] = delta_det_row optional_params['delta_voxel'] = delta_voxel optional_params["det_channel_offset"] = det_channel_offset optional_params["det_row_offset"] = det_row_offset return cone_beam_params, optional_params, det_rotation def load_projection_data_ornl(h5_file): """ Read and return the raw sinogram array from an ORNL HDF5 file. This function assumes the first entry in the 'projection' group is the object scan used to build –log(I/I0) sinogram data. Args: h5_file (h5py.File): Open NSI HDF5 file containing a 'projection' group. Returns: numpy.ndarray: Raw sinogram data as a float32 array. """ sinogram = h5_file['projection']['NegativeLogNorm_Proj'][()].astype(np.float32) return sinogram def apply_bh_correction(sinogram, BHCN_params): """ Perform beam hardening correction on a sinogram. Uses a polynomial fit derived from ORNL‐stored beam hardening parameters. Args: sinogram (numpy.ndarray): Uncorrected sinogram data. BHCN_params (array-like of length 4): Beam hardening parameters [alpha, mu1, mu2, max_thickness]. Returns: numpy.ndarray: Sinogram after beam hardening linearization. """ alpha, mu1, mu2, max_thickness = BHCN_params poly_coefs = find_linearization_fit(alpha, mu1, mu2, max_thick=max_thickness) corrected_sinogram = jnp.polyval(poly_coefs, sinogram) corrected_sinogram = np.asarray(corrected_sinogram) return corrected_sinogram def find_linearization_fit(alpha, density1, density2, poly_order=8, max_thick=30.25, step_size=0.25): """ Fit a polynomial that linearizes the beam‐hardened transmission curve. Args: alpha (float): Ratio parameter for two‐material model. density1 (float): Linear attenuation coefficient of material 1. density2 (float): Linear attenuation coefficient of material 2. poly_order (int, optional): Order of the polynomial fit. Defaults to 8. max_thick (float, optional): Maximum thickness to sample (in same units as sinogram). Defaults to 30.25. step_size (float, optional): Sampling interval for thickness. Defaults to 0.25. Returns: numpy.ndarray: Coefficients for a polynomial that correct BH effect in measured attenuation (–log I/I0). """ uavg0 = (alpha * density1 + density2) / (1 + alpha) t_l = np.arange(0, max_thick, step_size) dut = (density2 - density1) * t_l prfit_l = density2 * t_l + np.log((1 + alpha) / (1 + alpha * np.exp(dut))) xxi_l = np.concatenate(([0], prfit_l)) yyi_l = np.concatenate(([0], uavg0 * t_l)) coefs = np.polyfit(xxi_l, yyi_l, poly_order) return coefs # np.poly1d(np.polyfit(xxi_l, yyi_l, poly_order))