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