Mercurial > repos > rv43 > tomo
comparison tomo.py @ 49:26f99fdd8d61 draft
"planemo upload for repository https://github.com/rolfverberg/galaxytools commit 4f7738d02f4a3fd91373f43937ed311b6fe11a12"
author | rv43 |
---|---|
date | Thu, 28 Jul 2022 16:05:24 +0000 |
parents | 059819ea1f0e |
children | 75dd6e15f628 |
comparison
equal
deleted
inserted
replaced
48:059819ea1f0e | 49:26f99fdd8d61 |
---|---|
12 import os | 12 import os |
13 import sys | 13 import sys |
14 import getopt | 14 import getopt |
15 import re | 15 import re |
16 import io | 16 import io |
17 try: | |
18 import pyinputplus as pyip | |
19 except: | |
20 pass | |
21 import argparse | 17 import argparse |
22 import numpy as np | 18 import numpy as np |
23 try: | 19 try: |
24 import numexpr as ne | 20 import numexpr as ne |
25 except: | 21 except: |
41 try: | 37 try: |
42 from skimage.restoration import denoise_tv_chambolle | 38 from skimage.restoration import denoise_tv_chambolle |
43 except: | 39 except: |
44 pass | 40 pass |
45 | 41 |
46 import msnc_tools as msnc | 42 from detector import TomoDetectorConfig |
43 from fit import Fit | |
44 #from msnctools.general import illegal_value, is_int, is_num, is_index_range, get_trailing_int, \ | |
45 from general import illegal_value, is_int, is_num, is_index_range, get_trailing_int, \ | |
46 input_int, input_num, input_yesno, input_menu, findImageFiles, loadImageStack, clearPlot, \ | |
47 draw_mask_1d, quickPlot, clearImshow, quickImshow, combine_tiffs_in_h5, Config | |
48 from general import selectArrayBounds,selectImageRange, selectImageBounds | |
47 | 49 |
48 # the following tomopy routines don't run with more than 24 cores on Galaxy-Dev | 50 # the following tomopy routines don't run with more than 24 cores on Galaxy-Dev |
49 # - tomopy.find_center_vo | 51 # - tomopy.find_center_vo |
50 # - tomopy.prep.stripe.remove_stripe_fw | 52 # - tomopy.prep.stripe.remove_stripe_fw |
51 num_core_tomopy_limit = 24 | 53 num_core_tomopy_limit = 24 |
66 logging.debug(f'self.num_core={self.num_core}') | 68 logging.debug(f'self.num_core={self.num_core}') |
67 | 69 |
68 def __exit__(self, exc_type, exc_value, traceback): | 70 def __exit__(self, exc_type, exc_value, traceback): |
69 ne.set_num_threads(self.num_core_org) | 71 ne.set_num_threads(self.num_core_org) |
70 | 72 |
71 class ConfigTomo(msnc.Config): | 73 class ConfigTomo(Config): |
72 """Class for processing a config file. | 74 """Class for processing a config file. |
73 """ | 75 """ |
74 | 76 |
75 def __init__(self, config_file=None, config_dict=None): | 77 def __init__(self, config_file=None, config_dict=None): |
76 super().__init__(config_file, config_dict) | 78 super().__init__(config_file, config_dict) |
94 # Find tomography bright field images file/folder | 96 # Find tomography bright field images file/folder |
95 self.tbf_data_path = self.config.get('tbf_data_path') | 97 self.tbf_data_path = self.config.get('tbf_data_path') |
96 | 98 |
97 # Check number of tomography image stacks | 99 # Check number of tomography image stacks |
98 self.num_tomo_stacks = self.config.get('num_tomo_stacks', 1) | 100 self.num_tomo_stacks = self.config.get('num_tomo_stacks', 1) |
99 if not msnc.is_int(self.num_tomo_stacks, 1): | 101 if not is_int(self.num_tomo_stacks, 1): |
100 self.num_tomo_stacks = None | 102 self.num_tomo_stacks = None |
101 msnc.illegal_value('num_tomo_stacks', self.num_tomo_stacks, 'config file') | 103 illegal_value(self.num_tomo_stacks, 'num_tomo_stacks', 'config file') |
102 return False | 104 return False |
103 logging.info(f'num_tomo_stacks = {self.num_tomo_stacks}') | 105 logging.info(f'num_tomo_stacks = {self.num_tomo_stacks}') |
104 | 106 |
105 # Find tomography images file/folders and stack parameters | 107 # Find tomography images file/folders and stack parameters |
106 tomo_data_paths_indices = sorted({key:value for key,value in self.config.items() | 108 tomo_data_paths_indices = sorted({key:value for key,value in self.config.items() |
107 if 'tomo_data_path' in key}.items()) | 109 if 'tomo_data_path' in key}.items()) |
108 if len(tomo_data_paths_indices) != self.num_tomo_stacks: | 110 if len(tomo_data_paths_indices) != self.num_tomo_stacks: |
109 logging.error(f'Incorrect number of tomography data path names in config file') | 111 logging.error(f'Incorrect number of tomography data path names in config file') |
110 is_valid = False | 112 is_valid = False |
111 self.tomo_data_paths = [tomo_data_paths_indices[i][1] for i in range(self.num_tomo_stacks)] | 113 self.tomo_data_paths = [tomo_data_paths_indices[i][1] for i in range(self.num_tomo_stacks)] |
112 self.tomo_data_indices = [msnc.get_trailing_int(tomo_data_paths_indices[i][0]) | 114 self.tomo_data_indices = [get_trailing_int(tomo_data_paths_indices[i][0]) |
113 if msnc.get_trailing_int(tomo_data_paths_indices[i][0]) else None | 115 if get_trailing_int(tomo_data_paths_indices[i][0]) else None |
114 for i in range(self.num_tomo_stacks)] | 116 for i in range(self.num_tomo_stacks)] |
115 tomo_ref_height_indices = sorted({key:value for key,value in self.config.items() | 117 tomo_ref_height_indices = sorted({key:value for key,value in self.config.items() |
116 if 'z_pos' in key}.items()) | 118 if 'z_pos' in key}.items()) |
117 if self.num_tomo_stacks > 1 and len(tomo_ref_height_indices) != self.num_tomo_stacks: | 119 if self.num_tomo_stacks > 1 and len(tomo_ref_height_indices) != self.num_tomo_stacks: |
118 logging.error(f'Incorrect number of tomography reference heights in config file') | 120 logging.error(f'Incorrect number of tomography reference heights in config file') |
123 else: | 125 else: |
124 self.tomo_ref_heights = [0.0]*self.num_tomo_stacks | 126 self.tomo_ref_heights = [0.0]*self.num_tomo_stacks |
125 | 127 |
126 # Check tomo angle (theta) range | 128 # Check tomo angle (theta) range |
127 self.start_theta = self.config.get('start_theta', 0.) | 129 self.start_theta = self.config.get('start_theta', 0.) |
128 if not msnc.is_num(self.start_theta, 0.): | 130 if not is_num(self.start_theta, 0.): |
129 msnc.illegal_value('start_theta', self.start_theta, 'config file') | 131 illegal_value(self.start_theta, 'start_theta', 'config file') |
130 is_valid = False | 132 is_valid = False |
131 logging.debug(f'start_theta = {self.start_theta}') | 133 logging.debug(f'start_theta = {self.start_theta}') |
132 self.end_theta = self.config.get('end_theta', 180.) | 134 self.end_theta = self.config.get('end_theta', 180.) |
133 if not msnc.is_num(self.end_theta, self.start_theta): | 135 if not is_num(self.end_theta, self.start_theta): |
134 msnc.illegal_value('end_theta', self.end_theta, 'config file') | 136 illegal_value(self.end_theta, 'end_theta', 'config file') |
135 is_valid = False | 137 is_valid = False |
136 logging.debug(f'end_theta = {self.end_theta}') | 138 logging.debug(f'end_theta = {self.end_theta}') |
137 self.num_thetas = self.config.get('num_thetas') | 139 self.num_thetas = self.config.get('num_thetas') |
138 if not (self.num_thetas is None or msnc.is_int(self.num_thetas, 1)): | 140 if not (self.num_thetas is None or is_int(self.num_thetas, 1)): |
139 msnc.illegal_value('num_thetas', self.num_thetas, 'config file') | 141 illegal_value(self.num_thetas, 'num_thetas', 'config file') |
140 self.num_thetas = None | 142 self.num_thetas = None |
141 is_valid = False | 143 is_valid = False |
142 logging.debug(f'num_thetas = {self.num_thetas}') | 144 logging.debug(f'num_thetas = {self.num_thetas}') |
143 | 145 |
144 return is_valid | 146 return is_valid |
163 self.tbf_data_path = self.config['bright_field'].get('data_path') | 165 self.tbf_data_path = self.config['bright_field'].get('data_path') |
164 | 166 |
165 # Check number of tomography image stacks | 167 # Check number of tomography image stacks |
166 stack_info = self.config['stack_info'] | 168 stack_info = self.config['stack_info'] |
167 self.num_tomo_stacks = stack_info.get('num', 1) | 169 self.num_tomo_stacks = stack_info.get('num', 1) |
168 if not msnc.is_int(self.num_tomo_stacks, 1): | 170 if not is_int(self.num_tomo_stacks, 1): |
169 self.num_tomo_stacks = None | 171 self.num_tomo_stacks = None |
170 msnc.illegal_value('stack_info:num', self.num_tomo_stacks, 'config file') | 172 illegal_value(self.num_tomo_stacks, 'num_tomo_stacks', 'config file') |
171 return False | 173 return False |
172 logging.info(f'num_tomo_stacks = {self.num_tomo_stacks}') | 174 logging.info(f'num_tomo_stacks = {self.num_tomo_stacks}') |
173 | 175 |
174 # Find tomography images file/folders and stack parameters | 176 # Find tomography images file/folders and stack parameters |
175 stacks = stack_info.get('stacks') | 177 stacks = stack_info.get('stacks') |
176 if stacks is None or len(stacks) is not self.num_tomo_stacks: | 178 if stacks is None or len(stacks) is not self.num_tomo_stacks: |
177 msnc.illegal_value('stack_info:stacks', stacks, 'config file') | 179 illegal_value(stacks, 'stacks', 'config file') |
178 return False | 180 return False |
179 self.tomo_data_paths = [] | 181 self.tomo_data_paths = [] |
180 self.tomo_data_indices = [] | 182 self.tomo_data_indices = [] |
181 self.tomo_ref_heights = [] | 183 self.tomo_ref_heights = [] |
182 for stack in stacks: | 184 for stack in stacks: |
190 self.start_theta = 0. | 192 self.start_theta = 0. |
191 self.end_theta = 180. | 193 self.end_theta = 180. |
192 self.num_thetas = None | 194 self.num_thetas = None |
193 else: | 195 else: |
194 self.start_theta = theta_range.get('start', 0.) | 196 self.start_theta = theta_range.get('start', 0.) |
195 if not msnc.is_num(self.start_theta, 0.): | 197 if not is_num(self.start_theta, 0.): |
196 msnc.illegal_value('theta_range:start', self.start_theta, 'config file') | 198 illegal_value(self.start_theta, 'theta_range:start', 'config file') |
197 is_valid = False | 199 is_valid = False |
198 logging.debug(f'start_theta = {self.start_theta}') | 200 logging.debug(f'start_theta = {self.start_theta}') |
199 self.end_theta = theta_range.get('end', 180.) | 201 self.end_theta = theta_range.get('end', 180.) |
200 if not msnc.is_num(self.end_theta, self.start_theta): | 202 if not is_num(self.end_theta, self.start_theta): |
201 msnc.illegal_value('theta_range:end', self.end_theta, 'config file') | 203 illegal_value(self.end_theta, 'theta_range:end', 'config file') |
202 is_valid = False | 204 is_valid = False |
203 logging.debug(f'end_theta = {self.end_theta}') | 205 logging.debug(f'end_theta = {self.end_theta}') |
204 self.num_thetas = theta_range.get('num') | 206 self.num_thetas = theta_range.get('num') |
205 if self.num_thetas and not msnc.is_int(self.num_thetas, 1): | 207 if self.num_thetas and not is_int(self.num_thetas, 1): |
206 msnc.illegal_value('theta_range:num', self.num_thetas, 'config file') | 208 illegal_value(self.num_thetas, 'theta_range:num', 'config file') |
207 self.num_thetas = None | 209 self.num_thetas = None |
208 is_valid = False | 210 is_valid = False |
209 logging.debug(f'num_thetas = {self.num_thetas}') | 211 logging.debug(f'num_thetas = {self.num_thetas}') |
210 | 212 |
211 return is_valid | 213 return is_valid |
216 is_valid = True | 218 is_valid = True |
217 | 219 |
218 # Check work_folder (shared by both file formats) | 220 # Check work_folder (shared by both file formats) |
219 work_folder = os.path.abspath(self.config.get('work_folder', '')) | 221 work_folder = os.path.abspath(self.config.get('work_folder', '')) |
220 if not os.path.isdir(work_folder): | 222 if not os.path.isdir(work_folder): |
221 msnc.illegal_value('work_folder', work_folder, 'config file') | 223 illegal_value(work_folder, 'work_folder', 'config file') |
222 is_valid = False | 224 is_valid = False |
223 logging.info(f'work_folder: {work_folder}') | 225 logging.info(f'work_folder: {work_folder}') |
224 | 226 |
225 # Check data filetype (shared by both file formats) | 227 # Check data filetype (shared by both file formats) |
226 self.data_filetype = self.config.get('data_filetype', 'tif') | 228 self.data_filetype = self.config.get('data_filetype', 'tif') |
227 if not isinstance(self.data_filetype, str) or (self.data_filetype != 'tif' and | 229 if not isinstance(self.data_filetype, str) or (self.data_filetype != 'tif' and |
228 self.data_filetype != 'h5'): | 230 self.data_filetype != 'h5'): |
229 msnc.illegal_value('data_filetype', self.data_filetype, 'config file') | 231 illegal_value(self.data_filetype, 'data_filetype', 'config file') |
230 | 232 |
231 if self.suffix == '.yml' or self.suffix == '.yaml': | 233 if self.suffix == '.yml' or self.suffix == '.yaml': |
232 is_valid = self._validate_yaml() | 234 is_valid = self._validate_yaml() |
233 elif self.suffix == '.txt': | 235 elif self.suffix == '.txt': |
234 is_valid = self._validate_txt() | 236 is_valid = self._validate_txt() |
242 if isinstance(self.tdf_data_path, str): | 244 if isinstance(self.tdf_data_path, str): |
243 if not os.path.isabs(self.tdf_data_path): | 245 if not os.path.isabs(self.tdf_data_path): |
244 self.tdf_data_path = os.path.abspath( | 246 self.tdf_data_path = os.path.abspath( |
245 f'{work_folder}/{self.tdf_data_path}') | 247 f'{work_folder}/{self.tdf_data_path}') |
246 else: | 248 else: |
247 msnc.illegal_value('tdf_data_path', tdf_data_fil, 'config file') | 249 illegal_value(tdf_data_fil, 'tdf_data_path', 'config file') |
248 is_valid = False | 250 is_valid = False |
249 else: | 251 else: |
250 if isinstance(self.tdf_data_path, int): | 252 if isinstance(self.tdf_data_path, int): |
251 self.tdf_data_path = os.path.abspath( | 253 self.tdf_data_path = os.path.abspath( |
252 f'{work_folder}/{self.tdf_data_path}/nf') | 254 f'{work_folder}/{self.tdf_data_path}/nf') |
253 elif isinstance(self.tdf_data_path, str): | 255 elif isinstance(self.tdf_data_path, str): |
254 if not os.path.isabs(self.tdf_data_path): | 256 if not os.path.isabs(self.tdf_data_path): |
255 self.tdf_data_path = os.path.abspath( | 257 self.tdf_data_path = os.path.abspath( |
256 f'{work_folder}/{self.tdf_data_path}') | 258 f'{work_folder}/{self.tdf_data_path}') |
257 else: | 259 else: |
258 msnc.illegal_value('tdf_data_path', self.tdf_data_path, 'config file') | 260 illegal_value(self.tdf_data_path, 'tdf_data_path', 'config file') |
259 is_valid = False | 261 is_valid = False |
260 logging.info(f'dark field images path = {self.tdf_data_path}') | 262 logging.info(f'dark field images path = {self.tdf_data_path}') |
261 | 263 |
262 # Find tomography bright field images file/folder | 264 # Find tomography bright field images file/folder |
263 if self.tbf_data_path: | 265 if self.tbf_data_path: |
265 if isinstance(self.tbf_data_path, str): | 267 if isinstance(self.tbf_data_path, str): |
266 if not os.path.isabs(self.tbf_data_path): | 268 if not os.path.isabs(self.tbf_data_path): |
267 self.tbf_data_path = os.path.abspath( | 269 self.tbf_data_path = os.path.abspath( |
268 f'{work_folder}/{self.tbf_data_path}') | 270 f'{work_folder}/{self.tbf_data_path}') |
269 else: | 271 else: |
270 msnc.illegal_value('tbf_data_path', tbf_data_fil, 'config file') | 272 illegal_value(tbf_data_fil, 'tbf_data_path', 'config file') |
271 is_valid = False | 273 is_valid = False |
272 else: | 274 else: |
273 if isinstance(self.tbf_data_path, int): | 275 if isinstance(self.tbf_data_path, int): |
274 self.tbf_data_path = os.path.abspath( | 276 self.tbf_data_path = os.path.abspath( |
275 f'{work_folder}/{self.tbf_data_path}/nf') | 277 f'{work_folder}/{self.tbf_data_path}/nf') |
276 elif isinstance(self.tbf_data_path, str): | 278 elif isinstance(self.tbf_data_path, str): |
277 if not os.path.isabs(self.tbf_data_path): | 279 if not os.path.isabs(self.tbf_data_path): |
278 self.tbf_data_path = os.path.abspath( | 280 self.tbf_data_path = os.path.abspath( |
279 f'{work_folder}/{self.tbf_data_path}') | 281 f'{work_folder}/{self.tbf_data_path}') |
280 else: | 282 else: |
281 msnc.illegal_value('tbf_data_path', self.tbf_data_path, 'config file') | 283 illegal_value(self.tbf_data_path, 'tbf_data_path', 'config file') |
282 is_valid = False | 284 is_valid = False |
283 logging.info(f'bright field images path = {self.tbf_data_path}') | 285 logging.info(f'bright field images path = {self.tbf_data_path}') |
284 | 286 |
285 # Find tomography images file/folders and stack parameters | 287 # Find tomography images file/folders and stack parameters |
286 tomo_data_paths = [] | 288 tomo_data_paths = [] |
291 if self.data_filetype == 'h5': | 293 if self.data_filetype == 'h5': |
292 if isinstance(data_path, str): | 294 if isinstance(data_path, str): |
293 if not os.path.isabs(data_path): | 295 if not os.path.isabs(data_path): |
294 data_path = os.path.abspath(f'{work_folder}/{data_path}') | 296 data_path = os.path.abspath(f'{work_folder}/{data_path}') |
295 else: | 297 else: |
296 msnc.illegal_value(f'stack_info:stacks:data_path', data_path, 'config file') | 298 illegal_value(data_path, 'stack_info:stacks:data_path', 'config file') |
297 is_valid = False | 299 is_valid = False |
298 data_path = None | 300 data_path = None |
299 else: | 301 else: |
300 if isinstance(data_path, int): | 302 if isinstance(data_path, int): |
301 data_path = os.path.abspath(f'{work_folder}/{data_path}/nf') | 303 data_path = os.path.abspath(f'{work_folder}/{data_path}/nf') |
302 elif isinstance(data_path, str): | 304 elif isinstance(data_path, str): |
303 if not os.path.isabs(data_path): | 305 if not os.path.isabs(data_path): |
304 data_path = os.path.abspath(f'{work_folder}/{data_path}') | 306 data_path = os.path.abspath(f'{work_folder}/{data_path}') |
305 else: | 307 else: |
306 msnc.illegal_value(f'stack_info:stacks:data_path', data_path, 'config file') | 308 illegal_value(data_path, 'stack_info:stacks:data_path', 'config file') |
307 is_valid = False | 309 is_valid = False |
308 data_path = None | 310 data_path = None |
309 tomo_data_paths.append(data_path) | 311 tomo_data_paths.append(data_path) |
310 if index is None: | 312 if index is None: |
311 if self.num_tomo_stacks > 1: | 313 if self.num_tomo_stacks > 1: |
313 is_valid = False | 315 is_valid = False |
314 index = None | 316 index = None |
315 else: | 317 else: |
316 index = 1 | 318 index = 1 |
317 elif not isinstance(index, int): | 319 elif not isinstance(index, int): |
318 msnc.illegal_value(f'stack_info:stacks:index', index, 'config file') | 320 illegal_value(index, 'stack_info:stacks:index', 'config file') |
319 is_valid = False | 321 is_valid = False |
320 index = None | 322 index = None |
321 tomo_data_indices.append(index) | 323 tomo_data_indices.append(index) |
322 if ref_height is None: | 324 if ref_height is None: |
323 if self.num_tomo_stacks > 1: | 325 if self.num_tomo_stacks > 1: |
324 logging.error('Missing stack_info:stacks:ref_height in config file') | 326 logging.error('Missing stack_info:stacks:ref_height in config file') |
325 is_valid = False | 327 is_valid = False |
326 ref_height = None | 328 ref_height = None |
327 else: | 329 else: |
328 ref_height = 0. | 330 ref_height = 0. |
329 elif not msnc.is_num(ref_height): | 331 elif not is_num(ref_height): |
330 msnc.illegal_value(f'stack_info:stacks:ref_height', ref_height, 'config file') | 332 illegal_value(ref_height, 'stack_info:stacks:ref_height', 'config file') |
331 is_valid = False | 333 is_valid = False |
332 ref_height = None | 334 ref_height = None |
333 # Set reference heights relative to first stack | 335 # Set reference heights relative to first stack |
334 if (len(tomo_ref_heights) and msnc.is_num(ref_height) and | 336 if (len(tomo_ref_heights) and is_num(ref_height) and |
335 msnc.is_num(tomo_ref_heights[0])): | 337 is_num(tomo_ref_heights[0])): |
336 ref_height = (round(ref_height-tomo_ref_heights[0], 3)) | 338 ref_height = (round(ref_height-tomo_ref_heights[0], 3)) |
337 tomo_ref_heights.append(ref_height) | 339 tomo_ref_heights.append(ref_height) |
338 tomo_ref_heights[0] = 0.0 | 340 tomo_ref_heights[0] = 0.0 |
339 logging.info('tomography data paths:') | 341 logging.info('tomography data paths:') |
340 for i in range(self.num_tomo_stacks): | 342 for i in range(self.num_tomo_stacks): |
434 if path and not os.path.isdir(path): | 436 if path and not os.path.isdir(path): |
435 raise OSError(f'Invalid log_stream path') | 437 raise OSError(f'Invalid log_stream path') |
436 else: | 438 else: |
437 raise OSError(f'Invalid config_out input {config_out} {type(config_out)}') | 439 raise OSError(f'Invalid config_out input {config_out} {type(config_out)}') |
438 if not os.path.isdir(output_folder): | 440 if not os.path.isdir(output_folder): |
439 raise OSError(f'Invalid output_folder input {output_folder} {type(output_folder)}') | 441 os.mkdir(os.path.abspath(output_folder)) |
440 if isinstance(log_stream, str): | 442 if isinstance(log_stream, str): |
441 path = os.path.split(log_stream)[0] | 443 path = os.path.split(log_stream)[0] |
442 if path and not os.path.isdir(path): | 444 if path and not os.path.isdir(path): |
443 raise OSError(f'Invalid log_stream path') | 445 raise OSError(f'Invalid log_stream path') |
444 if not os.path.isabs(path): | 446 if not os.path.isabs(path): |
513 | 515 |
514 # Check config file parameters | 516 # Check config file parameters |
515 self.is_valid = self.cf.validate() | 517 self.is_valid = self.cf.validate() |
516 | 518 |
517 # Load detector info file | 519 # Load detector info file |
518 df = msnc.Detector(self.cf.config['detector']['id']) | 520 df = TomoDetectorConfig(self.cf.config['detector']['id']) |
519 | 521 |
520 # Check detector info file parameters | 522 # Check detector info file parameters |
521 if df.validate(): | 523 if df.valid: |
522 pixel_size = df.getPixelSize() | 524 pixel_size = df.pixel_size |
523 num_rows, num_columns = df.getDimensions() | 525 num_rows, num_columns = df.dimensions |
524 if not pixel_size or not num_rows or not num_columns: | 526 if not pixel_size or not num_rows or not num_columns: |
525 self.is_valid = False | 527 self.is_valid = False |
526 else: | 528 else: |
527 pixel_size = None | 529 pixel_size = None |
528 num_rows = None | 530 num_rows = None |
575 available_stacks = [False]*stack_info['num'] | 577 available_stacks = [False]*stack_info['num'] |
576 | 578 |
577 # Check number of tomography angles/thetas | 579 # Check number of tomography angles/thetas |
578 num_thetas = self.config['theta_range'].get('num') | 580 num_thetas = self.config['theta_range'].get('num') |
579 if num_thetas is None: | 581 if num_thetas is None: |
580 num_thetas = pyip.inputInt('\nEnter the number of thetas (>0): ', greaterThan=0) | 582 num_thetas = input_int('\nEnter the number of thetas', 1) |
581 elif not msnc.is_int(num_thetas, 0): | 583 elif not is_int(num_thetas, 0): |
582 msnc.illegal_value('num_thetas', num_thetas, 'config file') | 584 illegal_value(num_thetas, 'num_thetas', 'config file') |
583 self.is_valid = False | 585 self.is_valid = False |
584 return | 586 return |
585 self.config['theta_range']['num'] = num_thetas | 587 self.config['theta_range']['num'] = num_thetas |
586 logging.debug(f'num_thetas = {self.config["theta_range"]["num"]}') | 588 logging.debug(f'num_thetas = {self.config["theta_range"]["num"]}') |
587 | 589 |
589 dark_field = self.config['dark_field'] | 591 dark_field = self.config['dark_field'] |
590 img_start = dark_field.get('img_start', -1) | 592 img_start = dark_field.get('img_start', -1) |
591 img_offset = dark_field.get('img_offset', -1) | 593 img_offset = dark_field.get('img_offset', -1) |
592 num_imgs = dark_field.get('num', 0) | 594 num_imgs = dark_field.get('num', 0) |
593 if not self.test_mode: | 595 if not self.test_mode: |
594 img_start, img_offset, num_imgs = msnc.selectImageRange(img_start, img_offset, | 596 img_start, img_offset, num_imgs = selectImageRange(img_start, img_offset, |
595 num_imgs, 'dark field') | 597 num_imgs, 'dark field') |
596 if img_start < 0 or num_imgs < 1: | 598 if img_start < 0 or num_imgs < 1: |
597 logging.error('Unable to find suitable dark field images') | 599 logging.error('Unable to find suitable dark field images') |
598 if dark_field['data_path']: | 600 if dark_field['data_path']: |
599 self.is_valid = False | 601 self.is_valid = False |
608 bright_field = self.config['bright_field'] | 610 bright_field = self.config['bright_field'] |
609 img_start = bright_field.get('img_start', -1) | 611 img_start = bright_field.get('img_start', -1) |
610 img_offset = bright_field.get('img_offset', -1) | 612 img_offset = bright_field.get('img_offset', -1) |
611 num_imgs = bright_field.get('num', 0) | 613 num_imgs = bright_field.get('num', 0) |
612 if not self.test_mode: | 614 if not self.test_mode: |
613 img_start, img_offset, num_imgs = msnc.selectImageRange(img_start, img_offset, | 615 img_start, img_offset, num_imgs = selectImageRange(img_start, img_offset, |
614 num_imgs, 'bright field') | 616 num_imgs, 'bright field') |
615 if img_start < 0 or num_imgs < 1: | 617 if img_start < 0 or num_imgs < 1: |
616 logging.error('Unable to find suitable bright field images') | 618 logging.error('Unable to find suitable bright field images') |
617 self.is_valid = False | 619 self.is_valid = False |
618 bright_field['img_start'] = img_start | 620 bright_field['img_start'] = img_start |
630 index = stack['index'] | 632 index = stack['index'] |
631 img_start = stack.get('img_start', -1) | 633 img_start = stack.get('img_start', -1) |
632 img_offset = stack.get('img_offset', -1) | 634 img_offset = stack.get('img_offset', -1) |
633 num_imgs = stack.get('num', 0) | 635 num_imgs = stack.get('num', 0) |
634 if not self.test_mode: | 636 if not self.test_mode: |
635 img_start, img_offset, num_imgs = msnc.selectImageRange(img_start, img_offset, | 637 img_start, img_offset, num_imgs = selectImageRange(img_start, img_offset, |
636 num_imgs, f'tomography stack {index}', num_thetas) | 638 num_imgs, f'tomography stack {index}', num_thetas) |
637 if img_start < 0 or num_imgs != num_thetas: | 639 if img_start < 0 or num_imgs != num_thetas: |
638 logging.error('Unable to find suitable tomography images') | 640 logging.error('Unable to find suitable tomography images') |
639 self.is_valid = False | 641 self.is_valid = False |
640 stack['img_start'] = img_start | 642 stack['img_start'] = img_start |
654 """Generate dark field. | 656 """Generate dark field. |
655 """ | 657 """ |
656 # Load the dark field images | 658 # Load the dark field images |
657 logging.debug('Loading dark field...') | 659 logging.debug('Loading dark field...') |
658 dark_field = self.config['dark_field'] | 660 dark_field = self.config['dark_field'] |
659 tdf_stack = msnc.loadImageStack(tdf_files, self.config['data_filetype'], | 661 tdf_stack = loadImageStack(tdf_files, self.config['data_filetype'], |
660 dark_field['img_offset'], dark_field['num']) | 662 dark_field['img_offset'], dark_field['num']) |
661 | 663 |
662 # Take median | 664 # Take median |
663 self.tdf = np.median(tdf_stack, axis=0) | 665 self.tdf = np.median(tdf_stack, axis=0) |
664 del tdf_stack | 666 del tdf_stack |
669 tdf_mean = np.nanmean(self.tdf) | 671 tdf_mean = np.nanmean(self.tdf) |
670 logging.debug(f'tdf_cutoff = {tdf_cutoff}') | 672 logging.debug(f'tdf_cutoff = {tdf_cutoff}') |
671 logging.debug(f'tdf_mean = {tdf_mean}') | 673 logging.debug(f'tdf_mean = {tdf_mean}') |
672 np.nan_to_num(self.tdf, copy=False, nan=tdf_mean, posinf=tdf_mean, neginf=0.) | 674 np.nan_to_num(self.tdf, copy=False, nan=tdf_mean, posinf=tdf_mean, neginf=0.) |
673 if self.galaxy_flag: | 675 if self.galaxy_flag: |
674 msnc.quickImshow(self.tdf, title='dark field', path='setup_pngs', | 676 quickImshow(self.tdf, title='dark field', path='setup_pngs', |
675 save_fig=True, save_only=True) | 677 save_fig=True, save_only=True) |
676 elif not self.test_mode: | 678 elif not self.test_mode: |
677 msnc.quickImshow(self.tdf, title='dark field', path=self.output_folder, | 679 quickImshow(self.tdf, title='dark field', path=self.output_folder, |
678 save_fig=self.save_plots, save_only=self.save_plots_only) | 680 save_fig=self.save_plots, save_only=self.save_plots_only) |
679 | 681 |
680 def _genBright(self, tbf_files): | 682 def _genBright(self, tbf_files): |
681 """Generate bright field. | 683 """Generate bright field. |
682 """ | 684 """ |
683 # Load the bright field images | 685 # Load the bright field images |
684 logging.debug('Loading bright field...') | 686 logging.debug('Loading bright field...') |
685 bright_field = self.config['bright_field'] | 687 bright_field = self.config['bright_field'] |
686 tbf_stack = msnc.loadImageStack(tbf_files, self.config['data_filetype'], | 688 tbf_stack = loadImageStack(tbf_files, self.config['data_filetype'], |
687 bright_field['img_offset'], bright_field['num']) | 689 bright_field['img_offset'], bright_field['num']) |
688 | 690 |
689 # Take median | 691 # Take median |
690 """Median or mean: It may be best to try the median because of some image | 692 """Median or mean: It may be best to try the median because of some image |
691 artifacts that arise due to crinkles in the upstream kapton tape windows | 693 artifacts that arise due to crinkles in the upstream kapton tape windows |
703 if self.tdf.size: | 705 if self.tdf.size: |
704 self.tbf -= self.tdf | 706 self.tbf -= self.tdf |
705 else: | 707 else: |
706 logging.warning('Dark field unavailable') | 708 logging.warning('Dark field unavailable') |
707 if self.galaxy_flag: | 709 if self.galaxy_flag: |
708 msnc.quickImshow(self.tbf, title='bright field', path='setup_pngs', | 710 quickImshow(self.tbf, title='bright field', path='setup_pngs', |
709 save_fig=True, save_only=True) | 711 save_fig=True, save_only=True) |
710 elif not self.test_mode: | 712 elif not self.test_mode: |
711 msnc.quickImshow(self.tbf, title='bright field', path=self.output_folder, | 713 quickImshow(self.tbf, title='bright field', path=self.output_folder, |
712 save_fig=self.save_plots, save_only=self.save_plots_only) | 714 save_fig=self.save_plots, save_only=self.save_plots_only) |
713 | 715 |
714 def _setDetectorBounds(self, tomo_stack_files): | 716 def _setDetectorBounds(self, tomo_stack_files): |
715 """Set vertical detector bounds for image stack. | 717 """Set vertical detector bounds for image stack. |
716 """ | 718 """ |
719 img_x_bounds = [None, None] | 721 img_x_bounds = [None, None] |
720 else: | 722 else: |
721 img_x_bounds = preprocess.get('img_x_bounds', [0, self.tbf.shape[0]]) | 723 img_x_bounds = preprocess.get('img_x_bounds', [0, self.tbf.shape[0]]) |
722 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: | 724 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: |
723 if img_x_bounds[0] < 0: | 725 if img_x_bounds[0] < 0: |
724 msnc.illegal_value('img_x_bounds[0]', img_x_bounds[0], 'config file') | 726 illegal_value(img_x_bounds[0], 'preprocess:img_x_bounds[0]', 'config file') |
725 img_x_bounds[0] = 0 | 727 img_x_bounds[0] = 0 |
726 if not msnc.is_index_range(img_x_bounds, 0, self.tbf.shape[0]): | 728 if not is_index_range(img_x_bounds, 0, self.tbf.shape[0]): |
727 msnc.illegal_value('img_x_bounds[1]', img_x_bounds[1], 'config file') | 729 illegal_value(img_x_bounds[1], 'preprocess:img_x_bounds[1]', 'config file') |
728 img_x_bounds[1] = self.tbf.shape[0] | 730 img_x_bounds[1] = self.tbf.shape[0] |
729 if self.test_mode: | 731 if self.test_mode: |
730 # Update config and save to file | 732 # Update config and save to file |
731 if preprocess is None: | 733 if preprocess is None: |
732 self.cf.config['preprocess'] = {'img_x_bounds' : [0, self.tbf.shape[0]]} | 734 self.cf.config['preprocess'] = {'img_x_bounds' : [0, self.tbf.shape[0]]} |
761 x_sum_min = x_sum.min() | 763 x_sum_min = x_sum.min() |
762 x_sum_max = x_sum.max() | 764 x_sum_max = x_sum.max() |
763 x_low = 0 | 765 x_low = 0 |
764 x_upp = x_sum.size | 766 x_upp = x_sum.size |
765 if num_x_min is not None: | 767 if num_x_min is not None: |
766 fit = msnc.fitStep(y=x_sum, model='rectangle', form='atan') | 768 fit = Fit.fit_data(np.array(range(len(x_sum))), x_sum, models='rectangle', |
767 x_low = fit.get('center1', None) | 769 form='atan', guess=True) |
768 x_upp = fit.get('center2', None) | 770 parameters = fit.best_values |
769 sig_low = fit.get('sigma1', None) | 771 x_low = parameters.get('center1', None) |
770 sig_upp = fit.get('sigma2', None) | 772 x_upp = parameters.get('center2', None) |
773 sig_low = parameters.get('sigma1', None) | |
774 sig_upp = parameters.get('sigma2', None) | |
771 if (x_low is not None and x_upp is not None and sig_low is not None and | 775 if (x_low is not None and x_upp is not None and sig_low is not None and |
772 sig_upp is not None and 0 <= x_low < x_upp <= x_sum.size and | 776 sig_upp is not None and 0 <= x_low < x_upp <= x_sum.size and |
773 (sig_low+sig_upp)/(x_upp-x_low) < 0.1): | 777 (sig_low+sig_upp)/(x_upp-x_low) < 0.1): |
774 if num_tomo_stacks == 1 or num_x_min is None: | 778 if num_tomo_stacks == 1 or num_x_min is None: |
775 x_low = int(x_low-(x_upp-x_low)/10) | 779 x_low = int(x_low-(x_upp-x_low)/10) |
782 if x_upp > x_sum.size: | 786 if x_upp > x_sum.size: |
783 x_upp = x_sum.size | 787 x_upp = x_sum.size |
784 else: | 788 else: |
785 x_low = 0 | 789 x_low = 0 |
786 x_upp = x_sum.size | 790 x_upp = x_sum.size |
787 msnc.quickPlot((range(x_sum.size), x_sum), | 791 quickPlot((range(x_sum.size), x_sum), |
788 ([x_low, x_low], [x_sum_min, x_sum_max], 'r-'), | 792 ([x_low, x_low], [x_sum_min, x_sum_max], 'r-'), |
789 ([x_upp-1, x_upp-1], [x_sum_min, x_sum_max], 'r-'), | 793 ([x_upp-1, x_upp-1], [x_sum_min, x_sum_max], 'r-'), |
790 title=f'sum bright field over theta/y (row bounds: [{x_low}, {x_upp}])', | 794 title=f'sum bright field over theta/y (row bounds: [{x_low}, {x_upp}])', |
791 path='setup_pngs', name='detectorbounds.png', save_fig=True, save_only=True, | 795 path='setup_pngs', name='detectorbounds.png', save_fig=True, save_only=True, |
792 show_grid=True) | 796 show_grid=True) |
793 for i,stack in enumerate(stacks): | 797 for i,stack in enumerate(stacks): |
794 tomo_stack = msnc.loadImageStack(tomo_stack_files[i], self.config['data_filetype'], | 798 tomo_stack = loadImageStack(tomo_stack_files[i], self.config['data_filetype'], |
795 stack['img_offset'], 1) | 799 stack['img_offset'], 1) |
796 tomo_stack = tomo_stack[0,:,:] | 800 tomo_stack = tomo_stack[0,:,:] |
797 if num_x_min is not None: | 801 if num_x_min is not None: |
798 tomo_stack_max = tomo_stack.max() | 802 tomo_stack_max = tomo_stack.max() |
799 tomo_stack[x_low,:] = tomo_stack_max | 803 tomo_stack[x_low,:] = tomo_stack_max |
800 tomo_stack[x_upp-1,:] = tomo_stack_max | 804 tomo_stack[x_upp-1,:] = tomo_stack_max |
801 title = f'tomography image at theta={self.config["theta_range"]["start"]}' | 805 title = f'tomography image at theta={self.config["theta_range"]["start"]}' |
802 msnc.quickImshow(tomo_stack, title=title, path='setup_pngs', | 806 quickImshow(tomo_stack, title=title, path='setup_pngs', |
803 name=f'tomo_{stack["index"]}.png', save_fig=True, save_only=True, | 807 name=f'tomo_{stack["index"]}.png', save_fig=True, save_only=True, |
804 show_grid=True) | 808 show_grid=True) |
805 del tomo_stack | 809 del tomo_stack |
806 | 810 |
807 # Update config and save to file | 811 # Update config and save to file |
815 del x_sum | 819 del x_sum |
816 return | 820 return |
817 | 821 |
818 # For one tomography stack only: load the first image | 822 # For one tomography stack only: load the first image |
819 title = None | 823 title = None |
820 msnc.quickImshow(self.tbf, title='bright field') | 824 quickImshow(self.tbf, title='bright field') |
821 if num_tomo_stacks == 1: | 825 if num_tomo_stacks == 1: |
822 tomo_stack = msnc.loadImageStack(tomo_stack_files[0], self.config['data_filetype'], | 826 tomo_stack = loadImageStack(tomo_stack_files[0], self.config['data_filetype'], |
823 stacks[0]['img_offset'], 1) | 827 stacks[0]['img_offset'], 1) |
824 title = f'tomography image at theta={self.config["theta_range"]["start"]}' | 828 title = f'tomography image at theta={self.config["theta_range"]["start"]}' |
825 msnc.quickImshow(tomo_stack[0,:,:], title=title) | 829 quickImshow(tomo_stack[0,:,:], title=title) |
826 tomo_or_bright = pyip.inputNum('\nSelect image bounds from bright field (0) or '+ | 830 tomo_or_bright = input_menu(['bright field', 'first tomography image'], |
827 'from first tomography image (1): ', min=0, max=1) | 831 header='\nSelect image bounds from') |
828 else: | 832 else: |
829 print('\nSelect image bounds from bright field') | 833 print('\nSelect image bounds from bright field') |
830 tomo_or_bright = 0 | 834 tomo_or_bright = 0 |
831 if tomo_or_bright: | 835 if tomo_or_bright: |
832 x_sum = np.sum(tomo_stack[0,:,:], 1) | 836 x_sum = np.sum(tomo_stack[0,:,:], 1) |
833 use_bounds = 'no' | 837 use_bounds = False |
834 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: | 838 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: |
835 tmp = np.copy(tomo_stack[0,:,:]) | 839 tmp = np.copy(tomo_stack[0,:,:]) |
836 tmp_max = tmp.max() | 840 tmp_max = tmp.max() |
837 tmp[img_x_bounds[0],:] = tmp_max | 841 tmp[img_x_bounds[0],:] = tmp_max |
838 tmp[img_x_bounds[1]-1,:] = tmp_max | 842 tmp[img_x_bounds[1]-1,:] = tmp_max |
839 title = f'tomography image at theta={self.config["theta_range"]["start"]}' | 843 title = f'tomography image at theta={self.config["theta_range"]["start"]}' |
840 msnc.quickImshow(tmp, title=title) | 844 quickImshow(tmp, title=title) |
841 del tmp | 845 del tmp |
842 x_sum_min = x_sum.min() | 846 x_sum_min = x_sum.min() |
843 x_sum_max = x_sum.max() | 847 x_sum_max = x_sum.max() |
844 msnc.quickPlot((range(x_sum.size), x_sum), | 848 quickPlot((range(x_sum.size), x_sum), |
845 ([img_x_bounds[0], img_x_bounds[0]], [x_sum_min, x_sum_max], 'r-'), | 849 ([img_x_bounds[0], img_x_bounds[0]], [x_sum_min, x_sum_max], 'r-'), |
846 ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum_min, x_sum_max], 'r-'), | 850 ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum_min, x_sum_max], 'r-'), |
847 title='sum over theta and y') | 851 title='sum over theta and y') |
848 print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ | 852 print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ |
849 f'upper bound = {img_x_bounds[1]} (exclusive)]') | 853 f'upper bound = {img_x_bounds[1]} (exclusive)]') |
850 use_bounds = pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) | 854 use_bounds = input_yesno('Accept these bounds (y/n)?', 'y') |
851 if use_bounds == 'no': | 855 if not use_bounds: |
852 img_x_bounds = msnc.selectImageBounds(tomo_stack[0,:,:], 0, | 856 img_x_bounds = list(selectImageBounds(tomo_stack[0,:,:], 0, |
853 img_x_bounds[0], img_x_bounds[1], num_x_min, | 857 img_x_bounds[0], img_x_bounds[1], num_x_min, |
854 f'tomography image at theta={self.config["theta_range"]["start"]}') | 858 f'tomography image at theta={self.config["theta_range"]["start"]}')) |
855 if num_x_min is not None and img_x_bounds[1]-img_x_bounds[0]+1 < num_x_min: | 859 if num_x_min is not None and img_x_bounds[1]-img_x_bounds[0]+1 < num_x_min: |
856 logging.warning('Image bounds and pixel size prevent seamless stacking') | 860 logging.warning('Image bounds and pixel size prevent seamless stacking') |
857 title = f'tomography image at theta={self.config["theta_range"]["start"]}' | 861 title = f'tomography image at theta={self.config["theta_range"]["start"]}' |
858 msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, | 862 quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, |
859 save_fig=self.save_plots, save_only=True) | 863 save_fig=self.save_plots, save_only=True) |
860 msnc.quickPlot(range(img_x_bounds[0], img_x_bounds[1]), | 864 quickPlot(range(img_x_bounds[0], img_x_bounds[1]), |
861 x_sum[img_x_bounds[0]:img_x_bounds[1]], | 865 x_sum[img_x_bounds[0]:img_x_bounds[1]], |
862 title='sum over theta and y', path=self.output_folder, | 866 title='sum over theta and y', path=self.output_folder, |
863 save_fig=self.save_plots, save_only=True) | 867 save_fig=self.save_plots, save_only=True) |
864 else: | 868 else: |
865 x_sum = np.sum(self.tbf, 1) | 869 x_sum = np.sum(self.tbf, 1) |
866 x_sum_min = x_sum.min() | 870 x_sum_min = x_sum.min() |
867 x_sum_max = x_sum.max() | 871 x_sum_max = x_sum.max() |
868 use_bounds = 'no' | 872 use_bounds = False |
869 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: | 873 if img_x_bounds[0] is not None and img_x_bounds[1] is not None: |
870 tmp = np.copy(self.tbf) | 874 tmp = np.copy(self.tbf) |
871 tmp_max = tmp.max() | 875 tmp_max = tmp.max() |
872 tmp[img_x_bounds[0],:] = tmp_max | 876 tmp[img_x_bounds[0],:] = tmp_max |
873 tmp[img_x_bounds[1]-1,:] = tmp_max | 877 tmp[img_x_bounds[1]-1,:] = tmp_max |
874 title = 'bright field' | 878 title = 'bright field' |
875 msnc.quickImshow(tmp, title=title) | 879 quickImshow(tmp, title=title) |
876 del tmp | 880 del tmp |
877 msnc.quickPlot((range(x_sum.size), x_sum), | 881 quickPlot((range(x_sum.size), x_sum), |
878 ([img_x_bounds[0], img_x_bounds[0]], [x_sum_min, x_sum_max], 'r-'), | 882 ([img_x_bounds[0], img_x_bounds[0]], [x_sum_min, x_sum_max], 'r-'), |
879 ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum_min, x_sum_max], 'r-'), | 883 ([img_x_bounds[1]-1, img_x_bounds[1]-1], [x_sum_min, x_sum_max], 'r-'), |
880 title='sum over theta and y') | 884 title='sum over theta and y') |
881 print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ | 885 print(f'lower bound = {img_x_bounds[0]} (inclusive)\n'+ |
882 f'upper bound = {img_x_bounds[1]} (exclusive)]') | 886 f'upper bound = {img_x_bounds[1]} (exclusive)]') |
883 use_bounds = pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) | 887 use_bounds = input_yesno('Accept these bounds (y/n)?', 'y') |
884 if use_bounds == 'no': | 888 if not use_bounds: |
885 use_fit = 'no' | 889 use_fit = False |
886 fit = msnc.fitStep(y=x_sum, model='rectangle', form='atan') | 890 fit = Fit.fit_data(np.array(range(len(x_sum))), x_sum, models='rectangle', |
887 x_low = fit.get('center1', None) | 891 form='atan', guess=True) |
888 x_upp = fit.get('center2', None) | 892 parameters = fit.best_values |
889 sig_low = fit.get('sigma1', None) | 893 x_low = parameters.get('center1', None) |
890 sig_upp = fit.get('sigma2', None) | 894 x_upp = parameters.get('center2', None) |
895 sig_low = parameters.get('sigma1', None) | |
896 sig_upp = parameters.get('sigma2', None) | |
891 if (x_low is not None and x_upp is not None and sig_low is not None and | 897 if (x_low is not None and x_upp is not None and sig_low is not None and |
892 sig_upp is not None and 0 <= x_low < x_upp <= x_sum.size and | 898 sig_upp is not None and 0 <= x_low < x_upp <= x_sum.size and |
893 (sig_low+sig_upp)/(x_upp-x_low) < 0.1): | 899 (sig_low+sig_upp)/(x_upp-x_low) < 0.1): |
894 if num_tomo_stacks == 1 or num_x_min is None: | 900 if num_tomo_stacks == 1 or num_x_min is None: |
895 x_low = int(x_low-(x_upp-x_low)/10) | 901 x_low = int(x_low-(x_upp-x_low)/10) |
904 tmp = np.copy(self.tbf) | 910 tmp = np.copy(self.tbf) |
905 tmp_max = tmp.max() | 911 tmp_max = tmp.max() |
906 tmp[x_low,:] = tmp_max | 912 tmp[x_low,:] = tmp_max |
907 tmp[x_upp-1,:] = tmp_max | 913 tmp[x_upp-1,:] = tmp_max |
908 title = 'bright field' | 914 title = 'bright field' |
909 msnc.quickImshow(tmp, title=title) | 915 quickImshow(tmp, title=title) |
910 del tmp | 916 del tmp |
911 msnc.quickPlot((range(x_sum.size), x_sum), | 917 quickPlot((range(x_sum.size), x_sum), |
912 ([x_low, x_low], [x_sum_min, x_sum_max], 'r-'), | 918 ([x_low, x_low], [x_sum_min, x_sum_max], 'r-'), |
913 ([x_upp, x_upp], [x_sum_min, x_sum_max], 'r-'), | 919 ([x_upp, x_upp], [x_sum_min, x_sum_max], 'r-'), |
914 title='sum over theta and y') | 920 title='sum over theta and y') |
915 print(f'lower bound = {x_low} (inclusive)') | 921 print(f'lower bound = {x_low} (inclusive)') |
916 print(f'upper bound = {x_upp} (exclusive)]') | 922 print(f'upper bound = {x_upp} (exclusive)]') |
917 use_fit = pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) | 923 use_fit = input_yesno('Accept these bounds (y/n)?', 'y') |
918 if use_fit == 'no': | 924 if use_fit: |
919 img_x_bounds = msnc.selectArrayBounds(x_sum, img_x_bounds[0], img_x_bounds[1], | 925 img_x_bounds = [x_low, x_upp] |
920 num_x_min, 'sum over theta and y') | |
921 else: | 926 else: |
922 img_x_bounds = [x_low, x_upp] | 927 accept = False |
928 while not accept: | |
929 mask, img_x_bounds = draw_mask_1d(x_sum, title='select x data range', | |
930 legend='sum over theta and y') | |
931 print(f'img_x_bounds = {img_x_bounds}') | |
932 while (len(img_x_bounds) != 1 or (len(x_sum) >= num_x_min and | |
933 img_x_bounds[0][1]-img_x_bounds[0][0]+1 < num_x_min)): | |
934 exit('Should not be here') | |
935 print('Please select exactly one continuous range') | |
936 mask, img_x_bounds = draw_mask_1d(x_sum, title='select x data range', | |
937 legend='sum over theta and y') | |
938 img_x_bounds = list(img_x_bounds[0]) | |
939 quickPlot(x_sum, vlines=img_x_bounds, title='sum over theta and y') | |
940 print(f'img_x_bounds = {img_x_bounds} (lower bound inclusive, upper bound '+ | |
941 'exclusive)') | |
942 accept = input_yesno('Accept these bounds (y/n)?', 'y') | |
943 if not accept: | |
944 img_x_bounds = None | |
923 if num_x_min is not None and img_x_bounds[1]-img_x_bounds[0]+1 < num_x_min: | 945 if num_x_min is not None and img_x_bounds[1]-img_x_bounds[0]+1 < num_x_min: |
924 logging.warning('Image bounds and pixel size prevent seamless stacking') | 946 logging.warning('Image bounds and pixel size prevent seamless stacking') |
925 #msnc.quickPlot(range(img_x_bounds[0], img_x_bounds[1]), | 947 #quickPlot(range(img_x_bounds[0], img_x_bounds[1]), |
926 # x_sum[img_x_bounds[0]:img_x_bounds[1]], | 948 # x_sum[img_x_bounds[0]:img_x_bounds[1]], |
927 # title='sum over theta and y', path=self.output_folder, | 949 # title='sum over theta and y', path=self.output_folder, |
928 # save_fig=self.save_plots, save_only=True) | 950 # save_fig=self.save_plots, save_only=True) |
929 msnc.quickPlot((range(x_sum.size), x_sum), | 951 quickPlot((range(x_sum.size), x_sum), |
930 ([img_x_bounds[0], img_x_bounds[0]], [x_sum_min, x_sum_max], 'r-'), | 952 ([img_x_bounds[0], img_x_bounds[0]], [x_sum_min, x_sum_max], 'r-'), |
931 ([img_x_bounds[1], img_x_bounds[1]], [x_sum_min, x_sum_max], 'r-'), | 953 ([img_x_bounds[1], img_x_bounds[1]], [x_sum_min, x_sum_max], 'r-'), |
932 title='sum over theta and y', path=self.output_folder, | 954 title='sum over theta and y', path=self.output_folder, |
933 save_fig=self.save_plots, save_only=True) | 955 save_fig=self.save_plots, save_only=True) |
934 del x_sum | 956 del x_sum |
935 for i,stack in enumerate(stacks): | 957 for i,stack in enumerate(stacks): |
936 tomo_stack = msnc.loadImageStack(tomo_stack_files[i], self.config['data_filetype'], | 958 tomo_stack = loadImageStack(tomo_stack_files[i], self.config['data_filetype'], |
937 stack['img_offset'], 1) | 959 stack['img_offset'], 1) |
938 tomo_stack = tomo_stack[0,:,:] | 960 tomo_stack = tomo_stack[0,:,:] |
939 if num_x_min is not None: | 961 if num_x_min is not None: |
940 tomo_stack_max = tomo_stack.max() | 962 tomo_stack_max = tomo_stack.max() |
941 tomo_stack[img_x_bounds[0],:] = tomo_stack_max | 963 tomo_stack[img_x_bounds[0],:] = tomo_stack_max |
942 tomo_stack[img_x_bounds[1]-1,:] = tomo_stack_max | 964 tomo_stack[img_x_bounds[1]-1,:] = tomo_stack_max |
943 title = f'tomography image at theta={self.config["theta_range"]["start"]}' | 965 title = f'tomography image at theta={self.config["theta_range"]["start"]}' |
944 if self.galaxy_flag: | 966 if self.galaxy_flag: |
945 msnc.quickImshow(tomo_stack, title=title, path='setup_pngs', | 967 quickImshow(tomo_stack, title=title, path='setup_pngs', |
946 name=f'tomo_{stack["index"]}.png', save_fig=True, save_only=True, | 968 name=f'tomo_{stack["index"]}.png', save_fig=True, save_only=True, |
947 show_grid=True) | 969 show_grid=True) |
948 else: | 970 else: |
949 msnc.quickImshow(tomo_stack, title=title, path=self.output_folder, | 971 quickImshow(tomo_stack, title=title, path=self.output_folder, |
950 name=f'tomo_{stack["index"]}.png', save_fig=self.save_plots, | 972 name=f'tomo_{stack["index"]}.png', save_fig=self.save_plots, |
951 save_only=True, show_grid=True) | 973 save_only=True, show_grid=True) |
952 del tomo_stack | 974 del tomo_stack |
953 logging.debug(f'img_x_bounds: {img_x_bounds}') | 975 logging.debug(f'img_x_bounds: {img_x_bounds}') |
954 | 976 |
955 if self.save_plots_only: | 977 if self.save_plots_only: |
956 msnc.clearFig('bright field') | 978 clearImshow('bright field') |
957 msnc.clearFig('sum over theta and y') | 979 clearPlot('sum over theta and y') |
958 if title: | 980 if title: |
959 msnc.clearFig(title) | 981 clearPlot(title) |
960 | 982 |
961 # Update config and save to file | 983 # Update config and save to file |
962 if preprocess is None: | 984 if preprocess is None: |
963 self.cf.config['preprocess'] = {'img_x_bounds' : img_x_bounds} | 985 self.cf.config['preprocess'] = {'img_x_bounds' : img_x_bounds} |
964 else: | 986 else: |
970 """ | 992 """ |
971 preprocess = self.config.get('preprocess') | 993 preprocess = self.config.get('preprocess') |
972 zoom_perc = 100 | 994 zoom_perc = 100 |
973 if not self.galaxy_flag: | 995 if not self.galaxy_flag: |
974 if preprocess is None or 'zoom_perc' not in preprocess: | 996 if preprocess is None or 'zoom_perc' not in preprocess: |
975 if pyip.inputYesNo( | 997 if input_yesno('\nDo you want to zoom in to reduce memory requirement (y/n)?', 'n'): |
976 '\nDo you want to zoom in to reduce memory requirement (y/[n])? ', | 998 zoom_perc = input_int(' Enter zoom percentage', 1, 100) |
977 blank=True) == 'yes': | |
978 zoom_perc = pyip.inputInt(' Enter zoom percentage [1, 100]: ', | |
979 min=1, max=100) | |
980 else: | 999 else: |
981 zoom_perc = preprocess['zoom_perc'] | 1000 zoom_perc = preprocess['zoom_perc'] |
982 if msnc.is_num(zoom_perc, 1., 100.): | 1001 if is_num(zoom_perc, 1., 100.): |
983 zoom_perc = int(zoom_perc) | 1002 zoom_perc = int(zoom_perc) |
984 else: | 1003 else: |
985 msnc.illegal_value('zoom_perc', zoom_perc, 'config file') | 1004 illegal_value(zoom_perc, 'preprocess:zoom_perc', 'config file') |
986 zoom_perc = 100 | 1005 zoom_perc = 100 |
987 num_theta_skip = 0 | 1006 num_theta_skip = 0 |
988 if not self.galaxy_flag: | 1007 if not self.galaxy_flag: |
989 if preprocess is None or 'num_theta_skip' not in preprocess: | 1008 if preprocess is None or 'num_theta_skip' not in preprocess: |
990 if pyip.inputYesNo( | 1009 if input_yesno('Do you want to skip thetas to reduce memory requirement (y/n)?', |
991 'Do you want to skip thetas to reduce memory requirement (y/[n])? ', | 1010 'n'): |
992 blank=True) == 'yes': | 1011 num_theta_skip = input_int(' Enter the number skip theta interval', 0, |
993 num_theta_skip = pyip.inputInt(' Enter the number skip theta interval'+ | 1012 self.num_thetas-1) |
994 f' [0, {self.num_thetas-1}]: ', min=0, max=self.num_thetas-1) | |
995 else: | 1013 else: |
996 num_theta_skip = preprocess['num_theta_skip'] | 1014 num_theta_skip = preprocess['num_theta_skip'] |
997 if not msnc.is_int(num_theta_skip, 0): | 1015 if not is_int(num_theta_skip, 0): |
998 msnc.illegal_value('num_theta_skip', num_theta_skip, 'config file') | 1016 illegal_value(num_theta_skip, 'preprocess:num_theta_skip', 'config file') |
999 num_theta_skip = 0 | 1017 num_theta_skip = 0 |
1000 logging.debug(f'zoom_perc = {zoom_perc}') | 1018 logging.debug(f'zoom_perc = {zoom_perc}') |
1001 logging.debug(f'num_theta_skip = {num_theta_skip}') | 1019 logging.debug(f'num_theta_skip = {num_theta_skip}') |
1002 | 1020 |
1003 # Update config and save to file | 1021 # Update config and save to file |
1021 title = f'{base_name} fullres' | 1039 title = f'{base_name} fullres' |
1022 else: | 1040 else: |
1023 title = f'{base_name} {zoom_perc}p' | 1041 title = f'{base_name} {zoom_perc}p' |
1024 title += f'_{index}' | 1042 title += f'_{index}' |
1025 tomo_file = re.sub(r"\s+", '_', f'{self.output_folder}/{title}.npy') | 1043 tomo_file = re.sub(r"\s+", '_', f'{self.output_folder}/{title}.npy') |
1026 load_flag = 'no' | 1044 load_flag = False |
1027 available = False | 1045 available = False |
1028 if os.path.isfile(tomo_file): | 1046 if os.path.isfile(tomo_file): |
1029 available = True | 1047 available = True |
1030 if required: | 1048 if required: |
1031 load_flag = 'yes' | 1049 load_flag = True |
1032 else: | 1050 else: |
1033 load_flag = pyip.inputYesNo(f'\nDo you want to load {tomo_file} (y/n)? ') | 1051 load_flag = input_yesno(f'\nDo you want to load {tomo_file} (y/n)?') |
1034 stack = np.array([]) | 1052 stack = np.array([]) |
1035 if load_flag == 'yes': | 1053 if load_flag: |
1036 t0 = time() | 1054 t0 = time() |
1037 logging.info(f'Loading {tomo_file} ...') | 1055 logging.info(f'Loading {tomo_file} ...') |
1038 try: | 1056 try: |
1039 stack = np.load(tomo_file) | 1057 stack = np.load(tomo_file) |
1040 except IOError or ValueError: | 1058 except IOError or ValueError: |
1041 stack = np.array([]) | 1059 stack = np.array([]) |
1042 logging.error(f'Error loading {tomo_file}') | 1060 logging.error(f'Error loading {tomo_file}') |
1043 logging.info(f'... done in {time()-t0:.2f} seconds!') | 1061 logging.info(f'... done in {time()-t0:.2f} seconds!') |
1044 if stack.size: | 1062 if stack.size: |
1045 msnc.quickImshow(stack[:,0,:], title=title, path=self.output_folder, | 1063 quickImshow(stack[:,0,:], title=title, path=self.output_folder, |
1046 save_fig=self.save_plots, save_only=self.save_plots_only) | 1064 save_fig=self.save_plots, save_only=self.save_plots_only) |
1047 return stack, available | 1065 return stack, available |
1048 | 1066 |
1049 def _saveTomo(self, base_name, stack, index=None): | 1067 def _saveTomo(self, base_name, stack, index=None): |
1050 """Save a tomography stack. | 1068 """Save a tomography stack. |
1104 continue | 1122 continue |
1105 | 1123 |
1106 # Load a stack of tomography images | 1124 # Load a stack of tomography images |
1107 index = stack['index'] | 1125 index = stack['index'] |
1108 t0 = time() | 1126 t0 = time() |
1109 tomo_stack = msnc.loadImageStack(tomo_stack_files[i], self.config['data_filetype'], | 1127 tomo_stack = loadImageStack(tomo_stack_files[i], self.config['data_filetype'], |
1110 stack['img_offset'], self.config['theta_range']['num'], num_theta_skip, | 1128 stack['img_offset'], self.config['theta_range']['num'], num_theta_skip, |
1111 img_x_bounds, img_y_bounds) | 1129 img_x_bounds, img_y_bounds) |
1112 tomo_stack = tomo_stack.astype('float64') | 1130 tomo_stack = tomo_stack.astype('float64') |
1113 logging.debug(f'loading stack {index} took {time()-t0:.2f} seconds!') | 1131 logging.debug(f'loading stack {index} took {time()-t0:.2f} seconds!') |
1114 | 1132 |
1143 # Downsize tomography stack to smaller size | 1161 # Downsize tomography stack to smaller size |
1144 tomo_stack = tomo_stack.astype('float32') | 1162 tomo_stack = tomo_stack.astype('float32') |
1145 if not self.galaxy_flag: | 1163 if not self.galaxy_flag: |
1146 title = f'red stack fullres {index}' | 1164 title = f'red stack fullres {index}' |
1147 if not self.test_mode: | 1165 if not self.test_mode: |
1148 msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, | 1166 quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, |
1149 save_fig=self.save_plots, save_only=self.save_plots_only) | 1167 save_fig=self.save_plots, save_only=self.save_plots_only) |
1150 if zoom_perc != 100: | 1168 if zoom_perc != 100: |
1151 t0 = time() | 1169 t0 = time() |
1152 logging.info(f'Zooming in ...') | 1170 logging.info(f'Zooming in ...') |
1153 tomo_zoom_list = [] | 1171 tomo_zoom_list = [] |
1158 logging.info(f'... done in {time()-t0:.2f} seconds!') | 1176 logging.info(f'... done in {time()-t0:.2f} seconds!') |
1159 del tomo_zoom_list | 1177 del tomo_zoom_list |
1160 if not self.galaxy_flag: | 1178 if not self.galaxy_flag: |
1161 title = f'red stack {zoom_perc}p {index}' | 1179 title = f'red stack {zoom_perc}p {index}' |
1162 if not self.test_mode: | 1180 if not self.test_mode: |
1163 msnc.quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, | 1181 quickImshow(tomo_stack[0,:,:], title=title, path=self.output_folder, |
1164 save_fig=self.save_plots, save_only=self.save_plots_only) | 1182 save_fig=self.save_plots, save_only=self.save_plots_only) |
1165 | 1183 |
1166 # Convert tomography stack from theta,row,column to row,theta,column | 1184 # Convert tomography stack from theta,row,column to row,theta,column |
1167 tomo_stack = np.swapaxes(tomo_stack, 0, 1) | 1185 tomo_stack = np.swapaxes(tomo_stack, 0, 1) |
1168 | 1186 |
1212 sinogram = tomo_plane_T[two_offset+dist_from_edge:-dist_from_edge,:] | 1230 sinogram = tomo_plane_T[two_offset+dist_from_edge:-dist_from_edge,:] |
1213 else: | 1231 else: |
1214 logging.debug(f'sinogram range = [{dist_from_edge}, {two_offset-dist_from_edge}]') | 1232 logging.debug(f'sinogram range = [{dist_from_edge}, {two_offset-dist_from_edge}]') |
1215 sinogram = tomo_plane_T[dist_from_edge:two_offset-dist_from_edge,:] | 1233 sinogram = tomo_plane_T[dist_from_edge:two_offset-dist_from_edge,:] |
1216 if plot_sinogram and not self.test_mode: | 1234 if plot_sinogram and not self.test_mode: |
1217 msnc.quickImshow(sinogram.T, f'sinogram center offset{center_offset:.2f}', | 1235 quickImshow(sinogram.T, f'sinogram center offset{center_offset:.2f}', |
1218 path=self.output_folder, save_fig=self.save_plots, | 1236 path=self.output_folder, save_fig=self.save_plots, |
1219 save_only=self.save_plots_only, aspect='auto') | 1237 save_only=self.save_plots_only, aspect='auto') |
1220 | 1238 |
1221 # Inverting sinogram | 1239 # Inverting sinogram |
1222 t0 = time() | 1240 t0 = time() |
1242 # RV parameters for the denoise, gaussian, and ring removal will be different for different feature sizes | 1260 # RV parameters for the denoise, gaussian, and ring removal will be different for different feature sizes |
1243 edges = denoise_tv_chambolle(recon_plane, weight = weight) | 1261 edges = denoise_tv_chambolle(recon_plane, weight = weight) |
1244 vmax = np.max(edges[0,:,:]) | 1262 vmax = np.max(edges[0,:,:]) |
1245 vmin = -vmax | 1263 vmin = -vmax |
1246 if self.galaxy_flag: | 1264 if self.galaxy_flag: |
1247 msnc.quickImshow(edges[0,:,:], title, path=path, save_fig=True, save_only=True, | 1265 quickImshow(edges[0,:,:], title, path=path, save_fig=True, save_only=True, |
1248 cmap='gray', vmin=vmin, vmax=vmax) | 1266 cmap='gray', vmin=vmin, vmax=vmax) |
1249 else: | 1267 else: |
1250 if path is None: | 1268 if path is None: |
1251 path=self.output_folder | 1269 path=self.output_folder |
1252 msnc.quickImshow(edges[0,:,:], f'{title} coolwarm', path=path, | 1270 quickImshow(edges[0,:,:], f'{title} coolwarm', path=path, |
1253 save_fig=self.save_plots, save_only=self.save_plots_only, cmap='coolwarm') | 1271 save_fig=self.save_plots, save_only=self.save_plots_only, cmap='coolwarm') |
1254 msnc.quickImshow(edges[0,:,:], f'{title} gray', path=path, | 1272 quickImshow(edges[0,:,:], f'{title} gray', path=path, |
1255 save_fig=self.save_plots, save_only=self.save_plots_only, cmap='gray', | 1273 save_fig=self.save_plots, save_only=self.save_plots_only, cmap='gray', |
1256 vmin=vmin, vmax=vmax) | 1274 vmin=vmin, vmax=vmax) |
1257 del edges | 1275 del edges |
1258 | 1276 |
1259 def _findCenterOnePlane(self, sinogram, row, thetas_deg, eff_pixel_size, cross_sectional_dim, | 1277 def _findCenterOnePlane(self, sinogram, row, thetas_deg, eff_pixel_size, cross_sectional_dim, |
1301 recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg, | 1319 recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg, |
1302 eff_pixel_size, cross_sectional_dim, False, num_core) | 1320 eff_pixel_size, cross_sectional_dim, False, num_core) |
1303 title = f'edges row{row} center offset{center_offset_vo:.2f} Vo' | 1321 title = f'edges row{row} center offset{center_offset_vo:.2f} Vo' |
1304 self._plotEdgesOnePlane(recon_plane, title) | 1322 self._plotEdgesOnePlane(recon_plane, title) |
1305 if not self.galaxy_flag: | 1323 if not self.galaxy_flag: |
1306 if (pyip.inputYesNo('Try finding center using phase correlation '+ | 1324 if input_yesno('Try finding center using phase correlation (y/n)?', 'n'): |
1307 '(y/[n])? ', blank=True) == 'yes'): | |
1308 tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=0.1, | 1325 tomo_center = tomopy.find_center_pc(sinogram, sinogram, tol=0.1, |
1309 rotc_guess=tomo_center) | 1326 rotc_guess=tomo_center) |
1310 error = 1. | 1327 error = 1. |
1311 while error > tol: | 1328 while error > tol: |
1312 prev = tomo_center | 1329 prev = tomo_center |
1317 print(f'Center at row {row} using phase correlation = {center_offset:.2f}') | 1334 print(f'Center at row {row} using phase correlation = {center_offset:.2f}') |
1318 recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg, | 1335 recon_plane = self._reconstructOnePlane(sinogram_T, tomo_center, thetas_deg, |
1319 eff_pixel_size, cross_sectional_dim, False, num_core) | 1336 eff_pixel_size, cross_sectional_dim, False, num_core) |
1320 title = f'edges row{row} center_offset{center_offset:.2f} PC' | 1337 title = f'edges row{row} center_offset{center_offset:.2f} PC' |
1321 self._plotEdgesOnePlane(recon_plane, title) | 1338 self._plotEdgesOnePlane(recon_plane, title) |
1322 if (pyip.inputYesNo('Accept a center location ([y]) or continue '+ | 1339 if input_yesno('Accept a center location (y) or continue search (n)?', 'y'): |
1323 'search (n)? ', blank=True) != 'no'): | 1340 center_offset = input_num(' Enter chosen center offset', -center, center, |
1324 center_offset = pyip.inputNum( | 1341 center_offset_vo) |
1325 f' Enter chosen center offset [{-int(center)}, {int(center)}] '+ | |
1326 f'([{center_offset_vo:.2f}])): ', blank=True) | |
1327 if center_offset == '': | |
1328 center_offset = center_offset_vo | |
1329 del sinogram_T | 1342 del sinogram_T |
1330 del recon_plane | 1343 del recon_plane |
1331 return float(center_offset) | 1344 return float(center_offset) |
1332 | 1345 |
1333 # perform center finding search | 1346 # perform center finding search |
1336 set_center = center_offset_vo | 1349 set_center = center_offset_vo |
1337 if galaxy_param['center_type_selector'] == 'user': | 1350 if galaxy_param['center_type_selector'] == 'user': |
1338 set_center = galaxy_param['set_center'] | 1351 set_center = galaxy_param['set_center'] |
1339 set_range = galaxy_param['set_range'] | 1352 set_range = galaxy_param['set_range'] |
1340 center_offset_step = galaxy_param['set_step'] | 1353 center_offset_step = galaxy_param['set_step'] |
1341 if (not msnc.is_num(set_range, 0) or not msnc.is_num(center_offset_step) or | 1354 if (not is_num(set_range, 0) or not is_num(center_offset_step) or |
1342 center_offset_step <= 0): | 1355 center_offset_step <= 0): |
1343 logging.warning('Illegal center finding search parameter, skip search') | 1356 logging.warning('Illegal center finding search parameter, skip search') |
1344 del sinogram_T | 1357 del sinogram_T |
1345 return float(center_offset_vo) | 1358 return float(center_offset_vo) |
1346 set_range = center_offset_step*max(1, int(set_range/center_offset_step)) | 1359 set_range = center_offset_step*max(1, int(set_range/center_offset_step)) |
1347 center_offset_low = set_center-set_range | 1360 center_offset_low = set_center-set_range |
1348 center_offset_upp = set_center+set_range | 1361 center_offset_upp = set_center+set_range |
1349 else: | 1362 else: |
1350 center_offset_low = pyip.inputInt('\nEnter lower bound for center offset '+ | 1363 center_offset_low = input_int('\nEnter lower bound for center offset', |
1351 f'[{-int(center)}, {int(center)}]: ', min=-int(center), max=int(center)) | 1364 -center, center) |
1352 center_offset_upp = pyip.inputInt('Enter upper bound for center offset '+ | 1365 center_offset_upp = input_int('Enter upper bound for center offset', |
1353 f'[{center_offset_low}, {int(center)}]: ', | 1366 center_offset_low, center) |
1354 min=center_offset_low, max=int(center)) | |
1355 if center_offset_upp == center_offset_low: | 1367 if center_offset_upp == center_offset_low: |
1356 center_offset_step = 1 | 1368 center_offset_step = 1 |
1357 else: | 1369 else: |
1358 center_offset_step = pyip.inputInt('Enter step size for center offset search '+ | 1370 center_offset_step = input_int('Enter step size for center offset search', |
1359 f'[1, {center_offset_upp-center_offset_low}]: ', | 1371 1, center_offset_upp-center_offset_low) |
1360 min=1, max=center_offset_upp-center_offset_low) | |
1361 num_center_offset = 1+int((center_offset_upp-center_offset_low)/center_offset_step) | 1372 num_center_offset = 1+int((center_offset_upp-center_offset_low)/center_offset_step) |
1362 center_offsets = np.linspace(center_offset_low, center_offset_upp, num_center_offset) | 1373 center_offsets = np.linspace(center_offset_low, center_offset_upp, num_center_offset) |
1363 for center_offset in center_offsets: | 1374 for center_offset in center_offsets: |
1364 if center_offset == center_offset_vo: | 1375 if center_offset == center_offset_vo: |
1365 continue | 1376 continue |
1371 title = f'edges row{row} center_offset{center_offset:.2f}' | 1382 title = f'edges row{row} center_offset{center_offset:.2f}' |
1372 if self.galaxy_flag: | 1383 if self.galaxy_flag: |
1373 self._plotEdgesOnePlane(recon_plane, title, path='find_center_pngs') | 1384 self._plotEdgesOnePlane(recon_plane, title, path='find_center_pngs') |
1374 else: | 1385 else: |
1375 self._plotEdgesOnePlane(recon_plane, title) | 1386 self._plotEdgesOnePlane(recon_plane, title) |
1376 if self.galaxy_flag or pyip.inputInt('\nContinue (0) or end the search (1): ', | 1387 if self.galaxy_flag or input_int('\nContinue (0) or end the search (1)', 0, 1): |
1377 min=0, max=1): | |
1378 break | 1388 break |
1379 | 1389 |
1380 del sinogram_T | 1390 del sinogram_T |
1381 del recon_plane | 1391 del recon_plane |
1382 if self.galaxy_flag: | 1392 if self.galaxy_flag: |
1383 center_offset = center_offset_vo | 1393 center_offset = center_offset_vo |
1384 else: | 1394 else: |
1385 center_offset = pyip.inputNum(f' Enter chosen center offset '+ | 1395 center_offset = input_num(' Enter chosen center offset', -center, center) |
1386 f'[{-int(center)}, {int(center)}]: ', min=-int(center), max=int(center)) | |
1387 return float(center_offset) | 1396 return float(center_offset) |
1388 | 1397 |
1389 def _reconstructOneTomoStack(self, tomo_stack, thetas, row_bounds=None, | 1398 def _reconstructOneTomoStack(self, tomo_stack, thetas, row_bounds=None, |
1390 center_offsets=[], sigma=0.1, rwidth=30, num_core=1, algorithm='gridrec', | 1399 center_offsets=[], sigma=0.1, rwidth=30, num_core=1, algorithm='gridrec', |
1391 run_secondary_sirt=False, secondary_iter=100): | 1400 run_secondary_sirt=False, secondary_iter=100): |
1400 # https://tomopy.readthedocs.io/en/latest/api/tomopy.misc.corr.html | 1409 # https://tomopy.readthedocs.io/en/latest/api/tomopy.misc.corr.html |
1401 # RV: Add an option to do (extra) secondary iterations later or to do some sort of convergence test? | 1410 # RV: Add an option to do (extra) secondary iterations later or to do some sort of convergence test? |
1402 if row_bounds is None: | 1411 if row_bounds is None: |
1403 row_bounds = [0, tomo_stack.shape[0]] | 1412 row_bounds = [0, tomo_stack.shape[0]] |
1404 else: | 1413 else: |
1405 if not msnc.is_index_range(row_bounds, 0, tomo_stack.shape[0]): | 1414 if not is_index_range(row_bounds, 0, tomo_stack.shape[0]): |
1406 raise ValueError('Illegal row bounds in reconstructOneTomoStack') | 1415 raise ValueError('Illegal row bounds in reconstructOneTomoStack') |
1407 if thetas.size != tomo_stack.shape[1]: | 1416 if thetas.size != tomo_stack.shape[1]: |
1408 raise ValueError('theta dimension mismatch in reconstructOneTomoStack') | 1417 raise ValueError('theta dimension mismatch in reconstructOneTomoStack') |
1409 if not len(center_offsets): | 1418 if not len(center_offsets): |
1410 centers = np.zeros((row_bounds[1]-row_bounds[0])) | 1419 centers = np.zeros((row_bounds[1]-row_bounds[0])) |
1465 """ | 1474 """ |
1466 self.is_valid = True | 1475 self.is_valid = True |
1467 | 1476 |
1468 # Find dark field images | 1477 # Find dark field images |
1469 dark_field = self.config['dark_field'] | 1478 dark_field = self.config['dark_field'] |
1470 img_start, num_imgs, dark_files = msnc.findImageFiles( | 1479 img_start, num_imgs, dark_files = findImageFiles( |
1471 dark_field['data_path'], self.config['data_filetype'], 'dark field') | 1480 dark_field['data_path'], self.config['data_filetype'], 'dark field') |
1472 if img_start < 0 or num_imgs < 1: | 1481 if img_start < 0 or num_imgs < 1: |
1473 logging.error('Unable to find suitable dark field images') | 1482 logging.error('Unable to find suitable dark field images') |
1474 if dark_field['data_path']: | 1483 if dark_field['data_path']: |
1475 self.is_valid = False | 1484 self.is_valid = False |
1490 if dark_field['data_path']: | 1499 if dark_field['data_path']: |
1491 self.is_valid = False | 1500 self.is_valid = False |
1492 logging.info(f'Number of dark field images = {dark_field["num"]}') | 1501 logging.info(f'Number of dark field images = {dark_field["num"]}') |
1493 logging.info(f'Dark field image start index = {dark_field["img_start"]}') | 1502 logging.info(f'Dark field image start index = {dark_field["img_start"]}') |
1494 if num_imgs and tiff_to_h5_flag and self.config['data_filetype'] == 'tif': | 1503 if num_imgs and tiff_to_h5_flag and self.config['data_filetype'] == 'tif': |
1495 dark_files = msnc.combine_tiffs_in_h5(dark_files, num_imgs, | 1504 dark_files = combine_tiffs_in_h5(dark_files, num_imgs, |
1496 f'{self.config["work_folder"]}/dark_field.h5') | 1505 f'{self.config["work_folder"]}/dark_field.h5') |
1497 dark_field['data_path'] = dark_files[0] | 1506 dark_field['data_path'] = dark_files[0] |
1498 | 1507 |
1499 # Find bright field images | 1508 # Find bright field images |
1500 bright_field = self.config['bright_field'] | 1509 bright_field = self.config['bright_field'] |
1501 img_start, num_imgs, bright_files = msnc.findImageFiles( | 1510 img_start, num_imgs, bright_files = findImageFiles( |
1502 bright_field['data_path'], self.config['data_filetype'], 'bright field') | 1511 bright_field['data_path'], self.config['data_filetype'], 'bright field') |
1503 if img_start < 0 or num_imgs < 1: | 1512 if img_start < 0 or num_imgs < 1: |
1504 logging.error('Unable to find suitable bright field images') | 1513 logging.error('Unable to find suitable bright field images') |
1505 self.is_valid = False | 1514 self.is_valid = False |
1506 img_start_old = bright_field.get('img_start') | 1515 img_start_old = bright_field.get('img_start') |
1518 logging.warning('Inconsistent image start index for bright field images') | 1527 logging.warning('Inconsistent image start index for bright field images') |
1519 self.is_valid = False | 1528 self.is_valid = False |
1520 logging.info(f'Number of bright field images = {bright_field["num"]}') | 1529 logging.info(f'Number of bright field images = {bright_field["num"]}') |
1521 logging.info(f'Bright field image start index = {bright_field["img_start"]}') | 1530 logging.info(f'Bright field image start index = {bright_field["img_start"]}') |
1522 if num_imgs and tiff_to_h5_flag and self.config['data_filetype'] == 'tif': | 1531 if num_imgs and tiff_to_h5_flag and self.config['data_filetype'] == 'tif': |
1523 bright_files = msnc.combine_tiffs_in_h5(bright_files, num_imgs, | 1532 bright_files = combine_tiffs_in_h5(bright_files, num_imgs, |
1524 f'{self.config["work_folder"]}/bright_field.h5') | 1533 f'{self.config["work_folder"]}/bright_field.h5') |
1525 bright_field['data_path'] = bright_files[0] | 1534 bright_field['data_path'] = bright_files[0] |
1526 | 1535 |
1527 # Find tomography images | 1536 # Find tomography images |
1528 tomo_stack_files = [] | 1537 tomo_stack_files = [] |
1529 for stack in self.config['stack_info']['stacks']: | 1538 for stack in self.config['stack_info']['stacks']: |
1530 index = stack['index'] | 1539 index = stack['index'] |
1531 img_start, num_imgs, tomo_files = msnc.findImageFiles( | 1540 img_start, num_imgs, tomo_files = findImageFiles( |
1532 stack['data_path'], self.config['data_filetype'], f'tomography set {index}') | 1541 stack['data_path'], self.config['data_filetype'], f'tomography set {index}') |
1533 if img_start < 0 or num_imgs < 1: | 1542 if img_start < 0 or num_imgs < 1: |
1534 logging.error('Unable to find suitable tomography images') | 1543 logging.error('Unable to find suitable tomography images') |
1535 self.is_valid = False | 1544 self.is_valid = False |
1536 img_start_old = stack.get('img_start') | 1545 img_start_old = stack.get('img_start') |
1548 logging.warning('Inconsistent image start index for tomography images') | 1557 logging.warning('Inconsistent image start index for tomography images') |
1549 self.is_valid = False | 1558 self.is_valid = False |
1550 logging.info(f'Number of tomography images for set {index} = {stack["num"]}') | 1559 logging.info(f'Number of tomography images for set {index} = {stack["num"]}') |
1551 logging.info(f'Tomography set {index} image start index = {stack["img_start"]}') | 1560 logging.info(f'Tomography set {index} image start index = {stack["img_start"]}') |
1552 if num_imgs and tiff_to_h5_flag and self.config['data_filetype'] == 'tif': | 1561 if num_imgs and tiff_to_h5_flag and self.config['data_filetype'] == 'tif': |
1553 tomo_files = msnc.combine_tiffs_in_h5(tomo_files, num_imgs, | 1562 tomo_files = combine_tiffs_in_h5(tomo_files, num_imgs, |
1554 f'{self.config["work_folder"]}/tomo_field_{index}.h5') | 1563 f'{self.config["work_folder"]}/tomo_field_{index}.h5') |
1555 stack['data_path'] = tomo_files[0] | 1564 stack['data_path'] = tomo_files[0] |
1556 tomo_stack_files.append(tomo_files) | 1565 tomo_stack_files.append(tomo_files) |
1557 del tomo_files | 1566 del tomo_files |
1558 | 1567 |
1707 center_stack_index = None | 1716 center_stack_index = None |
1708 if find_center and 'center_stack_index' in find_center: | 1717 if find_center and 'center_stack_index' in find_center: |
1709 center_stack_index = find_center['center_stack_index'] | 1718 center_stack_index = find_center['center_stack_index'] |
1710 if (not isinstance(center_stack_index, int) or | 1719 if (not isinstance(center_stack_index, int) or |
1711 center_stack_index not in available_stacks): | 1720 center_stack_index not in available_stacks): |
1712 msnc.illegal_value('center_stack_index', center_stack_index, 'config file') | 1721 illegal_value(center_stack_index, 'find_center:center_stack_index', 'config file') |
1713 else: | 1722 else: |
1714 if self.test_mode: | 1723 if self.test_mode: |
1715 assert(find_center.get('completed', False) == False) | 1724 assert(find_center.get('completed', False) == False) |
1716 else: | 1725 else: |
1717 print('\nFound calibration center offset info for stack '+ | 1726 print('\nFound calibration center offset info for stack '+ |
1718 f'{center_stack_index}') | 1727 f'{center_stack_index}') |
1719 if (pyip.inputYesNo('Do you want to use this again ([y]/n)? ', | 1728 if (input_yesno('Do you want to use this again (y/n)?', 'y') |
1720 blank=True) != 'no' and find_center.get('completed', False) == True): | 1729 and find_center.get('completed', False) == True): |
1721 return | 1730 return |
1722 | 1731 |
1723 # Load the required preprocessed stack | 1732 # Load the required preprocessed stack |
1724 # preprocessed stack order: row,theta,column | 1733 # preprocessed stack order: row,theta,column |
1725 num_tomo_stacks = self.config['stack_info']['num'] | 1734 num_tomo_stacks = self.config['stack_info']['num'] |
1747 raise OSError('Unable to load the required preprocessed tomography stack') | 1756 raise OSError('Unable to load the required preprocessed tomography stack') |
1748 assert(stacks[tomo_stack_index].get('preprocessed', False) == True) | 1757 assert(stacks[tomo_stack_index].get('preprocessed', False) == True) |
1749 else: | 1758 else: |
1750 while True: | 1759 while True: |
1751 if not center_stack_index: | 1760 if not center_stack_index: |
1752 center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' | 1761 center_stack_index = input_int('\nEnter tomography stack index to get ' |
1753 f'rotation axis centers {available_stacks}: ') | 1762 f'rotation axis centers ({available_stacks})', inset=available_stacks) |
1754 while center_stack_index not in available_stacks: | 1763 while center_stack_index not in available_stacks: |
1755 center_stack_index = pyip.inputInt('\nEnter tomography stack index to get ' | 1764 center_stack_index = input_int('\nEnter tomography stack index to get ' |
1756 f'rotation axis centers {available_stacks}: ') | 1765 f'rotation axis centers ({available_stacks})', inset=available_stacks) |
1757 tomo_stack_index = available_stacks.index(center_stack_index) | 1766 tomo_stack_index = available_stacks.index(center_stack_index) |
1758 if not self.tomo_stacks[tomo_stack_index].size: | 1767 if not self.tomo_stacks[tomo_stack_index].size: |
1759 self.tomo_stacks[tomo_stack_index], available = self._loadTomo( | 1768 self.tomo_stacks[tomo_stack_index], available = self._loadTomo( |
1760 'red stack', center_stack_index, required=True) | 1769 'red stack', center_stack_index, required=True) |
1761 center_stack = self.tomo_stacks[tomo_stack_index] | 1770 center_stack = self.tomo_stacks[tomo_stack_index] |
1783 row_bounds[1] = center_stack.shape[0] | 1792 row_bounds[1] = center_stack.shape[0] |
1784 if center_rows[0] == -1: | 1793 if center_rows[0] == -1: |
1785 center_rows[0] = 0 | 1794 center_rows[0] = 0 |
1786 if center_rows[1] == -1: | 1795 if center_rows[1] == -1: |
1787 center_rows[1] = center_stack.shape[0]-1 | 1796 center_rows[1] = center_stack.shape[0]-1 |
1788 if not msnc.is_index_range(row_bounds, 0, center_stack.shape[0]): | 1797 if not is_index_range(row_bounds, 0, center_stack.shape[0]): |
1789 msnc.illegal_value('row_bounds', row_bounds) | 1798 illegal_value(row_bounds, 'row_bounds', 'Tomo:findCenters') |
1790 return | 1799 return |
1791 | 1800 |
1792 # Set thetas (in degrees) | 1801 # Set thetas (in degrees) |
1793 theta_range = self.config['theta_range'] | 1802 theta_range = self.config['theta_range'] |
1794 theta_start = theta_range['start'] | 1803 theta_start = theta_range['start'] |
1804 if pixel_size is None: | 1813 if pixel_size is None: |
1805 raise ValueError('Detector pixel size unavailable') | 1814 raise ValueError('Detector pixel size unavailable') |
1806 eff_pixel_size = 100.*pixel_size/zoom_perc | 1815 eff_pixel_size = 100.*pixel_size/zoom_perc |
1807 logging.debug(f'eff_pixel_size = {eff_pixel_size}') | 1816 logging.debug(f'eff_pixel_size = {eff_pixel_size}') |
1808 if num_tomo_stacks == 1: | 1817 if num_tomo_stacks == 1: |
1809 accept = 'yes' | 1818 accept = True |
1810 if not self.test_mode and not self.galaxy_flag: | 1819 if not self.test_mode and not self.galaxy_flag: |
1811 accept = 'no' | 1820 accept = False |
1812 print('\nSelect bounds for image reconstruction') | 1821 print('\nSelect bounds for image reconstruction') |
1813 if msnc.is_index_range(row_bounds, 0, center_stack.shape[0]): | 1822 if is_index_range(row_bounds, 0, center_stack.shape[0]): |
1814 a_tmp = np.copy(center_stack[:,0,:]) | 1823 a_tmp = np.copy(center_stack[:,0,:]) |
1815 a_tmp_max = a_tmp.max() | 1824 a_tmp_max = a_tmp.max() |
1816 a_tmp[row_bounds[0],:] = a_tmp_max | 1825 a_tmp[row_bounds[0],:] = a_tmp_max |
1817 a_tmp[row_bounds[1]-1,:] = a_tmp_max | 1826 a_tmp[row_bounds[1]-1,:] = a_tmp_max |
1818 print(f'lower bound = {row_bounds[0]} (inclusive)') | 1827 print(f'lower bound = {row_bounds[0]} (inclusive)') |
1819 print(f'upper bound = {row_bounds[1]} (exclusive)') | 1828 print(f'upper bound = {row_bounds[1]} (exclusive)') |
1820 msnc.quickImshow(a_tmp, title=f'center stack theta={theta_start}', | 1829 quickImshow(a_tmp, title=f'center stack theta={theta_start}', |
1821 aspect='auto') | 1830 aspect='auto') |
1822 del a_tmp | 1831 del a_tmp |
1823 accept = pyip.inputYesNo('Accept these bounds ([y]/n)?: ', blank=True) | 1832 accept = input_yesno('Accept these bounds (y/n)?', 'y') |
1824 if accept == 'no': | 1833 if accept: |
1825 (n1, n2) = msnc.selectImageBounds(center_stack[:,0,:], 0, | |
1826 title=f'center stack theta={theta_start}') | |
1827 else: | |
1828 n1 = row_bounds[0] | 1834 n1 = row_bounds[0] |
1829 n2 = row_bounds[1] | 1835 n2 = row_bounds[1] |
1836 else: | |
1837 n1, n2 = selectImageBounds(center_stack[:,0,:], 0, | |
1838 title=f'center stack theta={theta_start}') | |
1830 else: | 1839 else: |
1831 tomo_ref_heights = [stack['ref_height'] for stack in stacks] | 1840 tomo_ref_heights = [stack['ref_height'] for stack in stacks] |
1832 n1 = int((1.+(tomo_ref_heights[0]+center_stack.shape[0]*eff_pixel_size- | 1841 n1 = int((1.+(tomo_ref_heights[0]+center_stack.shape[0]*eff_pixel_size- |
1833 tomo_ref_heights[1])/eff_pixel_size)/2) | 1842 tomo_ref_heights[1])/eff_pixel_size)/2) |
1834 n2 = center_stack.shape[0]-n1 | 1843 n2 = center_stack.shape[0]-n1 |
1836 if not self.test_mode and not self.galaxy_flag: | 1845 if not self.test_mode and not self.galaxy_flag: |
1837 tmp = center_stack[:,0,:] | 1846 tmp = center_stack[:,0,:] |
1838 tmp_max = tmp.max() | 1847 tmp_max = tmp.max() |
1839 tmp[n1,:] = tmp_max | 1848 tmp[n1,:] = tmp_max |
1840 tmp[n2-1,:] = tmp_max | 1849 tmp[n2-1,:] = tmp_max |
1841 if msnc.is_index_range(center_rows, 0, tmp.shape[0]): | 1850 if is_index_range(center_rows, 0, tmp.shape[0]): |
1842 tmp[center_rows[0],:] = tmp_max | 1851 tmp[center_rows[0],:] = tmp_max |
1843 tmp[center_rows[1]-1,:] = tmp_max | 1852 tmp[center_rows[1]-1,:] = tmp_max |
1844 extent = [0, tmp.shape[1], tmp.shape[0], 0] | 1853 extent = [0, tmp.shape[1], tmp.shape[0], 0] |
1845 msnc.quickImshow(tmp, title=f'center stack theta={theta_start}', | 1854 quickImshow(tmp, title=f'center stack theta={theta_start}', |
1846 path=self.output_folder, extent=extent, save_fig=self.save_plots, | 1855 path=self.output_folder, extent=extent, save_fig=self.save_plots, |
1847 save_only=self.save_plots_only, aspect='auto') | 1856 save_only=self.save_plots_only, aspect='auto') |
1848 del tmp | 1857 del tmp |
1849 #extent = [0, center_stack.shape[2], n2, n1] | 1858 #extent = [0, center_stack.shape[2], n2, n1] |
1850 #msnc.quickImshow(center_stack[n1:n2,0,:], title=f'center stack theta={theta_start}', | 1859 #quickImshow(center_stack[n1:n2,0,:], title=f'center stack theta={theta_start}', |
1851 # path=self.output_folder, extent=extent, save_fig=self.save_plots, | 1860 # path=self.output_folder, extent=extent, save_fig=self.save_plots, |
1852 # save_only=self.save_plots_only, show_grid=True, aspect='auto') | 1861 # save_only=self.save_plots_only, show_grid=True, aspect='auto') |
1853 | 1862 |
1854 # Get cross sectional diameter in mm | 1863 # Get cross sectional diameter in mm |
1855 cross_sectional_dim = center_stack.shape[2]*eff_pixel_size | 1864 cross_sectional_dim = center_stack.shape[2]*eff_pixel_size |
1857 | 1866 |
1858 # Determine center offset at sample row boundaries | 1867 # Determine center offset at sample row boundaries |
1859 logging.info('Determine center offset at sample row boundaries') | 1868 logging.info('Determine center offset at sample row boundaries') |
1860 | 1869 |
1861 # Lower row center | 1870 # Lower row center |
1862 use_row = 'no' | 1871 use_row = False |
1863 use_center = 'no' | 1872 use_center = False |
1864 row = center_rows[0] | 1873 row = center_rows[0] |
1865 if self.test_mode or self.galaxy_flag: | 1874 if self.test_mode or self.galaxy_flag: |
1866 assert(msnc.is_int(row, n1, n2-2)) | 1875 assert(is_int(row, n1, n2-2)) |
1867 if msnc.is_int(row, n1, n2-2): | 1876 if is_int(row, n1, n2-2): |
1868 if self.test_mode or self.galaxy_flag: | 1877 if self.test_mode or self.galaxy_flag: |
1869 use_row = 'yes' | 1878 use_row = True |
1870 else: | 1879 else: |
1871 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') | 1880 quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') |
1872 use_row = pyip.inputYesNo('\nCurrent row index for lower center = ' | 1881 use_row = input_yesno('\nCurrent row index for lower center = ' |
1873 f'{row}, use this value ([y]/n)? ', blank=True) | 1882 f'{row}, use this value (y/n)?', 'y') |
1874 if self.save_plots_only: | 1883 if self.save_plots_only: |
1875 msnc.clearFig(f'theta={theta_start}') | 1884 clearImshow(f'theta={theta_start}') |
1876 if use_row != 'no': | 1885 if use_row: |
1877 center_offset = find_center.get('lower_center_offset') | 1886 center_offset = find_center.get('lower_center_offset') |
1878 if msnc.is_num(center_offset): | 1887 if is_num(center_offset): |
1879 use_center = pyip.inputYesNo('Current lower center offset = '+ | 1888 use_center = input_yesno('Current lower center offset = '+ |
1880 f'{center_offset}, use this value ([y]/n)? ', blank=True) | 1889 f'{center_offset}, use this value (y/n)?', 'y') |
1881 if use_center == 'no': | 1890 if not use_center: |
1882 if use_row == 'no': | 1891 if not use_row: |
1883 if not self.test_mode: | 1892 if not self.test_mode: |
1884 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', | 1893 quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', |
1885 aspect='auto') | 1894 aspect='auto') |
1886 row = pyip.inputInt('\nEnter row index to find lower center '+ | 1895 row = input_int('\nEnter row index to find lower center', n1, n2-2, n1) |
1887 f'[[{n1}], {n2-2}]: ', min=n1, max=n2-2, blank=True) | |
1888 if row == '': | 1896 if row == '': |
1889 row = n1 | 1897 row = n1 |
1890 if self.save_plots_only: | 1898 if self.save_plots_only: |
1891 msnc.clearFig(f'theta={theta_start}') | 1899 clearImshow(f'theta={theta_start}') |
1892 # center_stack order: row,theta,column | 1900 # center_stack order: row,theta,column |
1893 center_offset = self._findCenterOnePlane(center_stack[row,:,:], row, thetas_deg, | 1901 center_offset = self._findCenterOnePlane(center_stack[row,:,:], row, thetas_deg, |
1894 eff_pixel_size, cross_sectional_dim, num_core=num_core, | 1902 eff_pixel_size, cross_sectional_dim, num_core=num_core, |
1895 galaxy_param=galaxy_param) | 1903 galaxy_param=galaxy_param) |
1896 logging.info(f'lower_center_offset = {center_offset:.2f} {type(center_offset)}') | 1904 logging.info(f'lower_center_offset = {center_offset:.2f} {type(center_offset)}') |
1901 find_center['lower_center_offset'] = center_offset | 1909 find_center['lower_center_offset'] = center_offset |
1902 self.cf.saveFile(self.config_out) | 1910 self.cf.saveFile(self.config_out) |
1903 lower_row = row | 1911 lower_row = row |
1904 | 1912 |
1905 # Upper row center | 1913 # Upper row center |
1906 use_row = 'no' | 1914 use_row = False |
1907 use_center = 'no' | 1915 use_center = False |
1908 row = center_rows[1] | 1916 row = center_rows[1] |
1909 if self.test_mode or self.galaxy_flag: | 1917 if self.test_mode or self.galaxy_flag: |
1910 assert(msnc.is_int(row, lower_row+1, n2-1)) | 1918 assert(is_int(row, lower_row+1, n2-1)) |
1911 if msnc.is_int(row, lower_row+1, n2-1): | 1919 if is_int(row, lower_row+1, n2-1): |
1912 if self.test_mode or self.galaxy_flag: | 1920 if self.test_mode or self.galaxy_flag: |
1913 use_row = 'yes' | 1921 use_row = True |
1914 else: | 1922 else: |
1915 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') | 1923 quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', aspect='auto') |
1916 use_row = pyip.inputYesNo('\nCurrent row index for upper center = ' | 1924 use_row = input_yesno('\nCurrent row index for upper center = ' |
1917 f'{row}, use this value ([y]/n)? ', blank=True) | 1925 f'{row}, use this value (y/n)?', 'y') |
1918 if self.save_plots_only: | 1926 if self.save_plots_only: |
1919 msnc.clearFig(f'theta={theta_start}') | 1927 clearImshow(f'theta={theta_start}') |
1920 if use_row != 'no': | 1928 if use_row: |
1921 center_offset = find_center.get('upper_center_offset') | 1929 center_offset = find_center.get('upper_center_offset') |
1922 if msnc.is_num(center_offset): | 1930 if is_num(center_offset): |
1923 use_center = pyip.inputYesNo('Current upper center offset = '+ | 1931 use_center = input_yesrnNo('Current upper center offset = '+ |
1924 f'{center_offset}, use this value ([y]/n)? ', blank=True) | 1932 f'{center_offset}, use this value (y/n)?', 'y') |
1925 if use_center == 'no': | 1933 if not use_center: |
1926 if use_row == 'no': | 1934 if not use_row: |
1927 if not self.test_mode: | 1935 if not self.test_mode: |
1928 msnc.quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', | 1936 quickImshow(center_stack[:,0,:], title=f'theta={theta_start}', |
1929 aspect='auto') | 1937 aspect='auto') |
1930 row = pyip.inputInt('\nEnter row index to find upper center '+ | 1938 row = input_int('\nEnter row index to find upper center', lower_row+1, n2-1, n2-1) |
1931 f'[{lower_row+1}, [{n2-1}]]: ', min=lower_row+1, max=n2-1, blank=True) | |
1932 if row == '': | 1939 if row == '': |
1933 row = n2-1 | 1940 row = n2-1 |
1934 if self.save_plots_only: | 1941 if self.save_plots_only: |
1935 msnc.clearFig(f'theta={theta_start}') | 1942 clearImshow(f'theta={theta_start}') |
1936 # center_stack order: row,theta,column | 1943 # center_stack order: row,theta,column |
1937 center_offset = self._findCenterOnePlane(center_stack[row,:,:], row, thetas_deg, | 1944 center_offset = self._findCenterOnePlane(center_stack[row,:,:], row, thetas_deg, |
1938 eff_pixel_size, cross_sectional_dim, num_core=num_core, | 1945 eff_pixel_size, cross_sectional_dim, num_core=num_core, |
1939 galaxy_param=galaxy_param) | 1946 galaxy_param=galaxy_param) |
1940 logging.info(f'upper_center_offset = {center_offset:.2f}') | 1947 logging.info(f'upper_center_offset = {center_offset:.2f}') |
1990 plane1 = stack1[upper_row,:] | 1997 plane1 = stack1[upper_row,:] |
1991 plane2 = stack2[lower_row,:] | 1998 plane2 = stack2[lower_row,:] |
1992 for i in range(-2, 3): | 1999 for i in range(-2, 3): |
1993 shift_i = shift+2*i | 2000 shift_i = shift+2*i |
1994 plane1_shifted = spi.shift(plane2, [0, shift_i]) | 2001 plane1_shifted = spi.shift(plane2, [0, shift_i]) |
1995 msnc.quickPlot((plane1[0,:],), (plane1_shifted[0,:],), | 2002 quickPlot((plane1[0,:],), (plane1_shifted[0,:],), |
1996 title=f'stacks {stack1} {stack2} shifted {2*i} theta={self.start_theta}', | 2003 title=f'stacks {stack1} {stack2} shifted {2*i} theta={self.start_theta}', |
1997 path=self.output_folder, save_fig=self.save_plots, | 2004 path=self.output_folder, save_fig=self.save_plots, |
1998 save_only=self.save_plots_only) | 2005 save_only=self.save_plots_only) |
1999 if center_stack_index < self.num_tomo_stacks-1: | 2006 if center_stack_index < self.num_tomo_stacks-1: |
2000 stack1 = self.tomo_stacks[center_stack_index] | 2007 stack1 = self.tomo_stacks[center_stack_index] |
2009 plane1 = stack1[upper_row,:] | 2016 plane1 = stack1[upper_row,:] |
2010 plane2 = stack2[lower_row,:] | 2017 plane2 = stack2[lower_row,:] |
2011 for i in range(-2, 3): | 2018 for i in range(-2, 3): |
2012 shift_i = -shift+2*i | 2019 shift_i = -shift+2*i |
2013 plane1_shifted = spi.shift(plane2, [0, shift_i]) | 2020 plane1_shifted = spi.shift(plane2, [0, shift_i]) |
2014 msnc.quickPlot((plane1[0,:],), (plane1_shifted[0,:],), | 2021 quickPlot((plane1[0,:],), (plane1_shifted[0,:],), |
2015 title=f'stacks {stack1} {stack2} shifted {2*i} theta={start_theta}', | 2022 title=f'stacks {stack1} {stack2} shifted {2*i} theta={start_theta}', |
2016 path=self.output_folder, save_fig=self.save_plots, | 2023 path=self.output_folder, save_fig=self.save_plots, |
2017 save_only=self.save_plots_only) | 2024 save_only=self.save_plots_only) |
2018 del plane1, plane2, plane1_shifted | 2025 del plane1, plane2, plane1_shifted |
2019 | 2026 |
2020 # Update config file | 2027 # Update config file |
2021 self.config = msnc.update('config.txt', 'check_centers', True, 'find_centers') | 2028 self.config = update('config.txt', 'check_centers', True, 'find_centers') |
2022 | 2029 |
2023 def reconstructTomoStacks(self, galaxy_param=None, num_core=None): | 2030 def reconstructTomoStacks(self, galaxy_param=None, num_core=None): |
2024 """Reconstruct tomography stacks. | 2031 """Reconstruct tomography stacks. |
2025 """ | 2032 """ |
2026 if num_core is None: | 2033 if num_core is None: |
2039 assert(isinstance(galaxy_param, dict)) | 2046 assert(isinstance(galaxy_param, dict)) |
2040 # Get rotation axis centers | 2047 # Get rotation axis centers |
2041 center_offsets = galaxy_param['center_offsets'] | 2048 center_offsets = galaxy_param['center_offsets'] |
2042 assert(isinstance(center_offsets, list) and len(center_offsets) == 2) | 2049 assert(isinstance(center_offsets, list) and len(center_offsets) == 2) |
2043 lower_center_offset = center_offsets[0] | 2050 lower_center_offset = center_offsets[0] |
2044 assert(msnc.is_num(lower_center_offset)) | 2051 assert(is_num(lower_center_offset)) |
2045 upper_center_offset = center_offsets[1] | 2052 upper_center_offset = center_offsets[1] |
2046 assert(msnc.is_num(upper_center_offset)) | 2053 assert(is_num(upper_center_offset)) |
2047 else: | 2054 else: |
2048 if galaxy_param: | 2055 if galaxy_param: |
2049 logging.warning('Ignoring galaxy_param in reconstructTomoStacks (only for Galaxy)') | 2056 logging.warning('Ignoring galaxy_param in reconstructTomoStacks (only for Galaxy)') |
2050 galaxy_param = None | 2057 galaxy_param = None |
2051 lower_center_offset = None | 2058 lower_center_offset = None |
2126 logging.debug(f'... _reconstructOneTomoStack took {time()-t0:.2f} seconds!') | 2133 logging.debug(f'... _reconstructOneTomoStack took {time()-t0:.2f} seconds!') |
2127 logging.info(f'Reconstruction of stack {index} took {time()-t0:.2f} seconds!') | 2134 logging.info(f'Reconstruction of stack {index} took {time()-t0:.2f} seconds!') |
2128 if self.galaxy_flag: | 2135 if self.galaxy_flag: |
2129 x_slice = int(self.tomo_stacks[i].shape[0]/2) | 2136 x_slice = int(self.tomo_stacks[i].shape[0]/2) |
2130 title = f'{basetitle} {index} xslice{x_slice}' | 2137 title = f'{basetitle} {index} xslice{x_slice}' |
2131 msnc.quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, | 2138 quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, |
2132 path='center_slice_pngs', save_fig=True, save_only=True) | 2139 path='center_slice_pngs', save_fig=True, save_only=True) |
2133 y_slice = int(self.tomo_stacks[i].shape[0]/2) | 2140 y_slice = int(self.tomo_stacks[i].shape[0]/2) |
2134 title = f'{basetitle} {index} yslice{y_slice}' | 2141 title = f'{basetitle} {index} yslice{y_slice}' |
2135 msnc.quickImshow(self.tomo_recon_stacks[i][:,y_slice,:], title=title, | 2142 quickImshow(self.tomo_recon_stacks[i][:,y_slice,:], title=title, |
2136 path='center_slice_pngs', save_fig=True, save_only=True) | 2143 path='center_slice_pngs', save_fig=True, save_only=True) |
2137 z_slice = int(self.tomo_stacks[i].shape[0]/2) | 2144 z_slice = int(self.tomo_stacks[i].shape[0]/2) |
2138 title = f'{basetitle} {index} zslice{z_slice}' | 2145 title = f'{basetitle} {index} zslice{z_slice}' |
2139 msnc.quickImshow(self.tomo_recon_stacks[i][:,:,z_slice], title=title, | 2146 quickImshow(self.tomo_recon_stacks[i][:,:,z_slice], title=title, |
2140 path='center_slice_pngs', save_fig=True, save_only=True) | 2147 path='center_slice_pngs', save_fig=True, save_only=True) |
2141 elif not self.test_mode: | 2148 elif not self.test_mode: |
2142 x_slice = int(self.tomo_stacks[i].shape[0]/2) | 2149 x_slice = int(self.tomo_stacks[i].shape[0]/2) |
2143 title = f'{basetitle} {index} xslice{x_slice}' | 2150 title = f'{basetitle} {index} xslice{x_slice}' |
2144 msnc.quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, | 2151 quickImshow(self.tomo_recon_stacks[i][x_slice,:,:], title=title, |
2145 path=self.output_folder, save_fig=self.save_plots, | 2152 path=self.output_folder, save_fig=self.save_plots, |
2146 save_only=self.save_plots_only) | 2153 save_only=self.save_plots_only) |
2147 msnc.quickPlot(self.tomo_recon_stacks[i] | 2154 quickPlot(self.tomo_recon_stacks[i] |
2148 [x_slice,int(self.tomo_recon_stacks[i].shape[2]/2),:], | 2155 [x_slice,int(self.tomo_recon_stacks[i].shape[2]/2),:], |
2149 title=f'{title} cut{int(self.tomo_recon_stacks[i].shape[2]/2)}', | 2156 title=f'{title} cut{int(self.tomo_recon_stacks[i].shape[2]/2)}', |
2150 path=self.output_folder, save_fig=self.save_plots, | 2157 path=self.output_folder, save_fig=self.save_plots, |
2151 save_only=self.save_plots_only) | 2158 save_only=self.save_plots_only) |
2152 self._saveTomo('recon stack', self.tomo_recon_stacks[i], index) | 2159 self._saveTomo('recon stack', self.tomo_recon_stacks[i], index) |
2171 | 2178 |
2172 # Create cross section profile in yz-plane | 2179 # Create cross section profile in yz-plane |
2173 tomosum = 0 | 2180 tomosum = 0 |
2174 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,2)) for tomo_recon_stack in | 2181 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,2)) for tomo_recon_stack in |
2175 self.tomo_recon_stacks] | 2182 self.tomo_recon_stacks] |
2176 msnc.quickPlot(tomosum, title='recon stack sum yz', path='center_slice_pngs', | 2183 quickPlot(tomosum, title='recon stack sum yz', path='center_slice_pngs', |
2177 save_fig=True, save_only=True) | 2184 save_fig=True, save_only=True) |
2178 | 2185 |
2179 # Create cross section profile in xz-plane | 2186 # Create cross section profile in xz-plane |
2180 tomosum = 0 | 2187 tomosum = 0 |
2181 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in | 2188 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in |
2182 self.tomo_recon_stacks] | 2189 self.tomo_recon_stacks] |
2183 msnc.quickPlot(tomosum, title='recon stack sum xz', path='center_slice_pngs', | 2190 quickPlot(tomosum, title='recon stack sum xz', path='center_slice_pngs', |
2184 save_fig=True, save_only=True) | 2191 save_fig=True, save_only=True) |
2185 | 2192 |
2186 # Create cross section profile in xy-plane | 2193 # Create cross section profile in xy-plane |
2187 num_tomo_stacks = len(stacks) | 2194 num_tomo_stacks = len(stacks) |
2188 row_bounds = self.config['find_center']['row_bounds'] | 2195 row_bounds = self.config['find_center']['row_bounds'] |
2189 if not msnc.is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): | 2196 if not is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): |
2190 msnc.illegal_value('row_bounds', row_bounds, 'config file') | 2197 illegal_value(row_bounds, 'find_center:row_bounds', 'config file') |
2191 return | 2198 return |
2192 if num_tomo_stacks == 1: | 2199 if num_tomo_stacks == 1: |
2193 low_bound = row_bounds[0] | 2200 low_bound = row_bounds[0] |
2194 else: | 2201 else: |
2195 low_bound = 0 | 2202 low_bound = 0 |
2200 axis=(1,2)) for i in range(1, num_tomo_stacks-1)]) | 2207 axis=(1,2)) for i in range(1, num_tomo_stacks-1)]) |
2201 if num_tomo_stacks > 1: | 2208 if num_tomo_stacks > 1: |
2202 tomosum = np.concatenate([tomosum, | 2209 tomosum = np.concatenate([tomosum, |
2203 np.sum(self.tomo_recon_stacks[num_tomo_stacks-1][row_bounds[0]:,:,:], | 2210 np.sum(self.tomo_recon_stacks[num_tomo_stacks-1][row_bounds[0]:,:,:], |
2204 axis=(1,2))]) | 2211 axis=(1,2))]) |
2205 msnc.quickPlot(tomosum, title='recon stack sum xy', path='center_slice_pngs', | 2212 quickPlot(tomosum, title='recon stack sum xy', path='center_slice_pngs', |
2206 save_fig=True, save_only=True) | 2213 save_fig=True, save_only=True) |
2207 | 2214 |
2208 def combineTomoStacks(self, galaxy_param=None): | 2215 def combineTomoStacks(self, galaxy_param=None): |
2209 """Combine the reconstructed tomography stacks. | 2216 """Combine the reconstructed tomography stacks. |
2210 """ | 2217 """ |
2248 logging.error('Incompatible reconstructed tomography stack dimensions') | 2255 logging.error('Incompatible reconstructed tomography stack dimensions') |
2249 return | 2256 return |
2250 | 2257 |
2251 # Get center stack boundaries | 2258 # Get center stack boundaries |
2252 row_bounds = self.config['find_center']['row_bounds'] | 2259 row_bounds = self.config['find_center']['row_bounds'] |
2253 if not msnc.is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): | 2260 if not is_index_range(row_bounds, 0, self.tomo_recon_stacks[0].shape[0]): |
2254 msnc.illegal_value('row_bounds', row_bounds, 'config file') | 2261 illegal_value(row_bounds, 'find_center:row_bounds', 'config file') |
2255 return | 2262 return |
2256 | 2263 |
2257 # Selecting x bounds (in yz-plane) | 2264 # Selecting x bounds (in yz-plane) |
2258 tomosum = 0 | 2265 tomosum = 0 |
2259 #RV FIX := | 2266 #RV FIX := |
2263 if self.galaxy_flag: | 2270 if self.galaxy_flag: |
2264 if x_bounds[0] == -1: | 2271 if x_bounds[0] == -1: |
2265 x_bounds[0] = 0 | 2272 x_bounds[0] = 0 |
2266 if x_bounds[1] == -1: | 2273 if x_bounds[1] == -1: |
2267 x_bounds[1] = tomosum.size | 2274 x_bounds[1] = tomosum.size |
2268 if not msnc.is_index_range(x_bounds, 0, tomosum.size): | 2275 if not is_index_range(x_bounds, 0, tomosum.size): |
2269 msnc.illegal_value('x_bounds', x_bounds, 'galaxy input') | 2276 illegal_value(x_bounds, 'x_bounds', 'galaxy input') |
2270 tomosum_min = tomosum.min() | 2277 tomosum_min = tomosum.min() |
2271 tomosum_max = tomosum.max() | 2278 tomosum_max = tomosum.max() |
2272 msnc.quickPlot((range(tomosum.size), tomosum), | 2279 quickPlot((range(tomosum.size), tomosum), |
2273 ([x_bounds[0], x_bounds[0]], [tomosum_min, tomosum_max], 'r-'), | 2280 ([x_bounds[0], x_bounds[0]], [tomosum_min, tomosum_max], 'r-'), |
2274 ([x_bounds[1]-1, x_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), | 2281 ([x_bounds[1]-1, x_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), |
2275 title=f'recon stack sum yz', path='combine_pngs', save_fig=True, save_only=True) | 2282 title=f'recon stack sum yz', path='combine_pngs', save_fig=True, save_only=True) |
2276 else: | 2283 else: |
2277 if combine_stacks and 'x_bounds' in combine_stacks: | 2284 if combine_stacks and 'x_bounds' in combine_stacks: |
2278 x_bounds = combine_stacks['x_bounds'] | 2285 x_bounds = combine_stacks['x_bounds'] |
2279 if not msnc.is_index_range(x_bounds, 0, tomosum.size): | 2286 if not is_index_range(x_bounds, 0, tomosum.size): |
2280 msnc.illegal_value('x_bounds', x_bounds, 'config file') | 2287 illegal_value(x_bounds, 'combine_stacks:x_bounds', 'config file') |
2281 elif not self.test_mode: | 2288 elif not self.test_mode: |
2282 msnc.quickPlot(tomosum, title='recon stack sum yz') | 2289 accept = False |
2283 if pyip.inputYesNo('\nCurrent image x-bounds: '+ | 2290 while not accept: |
2284 f'[{x_bounds[0]}, {x_bounds[1]}], use these values ([y]/n)? ', | 2291 mask, x_bounds = draw_mask_1d(tomosum, current_index_ranges=x_bounds, |
2285 blank=True) == 'no': | 2292 title='select x data range', legend='recon stack sum yz') |
2286 if pyip.inputYesNo( | 2293 while len(x_bounds) != 1: |
2287 'Do you want to change the image x-bounds ([y]/n)? ', | 2294 print('Please select exactly one continuous range') |
2288 blank=True) == 'no': | 2295 mask, x_bounds = draw_mask_1d(tomosum, title='select x data range', |
2289 x_bounds = [0, tomosum.size] | 2296 legend='recon stack sum yz') |
2290 else: | 2297 x_bounds = list(x_bounds[0]) |
2291 x_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum yz') | 2298 quickPlot(tomosum, vlines=x_bounds, title='recon stack sum yz') |
2292 else: | 2299 print(f'x_bounds = {x_bounds} (lower bound inclusive, upper bound '+ |
2293 msnc.quickPlot(tomosum, title='recon stack sum yz') | 2300 'exclusive)') |
2294 if pyip.inputYesNo('\nDo you want to change the image x-bounds (y/n)? ') == 'no': | 2301 accept = input_yesno('Accept these bounds (y/n)?', 'y') |
2302 if not accept: | |
2303 x_bounds = None | |
2304 else: | |
2305 if not input_yesno('\nDo you want to change the image x-bounds (y/n)?', 'n'): | |
2295 x_bounds = [0, tomosum.size] | 2306 x_bounds = [0, tomosum.size] |
2296 else: | 2307 else: |
2297 x_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum yz') | 2308 accept = False |
2309 while not accept: | |
2310 mask, x_bounds = draw_mask_1d(tomosum, title='select x data range', | |
2311 legend='recon stack sum yz') | |
2312 while len(x_bounds) != 1: | |
2313 print('Please select exactly one continuous range') | |
2314 mask, x_bounds = draw_mask_1d(tomosum, title='select x data range', | |
2315 legend='recon stack sum yz') | |
2316 x_bounds = list(x_bounds[0]) | |
2317 quickPlot(tomosum, vlines=x_bounds, title='recon stack sum yz') | |
2318 print(f'x_bounds = {x_bounds} (lower bound inclusive, upper bound '+ | |
2319 'exclusive)') | |
2320 accept = input_yesno('Accept these bounds (y/n)?', 'y') | |
2298 if False and self.test_mode: | 2321 if False and self.test_mode: |
2299 np.savetxt(f'{self.output_folder}/recon_stack_sum_yz.txt', | 2322 np.savetxt(f'{self.output_folder}/recon_stack_sum_yz.txt', |
2300 tomosum[x_bounds[0]:x_bounds[1]], fmt='%.6e') | 2323 tomosum[x_bounds[0]:x_bounds[1]], fmt='%.6e') |
2301 if self.save_plots_only: | 2324 if self.save_plots_only: |
2302 msnc.clearFig('recon stack sum yz') | 2325 clearPlot('recon stack sum yz') |
2303 | 2326 |
2304 # Selecting y bounds (in xz-plane) | 2327 # Selecting y bounds (in xz-plane) |
2305 tomosum = 0 | 2328 tomosum = 0 |
2306 #RV FIX := | 2329 #RV FIX := |
2307 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in | 2330 [tomosum := tomosum+np.sum(tomo_recon_stack, axis=(0,1)) for tomo_recon_stack in |
2309 if self.galaxy_flag: | 2332 if self.galaxy_flag: |
2310 if y_bounds[0] == -1: | 2333 if y_bounds[0] == -1: |
2311 y_bounds[0] = 0 | 2334 y_bounds[0] = 0 |
2312 if y_bounds[1] == -1: | 2335 if y_bounds[1] == -1: |
2313 y_bounds[1] = tomosum.size | 2336 y_bounds[1] = tomosum.size |
2314 if not msnc.is_index_range(y_bounds, 0, tomosum.size): | 2337 if not is_index_range(y_bounds, 0, tomosum.size): |
2315 msnc.illegal_value('y_bounds', y_bounds, 'galaxy input') | 2338 illegal_value(y_bounds, 'y_bounds', 'galaxy input') |
2316 tomosum_min = tomosum.min() | 2339 tomosum_min = tomosum.min() |
2317 tomosum_max = tomosum.max() | 2340 tomosum_max = tomosum.max() |
2318 msnc.quickPlot((range(tomosum.size), tomosum), | 2341 quickPlot((range(tomosum.size), tomosum), |
2319 ([y_bounds[0], y_bounds[0]], [tomosum_min, tomosum_max], 'r-'), | 2342 ([y_bounds[0], y_bounds[0]], [tomosum_min, tomosum_max], 'r-'), |
2320 ([y_bounds[1]-1, y_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), | 2343 ([y_bounds[1]-1, y_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), |
2321 title=f'recon stack sum xz', path='combine_pngs', save_fig=True, save_only=True) | 2344 title=f'recon stack sum xz', path='combine_pngs', save_fig=True, save_only=True) |
2322 else: | 2345 else: |
2323 if combine_stacks and 'y_bounds' in combine_stacks: | 2346 if combine_stacks and 'y_bounds' in combine_stacks: |
2324 y_bounds = combine_stacks['y_bounds'] | 2347 y_bounds = combine_stacks['y_bounds'] |
2325 if not msnc.is_index_range(y_bounds, 0, tomosum.size): | 2348 if not is_index_range(y_bounds, 0, tomosum.size): |
2326 msnc.illegal_value('y_bounds', y_bounds, 'config file') | 2349 illegal_value(y_bounds, 'combine_stacks:y_bounds', 'config file') |
2327 elif not self.test_mode: | 2350 elif not self.test_mode: |
2328 msnc.quickPlot(tomosum, title='recon stack sum xz') | 2351 accept = False |
2329 if pyip.inputYesNo('\nCurrent image y-bounds: '+ | 2352 while not accept: |
2330 f'[{y_bounds[0]}, {y_bounds[1]}], use these values ([y]/n)? ', | 2353 mask, y_bounds = draw_mask_1d(tomosum, current_index_ranges=y_bounds, |
2331 blank=True) == 'no': | 2354 title='select y data range', legend='recon stack sum xz') |
2332 if pyip.inputYesNo( | 2355 while len(y_bounds) != 1: |
2333 'Do you want to change the image y-bounds ([y]/n)? ', | 2356 print('Please select exactly one continuous range') |
2334 blank=True) == 'no': | 2357 mask, y_bounds = draw_mask_1d(tomosum, title='select y data range', |
2335 y_bounds = [0, tomosum.size] | 2358 legend='recon stack sum xz') |
2336 else: | 2359 y_bounds = list(y_bounds[0]) |
2337 y_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum yz') | 2360 quickPlot(tomosum, vlines=y_bounds, title='recon stack sum xz') |
2338 else: | 2361 print(f'y_bounds = {y_bounds} (lower bound inclusive, upper bound '+ |
2339 msnc.quickPlot(tomosum, title='recon stack sum xz') | 2362 'exclusive)') |
2340 if pyip.inputYesNo('\nDo you want to change the image y-bounds (y/n)? ') == 'no': | 2363 accept = input_yesno('Accept these bounds (y/n)?', 'y') |
2364 if not accept: | |
2365 y_bounds = None | |
2366 else: | |
2367 if not input_yesno('\nDo you want to change the image y-bounds (y/n)?', 'n'): | |
2341 y_bounds = [0, tomosum.size] | 2368 y_bounds = [0, tomosum.size] |
2342 else: | 2369 else: |
2343 y_bounds = msnc.selectArrayBounds(tomosum, title='recon stack sum xz') | 2370 accept = False |
2371 while not accept: | |
2372 mask, y_bounds = draw_mask_1d(tomosum, title='select y data range', | |
2373 legend='recon stack sum xz') | |
2374 while len(y_bounds) != 1: | |
2375 print('Please select exactly one continuous range') | |
2376 mask, y_bounds = draw_mask_1d(tomosum, title='select y data range', | |
2377 legend='recon stack sum xz') | |
2378 y_bounds = list(y_bounds[0]) | |
2379 quickPlot(tomosum, vlines=y_bounds, title='recon stack sum xz') | |
2380 print(f'y_bounds = {y_bounds} (lower bound inclusive, upper bound '+ | |
2381 'exclusive)') | |
2382 accept = input_yesno('Accept these bounds (y/n)?', 'y') | |
2344 if False and self.test_mode: | 2383 if False and self.test_mode: |
2345 np.savetxt(f'{self.output_folder}/recon_stack_sum_xz.txt', | 2384 np.savetxt(f'{self.output_folder}/recon_stack_sum_xz.txt', |
2346 tomosum[y_bounds[0]:y_bounds[1]], fmt='%.6e') | 2385 tomosum[y_bounds[0]:y_bounds[1]], fmt='%.6e') |
2347 if self.save_plots_only: | 2386 if self.save_plots_only: |
2348 msnc.clearFig('recon stack sum xz') | 2387 clearPlot('recon stack sum xz') |
2349 | 2388 |
2350 # Combine reconstructed tomography stacks | 2389 # Combine reconstructed tomography stacks |
2351 logging.info(f'Combining reconstructed stacks ...') | 2390 logging.info(f'Combining reconstructed stacks ...') |
2352 t0 = time() | 2391 t0 = time() |
2353 num_tomo_stacks = len(stacks) | 2392 num_tomo_stacks = len(stacks) |
2376 filename = 'recon combined sum xy' | 2415 filename = 'recon combined sum xy' |
2377 if zoom_perc is None or zoom_perc == 100: | 2416 if zoom_perc is None or zoom_perc == 100: |
2378 filename += ' fullres.dat' | 2417 filename += ' fullres.dat' |
2379 else: | 2418 else: |
2380 filename += f' {zoom_perc}p.dat' | 2419 filename += f' {zoom_perc}p.dat' |
2381 msnc.quickPlot(tomosum, title='recon combined sum xy', | 2420 quickPlot(tomosum, title='recon combined sum xy', |
2382 path=self.output_folder, save_fig=self.save_plots, | 2421 path=self.output_folder, save_fig=self.save_plots, |
2383 save_only=self.save_plots_only) | 2422 save_only=self.save_plots_only) |
2384 if False: | 2423 if False: |
2385 np.savetxt(f'{self.output_folder}/recon_combined_sum_xy.txt', | 2424 np.savetxt(f'{self.output_folder}/recon_combined_sum_xy.txt', |
2386 tomosum, fmt='%.6e') | 2425 tomosum, fmt='%.6e') |
2402 if self.galaxy_flag: | 2441 if self.galaxy_flag: |
2403 if z_bounds[0] == -1: | 2442 if z_bounds[0] == -1: |
2404 z_bounds[0] = 0 | 2443 z_bounds[0] = 0 |
2405 if z_bounds[1] == -1: | 2444 if z_bounds[1] == -1: |
2406 z_bounds[1] = tomosum.size | 2445 z_bounds[1] = tomosum.size |
2407 if not msnc.is_index_range(z_bounds, 0, tomosum.size): | 2446 if not is_index_range(z_bounds, 0, tomosum.size): |
2408 msnc.illegal_value('z_bounds', z_bounds, 'galaxy input') | 2447 illegal_value(z_bounds, 'z_bounds', 'galaxy input') |
2409 tomosum_min = tomosum.min() | 2448 tomosum_min = tomosum.min() |
2410 tomosum_max = tomosum.max() | 2449 tomosum_max = tomosum.max() |
2411 msnc.quickPlot((range(tomosum.size), tomosum), | 2450 quickPlot((range(tomosum.size), tomosum), |
2412 ([z_bounds[0], z_bounds[0]], [tomosum_min, tomosum_max], 'r-'), | 2451 ([z_bounds[0], z_bounds[0]], [tomosum_min, tomosum_max], 'r-'), |
2413 ([z_bounds[1]-1, z_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), | 2452 ([z_bounds[1]-1, z_bounds[1]-1], [tomosum_min, tomosum_max], 'r-'), |
2414 title=f'recon stack sum xy', path='combine_pngs', save_fig=True, save_only=True) | 2453 title=f'recon stack sum xy', path='combine_pngs', save_fig=True, save_only=True) |
2415 else: | 2454 else: |
2416 msnc.quickPlot(tomosum, title='recon combined sum xy') | 2455 if not input_yesno('\nDo you want to change the image z-bounds (y/n)?', 'n'): |
2417 if pyip.inputYesNo( | |
2418 '\nDo you want to change the image z-bounds (y/[n])? ', | |
2419 blank=True) != 'yes': | |
2420 z_bounds = [0, tomosum.size] | 2456 z_bounds = [0, tomosum.size] |
2421 else: | 2457 else: |
2422 z_bounds = msnc.selectArrayBounds(tomosum, title='recon combined sum xy') | 2458 accept = False |
2459 while not accept: | |
2460 mask, z_bounds = draw_mask_1d(tomosum, title='select z data range', | |
2461 legend='recon stack sum xy') | |
2462 while len(z_bounds) != 1: | |
2463 print('Please select exactly one continuous range') | |
2464 mask, z_bounds = draw_mask_1d(tomosum, title='select z data range', | |
2465 legend='recon stack sum xy') | |
2466 z_bounds = list(z_bounds[0]) | |
2467 quickPlot(tomosum, vlines=z_bounds, title='recon stack sum xy') | |
2468 print(f'z_bounds = {z_bounds} (lower bound inclusive, upper bound exclusive)') | |
2469 accept = input_yesno('Accept these bounds (y/n)?', 'y') | |
2423 if self.save_plots_only: | 2470 if self.save_plots_only: |
2424 msnc.clearFig('recon combined sum xy') | 2471 clearPlot('recon combined sum xy') |
2425 if z_bounds[0] != 0 or z_bounds[1] != tomosum.size: | 2472 if z_bounds[0] != 0 or z_bounds[1] != tomosum.size: |
2426 tomo_recon_combined = tomo_recon_combined[z_bounds[0]:z_bounds[1],:,:] | 2473 tomo_recon_combined = tomo_recon_combined[z_bounds[0]:z_bounds[1],:,:] |
2427 | 2474 |
2428 # Plot center slices | 2475 # Plot center slices |
2429 if self.galaxy_flag: | 2476 if self.galaxy_flag: |
2432 save_only = True | 2479 save_only = True |
2433 else: | 2480 else: |
2434 path = self.output_folder | 2481 path = self.output_folder |
2435 save_fig = self.save_plots | 2482 save_fig = self.save_plots |
2436 save_only = self.save_plots_only | 2483 save_only = self.save_plots_only |
2437 msnc.quickImshow(tomo_recon_combined[int(tomo_recon_combined.shape[0]/2),:,:], | 2484 quickImshow(tomo_recon_combined[int(tomo_recon_combined.shape[0]/2),:,:], |
2438 title=f'recon combined xslice{int(tomo_recon_combined.shape[0]/2)}', | 2485 title=f'recon combined xslice{int(tomo_recon_combined.shape[0]/2)}', |
2439 path=path, save_fig=save_fig, save_only=save_only) | 2486 path=path, save_fig=save_fig, save_only=save_only) |
2440 msnc.quickImshow(tomo_recon_combined[:,int(tomo_recon_combined.shape[1]/2),:], | 2487 quickImshow(tomo_recon_combined[:,int(tomo_recon_combined.shape[1]/2),:], |
2441 title=f'recon combined yslice{int(tomo_recon_combined.shape[1]/2)}', | 2488 title=f'recon combined yslice{int(tomo_recon_combined.shape[1]/2)}', |
2442 path=path, save_fig=save_fig, save_only=save_only) | 2489 path=path, save_fig=save_fig, save_only=save_only) |
2443 msnc.quickImshow(tomo_recon_combined[:,:,int(tomo_recon_combined.shape[2]/2)], | 2490 quickImshow(tomo_recon_combined[:,:,int(tomo_recon_combined.shape[2]/2)], |
2444 title=f'recon combined zslice{int(tomo_recon_combined.shape[2]/2)}', | 2491 title=f'recon combined zslice{int(tomo_recon_combined.shape[2]/2)}', |
2445 path=path, save_fig=save_fig, save_only=save_only) | 2492 path=path, save_fig=save_fig, save_only=save_only) |
2446 | 2493 |
2447 # Save combined reconstructed tomography stack to file | 2494 # Save combined reconstructed tomography stack to file |
2448 if self.galaxy_flag: | 2495 if self.galaxy_flag: |