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

1''' 

2This module defines a functions necessary for tomography calculations. 

3 

4Authors/Modifications: 

5---------------------- 

6* Margaret Koker, koker@cars.uchicago.edu 

7''' 

8 

9import logging 

10logger = logging.getLogger(__name__) 

11logger.level = logging.ERROR 

12logger = logging.getLogger('tomopy.recon') 

13logger.level = logging.ERROR 

14 

15import numpy as np 

16from scipy.optimize import leastsq, minimize 

17 

18HAS_tomopy = False 

19try: 

20 import tomopy 

21 HAS_tomopy = True 

22except ImportError: 

23 pass 

24 

25# GLOBAL VARIABLES 

26TOMOPY_ALG = ['gridrec', 'art', 'bart', 'mlem', 'osem', 'ospml_hybrid', 

27 'ospml_quad', 'pml_hybrid', 'pml_quad', 'sirt' ] 

28 

29TOMOPY_FILT = ['shepp', 'ramlak', 'butterworth','parzen', 'cosine', 'hann', 

30 'hamming', 'None'] 

31 

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 

39 

40 

41def reshape_sinogram(A, x, omega): 

42 

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) 

51 

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 

61 

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]) 

67 

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] 

74 

75 return A, sinogram_order 

76 

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)] 

82 

83 sino = sino[:,pixel_trim:-1*(pixel_trim+1)] 

84 

85 return sino, x, omega 

86 

87def find_tomo_center(sino, omega, center=None, tol=0.25, blur_weight=2.0, 

88 sinogram_order=True): 

89 

90 """find rotation axis center for a sinogram, 

91 mixing negative entropy (as tomopy uses) and other focusing scores 

92 

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 

100 

101 Returns 

102 ------- 

103 pixel value for refined center 

104 

105 Notes 

106 ------ 

107 

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) 

110 

111 For a reconstructed image `img` the variance is calculated as 

112 

113 blur = -((img - img.mean())**2).sum()/img.size 

114 

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)) 

123 

124 

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 

131 

132 omega = ensure_radians(omega) 

133 

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 

141 

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] 

145 

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) 

153 

154 name formula in paper python 

155 

156 blur (F 10) -((img - img.mean())**2).sum()/img.size 

157 

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)) 

163 

164 """ 

165 ns, nang, nx = sino.shape 

166 if isinstance(center, (list, tuple, np.ndarray)): 

167 center = center[0] 

168 

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 

174 

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 

191 

192 

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. 

202 

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) 

209 

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 

220 

221 tomo = tomopy.recon(sino, romega, algorithm=algorithm, 

222 center=center, sinogram_order=sinogram_order, **recon_kws) 

223 return center, tomo