Welcome to PySABER’s documentation!¶
Introduction¶
PySABER is a python package for characterizing the X-ray source and detector blurs in cone-beam X-ray imaging systems. SABER is an abbreviation for systems approach to blur estimation and reduction. Note that even parallel beam X-rays in synchrotrons are in fact cone beams albeit with a large source to object distance (SOD). X-ray images, also called radiographs, are simultaneously blurred by both the X-ray source spot blur and detector blur. This python package uses a numerical optimization algorithm to disentangle and estimate both forms of blur simultaneously. The point spread function (PSF) of X-ray source blur is modeled using a density function with two parameters. The first parameter is the full width half maximum (FWHM) of the PSF along the x-axis (row-wise) and second is the FWHM along the y-axis (column-axis). The PSF of detector blur is modeled as the sum of two density functions, each with its own FWHM parameter, that are mixed together by a mixture (or weight) parameter. All these parameters are then estimated using numerical optimization from normalized radiographs of a sharp edge such as a thick Tungsten plate rollbar. To simultaneously estimate the PSFs of both source and detector blurs, radiographs must be acquired at two different values for the ratio of the source to object distance (SOD) and object to detector distance (ODD). If each radiograph has a single straight edge, then the measurements must be repeated for two different, preferably perpendicular, orientations of the edge. If the radiograph consists of two intersecting perpendicular edges, then a single radiograph at each specified SOD/ODD is sufficient.
Once the parameters of both source and detector blurs are estimated, this package is also useful to reduce blur in radiographs using deblurring algorithms. Currently, Wiener filtering and regularized least squares deconvolution are two deblurring algorithms that are supported for deblurring. Both these techniques use the estimated blur parameters to deblur radiographs. For more detailed explanation on the experimental methodology and theory of PySABER, please read the paper in References.
References¶
If you use pysaber Package, we request you to cite the following article -
K. Aditya Mohan, Robert M. Panas, and Jefferson A. Cuadra. “SABER: A Systems Approach to Blur Estimation and Reduction in X-ray Imaging.” arXiv preprint arXiv:1905.03935 (2019) [pdf]
License¶
LLNL-CODE-766837. This project is licensed under the MIT License.
MIT License
Copyright (c) 2018, Lawrence Livermore National Security, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Installation¶
pysaber is installed using the python package manager pip. To install pysaber, run the following command in a terminal:
pip install pysaber
Alternatively, to install using the source code in the github repository pysaber, first download the repository using the download link in the top right corner of the webpage. Or, you can also git clone the repository directly from github. In a terminal, change the current directory to the outermost folder of this downloaded repository, which contains the README file, and run the following command:
pip install .
It is recommended to install the pysaber Package within a python virtual environment.
Tutorial¶
The steps involved in estimating blur PSFs and deblurring radiographs are outlined in the links below.
Input Sanity Check¶
The first step to estimating the blur model involves computation of the transmission function, which is the ideal radiograph image that is formed in the absence of X-ray source and detector blurs. This computation of transmission function is performed internally in the function
pysaber.estimate_blur()
, which is used to estimate the blur model by computing the parameters of X-ray source and detector blurs. However, this computation of transmission function is not fail-proof and may result in inaccurate edge localization if certain assumptions made when computing the transmission function are not satisfied.Before using
pysaber.estimate_blur()
to estimate blur model, it is recommended to check for accurate edge localization in the transmission function. The transmission function can be computed using the functionpysaber.get_trans_masks()
.The function
pysaber.get_trans_masks()
also returns the mask arrays for the transmission function and radiograph, which are used to include or exclude certain pixels from blur estimation. By default, the radiograph mask only excludes a small number of pixels along the boundary of the radiograph from blur estimation. Additional pixels can be excluded from blur estimation by appropriately setting the input arguments of the functionspysaber.get_trans_masks()
andpysaber.estimate_blur()
. The mask for transmission function should also exclude the padded pixels in addition to those pixels excluded by the radiograph mask. Hence,pysaber.get_trans_masks()
is also useful to check if user expectations for the mask arrays are satisfied.Example python scripts that demonstrate the above procedure are shown below. To obtain the data that is required to run this script, download and unzip the zip file at the link
data
. To run the script as is within the current working directory, the files in the zip file must be placed within a folder calleddata
.
import numpy as np
from PIL import Image #To read images in TIFF format
from pysaber import get_trans_masks #To compute transmission function and masks
import matplotlib.pyplot as plt #To display images
pix_wid = 0.675 #Width of each pixel in micrometers
#Read a horizontal edge radiograph for which the transmission function must be computed
rad = Image.open('data/horz_edge_25mm.tif') #Read radiograph
rad = np.asarray(rad) #Convert to numpy array
bright = Image.open('data/horz_bright.tif') #Read bright field
bright = np.asarray(bright) #Convert to numpy array
dark = Image.open('data/horz_dark.tif') #Read dark field
dark = np.asarray(dark) #Convert to numpy array
nrad = (rad-dark)/(bright-dark) #Normalize radiograph
#Get the transmission function and masks
trans,trans_mask,rad_mask = get_trans_masks(nrad,edge='straight',pad=[3,3])
#Use pad = [1,1] if you do not want padding
#Display the array trans
#Visually check for inaccurate localization of the sharp-edge
plt.imshow(trans,cmap='gray')
plt.colorbar()
plt.show()
#Display and inspect the mask for transmission function
plt.imshow(trans_mask,cmap='gray')
plt.show()
#Display and inspect the mask for radiograph
plt.imshow(rad_mask,cmap='gray')
plt.show()
#Show a line plot comparing the measured radiograph and transmission function
sz = nrad.shape
coords = np.arange(-(sz[0]//2),sz[0]//2,1)*pix_wid
mid = (trans.shape[0]//2,trans.shape[1]//2)
plt.plot(coords,nrad[:,sz[1]//2])
#Due to padding, trans is three times the size of nrad in each dimension
#For proper alignment in the presence of padding, both nrad and trans are center aligned
#Center alignment is used since an equal amount of padding is applied at both ends of each axis
plt.plot(coords,trans[mid[0]-(sz[0]//2):mid[0]+(sz[0]//2),mid[1]])
plt.xlabel('micrometers')
plt.legend(['Measured','Transmission'])
plt.show()
import numpy as np
from PIL import Image #To read images in TIFF format
from pysaber import get_trans_masks #To compute transmission function and masks
import matplotlib.pyplot as plt #To display images
pix_wid = 0.675 #Width of each pixel in micrometers
#Read a vertical edge radiograph for which the transmission function must be computed
rad = Image.open('data/vert_edge_25mm.tif') #Read radiograph
rad = np.asarray(rad) #Convert to numpy array
bright = Image.open('data/vert_bright.tif') #Read bright field
bright = np.asarray(bright) #Convert to numpy array
dark = Image.open('data/vert_dark.tif') #Read dark field
dark = np.asarray(dark) #Convert to numpy array
nrad = (rad-dark)/(bright-dark) #Normalize radiograph
#Get the transmission function and masks
trans,trans_mask,rad_mask = get_trans_masks(nrad,edge='straight',pad=[3,3])
#Use pad = [1,1] if you do not want padding
#Display the array trans
#Visually check for inaccurate localization of the sharp-edge
plt.imshow(trans,cmap='gray')
plt.colorbar()
plt.show()
#Display and inspect the mask for transmission function
plt.imshow(trans_mask,cmap='gray')
plt.show()
#Display and inspect the mask for radiograph
plt.imshow(rad_mask,cmap='gray')
plt.show()
#Show a line plot comparing the measured radiograph and transmission function
sz = nrad.shape
coords = np.arange(-(sz[1]//2),sz[1]//2,1)*pix_wid
mid = (trans.shape[0]//2,trans.shape[1]//2)
plt.plot(coords,nrad[sz[0]//2,:])
#Due to padding, trans is three times the size of nrad in each dimension
#For proper alignment in the presence of padding, both nrad and trans are center aligned
#Center alignment is used since an equal amount of padding is applied at both ends of each axis
plt.plot(coords,trans[mid[0],mid[1]-(sz[1]//2):mid[1]+(sz[1]//2)])
plt.xlabel('micrometers')
plt.legend(['Measured','Transmission'])
plt.show()
Estimate Blur Model¶
To estimate the blur model, we must acquire radiographs of a sharp edge such as a Tungsten rollbar. Each sharp edge radiograph can either contain a single straight edge or two mutually perpendicular intersecting edges. If imaging a single straight edge, then radiographs must be acquired at two different perpendicular orientations of the straight edge. Also, radiographs must be acquired at two different values of SOD/ODD, where SOD is the source to object distance and ODD is the object to detector distance.
Next, the radiographs must be appropriately normalized. For each radiograph, acquire a bright field image (measurements with X-rays but no sample) and a dark field image (measurements without X-rays). Then, compute the normalized radiograph by dividing the difference between the radiograph and the dark field image with the difference between the bright field and the dark field image.
Using the normalized radiographs, estimate parameters of X-ray source blur and detector blur using the function
pysaber.estimate_blur()
.An example python script that demonstrates blur estimation using radiographs of a single straight edge at various orientations and SOD/ODD values is shown below. To obtain the data that is required to run this script, download and unzip the zip file at the link
data
. To run the script as is within the current working directory, the files in the zip file must be placed within a folder calleddata
.
import numpy as np #For mathematics on vectors
from PIL import Image #To read images in TIFF format
from pysaber import estimate_blur #To estimate blur PSF parameters
pix_wid = 0.675 #Width of each pixel in micrometers
#Horizontal and vertical edge radiographs
edge_files = ['data/horz_edge_25mm.tif','data/horz_edge_50mm.tif',
'data/vert_edge_25mm.tif','data/vert_edge_50mm.tif']
#Filenames of bright field images for normalization
bright_files = ['data/horz_bright.tif','data/horz_bright.tif',
'data/vert_bright.tif','data/vert_bright.tif']
#Filenames of dark field images for normalization
dark_files = ['data/horz_dark.tif','data/horz_dark.tif',
'data/vert_dark.tif','data/vert_dark.tif']
#Source to object (SOD) distances in micrometers for each radiograph in edge_files
sod = [24751.89,50251.79,24753.05,50253.35]
#Source to detector (SDD) distances in micrometers for each radiograph in edge_files
sdd = [71003.08,71003.08,71010.86,71010.86]
rads = [] #List that will contain normalized radiographs
odd = [] #Object to detector distance (ODD) for each radiograph in rads
for i in range(len(edge_files)): #Loop through all the radiograph files
rad = Image.open(edge_files[i]) #Read radiograph
rad = np.asarray(rad) #Convert to numpy array
bright = Image.open(bright_files[i]) #Read bright field
bright = np.asarray(bright) #Convert to numpy array
dark = Image.open(dark_files[i]) #Read dark field
dark = np.asarray(dark) #Convert to numpy array
nrad = (rad-dark)/(bright-dark) #Normalize radiograph
rads.append(nrad) #Add normalized radiograph to the list rads
odd.append(sdd[i]-sod[i]) #Add corresponding ODD to the list odd
#Estimate X-ray source blur, detector blur, and every radiograph's transmission function
#To reduce run time and quickly produce a result, the value for argument thresh can be reduced.
#However, reducing thresh may produce an inaccurate blur model that does not fit the measured data
src_params,det_params,trans_params = estimate_blur(rads,sod,odd,pix_wid,
edge='straight',thresh=1e-6,pad=[3,3],power=1.0,save_dir='./')
#src_params is a python dictionary of parameters that quantify X-ray source blur
#det_params is a python dictionary of parameters that quantify blur from the detector panel
#Both src_params and det_params characterize the effective blur and are used during deblurring
#trans_params is a list of lists, each of which contains the low/high values of transmission function
#trans_params is useful to check accuracy of fit and not useful for deblurring.
#help(estimate_blur)
#Uncomment above line to get help on using the function estimate_blur
print("-------------------------------------------------------")
print("Source blur model parameters are {}".format(src_params))
#Print parameters of source blur
print("Detector blur model parameters are {}".format(det_params))
#Print parameters of detector blur
print("Transmission function parameters are {}".format(trans_params))
#Print parameters of transmission functions
Validate Blur Model¶
We must ensure that the estimated parameters are indeed a good fit for the measured data. This is done by comparing line profiles across the sharp edge between the measured radiograph and the predicted radiograph from the blur model. The output of the blur model given parameters of source blur, detector blur, and transmission function is computed using the function
pysaber.get_trans_fit()
. The fit must be evaluated for every sharp-edge radiograph that is input topysaber.estimate_blur()
.Verify the agreement between the measured radiograph and the blur model prediction. Carefully zoom into the region containing the sharp edge and verify if the predicted blur matches with the blur in the measured radiograph. Also, verify the agreement between the measured radiograph values and blur model prediction in regions further away from the sharp edge. The predicted radiograph that is output by the blur model contains additional padding. Hence, it is necessary to account for this padding when comparing with the measured radiograph.
If the fit is not tight, consider reducing the value of the input argument
thresh
of the functionpysaber.estimate_blur()
to obtain a better fit. Reducing the convergence threshold,thresh
, can improve the agreement between the measured radiograph and the blur model prediction, but will inevitably result in longer run times. A good fit indicates that the blur model is able to accurately model the X-ray source and detector blurs.Example python scripts for line profile comparisons between the blur model prediction and measured radiograph are shown below. To obtain the data that is required to run this script, download and unzip the zip file at the link
data
. To run the script as is within the current working directory, the files in the zip file must be placed within a folder calleddata
.
import numpy as np
from PIL import Image #To read images in TIFF format
from pysaber import get_trans_fit #To get blurred radiograph as predicted by the blur model
import matplotlib.pyplot as plt #To display images
pix_wid = 0.675 #Width of each pixel in micrometers
#Read a horizontal edge radiograph for which accuracy of fit must be analyzed
rad = Image.open('data/horz_edge_25mm.tif') #Read radiograph
rad = np.asarray(rad) #Convert to numpy array
bright = Image.open('data/horz_bright.tif') #Read bright field
bright = np.asarray(bright) #Convert to numpy array
dark = Image.open('data/horz_dark.tif') #Read dark field
dark = np.asarray(dark) #Convert to numpy array
nrad = (rad-dark)/(bright-dark) #Normalize radiograph
sod = 24751.89 #Source to object distance (SOD) of radiograph
sdd = 71003.08 #Source to detector distance (SDD) of radiograph
#Parameters of X-ray source blur
src_params = {'source_FWHM_x_axis':2.69,
'source_FWHM_y_axis':3.01,
'norm_power':1.0,
'cutoff_FWHM_multiplier':10}
#Parameters of detector blur
det_params = {'detector_FWHM_1':1.85,
'detector_FWHM_2':126.5,
'detector_weight_1':0.916,
'norm_power':1.0,
'cutoff_FWHM_1_multiplier':10,
'cutoff_FWHM_2_multiplier':10}
#Transmission function parameters
trans_params = [0.015,0.98]
#Get the blurred radiograph as predicted by the blur model
pred_nrad,_ = get_trans_fit(nrad,sod,sdd-sod,pix_wid,src_params,det_params,trans_params,edge='straight',pad=[3,3])
#Show a line plot comparing the measured radiograph and the predicted blurred radiograph
sz = nrad.shape
coords = np.arange(-(sz[0]//2),sz[0]//2,1)*pix_wid
mid = (pred_nrad.shape[0]//2,pred_nrad.shape[1]//2)
plt.plot(coords,nrad[:,sz[1]//2])
#Due to padding, pred_nrad is three times the size of nrad in each dimension
#For proper alignment in the presence of padding, both nrad and pred_nrad are center aligned
#Center alignment is used since an equal amount of padding is applied at both ends of each axis
plt.plot(coords,pred_nrad[mid[0]-(sz[0]//2):mid[0]+(sz[0]//2),mid[1]])
plt.xlabel('micrometers')
plt.legend(['Measured','Prediction'])
plt.show()
import numpy as np
from PIL import Image #To read images in TIFF format
from pysaber import get_trans_fit #To get blurred radiograph as predicted by the blur model
import matplotlib.pyplot as plt #To display images
pix_wid = 0.675 #Width of each pixel in micrometers
#Read a vertical edge radiograph for which accuracy of fit must be analyzed
rad = Image.open('data/vert_edge_25mm.tif') #Read radiograph
rad = np.asarray(rad) #Convert to numpy array
bright = Image.open('data/vert_bright.tif') #Read bright field
bright = np.asarray(bright) #Convert to numpy array
dark = Image.open('data/vert_dark.tif') #Read dark field
dark = np.asarray(dark) #Convert to numpy array
nrad = (rad-dark)/(bright-dark) #Normalize radiograph
sod = 24753.05 #Source to object distance (SOD) of radiograph
sdd = 71010.86 #Source to detector distance (SDD) of radiograph
#Parameters of X-ray source blur
src_params = {'source_FWHM_x_axis':2.69,
'source_FWHM_y_axis':3.01,
'norm_power':1.0,
'cutoff_FWHM_multiplier':10}
#Parameters of detector blur
det_params = {'detector_FWHM_1':1.85,
'detector_FWHM_2':126.5,
'detector_weight_1':0.916,
'norm_power':1.0,
'cutoff_FWHM_1_multiplier':10,
'cutoff_FWHM_2_multiplier':10}
#Transmission function parameters
trans_params = [0.015,0.98]
#Get the blurred radiograph as predicted by the blur model
pred_nrad,_ = get_trans_fit(nrad,sod,sdd-sod,pix_wid,src_params,det_params,trans_params,edge='straight',pad=[3,3])
#Show a line plot comparing the measured radiograph and the predicted blurred radiograph
sz = nrad.shape
coords = np.arange(-(sz[1]//2),sz[1]//2,1)*pix_wid
mid = (pred_nrad.shape[0]//2,pred_nrad.shape[1]//2)
plt.plot(coords,nrad[sz[0]//2,:])
#Due to padding, pred_nrad is three times the size of nrad in each dimension
#For proper alignment in the presence of padding, both nrad and pred_nrad are center aligned
#Center alignment is used since an equal amount of padding is applied at both ends of each axis
plt.plot(coords,pred_nrad[mid[0],mid[1]-(sz[1]//2):mid[1]+(sz[1]//2)])
plt.xlabel('micrometers')
plt.legend(['Measured','Prediction'])
plt.show()
Visualize Blur PSF¶
For further analysis and visualization, we can also compute the point spread functions (PSF) of source blur and detector blur. The PSF of source blur is computed using the function
pysaber.get_source_psf()
and PSF of detector blur is computed usingpysaber.get_detector_psf()
.The function
pysaber.get_source_psf()
is useful to compute PSF either in the plane of the X-ray source or the plane of the detector. Since source blur PSF on the detector plane is a function of the object’s source to object distance (SOD) and object to detector distance (ODD), SOD and ODD must be specified when computing source PSF in the plane of the detector. To compute source blur PSF in the source plane, it is sufficient to use the default values for SOD and ODD inpysaber.get_source_psf()
.The detector blur PSF obtained using
pysaber.get_detector_psf()
models blur due to the scintillator and detector panel. Hence, it is independent of SOD and ODD.Example python scripts that demonstrate visualization of source and detector PSFs are shown below.
import numpy as np #For mathematics on vectors
import matplotlib.pyplot as plt #For plotting and showing images
from pysaber import get_source_psf #To compute PSF of source blur
pix_wid = 0.675 #Width of each pixel in micrometers
#Parameters of X-ray source blur
src_params = {'source_FWHM_x_axis':2.69,
'source_FWHM_y_axis':3.01,
'norm_power':1.0,
'cutoff_FWHM_multiplier':10}
#Get point spread function (PSF) of source blur in the plane of the X-ray source.
#Do not supply SOD and SDD if you need PSF in the source plane.
source_psf = get_source_psf(pix_wid,src_params)
#Display the source blur PSF on the source plane as an image
sz = source_psf.shape
x = np.arange(-(sz[1]//2),(sz[1]//2)+1,1)*pix_wid
y = np.arange(-(sz[0]//2),(sz[0]//2)+1,1)*pix_wid
plt.pcolormesh(x,y,source_psf,cmap='gray')
plt.xlabel('micrometers')
plt.ylabel('micrometers')
plt.title('X-ray source PSF at source plane')
plt.colorbar()
plt.show()
#To get source blur PSF on the detector plane, supply SOD and ODD = SDD - SOD to the function get_source_psf
sod = 25000 #in micrometers
sdd = 71000 #in micrometers
source_psf = get_source_psf(pix_wid,src_params,sod,sdd-sod)
#help(get_source_psf)
#Uncomment the above line to get help on using the function get_source_psf
#Display the source blur PSF on the detector plane as an image
sz = source_psf.shape
x = np.arange(-(sz[1]//2),(sz[1]//2)+1,1)*pix_wid
y = np.arange(-(sz[0]//2),(sz[0]//2)+1,1)*pix_wid
plt.pcolormesh(x,y,source_psf,cmap='gray')
plt.xlabel('micrometers')
plt.ylabel('micrometers')
plt.title('X-ray source PSF at detector plane')
plt.colorbar()
plt.show()
import numpy as np #For mathematics on vectors
import matplotlib.pyplot as plt #For displaying images
from matplotlib.colors import LogNorm #To display image values in logarithm scale
from pysaber import get_detector_psf #To compute PSF of detector blur
pix_wid = 0.675 #Width of each pixel in micrometers
#Parameters of detector blur
det_params = {'detector_FWHM_1':1.85,
'detector_FWHM_2':126.5,
'detector_weight_1':0.916,
'norm_power':1.0,
'cutoff_FWHM_1_multiplier':10,
'cutoff_FWHM_2_multiplier':10}
#Get point spread function (PSF) of detector blur as a 2D numpy array.
detector_psf = get_detector_psf(pix_wid,det_params)
#help(get_detector_psf)
#Uncomment the above line to get help in using the function get_detector_psf
#Display the PSF of detector blur
sz = detector_psf.shape
x = np.arange(-(sz[1]//2),(sz[1]//2)+1,1)*pix_wid
y = np.arange(-(sz[0]//2),(sz[0]//2)+1,1)*pix_wid
plt.pcolormesh(x,y,detector_psf,cmap='gray',norm=LogNorm())
plt.xlabel('micrometers')
plt.ylabel('micrometers')
plt.title('Detector PSF')
plt.colorbar()
plt.show()
Deblur Radiographs¶
Once the parameters of source and detector PSFs are estimated, radiographs of any arbitrary sample acquired at any source to object distance (SOD) and source to detector distance (SDD) can be deblurred using various techniques.
To deblur a radiograph using Wiener filter, the function
pysaber.wiener_deblur()
is used. To deblur using regularized least squares deconvolution (RLSD), use the functionpysaber.least_squares_deblur()
.Deblurring increases sharpness and resolution. However, it also introduces ringing artifacts and increases noise. To reduce noise and ringing artifacts, the regularization parameter can be increased. Ringing artifacts also increase with increasing inaccuracy of the blur model. Thus, it is essential to obtain a good fit between the measured radiograph and the blur model prediction as explained in the section Validate Blur Model.
The python scripts shown below demonstrate deblurring of radiographs using Wiener filter and RLSD. To obtain the data that is required to run this script, download and unzip the zip file at the link
data
. To run the script as is within the current working directory, the files in the zip file must be placed within a folder calleddata
.
import numpy as np
from pysaber import wiener_deblur #To deblur using Wiener filtering
import matplotlib.pyplot as plt #To display images
from PIL import Image #To read images in TIFF format
rad_file = 'data/horz_edge_25mm.tif' #Filename of radiograph
bright_file = 'data/horz_bright.tif' #Bright field
dark_file = 'data/horz_dark.tif' #Dark field
sdd = 71003.08 #Source to detector distance (SDD) in micrometers
sod = 24751.89 #Source to object distance (SOD) in micrometers
pix_wid = 0.675 #Pixel width in micrometers
reg_param = 0.1 #Regularization parameter
rad = np.asarray(Image.open(rad_file)) #Read radiograph and convert to numpy array
bright = np.asarray(Image.open(bright_file)) #Read bright field image and convert to numpy array
dark = np.asarray(Image.open(dark_file)) #Read dark field image and convert to numpy array
norm_rad = (rad-dark)/(bright-dark) #Normalize radiograph
#X-ray source blur parameters
src_params = {'source_FWHM_x_axis':2.69,
'source_FWHM_y_axis':3.01,
'norm_power':1.0,
'cutoff_FWHM_multiplier':10}
#Detector blur parameters
det_params = {'detector_FWHM_1':1.85,
'detector_FWHM_2':126.5,
'detector_weight_1':0.916,
'norm_power':1.0,
'cutoff_FWHM_1_multiplier':10,
'cutoff_FWHM_2_multiplier':10}
#Deblur the radiograph using Wiener filter
wiener_rad = wiener_deblur(norm_rad,sod,sdd-sod,pix_wid,src_params,det_params,reg_param)
#Display deblurred radiograph
sz = wiener_rad.shape
x = np.arange(-(sz[1]//2),(sz[1]//2)+1,1)*pix_wid
y = np.arange(-(sz[0]//2),(sz[0]//2)+1,1)*pix_wid
plt.pcolormesh(x,y,wiener_rad,cmap='gray')
plt.xlabel('micrometers')
plt.ylabel('micrometers')
plt.title('Wiener deblur')
plt.colorbar()
plt.show()
import numpy as np
from pysaber import least_squares_deblur #To deblur using regularized least squares deconvolution (RLSD)
import matplotlib.pyplot as plt #To display images
from PIL import Image #To read images in TIFF format
rad_file = 'data/horz_edge_25mm.tif' #Filename of radiograph
bright_file = 'data/horz_bright.tif' #Bright field image
dark_file = 'data/horz_dark.tif' #Dark field image
sdd = 71003.08 #Source to detector distance (SDD) in micrometers
sod = 24751.89 #Source to object distance (SOD) in micrometers
pix_wid = 0.675 #Pixel width in micrometers
reg_param = 0.001 #Regularization parameter
rad = np.asarray(Image.open(rad_file)) #Read radiograph and convert to numpy array
bright = np.asarray(Image.open(bright_file)) #Read bright field image and convert to numpy array
dark = np.asarray(Image.open(dark_file)) #Read dark field image and convert to numpy array
norm_rad = (rad-dark)/(bright-dark) #Normalize radiograph
#X-ray source blur parameters
src_params = {'source_FWHM_x_axis':2.69,
'source_FWHM_y_axis':3.01,
'norm_power':1.0,
'cutoff_FWHM_multiplier':10}
#Detector blur parameters
det_params = {'detector_FWHM_1':1.85,
'detector_FWHM_2':126.5,
'detector_weight_1':0.916,
'norm_power':1.0,
'cutoff_FWHM_1_multiplier':10,
'cutoff_FWHM_2_multiplier':10}
#Deblur the radiograph using regularized least squares deconvolution (RLSD)
rlsd_rad = least_squares_deblur(norm_rad,sod,sdd-sod,pix_wid,src_params,det_params,reg_param,thresh=2e-4)
#Display deblurred radiograph of artifact
sz = rlsd_rad.shape
x = np.arange(-(sz[1]//2),(sz[1]//2)+1,1)*pix_wid
y = np.arange(-(sz[0]//2),(sz[0]//2)+1,1)*pix_wid
plt.pcolormesh(x,y,rlsd_rad,cmap='gray')
plt.xlabel('micrometers')
plt.ylabel('micrometers')
plt.title('RLSD deblur')
plt.colorbar()
plt.show()
pysaber Package¶
Functions¶
|
Function to blur the input radiograph with point spread functions (PSF) of X-ray source and detector blurs. |
|
Estimate parameters of point spread functions (PSF) that model X-ray source blur and/or detector blur from normalized radiographs of a straight sharp edge or mutually perpendicular intersecting pair of sharp edges. |
|
Function to compute point spread function (PSF) of detector blur. |
|
Function to compute the effective point spread function (PSF), which is the convolution of X-ray source and detector PSFs. |
|
Function to compute the point spread function (PSF) of X-ray source blur in the plane of the X-ray source or the detector. |
|
Function to compute the blur model prediction and ideal transmission function for a radiograph with a single straight edge or two mutually perpendicular edges. |
|
Function to compute transmission function and masks for a radiograph with a single straight edge or two mutually perpendicular edges. |
|
Function to reduce blur (deblur) in radiographs using a regularized least squares iterative algorithm. |
|
Function to reduce blur (deblur) in a radiograph using Wiener filtering. |
Feedback¶
This software is under development and may have bugs. If you run into any problems, please raise a issue on github at the link issues. There is a lot of scope to improve the performance and functionality of this python package. Furthermore, since this package solves a non-convex optimization problem, there is a remote possibility that the final solution may be a local optima that does not properly fit the data. If there is sufficient interest, we will invest time to significantly reduce the run time, improve convergence and usability, and add additional features and functionalities.
Sponsor¶
Acknowledgements¶
LLNL-CODE-766837. LLNL-SM-809826. This work performed under the auspices of the U.S. Department of Energy by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344.
Disclaimer¶
This document was prepared as an account of work sponsored by an agency of the United States government. Neither the United States government nor Lawrence Livermore National Security, LLC, nor any of their employees makes any warranty, expressed or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States government or Lawrence Livermore National Security, LLC. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States government or Lawrence Livermore National Security, LLC, and shall not be used for advertising or product endorsement purposes.