Coverage for /Users/Newville/Codes/xraylarch/larch/math/tomography.py: 19%
100 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
1'''
2This module defines a functions necessary for tomography calculations.
4Authors/Modifications:
5----------------------
6* Margaret Koker, koker@cars.uchicago.edu
7'''
9import logging
10logger = logging.getLogger(__name__)
11logger.level = logging.ERROR
12logger = logging.getLogger('tomopy.recon')
13logger.level = logging.ERROR
15import numpy as np
16from scipy.optimize import leastsq, minimize
18HAS_tomopy = False
19try:
20 import tomopy
21 HAS_tomopy = True
22except ImportError:
23 pass
25# GLOBAL VARIABLES
26TOMOPY_ALG = ['gridrec', 'art', 'bart', 'mlem', 'osem', 'ospml_hybrid',
27 'ospml_quad', 'pml_hybrid', 'pml_quad', 'sirt' ]
29TOMOPY_FILT = ['shepp', 'ramlak', 'butterworth','parzen', 'cosine', 'hann',
30 'hamming', 'None']
32def ensure_radians(a):
33 """ensure angle data is in radians, not degrees,
34 converts degrees to radians if a peak-to-peak > 32 or step size > 0.2
35 """
36 if a.ptp() > 32 or np.diff(a).mean() > 0.20:
37 a = np.radians(a)
38 return a
41def reshape_sinogram(A, x, omega):
43 ## == INPUTS ==
44 ## A : array from .get_roimap()
45 ## x : x array for checking shape of A
46 ## omega : omega array for checking shape of A
47 ##
48 ## == RETURNS ==
49 ## A : A in shape/format needed for tomopy reconstruction
50 ## sinogram_order : flag/argument for tomopy reconstruction (shape dependent)
52 if len(x) == len(omega):
53 print("Warning: guessing that 2nd axis is omega")
54 # Cannot reorder sinogram based on length of positional
55 # arrays when same length. Acceptable orders:
56 # sinogram_order = False : sino = [ 2th , slice, X ]
57 # sinogram_order = True : sino = [ slice , 2th , X ]''')
58 if len(A.shape) == 2:
59 A = A.reshape(1, A.shape[0], A.shape[1])
60 return A, True
62 if len(x) < 1 or len(omega) < 1:
63 return A,False
64 if len(A.shape) != 3:
65 if len(A.shape) == 2:
66 A = A.reshape(1,A.shape[0],A.shape[1])
68 if len(A.shape) == 3:
69 if len(x) == A.shape[0]:
70 A = np.einsum('kij->ijk', A)
71 if len(x) == A.shape[1]:
72 A = np.einsum('ikj->ijk', A)
73 sinogram_order = len(omega) == A.shape[1]
75 return A, sinogram_order
77def trim_sinogram(sino, x, omega, pixel_trim=10):
78 if len(omega) == sino.shape[-1]:
79 omega = omega[pixel_trim:-1*(pixel_trim+1)]
80 elif len(x) == sino.shape[-1]:
81 x = x[pixel_trim:-1*(pixel_trim+1)]
83 sino = sino[:,pixel_trim:-1*(pixel_trim+1)]
85 return sino, x, omega
87def find_tomo_center(sino, omega, center=None, tol=0.25, blur_weight=2.0,
88 sinogram_order=True):
90 """find rotation axis center for a sinogram,
91 mixing negative entropy (as tomopy uses) and other focusing scores
93 Arguments
94 ---------
95 sino : ndarray for sinogram
96 omega: ndarray of angles in radians
97 center: initial value for center [mid-point]
98 tol: fit tolerance for center pixel [0.25]
99 sinogram_order: bool for axis order of sinogram
101 Returns
102 -------
103 pixel value for refined center
105 Notes
106 ------
108 The algormithm combines a few focusing scores from Y. Sun, S. Duthaler, and B. Nelson,
109 MICROSCOPY RESEARCH AND TECHNIQUE 65:139–149 (2004) (doi: 10.1002/jemt.20118a)
111 For a reconstructed image `img` the variance is calculated as
113 blur = -((img - img.mean())**2).sum()/img.size
115 and is combined with negative-entropy is calculated as
116 ioff = (img.max() - img.min())/25.0
117 imin = img.min() - ioff
118 imax = img.max() + ioff
119 hist, _ = np.histogram(img, bins=512, range=[imin, imax])
120 hist = hist/(2*(imax-imin))
121 hist[np.where(hist==0)] = 1.e-20
122 negent = -np.dot(hist, np.log(hist))
125 """
126 xmax = sino.shape[0]
127 if sinogram_order:
128 xmax = sino.shape[2]
129 if center is None:
130 center = xmax/2.0
132 omega = ensure_radians(omega)
134 img = tomopy.recon(sino, omega, center,
135 sinogram_order=sinogram_order,
136 algorithm='gridrec', filter_name='shepp')
137 img = tomopy.circ_mask(img, axis=0)
138 ioff = (img.max() - img.min())/25.0
139 imin = img.min() - ioff
140 imax = img.max() + ioff
142 out = minimize(center_score, center, method='Nelder-Mead', tol=tol,
143 args=(sino, omega, blur_weight, sinogram_order, imin, imax))
144 return out.x[0]
146def center_score(center, sino, omega, blur_weight=2.0, sinogram_order=True,
147 imin=None, imax=None, verbose=False):
148 """Cost function used for the ``find_center`` routine:
149 combines a few focusing scores from
150 Y. Sun, S. Duthaler, and B. Nelson,
151 MICROSCOPY RESEARCH AND TECHNIQUE 65:139–149 (2004)
152 (doi: 10.1002/jemt.20118a)
154 name formula in paper python
156 blur (F 10) -((img - img.mean())**2).sum()/img.size
158 negent is calculated as
159 hist, _ = np.histogram(img, bins=512, range=[imin, imax])
160 hist = hist/(2*(imax-imin))
161 hist[np.where(hist==0)] = 1.e-20
162 negent = -np.dot(hist, np.log(hist))
164 """
165 ns, nang, nx = sino.shape
166 if isinstance(center, (list, tuple, np.ndarray)):
167 center = center[0]
169 img = tomopy.recon(sino, ensure_radians(omega), center,
170 sinogram_order=sinogram_order,
171 algorithm='gridrec', filter_name='shepp')
172 img = tomopy.circ_mask(img, axis=0)
173 blur = -((img - img.mean())**2).sum()/img.size
175 if imin is None or imax is None:
176 ioff = (img.max() - img.min())/25.0
177 if imin is None:
178 imin = img.min() - ioff
179 if imax is None:
180 imax = img.max() + ioff
181 try:
182 hist, _ = np.histogram(img, bins=512, range=[imin, imax])
183 hist = hist/(2*(imax-imin))
184 hist[np.where(hist==0)] = 1.e-20
185 negent = -np.dot(hist, np.log(hist))
186 except:
187 negent = blur
188 if verbose:
189 print("Center %.3f %13.5g, %13.5g" % (center, blur, negent))
190 return blur*blur_weight + negent
193def tomo_reconstruction(sino, omega, algorithm='gridrec',
194 filter_name='shepp', num_iter=1, center=None,
195 refine_center=False, sinogram_order=True):
196 '''
197 INPUT -> sino : slice, 2th, x OR 2th, slice, x (with flag sinogram_order=True/False)
198 OUTPUT -> tomo : slice, x, y
199 '''
200 if center is None:
201 center = sino.shape[1]/2.
203 x = tomopy.init_tomo(sino, sinogram_order)
204 nomega = len(omega)
205 ns, nth, nx = sino.shape
206 if nth > nomega:
207 sino = sino[:, :nomega, :]
208 romega = ensure_radians(omega)
210 if refine_center:
211 center = find_tomo_center(sino, romega, center=center,
212 sinogram_order=sinogram_order)
213 print(">> Refine Center done>> ", center, sinogram_order)
214 algorithm = algorithm.lower()
215 recon_kws = {}
216 if algorithm.startswith('gridr'):
217 recon_kws['filter_name'] = filter_name
218 else:
219 recon_kws['num_iter'] = num_iter
221 tomo = tomopy.recon(sino, romega, algorithm=algorithm,
222 center=center, sinogram_order=sinogram_order, **recon_kws)
223 return center, tomo