Source code for tomophantom.flatsgen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2023
The University of Manchester & Diamond Light Source
Licensed under the Apache License, Version 2.0.
"""

from scipy.special import spherical_yn
from scipy.ndimage import gaussian_filter
from scipy.ndimage import shift
import random
import numpy as np
from tomophantom.artefacts import noise
from tomophantom.supp.speckle_routines import simulate_speckles_with_shot_noise


[docs]def synth_flats( projData3D_clean: np.ndarray, source_intensity: int, detectors_miscallibration: float = 0.05, variations_number: int = 3, arguments_Bessel: tuple = (1, 25), specklesize: int = 2, kbar: int = 2, sigmasmooth: int = 3, jitter_projections: float = 0.0, flatsnum: int = 20, ) -> list: """Function to generate synthetic flat field images and raw data for projection data normalisation. This is more realistic modelling of stripes and various normalisation artefacts. Args: projData3D_clean (np.ndarray): 3D projection data of the following shape: (DetectorsVert, anglesDim, DetectorsHoriz). source_intensity (int): Source intensity which affects the amount of the Poisson noise added to data. detectors_miscallibration (float, optional): A constant which perturbs some detectors positions, leading to more prounonced ring artefacts. Defaults to 0.05. variations_number (int, optional): A type of function to control stripe type (1 - linear, 2, - sinusoidal, 3 - exponential). Defaults to 3. arguments_Bessel (tuple, optional): A tuple of 2 arguments for 2 Bessel functions to control background variations in flats. Defaults to (1,25). specklesize (int, optional): Speckle size in pixel units for flats background simulation. Defaults to 2. kbar (int, optional): Mean photon density (photons per pixel) for background simulation. Defaults to 2. sigmasmooth (int, optional): Gaussian smoothing parameter to blur the speckled backround (1,3,5,7,...). Defaults to 3. jitter_projections (float, optional): An additional random jitter to the projections in pixels. Defaults to 0.0. flatsnum (int, optional): A number of flats to generate. Defaults to 20. Returns: list: List that contains [np.uint16(projData3D_raw), np.uint16(simulated_flats), blurred_speckles_map] """ [DetectorsDimV, projectionsNo, DetectorsDimH] = np.shape(projData3D_clean) # output datasets flats_combined3D = np.zeros( (DetectorsDimV, flatsnum, DetectorsDimH), dtype="uint16" ) projData3D_raw = np.zeros(np.shape(projData3D_clean), dtype="float32") # normalise the data projData3D_clean /= np.max(projData3D_clean) # using spherical Bessel functions to emulate the background (scintillator) variations func = spherical_yn( 1, np.linspace( arguments_Bessel[0], arguments_Bessel[1], DetectorsDimV, dtype="float32" ), ) func += abs(np.min(func)) flatfield = np.zeros((DetectorsDimV, DetectorsDimH)) for i in range(0, DetectorsDimH): flatfield[:, i] = func for i in range(0, DetectorsDimH): flatfield[:, i] += np.flipud(func) if specklesize != 0.0: # using speckle generator routines to create a photon count texture in the background speckle_background = simulate_speckles_with_shot_noise( [DetectorsDimV, DetectorsDimH], 1, specklesize, kbar ) else: speckle_background = np.ones((DetectorsDimV, DetectorsDimH)) # model miscallibrated detectors (a possible path to generate ring artifacts) blurred_speckles_map = np.zeros((DetectorsDimV, DetectorsDimH, variations_number)) for i in range(0, variations_number): speckles = simulate_speckles_with_shot_noise( [DetectorsDimV, DetectorsDimH], 1, 10, 0.03 ) # blur the speckled background blurred_speckles = gaussian_filter(speckles.copy(), sigma=sigmasmooth) # threshold the result blurred_speckles[blurred_speckles < 0.6 * np.max(blurred_speckles)] = 0 blurred_speckles_map[:, :, i] = blurred_speckles blurred_speckles_map /= np.max(blurred_speckles_map) sinusoidal_response = ( np.sin(np.linspace(0, 1.5 * np.pi, projectionsNo)) + np.random.random(projectionsNo) * 0.1 ) sinusoidal_response /= np.max(sinusoidal_response) exponential_response = ( np.exp(np.linspace(0, np.pi, projectionsNo)) + np.random.random(projectionsNo) * 0.1 ) exponential_response /= np.max(exponential_response) # prepeare flat fields for i in range(0, flatsnum): # add speckled background to the initial image with the Bessel background flatfield_combined = flatfield.copy() + 0.5 * ( speckle_background / np.max(speckle_background) ) flatfield_combined /= np.max(flatfield_combined) # adding Poisson noise to flat fields flatfield_poisson = noise( flatfield_combined * source_intensity, source_intensity, noisetype="Poisson" ) flatfield_poisson /= np.max(flatfield_poisson) flats_combined3D[:, i, :] = np.uint16(flatfield_poisson * 65535) # convert synthetic projections to raw-data like projection ready for normalisation for i in range(0, projectionsNo): proj_exp = ( np.exp(-projData3D_clean[:, i, :]) * source_intensity * flatfield_poisson ) # raw projection for j in range(0, variations_number): if j == 0: # adding a consistent offset for certain detectors proj_exp += ( blurred_speckles_map[:, :, j] * detectors_miscallibration * source_intensity ) if j == 1: # adding a sinusoidal-like response offset for certain detectors proj_exp += ( sinusoidal_response[i] * blurred_speckles_map[:, :, j] * detectors_miscallibration * source_intensity ) if j == 2: # adding an exponential response offset for certain detectors proj_exp += ( exponential_response[i] * blurred_speckles_map[:, :, j] * detectors_miscallibration * source_intensity ) projection_poisson = noise(proj_exp, source_intensity, noisetype="Poisson") # apply jitter to projections if jitter_projections != 0.0: horiz_shift = random.uniform( -jitter_projections, jitter_projections ) # generate random directional shift vert_shift = random.uniform( -jitter_projections, jitter_projections ) # generate random directional shift projection_poisson = shift( projection_poisson.copy(), [vert_shift, horiz_shift], mode="reflect" ) projData3D_raw[:, i, :] = projection_poisson projData3D_raw /= np.max(projData3D_raw) return [ np.uint16(projData3D_raw * 65535), np.uint16(flats_combined3D), blurred_speckles_map, ]