Mercurial > repos > rv43 > tomo
comparison workflow/run_tomo.py @ 71:1cf15b61cd83 draft
planemo upload for repository https://github.com/rolfverberg/galaxytools commit 366e516aef0735af2998c6ff3af037181c8d5213
author | rv43 |
---|---|
date | Mon, 20 Mar 2023 13:56:57 +0000 |
parents | fba792d5f83b |
children | d5e1d4ea2b7e |
comparison
equal
deleted
inserted
replaced
70:97c4e2cbbad9 | 71:1cf15b61cd83 |
---|---|
30 import tomopy | 30 import tomopy |
31 except: | 31 except: |
32 pass | 32 pass |
33 from yaml import safe_load, safe_dump | 33 from yaml import safe_load, safe_dump |
34 | 34 |
35 from msnctools.fit import Fit | 35 try: |
36 from msnctools.general import illegal_value, is_int, is_int_pair, is_num, is_index_range, \ | 36 from msnctools.fit import Fit |
37 input_int, input_num, input_yesno, input_menu, draw_mask_1d, select_image_bounds, \ | 37 except: |
38 select_one_image_bound, clear_imshow, quick_imshow, clear_plot, quick_plot | 38 from fit import Fit |
39 | 39 try: |
40 from workflow.models import import_scanparser, FlatField, TomoField, TomoWorkflow | 40 from msnctools.general import illegal_value, is_int, is_int_pair, is_num, is_index_range, \ |
41 from workflow.__version__ import __version__ | 41 input_int, input_num, input_yesno, input_menu, draw_mask_1d, select_image_bounds, \ |
42 select_one_image_bound, clear_imshow, quick_imshow, clear_plot, quick_plot | |
43 except: | |
44 from general import illegal_value, is_int, is_int_pair, is_num, is_index_range, \ | |
45 input_int, input_num, input_yesno, input_menu, draw_mask_1d, select_image_bounds, \ | |
46 select_one_image_bound, clear_imshow, quick_imshow, clear_plot, quick_plot | |
47 | |
48 try: | |
49 from workflow.models import import_scanparser, FlatField, TomoField, TomoWorkflow | |
50 from workflow.__version__ import __version__ | |
51 except: | |
52 pass | |
42 | 53 |
43 num_core_tomopy_limit = 24 | 54 num_core_tomopy_limit = 24 |
44 | 55 |
45 def nxcopy(nxobject:NXobject, exclude_nxpaths:list[str]=[], nxpath_prefix:str='') -> NXobject: | 56 def nxcopy(nxobject:NXobject, exclude_nxpaths:list[str]=[], nxpath_prefix:str='') -> NXobject: |
46 '''Function that returns a copy of a nexus object, optionally exluding certain child items. | 57 '''Function that returns a copy of a nexus object, optionally exluding certain child items. |
264 nxentry.data.makelink(nxentry.reduced_data.rotation_angle, name='rotation_angle') | 275 nxentry.data.makelink(nxentry.reduced_data.rotation_angle, name='rotation_angle') |
265 nxentry.data.attrs['signal'] = 'reduced_data' | 276 nxentry.data.attrs['signal'] = 'reduced_data' |
266 | 277 |
267 return(nxroot) | 278 return(nxroot) |
268 | 279 |
269 def find_centers(self, nxroot, center_rows=None): | 280 def find_centers(self, nxroot, center_rows=None, center_stack_index=None): |
270 """Find the calibrated center axis info | 281 """Find the calibrated center axis info |
271 """ | 282 """ |
272 logger.info('Find the calibrated center axis info') | 283 logger.info('Find the calibrated center axis info') |
273 | 284 |
274 if not isinstance(nxroot, NXroot): | 285 if not isinstance(nxroot, NXroot): |
275 raise ValueError(f'Invalid parameter nxroot ({nxroot})') | 286 raise ValueError(f'Invalid parameter nxroot ({nxroot})') |
276 nxentry = nxroot[nxroot.attrs['default']] | 287 nxentry = nxroot[nxroot.attrs['default']] |
277 if not isinstance(nxentry, NXentry): | 288 if not isinstance(nxentry, NXentry): |
278 raise ValueError(f'Invalid nxentry ({nxentry})') | 289 raise ValueError(f'Invalid nxentry ({nxentry})') |
279 if self.galaxy_flag: | 290 if self.galaxy_flag: |
280 if center_rows is None: | 291 if center_rows is not None: |
281 raise ValueError(f'Missing parameter center_rows ({center_rows})') | 292 center_rows = tuple(center_rows) |
282 if not is_int_pair(center_rows): | 293 if not is_int_pair(center_rows): |
283 raise ValueError(f'Invalid parameter center_rows ({center_rows})') | 294 raise ValueError(f'Invalid parameter center_rows ({center_rows})') |
284 elif center_rows is not None: | 295 elif center_rows is not None: |
285 logging.warning(f'Ignoring parameter center_rows ({center_rows})') | 296 logger.warning(f'Ignoring parameter center_rows ({center_rows})') |
286 center_rows = None | 297 center_rows = None |
298 if self.galaxy_flag: | |
299 if center_stack_index is not None and not is_int(center_stack_index, ge=0): | |
300 raise ValueError(f'Invalid parameter center_stack_index ({center_stack_index})') | |
301 elif center_stack_index is not None: | |
302 logger.warning(f'Ignoring parameter center_stack_index ({center_stack_index})') | |
303 center_stack_index = None | |
287 | 304 |
288 # Create plot galaxy path directory and path if needed | 305 # Create plot galaxy path directory and path if needed |
289 if self.galaxy_flag: | 306 if self.galaxy_flag: |
290 if not os_path.exists('tomo_find_centers_plots'): | 307 if not os_path.exists('tomo_find_centers_plots'): |
291 mkdir('tomo_find_centers_plots') | 308 mkdir('tomo_find_centers_plots') |
296 # Check if reduced data is available | 313 # Check if reduced data is available |
297 if ('reduced_data' not in nxentry or 'reduced_data' not in nxentry.data): | 314 if ('reduced_data' not in nxentry or 'reduced_data' not in nxentry.data): |
298 raise KeyError(f'Unable to find valid reduced data in {nxentry}.') | 315 raise KeyError(f'Unable to find valid reduced data in {nxentry}.') |
299 | 316 |
300 # Select the image stack to calibrate the center axis | 317 # Select the image stack to calibrate the center axis |
301 # reduced data axes order: stack,row,theta,column | 318 # reduced data axes order: stack,theta,row,column |
302 # Note: Nexus cannot follow a link if the data it points to is too big, | 319 # Note: Nexus cannot follow a link if the data it points to is too big, |
303 # so get the data from the actual place, not from nxentry.data | 320 # so get the data from the actual place, not from nxentry.data |
304 num_tomo_stacks = nxentry.reduced_data.data.tomo_fields.shape[0] | 321 tomo_fields_shape = nxentry.reduced_data.data.tomo_fields.shape |
322 if len(tomo_fields_shape) != 4 or any(True for dim in tomo_fields_shape if not dim): | |
323 raise KeyError('Unable to load the required reduced tomography stack') | |
324 num_tomo_stacks = tomo_fields_shape[0] | |
305 if num_tomo_stacks == 1: | 325 if num_tomo_stacks == 1: |
306 center_stack_index = 0 | 326 center_stack_index = 0 |
307 center_stack = np.asarray(nxentry.reduced_data.data.tomo_fields[0]) | |
308 if not center_stack.size: | |
309 raise KeyError('Unable to load the required reduced tomography stack') | |
310 default = 'n' | 327 default = 'n' |
311 else: | 328 else: |
312 if self.test_mode: | 329 if self.test_mode: |
313 center_stack_index = self.test_config['center_stack_index']-1 # make offset 0 | 330 center_stack_index = self.test_config['center_stack_index']-1 # make offset 0 |
331 elif self.galaxy_flag: | |
332 if center_stack_index is None: | |
333 center_stack_index = int(num_tomo_stacks/2) | |
334 if center_stack_index >= num_tomo_stacks: | |
335 raise ValueError(f'Invalid parameter center_stack_index ({center_stack_index})') | |
314 else: | 336 else: |
315 center_stack_index = input_int('\nEnter tomography stack index to calibrate the ' | 337 center_stack_index = input_int('\nEnter tomography stack index to calibrate the ' |
316 'center axis', ge=0, le=num_tomo_stacks-1, default=int(num_tomo_stacks/2)) | 338 'center axis', ge=1, le=num_tomo_stacks, default=int(1+num_tomo_stacks/2)) |
317 center_stack = \ | 339 center_stack_index -= 1 |
318 np.asarray(nxentry.reduced_data.data.tomo_fields[center_stack_index]) | |
319 if not center_stack.size: | |
320 raise KeyError('Unable to load the required reduced tomography stack') | |
321 default = 'y' | 340 default = 'y' |
322 | 341 |
323 # Get thetas (in degrees) | 342 # Get thetas (in degrees) |
324 thetas = np.asarray(nxentry.reduced_data.rotation_angle) | 343 thetas = np.asarray(nxentry.reduced_data.rotation_angle) |
325 | 344 |
329 nxentry.reduced_data.attrs['zoom_perc']) | 348 nxentry.reduced_data.attrs['zoom_perc']) |
330 else: | 349 else: |
331 eff_pixel_size = nxentry.instrument.detector.x_pixel_size | 350 eff_pixel_size = nxentry.instrument.detector.x_pixel_size |
332 | 351 |
333 # Get cross sectional diameter | 352 # Get cross sectional diameter |
334 cross_sectional_dim = center_stack.shape[2]*eff_pixel_size | 353 cross_sectional_dim = tomo_fields_shape[3]*eff_pixel_size |
335 logger.debug(f'cross_sectional_dim = {cross_sectional_dim}') | 354 logger.debug(f'cross_sectional_dim = {cross_sectional_dim}') |
336 | 355 |
337 # Determine center offset at sample row boundaries | 356 # Determine center offset at sample row boundaries |
338 logger.info('Determine center offset at sample row boundaries') | 357 logger.info('Determine center offset at sample row boundaries') |
339 | 358 |
340 # Lower row center | 359 # Lower row center |
341 # center_stack order: row,theta,column | |
342 if self.test_mode: | 360 if self.test_mode: |
343 lower_row = self.test_config['lower_row'] | 361 lower_row = self.test_config['lower_row'] |
344 elif self.galaxy_flag: | 362 elif self.galaxy_flag: |
345 lower_row = min(center_rows) | 363 if center_rows is None: |
346 if not 0 <= lower_row < center_stack.shape[0]-1: | 364 lower_row = 0 |
347 raise ValueError(f'Invalid parameter center_rows ({center_rows})') | 365 else: |
348 else: | 366 lower_row = min(center_rows) |
349 lower_row = select_one_image_bound(center_stack[:,0,:], 0, bound=0, | 367 if not 0 <= lower_row < tomo_fields_shape[2]-1: |
368 raise ValueError(f'Invalid parameter center_rows ({center_rows})') | |
369 else: | |
370 lower_row = select_one_image_bound( | |
371 nxentry.reduced_data.data.tomo_fields[center_stack_index,0,:,:], 0, bound=0, | |
350 title=f'theta={round(thetas[0], 2)+0}', | 372 title=f'theta={round(thetas[0], 2)+0}', |
351 bound_name='row index to find lower center', default=default) | 373 bound_name='row index to find lower center', default=default, raise_error=True) |
352 lower_center_offset = self._find_center_one_plane(center_stack[lower_row,:,:], lower_row, | 374 logger.debug('Finding center...') |
353 thetas, eff_pixel_size, cross_sectional_dim, path=path, num_core=self.num_core) | 375 t0 = time() |
376 lower_center_offset = self._find_center_one_plane( | |
377 #np.asarray(nxentry.reduced_data.data.tomo_fields[center_stack_index,:,lower_row,:]), | |
378 nxentry.reduced_data.data.tomo_fields[center_stack_index,:,lower_row,:], | |
379 lower_row, thetas, eff_pixel_size, cross_sectional_dim, path=path, | |
380 num_core=self.num_core) | |
381 logger.debug(f'... done in {time()-t0:.2f} seconds') | |
354 logger.debug(f'lower_row = {lower_row:.2f}') | 382 logger.debug(f'lower_row = {lower_row:.2f}') |
355 logger.debug(f'lower_center_offset = {lower_center_offset:.2f}') | 383 logger.debug(f'lower_center_offset = {lower_center_offset:.2f}') |
356 | 384 |
357 # Upper row center | 385 # Upper row center |
358 if self.test_mode: | 386 if self.test_mode: |
359 upper_row = self.test_config['upper_row'] | 387 upper_row = self.test_config['upper_row'] |
360 elif self.galaxy_flag: | 388 elif self.galaxy_flag: |
361 upper_row = max(center_rows) | 389 if center_rows is None: |
362 if not lower_row < upper_row < center_stack.shape[0]: | 390 upper_row = tomo_fields_shape[2]-1 |
363 raise ValueError(f'Invalid parameter center_rows ({center_rows})') | 391 else: |
364 else: | 392 upper_row = max(center_rows) |
365 upper_row = select_one_image_bound(center_stack[:,0,:], 0, | 393 if not lower_row < upper_row < tomo_fields_shape[2]: |
366 bound=center_stack.shape[0]-1, title=f'theta={round(thetas[0], 2)+0}', | 394 raise ValueError(f'Invalid parameter center_rows ({center_rows})') |
367 bound_name='row index to find upper center', default=default) | 395 else: |
368 upper_center_offset = self._find_center_one_plane(center_stack[upper_row,:,:], upper_row, | 396 upper_row = select_one_image_bound( |
369 thetas, eff_pixel_size, cross_sectional_dim, path=path, num_core=self.num_core) | 397 nxentry.reduced_data.data.tomo_fields[center_stack_index,0,:,:], 0, |
398 bound=tomo_fields_shape[2]-1, title=f'theta={round(thetas[0], 2)+0}', | |
399 bound_name='row index to find upper center', default=default, raise_error=True) | |
400 logger.debug('Finding center...') | |
401 t0 = time() | |
402 upper_center_offset = self._find_center_one_plane( | |
403 #np.asarray(nxentry.reduced_data.data.tomo_fields[center_stack_index,:,upper_row,:]), | |
404 nxentry.reduced_data.data.tomo_fields[center_stack_index,:,upper_row,:], | |
405 upper_row, thetas, eff_pixel_size, cross_sectional_dim, path=path, | |
406 num_core=self.num_core) | |
407 logger.debug(f'... done in {time()-t0:.2f} seconds') | |
370 logger.debug(f'upper_row = {upper_row:.2f}') | 408 logger.debug(f'upper_row = {upper_row:.2f}') |
371 logger.debug(f'upper_center_offset = {upper_center_offset:.2f}') | 409 logger.debug(f'upper_center_offset = {upper_center_offset:.2f}') |
372 del center_stack | |
373 | 410 |
374 center_config = {'lower_row': lower_row, 'lower_center_offset': lower_center_offset, | 411 center_config = {'lower_row': lower_row, 'lower_center_offset': lower_center_offset, |
375 'upper_row': upper_row, 'upper_center_offset': upper_center_offset} | 412 'upper_row': upper_row, 'upper_center_offset': upper_center_offset} |
376 if num_tomo_stacks > 1: | 413 if num_tomo_stacks > 1: |
377 center_config['center_stack_index'] = center_stack_index+1 # save as offset 1 | 414 center_config['center_stack_index'] = center_stack_index+1 # save as offset 1 |
407 # Check if reduced data is available | 444 # Check if reduced data is available |
408 if ('reduced_data' not in nxentry or 'reduced_data' not in nxentry.data): | 445 if ('reduced_data' not in nxentry or 'reduced_data' not in nxentry.data): |
409 raise KeyError(f'Unable to find valid reduced data in {nxentry}.') | 446 raise KeyError(f'Unable to find valid reduced data in {nxentry}.') |
410 | 447 |
411 # Create an NXprocess to store image reconstruction (meta)data | 448 # Create an NXprocess to store image reconstruction (meta)data |
412 # if 'reconstructed_data' in nxentry: | |
413 # logger.warning(f'Existing reconstructed data in {nxentry} will be overwritten.') | |
414 # del nxentry['reconstructed_data'] | |
415 # if 'data' in nxentry and 'reconstructed_data' in nxentry.data: | |
416 # del nxentry.data['reconstructed_data'] | |
417 nxprocess = NXprocess() | 449 nxprocess = NXprocess() |
418 | 450 |
419 # Get rotation axis rows and centers | 451 # Get rotation axis rows and centers |
420 lower_row = center_info.get('lower_row') | 452 lower_row = center_info.get('lower_row') |
421 lower_center_offset = center_info.get('lower_center_offset') | 453 lower_center_offset = center_info.get('lower_center_offset') |
428 | 460 |
429 # Get thetas (in degrees) | 461 # Get thetas (in degrees) |
430 thetas = np.asarray(nxentry.reduced_data.rotation_angle) | 462 thetas = np.asarray(nxentry.reduced_data.rotation_angle) |
431 | 463 |
432 # Reconstruct tomography data | 464 # Reconstruct tomography data |
433 # reduced data axes order: stack,row,theta,column | 465 # reduced data axes order: stack,theta,row,column |
434 # reconstructed data order in each stack: row/z,x,y | 466 # reconstructed data order in each stack: row/z,x,y |
435 # Note: Nexus cannot follow a link if the data it points to is too big, | 467 # Note: Nexus cannot follow a link if the data it points to is too big, |
436 # so get the data from the actual place, not from nxentry.data | 468 # so get the data from the actual place, not from nxentry.data |
437 if 'zoom_perc' in nxentry.reduced_data: | 469 if 'zoom_perc' in nxentry.reduced_data: |
438 res_title = f'{nxentry.reduced_data.attrs["zoom_perc"]}p' | 470 res_title = f'{nxentry.reduced_data.attrs["zoom_perc"]}p' |
440 res_title = 'fullres' | 472 res_title = 'fullres' |
441 load_error = False | 473 load_error = False |
442 num_tomo_stacks = nxentry.reduced_data.data.tomo_fields.shape[0] | 474 num_tomo_stacks = nxentry.reduced_data.data.tomo_fields.shape[0] |
443 tomo_recon_stacks = num_tomo_stacks*[np.array([])] | 475 tomo_recon_stacks = num_tomo_stacks*[np.array([])] |
444 for i in range(num_tomo_stacks): | 476 for i in range(num_tomo_stacks): |
477 # Convert reduced data stack from theta,row,column to row,theta,column | |
478 logger.debug(f'Reading reduced data stack {i+1}...') | |
479 t0 = time() | |
445 tomo_stack = np.asarray(nxentry.reduced_data.data.tomo_fields[i]) | 480 tomo_stack = np.asarray(nxentry.reduced_data.data.tomo_fields[i]) |
446 if not tomo_stack.size: | 481 logger.debug(f'... done in {time()-t0:.2f} seconds') |
447 raise KeyError(f'Unable to load tomography stack {i} for reconstruction') | 482 if len(tomo_stack.shape) != 3 or any(True for dim in tomo_stack.shape if not dim): |
483 raise ValueError(f'Unable to load tomography stack {i+1} for reconstruction') | |
484 tomo_stack = np.swapaxes(tomo_stack, 0, 1) | |
485 assert(len(thetas) == tomo_stack.shape[1]) | |
448 assert(0 <= lower_row < upper_row < tomo_stack.shape[0]) | 486 assert(0 <= lower_row < upper_row < tomo_stack.shape[0]) |
449 center_offsets = [lower_center_offset-lower_row*center_slope, | 487 center_offsets = [lower_center_offset-lower_row*center_slope, |
450 upper_center_offset+(tomo_stack.shape[0]-1-upper_row)*center_slope] | 488 upper_center_offset+(tomo_stack.shape[0]-1-upper_row)*center_slope] |
451 t0 = time() | 489 t0 = time() |
452 logger.debug(f'Running _reconstruct_one_tomo_stack on {self.num_core} cores ...') | 490 logger.debug(f'Running _reconstruct_one_tomo_stack on {self.num_core} cores ...') |
453 tomo_recon_stack = self._reconstruct_one_tomo_stack(tomo_stack, thetas, | 491 tomo_recon_stack = self._reconstruct_one_tomo_stack(tomo_stack, thetas, |
454 center_offsets=center_offsets, num_core=self.num_core, algorithm='gridrec') | 492 center_offsets=center_offsets, num_core=self.num_core, algorithm='gridrec') |
455 logger.debug(f'... done in {time()-t0:.2f} seconds') | 493 logger.debug(f'... done in {time()-t0:.2f} seconds') |
456 logger.info(f'Reconstruction of stack {i} took {time()-t0:.2f} seconds') | 494 logger.info(f'Reconstruction of stack {i+1} took {time()-t0:.2f} seconds') |
457 | 495 |
458 # Combine stacks | 496 # Combine stacks |
459 tomo_recon_stacks[i] = tomo_recon_stack | 497 tomo_recon_stacks[i] = tomo_recon_stack |
460 | 498 |
461 # Resize the reconstructed tomography data | 499 # Resize the reconstructed tomography data |
495 | 533 |
496 # Plot a few reconstructed image slices | 534 # Plot a few reconstructed image slices |
497 if num_tomo_stacks == 1: | 535 if num_tomo_stacks == 1: |
498 basetitle = 'recon' | 536 basetitle = 'recon' |
499 else: | 537 else: |
500 basetitle = f'recon stack {i}' | 538 basetitle = f'recon stack {i+1}' |
501 for i, stack in enumerate(tomo_recon_stacks): | 539 for i, stack in enumerate(tomo_recon_stacks): |
502 title = f'{basetitle} {res_title} xslice{x_slice}' | 540 title = f'{basetitle} {res_title} xslice{x_slice}' |
503 quick_imshow(stack[z_range[0]:z_range[1],x_slice,y_range[0]:y_range[1]], | 541 quick_imshow(stack[z_range[0]:z_range[1],x_slice,y_range[0]:y_range[1]], |
504 title=title, path=path, save_fig=self.save_figs, save_only=self.save_only, | 542 title=title, path=path, save_fig=self.save_figs, save_only=self.save_only, |
505 block=self.block) | 543 block=self.block) |
548 nxentry_copy.data.makelink(nxprocess.data.reconstructed_data, name='reconstructed_data') | 586 nxentry_copy.data.makelink(nxprocess.data.reconstructed_data, name='reconstructed_data') |
549 nxentry_copy.data.attrs['signal'] = 'reconstructed_data' | 587 nxentry_copy.data.attrs['signal'] = 'reconstructed_data' |
550 | 588 |
551 return(nxroot_copy) | 589 return(nxroot_copy) |
552 | 590 |
553 def combine_data(self, nxroot): | 591 def combine_data(self, nxroot, x_bounds=None, y_bounds=None): |
554 """Combine the reconstructed tomography stacks. | 592 """Combine the reconstructed tomography stacks. |
555 """ | 593 """ |
556 logger.info('Combine the reconstructed tomography stacks') | 594 logger.info('Combine the reconstructed tomography stacks') |
557 | 595 |
558 if not isinstance(nxroot, NXroot): | 596 if not isinstance(nxroot, NXroot): |
572 # Check if reconstructed image data is available | 610 # Check if reconstructed image data is available |
573 if ('reconstructed_data' not in nxentry or 'reconstructed_data' not in nxentry.data): | 611 if ('reconstructed_data' not in nxentry or 'reconstructed_data' not in nxentry.data): |
574 raise KeyError(f'Unable to find valid reconstructed image data in {nxentry}.') | 612 raise KeyError(f'Unable to find valid reconstructed image data in {nxentry}.') |
575 | 613 |
576 # Create an NXprocess to store combined image reconstruction (meta)data | 614 # Create an NXprocess to store combined image reconstruction (meta)data |
577 # if 'combined_data' in nxentry: | |
578 # logger.warning(f'Existing combined data in {nxentry} will be overwritten.') | |
579 # del nxentry['combined_data'] | |
580 # if 'data' in nxentry 'combined_data' in nxentry.data: | |
581 # del nxentry.data['combined_data'] | |
582 nxprocess = NXprocess() | 615 nxprocess = NXprocess() |
583 | 616 |
584 # Get the reconstructed data | 617 # Get the reconstructed data |
585 # reconstructed data order: stack,row(z),x,y | 618 # reconstructed data order: stack,row(z),x,y |
586 # Note: Nexus cannot follow a link if the data it points to is too big, | 619 # Note: Nexus cannot follow a link if the data it points to is too big, |
587 # so get the data from the actual place, not from nxentry.data | 620 # so get the data from the actual place, not from nxentry.data |
588 tomo_recon_stacks = np.asarray(nxentry.reconstructed_data.data.reconstructed_data) | 621 num_tomo_stacks = nxentry.reconstructed_data.data.reconstructed_data.shape[0] |
589 num_tomo_stacks = tomo_recon_stacks.shape[0] | |
590 if num_tomo_stacks == 1: | 622 if num_tomo_stacks == 1: |
591 return(nxroot) | 623 logger.info('Only one stack available: leaving combine_data') |
624 return(None) | |
625 | |
626 # Combine the reconstructed stacks | |
627 # (load one stack at a time to reduce risk of hitting Nexus data access limit) | |
592 t0 = time() | 628 t0 = time() |
593 logger.debug(f'Combining the reconstructed stacks ...') | 629 logger.debug(f'Combining the reconstructed stacks ...') |
594 tomo_recon_combined = tomo_recon_stacks[0,:,:,:] | 630 tomo_recon_combined = np.asarray(nxentry.reconstructed_data.data.reconstructed_data[0]) |
595 if num_tomo_stacks > 2: | 631 if num_tomo_stacks > 2: |
596 tomo_recon_combined = np.concatenate([tomo_recon_combined]+ | 632 tomo_recon_combined = np.concatenate([tomo_recon_combined]+ |
597 [tomo_recon_stacks[i,:,:,:] for i in range(1, num_tomo_stacks-1)]) | 633 [nxentry.reconstructed_data.data.reconstructed_data[i] |
634 for i in range(1, num_tomo_stacks-1)]) | |
598 if num_tomo_stacks > 1: | 635 if num_tomo_stacks > 1: |
599 tomo_recon_combined = np.concatenate([tomo_recon_combined]+ | 636 tomo_recon_combined = np.concatenate([tomo_recon_combined]+ |
600 [tomo_recon_stacks[num_tomo_stacks-1,:,:,:]]) | 637 [nxentry.reconstructed_data.data.reconstructed_data[num_tomo_stacks-1]]) |
601 logger.debug(f'... done in {time()-t0:.2f} seconds') | 638 logger.debug(f'... done in {time()-t0:.2f} seconds') |
602 logger.info(f'Combining the reconstructed stacks took {time()-t0:.2f} seconds') | 639 logger.info(f'Combining the reconstructed stacks took {time()-t0:.2f} seconds') |
603 | 640 |
604 # Resize the combined tomography data set | 641 # Resize the combined tomography data stacks |
605 # combined data order: row/z,x,y | 642 # combined data order: row/z,x,y |
606 if self.test_mode: | 643 if self.test_mode: |
607 x_bounds = None | 644 x_bounds = None |
608 y_bounds = None | 645 y_bounds = None |
609 z_bounds = self.test_config.get('z_bounds') | 646 z_bounds = self.test_config.get('z_bounds') |
610 elif self.galaxy_flag: | 647 elif self.galaxy_flag: |
611 exit('TODO') | |
612 if x_bounds is not None and not is_int_pair(x_bounds, ge=0, | 648 if x_bounds is not None and not is_int_pair(x_bounds, ge=0, |
613 lt=tomo_recon_stacks[0].shape[1]): | 649 lt=tomo_recon_stacks[0].shape[1]): |
614 raise ValueError(f'Invalid parameter x_bounds ({x_bounds})') | 650 raise ValueError(f'Invalid parameter x_bounds ({x_bounds})') |
615 if y_bounds is not None and not is_int_pair(y_bounds, ge=0, | 651 if y_bounds is not None and not is_int_pair(y_bounds, ge=0, |
616 lt=tomo_recon_stacks[0].shape[1]): | 652 lt=tomo_recon_stacks[0].shape[1]): |
697 dark_field_scans = nxentry.spec_scans.dark_field | 733 dark_field_scans = nxentry.spec_scans.dark_field |
698 dark_field = FlatField.construct_from_nxcollection(dark_field_scans) | 734 dark_field = FlatField.construct_from_nxcollection(dark_field_scans) |
699 prefix = str(nxentry.instrument.detector.local_name) | 735 prefix = str(nxentry.instrument.detector.local_name) |
700 tdf_stack = dark_field.get_detector_data(prefix) | 736 tdf_stack = dark_field.get_detector_data(prefix) |
701 if isinstance(tdf_stack, list): | 737 if isinstance(tdf_stack, list): |
702 exit('TODO') | 738 assert(len(tdf_stack) == 1) # TODO |
739 tdf_stack = tdf_stack[0] | |
703 | 740 |
704 # Take median | 741 # Take median |
705 if tdf_stack.ndim == 2: | 742 if tdf_stack.ndim == 2: |
706 tdf = tdf_stack | 743 tdf = tdf_stack |
707 elif tdf_stack.ndim == 3: | 744 elif tdf_stack.ndim == 3: |
755 bright_field_scans = nxentry.spec_scans.bright_field | 792 bright_field_scans = nxentry.spec_scans.bright_field |
756 bright_field = FlatField.construct_from_nxcollection(bright_field_scans) | 793 bright_field = FlatField.construct_from_nxcollection(bright_field_scans) |
757 prefix = str(nxentry.instrument.detector.local_name) | 794 prefix = str(nxentry.instrument.detector.local_name) |
758 tbf_stack = bright_field.get_detector_data(prefix) | 795 tbf_stack = bright_field.get_detector_data(prefix) |
759 if isinstance(tbf_stack, list): | 796 if isinstance(tbf_stack, list): |
760 exit('TODO') | 797 assert(len(tbf_stack) == 1) # TODO |
798 tbf_stack = tbf_stack[0] | |
761 | 799 |
762 # Take median if more than one image | 800 # Take median if more than one image |
763 """Median or mean: It may be best to try the median because of some image | 801 """Median or mean: It may be best to try the median because of some image |
764 artifacts that arise due to crinkles in the upstream kapton tape windows | 802 artifacts that arise due to crinkles in the upstream kapton tape windows |
765 causing some phase contrast images to appear on the detector. | 803 causing some phase contrast images to appear on the detector. |
816 if image_key and 'data' in nxentry.instrument.detector: | 854 if image_key and 'data' in nxentry.instrument.detector: |
817 field_indices = [index for index, key in enumerate(image_key) if key == 0] | 855 field_indices = [index for index, key in enumerate(image_key) if key == 0] |
818 first_image = np.asarray(nxentry.instrument.detector.data[field_indices[0],:,:]) | 856 first_image = np.asarray(nxentry.instrument.detector.data[field_indices[0],:,:]) |
819 theta = float(nxentry.sample.rotation_angle[field_indices[0]]) | 857 theta = float(nxentry.sample.rotation_angle[field_indices[0]]) |
820 z_translation_all = nxentry.sample.z_translation[field_indices] | 858 z_translation_all = nxentry.sample.z_translation[field_indices] |
821 z_translation_levels = sorted(list(set(z_translation_all))) | 859 vertical_shifts = sorted(list(set(z_translation_all))) |
822 num_tomo_stacks = len(z_translation_levels) | 860 num_tomo_stacks = len(vertical_shifts) |
823 else: | 861 else: |
824 tomo_field_scans = nxentry.spec_scans.tomo_fields | 862 tomo_field_scans = nxentry.spec_scans.tomo_fields |
825 tomo_fields = TomoField.construct_from_nxcollection(tomo_field_scans) | 863 tomo_fields = TomoField.construct_from_nxcollection(tomo_field_scans) |
826 vertical_shifts = tomo_fields.get_vertical_shifts() | 864 vertical_shifts = tomo_fields.get_vertical_shifts() |
827 if not isinstance(vertical_shifts, list): | 865 if not isinstance(vertical_shifts, list): |
1110 tomo_stack = tomo_stack.astype('float32') | 1148 tomo_stack = tomo_stack.astype('float32') |
1111 if not self.test_mode: | 1149 if not self.test_mode: |
1112 if len(tomo_stacks) == 1: | 1150 if len(tomo_stacks) == 1: |
1113 title = f'red fullres theta {round(thetas[0], 2)+0}' | 1151 title = f'red fullres theta {round(thetas[0], 2)+0}' |
1114 else: | 1152 else: |
1115 title = f'red stack {i} fullres theta {round(thetas[0], 2)+0}' | 1153 title = f'red stack {i+1} fullres theta {round(thetas[0], 2)+0}' |
1116 quick_imshow(tomo_stack[0,:,:], title=title, path=path, save_fig=self.save_figs, | 1154 quick_imshow(tomo_stack[0,:,:], title=title, path=path, save_fig=self.save_figs, |
1117 save_only=self.save_only, block=self.block) | 1155 save_only=self.save_only, block=self.block) |
1118 # if not self.block: | 1156 # if not self.block: |
1119 # clear_imshow(title) | 1157 # clear_imshow(title) |
1120 if False and zoom_perc != 100: | 1158 if False and zoom_perc != 100: |
1133 quick_imshow(tomo_stack[0,:,:], title=title, path=path, save_fig=self.save_figs, | 1171 quick_imshow(tomo_stack[0,:,:], title=title, path=path, save_fig=self.save_figs, |
1134 save_only=self.save_only, block=self.block) | 1172 save_only=self.save_only, block=self.block) |
1135 # if not self.block: | 1173 # if not self.block: |
1136 # clear_imshow(title) | 1174 # clear_imshow(title) |
1137 | 1175 |
1138 # Convert tomography stack from theta,row,column to row,theta,column | |
1139 t0 = time() | |
1140 tomo_stack = np.swapaxes(tomo_stack, 0, 1) | |
1141 logger.debug(f'Converting coordinate order took {time()-t0:.2f} seconds') | |
1142 | |
1143 # Save test data to file | 1176 # Save test data to file |
1144 if self.test_mode: | 1177 if self.test_mode: |
1145 row_index = int(tomo_stack.shape[0]/2) | 1178 # row_index = int(tomo_stack.shape[0]/2) |
1146 np.savetxt(f'{self.output_folder}/red_stack_{i+1}.txt', tomo_stack[row_index,:,:], | 1179 # np.savetxt(f'{self.output_folder}/red_stack_{i+1}.txt', tomo_stack[row_index,:,:], |
1180 # fmt='%.6e') | |
1181 row_index = int(tomo_stack.shape[1]/2) | |
1182 np.savetxt(f'{self.output_folder}/red_stack_{i+1}.txt', tomo_stack[:,row_index,:], | |
1147 fmt='%.6e') | 1183 fmt='%.6e') |
1148 | 1184 |
1149 # Combine resized stacks | 1185 # Combine resized stacks |
1150 reduced_tomo_stacks.append(tomo_stack) | 1186 reduced_tomo_stacks.append(tomo_stack) |
1151 | 1187 |
1166 """Find center for a single tomography plane. | 1202 """Find center for a single tomography plane. |
1167 """ | 1203 """ |
1168 # Try automatic center finding routines for initial value | 1204 # Try automatic center finding routines for initial value |
1169 # sinogram index order: theta,column | 1205 # sinogram index order: theta,column |
1170 # need column,theta for iradon, so take transpose | 1206 # need column,theta for iradon, so take transpose |
1207 sinogram = np.asarray(sinogram) | |
1171 sinogram_T = sinogram.T | 1208 sinogram_T = sinogram.T |
1172 center = sinogram.shape[1]/2 | 1209 center = sinogram.shape[1]/2 |
1173 | 1210 |
1174 # Try using Nghia Vo’s method | 1211 # Try using Nghia Vo’s method |
1175 t0 = time() | 1212 t0 = time() |
1497 # 'exclusive)') | 1534 # 'exclusive)') |
1498 # accept = input_yesno('Accept these bounds (y/n)?', 'y') | 1535 # accept = input_yesno('Accept these bounds (y/n)?', 'y') |
1499 accept = True | 1536 accept = True |
1500 logger.debug(f'y_bounds = {y_bounds}') | 1537 logger.debug(f'y_bounds = {y_bounds}') |
1501 | 1538 |
1502 # Selecting z bounds (in xy-plane) (only valid for a single image set) | 1539 # Selecting z bounds (in xy-plane) (only valid for a single image stack) |
1503 if num_tomo_stacks != 1: | 1540 if num_tomo_stacks != 1: |
1504 z_bounds = None | 1541 z_bounds = None |
1505 else: | 1542 else: |
1506 tomosum = 0 | 1543 tomosum = 0 |
1507 [tomosum := tomosum+np.sum(tomo_recon_stacks[i], axis=(1,2)) | 1544 [tomosum := tomosum+np.sum(tomo_recon_stacks[i], axis=(1,2)) |
1546 logger.info(f'output_folder = {output_folder}') | 1583 logger.info(f'output_folder = {output_folder}') |
1547 logger.info(f'save_figs = {save_figs}') | 1584 logger.info(f'save_figs = {save_figs}') |
1548 logger.info(f'test_mode = {test_mode}') | 1585 logger.info(f'test_mode = {test_mode}') |
1549 | 1586 |
1550 # Check for correction modes | 1587 # Check for correction modes |
1588 legal_modes = ['reduce_data', 'find_center', 'reconstruct_data', 'combine_data', 'all'] | |
1551 if modes is None: | 1589 if modes is None: |
1552 modes = ['all'] | 1590 modes = ['all'] |
1553 logger.debug(f'modes {type(modes)} = {modes}') | 1591 if not all(True if mode in legal_modes else False for mode in modes): |
1592 raise ValueError(f'Invalid parameter modes ({modes})') | |
1554 | 1593 |
1555 # Instantiate Tomo object | 1594 # Instantiate Tomo object |
1556 tomo = Tomo(num_core=num_core, output_folder=output_folder, save_figs=save_figs, | 1595 tomo = Tomo(num_core=num_core, output_folder=output_folder, save_figs=save_figs, |
1557 test_mode=test_mode) | 1596 test_mode=test_mode) |
1558 | 1597 |
1579 # Combine reconstructed tomography stacks | 1618 # Combine reconstructed tomography stacks |
1580 if 'combine_data' in modes or 'all' in modes: | 1619 if 'combine_data' in modes or 'all' in modes: |
1581 data = tomo.combine_data(data) | 1620 data = tomo.combine_data(data) |
1582 | 1621 |
1583 # Write output file | 1622 # Write output file |
1584 if not test_mode: | 1623 if data is not None and not test_mode: |
1585 if center_data is None: | 1624 if center_data is None: |
1586 data = tomo.write(data, output_file) | 1625 data = tomo.write(data, output_file) |
1587 else: | 1626 else: |
1588 data = tomo.write(center_data, output_file) | 1627 data = tomo.write(center_data, output_file) |
1589 | 1628 |
1629 logger.info(f'Completed modes: {modes}') |