nabu 2023.2.1__py3-none-any.whl → 2024.1.0rc3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- doc/conf.py +1 -1
- doc/doc_config.py +32 -0
- nabu/__init__.py +2 -1
- nabu/app/bootstrap_stitching.py +1 -1
- nabu/app/cli_configs.py +122 -2
- nabu/app/composite_cor.py +27 -2
- nabu/app/correct_rot.py +70 -0
- nabu/app/create_distortion_map_from_poly.py +42 -18
- nabu/app/diag_to_pix.py +358 -0
- nabu/app/diag_to_rot.py +449 -0
- nabu/app/generate_header.py +4 -3
- nabu/app/histogram.py +2 -2
- nabu/app/multicor.py +6 -1
- nabu/app/parse_reconstruction_log.py +151 -0
- nabu/app/prepare_weights_double.py +83 -22
- nabu/app/reconstruct.py +5 -1
- nabu/app/reconstruct_helical.py +7 -0
- nabu/app/reduce_dark_flat.py +6 -3
- nabu/app/rotate.py +4 -4
- nabu/app/stitching.py +16 -2
- nabu/app/tests/test_reduce_dark_flat.py +18 -2
- nabu/app/validator.py +4 -4
- nabu/cuda/convolution.py +8 -376
- nabu/cuda/fft.py +4 -0
- nabu/cuda/kernel.py +4 -4
- nabu/cuda/medfilt.py +5 -158
- nabu/cuda/padding.py +5 -71
- nabu/cuda/processing.py +23 -2
- nabu/cuda/src/ElementOp.cu +78 -0
- nabu/cuda/src/backproj.cu +28 -2
- nabu/cuda/src/fourier_wavelets.cu +2 -2
- nabu/cuda/src/normalization.cu +23 -0
- nabu/cuda/src/padding.cu +2 -2
- nabu/cuda/src/transpose.cu +16 -0
- nabu/cuda/utils.py +39 -0
- nabu/estimation/alignment.py +10 -1
- nabu/estimation/cor.py +808 -38
- nabu/estimation/cor_sino.py +7 -9
- nabu/estimation/tests/test_cor.py +85 -3
- nabu/io/reader.py +26 -18
- nabu/io/tests/test_cast_volume.py +3 -3
- nabu/io/tests/test_detector_distortion.py +3 -3
- nabu/io/tiffwriter_zmm.py +2 -2
- nabu/io/utils.py +14 -4
- nabu/io/writer.py +5 -3
- nabu/misc/fftshift.py +6 -0
- nabu/misc/histogram.py +5 -285
- nabu/misc/histogram_cuda.py +8 -104
- nabu/misc/kernel_base.py +3 -121
- nabu/misc/padding_base.py +5 -69
- nabu/misc/processing_base.py +3 -107
- nabu/misc/rotation.py +5 -62
- nabu/misc/rotation_cuda.py +5 -65
- nabu/misc/transpose.py +6 -0
- nabu/misc/unsharp.py +3 -78
- nabu/misc/unsharp_cuda.py +5 -52
- nabu/misc/unsharp_opencl.py +8 -85
- nabu/opencl/fft.py +6 -0
- nabu/opencl/kernel.py +21 -6
- nabu/opencl/padding.py +5 -72
- nabu/opencl/processing.py +27 -5
- nabu/opencl/src/backproj.cl +3 -3
- nabu/opencl/src/fftshift.cl +65 -12
- nabu/opencl/src/padding.cl +2 -2
- nabu/opencl/src/roll.cl +96 -0
- nabu/opencl/src/transpose.cl +16 -0
- nabu/pipeline/config_validators.py +63 -3
- nabu/pipeline/dataset_validator.py +2 -2
- nabu/pipeline/estimators.py +193 -35
- nabu/pipeline/fullfield/chunked.py +34 -17
- nabu/pipeline/fullfield/chunked_cuda.py +7 -5
- nabu/pipeline/fullfield/computations.py +48 -13
- nabu/pipeline/fullfield/nabu_config.py +13 -13
- nabu/pipeline/fullfield/processconfig.py +10 -5
- nabu/pipeline/fullfield/reconstruction.py +1 -2
- nabu/pipeline/helical/fbp.py +5 -0
- nabu/pipeline/helical/filtering.py +12 -9
- nabu/pipeline/helical/gridded_accumulator.py +179 -33
- nabu/pipeline/helical/helical_chunked_regridded.py +262 -151
- nabu/pipeline/helical/helical_chunked_regridded_cuda.py +4 -11
- nabu/pipeline/helical/helical_reconstruction.py +56 -18
- nabu/pipeline/helical/span_strategy.py +1 -1
- nabu/pipeline/helical/tests/test_accumulator.py +4 -0
- nabu/pipeline/params.py +23 -2
- nabu/pipeline/processconfig.py +3 -8
- nabu/pipeline/tests/test_chunk_reader.py +78 -0
- nabu/pipeline/tests/test_estimators.py +120 -2
- nabu/pipeline/utils.py +25 -0
- nabu/pipeline/writer.py +2 -0
- nabu/preproc/ccd_cuda.py +9 -7
- nabu/preproc/ctf.py +21 -26
- nabu/preproc/ctf_cuda.py +25 -25
- nabu/preproc/double_flatfield.py +14 -2
- nabu/preproc/double_flatfield_cuda.py +7 -11
- nabu/preproc/flatfield_cuda.py +23 -27
- nabu/preproc/phase.py +19 -24
- nabu/preproc/phase_cuda.py +21 -21
- nabu/preproc/shift_cuda.py +58 -28
- nabu/preproc/tests/test_ctf.py +5 -5
- nabu/preproc/tests/test_double_flatfield.py +2 -2
- nabu/preproc/tests/test_vshift.py +13 -2
- nabu/processing/__init__.py +0 -0
- nabu/processing/convolution_cuda.py +375 -0
- nabu/processing/fft_base.py +163 -0
- nabu/processing/fft_cuda.py +256 -0
- nabu/processing/fft_opencl.py +54 -0
- nabu/processing/fftshift.py +134 -0
- nabu/processing/histogram.py +286 -0
- nabu/processing/histogram_cuda.py +103 -0
- nabu/processing/kernel_base.py +126 -0
- nabu/processing/medfilt_cuda.py +159 -0
- nabu/processing/muladd.py +29 -0
- nabu/processing/muladd_cuda.py +68 -0
- nabu/processing/padding_base.py +71 -0
- nabu/processing/padding_cuda.py +75 -0
- nabu/processing/padding_opencl.py +77 -0
- nabu/processing/processing_base.py +123 -0
- nabu/processing/roll_opencl.py +64 -0
- nabu/processing/rotation.py +63 -0
- nabu/processing/rotation_cuda.py +66 -0
- nabu/processing/tests/__init__.py +0 -0
- nabu/processing/tests/test_fft.py +268 -0
- nabu/processing/tests/test_fftshift.py +71 -0
- nabu/{misc → processing}/tests/test_histogram.py +2 -4
- nabu/{cuda → processing}/tests/test_medfilt.py +1 -1
- nabu/processing/tests/test_muladd.py +54 -0
- nabu/{cuda → processing}/tests/test_padding.py +119 -75
- nabu/processing/tests/test_roll.py +63 -0
- nabu/{misc → processing}/tests/test_rotation.py +3 -2
- nabu/processing/tests/test_transpose.py +72 -0
- nabu/{misc → processing}/tests/test_unsharp.py +41 -8
- nabu/processing/transpose.py +126 -0
- nabu/processing/unsharp.py +79 -0
- nabu/processing/unsharp_cuda.py +53 -0
- nabu/processing/unsharp_opencl.py +75 -0
- nabu/reconstruction/fbp.py +34 -10
- nabu/reconstruction/fbp_base.py +35 -16
- nabu/reconstruction/fbp_opencl.py +7 -12
- nabu/reconstruction/filtering.py +2 -2
- nabu/reconstruction/filtering_cuda.py +13 -14
- nabu/reconstruction/filtering_opencl.py +3 -4
- nabu/reconstruction/projection.py +2 -0
- nabu/reconstruction/rings.py +158 -1
- nabu/reconstruction/rings_cuda.py +218 -58
- nabu/reconstruction/sinogram_cuda.py +16 -12
- nabu/reconstruction/tests/test_deringer.py +116 -14
- nabu/reconstruction/tests/test_fbp.py +22 -31
- nabu/reconstruction/tests/test_filtering.py +11 -2
- nabu/resources/dataset_analyzer.py +89 -26
- nabu/resources/nxflatfield.py +2 -2
- nabu/resources/tests/test_nxflatfield.py +1 -1
- nabu/resources/utils.py +9 -2
- nabu/stitching/alignment.py +184 -0
- nabu/stitching/config.py +241 -39
- nabu/stitching/definitions.py +6 -0
- nabu/stitching/frame_composition.py +4 -2
- nabu/stitching/overlap.py +99 -3
- nabu/stitching/sample_normalization.py +60 -0
- nabu/stitching/slurm_utils.py +10 -10
- nabu/stitching/tests/test_alignment.py +99 -0
- nabu/stitching/tests/test_config.py +16 -1
- nabu/stitching/tests/test_overlap.py +68 -2
- nabu/stitching/tests/test_sample_normalization.py +49 -0
- nabu/stitching/tests/test_slurm_utils.py +5 -5
- nabu/stitching/tests/test_utils.py +3 -33
- nabu/stitching/tests/test_z_stitching.py +391 -22
- nabu/stitching/utils.py +144 -202
- nabu/stitching/z_stitching.py +309 -126
- nabu/testutils.py +18 -0
- nabu/thirdparty/tomocupy_remove_stripe.py +586 -0
- nabu/utils.py +32 -6
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/LICENSE +1 -1
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/METADATA +5 -5
- nabu-2024.1.0rc3.dist-info/RECORD +296 -0
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/WHEEL +1 -1
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/entry_points.txt +5 -1
- nabu/conftest.py +0 -14
- nabu/opencl/fftshift.py +0 -92
- nabu/opencl/tests/test_fftshift.py +0 -55
- nabu/opencl/tests/test_padding.py +0 -84
- nabu-2023.2.1.dist-info/RECORD +0 -252
- /nabu/cuda/src/{fftshift.cu → dfi_fftshift.cu} +0 -0
- {nabu-2023.2.1.dist-info → nabu-2024.1.0rc3.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,6 @@
|
|
1
1
|
# pylint: skip-file
|
2
2
|
|
3
3
|
from os import path
|
4
|
-
from time import time
|
5
4
|
import numpy as np
|
6
5
|
import math
|
7
6
|
from silx.image.tomography import get_next_power
|
@@ -11,19 +10,18 @@ import silx.io
|
|
11
10
|
import copy
|
12
11
|
from silx.io.url import DataUrl
|
13
12
|
from ...resources.logger import LoggerOrPrint
|
14
|
-
from ...resources.utils import is_hdf5_extension
|
13
|
+
from ...resources.utils import is_hdf5_extension
|
15
14
|
from ...io.reader_helical import ChunkReaderHelical, get_hdf5_dataset_shape
|
16
15
|
from ...preproc.flatfield_variable_region import FlatFieldDataVariableRegionUrls as FlatFieldDataHelicalUrls
|
17
16
|
from ...preproc.distortion import DistortionCorrection
|
18
17
|
from ...preproc.shift import VerticalShift
|
19
18
|
from ...preproc.double_flatfield_variable_region import DoubleFlatFieldVariableRegion as DoubleFlatFieldHelical
|
20
19
|
from ...preproc.phase import PaganinPhaseRetrieval
|
21
|
-
from ...reconstruction.sinogram import SinoBuilder
|
22
|
-
from ...
|
23
|
-
from ...
|
20
|
+
from ...reconstruction.sinogram import SinoBuilder
|
21
|
+
from ...processing.unsharp import UnsharpMask
|
22
|
+
from ...processing.histogram import PartialHistogram, hist_as_2Darray
|
24
23
|
from ..utils import use_options, pipeline_step
|
25
24
|
|
26
|
-
from ...resources.utils import extract_parameters
|
27
25
|
from ..detector_distortion_provider import DetectorDistortionProvider
|
28
26
|
|
29
27
|
from .utils import (
|
@@ -36,9 +34,26 @@ from numpy.lib.stride_tricks import sliding_window_view
|
|
36
34
|
from ...misc.binning import get_binning_function
|
37
35
|
from .helical_utils import find_mirror_indexes
|
38
36
|
|
39
|
-
from ...preproc.ccd import Log, CCDFilter
|
40
37
|
|
41
|
-
|
38
|
+
try:
|
39
|
+
import nabuxx
|
40
|
+
|
41
|
+
GriddedAccumulator = nabuxx.gridded_accumulator.GriddedAccumulator
|
42
|
+
CCDFilter = nabuxx.ccd.CCDFilter
|
43
|
+
Log = nabuxx.ccd.LogFilter
|
44
|
+
cxx_paganin = nabuxx.paganin
|
45
|
+
except:
|
46
|
+
logger_tmp = LoggerOrPrint(None)
|
47
|
+
logger_tmp.info(
|
48
|
+
"Nabuxx not available. Loading python implementation for gridded_accumulator, Log, CCDFilter, paganin"
|
49
|
+
)
|
50
|
+
from . import gridded_accumulator
|
51
|
+
|
52
|
+
GriddedAccumulator = gridded_accumulator.GriddedAccumulator
|
53
|
+
from ...preproc.ccd import Log, CCDFilter
|
54
|
+
|
55
|
+
cxx_paganin = None
|
56
|
+
|
42
57
|
|
43
58
|
# For now we don't have a plain python/numpy backend for reconstruction
|
44
59
|
Backprojector = None
|
@@ -81,6 +96,7 @@ class HelicalChunkedRegriddedPipeline:
|
|
81
96
|
phase_margin=None,
|
82
97
|
reading_granularity=10,
|
83
98
|
span_info=None,
|
99
|
+
diag_zpro_run=0,
|
84
100
|
):
|
85
101
|
"""
|
86
102
|
Initialize a "HelicalChunked" pipeline.
|
@@ -119,11 +135,12 @@ class HelicalChunkedRegriddedPipeline:
|
|
119
135
|
|
120
136
|
self.logger = LoggerOrPrint(logger)
|
121
137
|
|
122
|
-
self._set_params(process_config, sub_region, extra_options, phase_margin)
|
138
|
+
self._set_params(process_config, sub_region, extra_options, phase_margin, diag_zpro_run)
|
123
139
|
|
124
140
|
self._init_pipeline()
|
125
141
|
|
126
|
-
def _set_params(self, process_config, sub_region, extra_options, phase_margin):
|
142
|
+
def _set_params(self, process_config, sub_region, extra_options, phase_margin, diag_zpro_run):
|
143
|
+
self.diag_zpro_run = diag_zpro_run
|
127
144
|
self.process_config = process_config
|
128
145
|
self.dataset_info = self.process_config.dataset_info
|
129
146
|
|
@@ -443,23 +460,54 @@ class HelicalChunkedRegriddedPipeline:
|
|
443
460
|
|
444
461
|
### these radios are for diagnostic of the translations ( they will be optionally written, for being further used
|
445
462
|
## by correlation techniques ). Two radios for the first two pass over the first gridded angles
|
446
|
-
self.
|
447
|
-
|
448
|
-
|
463
|
+
if self.diag_zpro_run:
|
464
|
+
# 2 for the redundancy, 2 for +180 mirror
|
465
|
+
ndiag = 2 * 2 * self.diag_zpro_run
|
466
|
+
else:
|
467
|
+
ndiag = 2 * 2
|
468
|
+
|
469
|
+
self.diagnostic_searched_angles_rad_clipped = (
|
470
|
+
(0.5 + np.arange(ndiag // 2)) * (2 * np.pi / (ndiag // 2))
|
471
|
+
).astype("f")
|
472
|
+
|
473
|
+
self.diagnostic_radios = np.zeros((ndiag,) + subradio_shape, np.float32)
|
474
|
+
self.diagnostic_weights = np.zeros((ndiag,) + subradio_shape, np.float32)
|
475
|
+
self.diagnostic_proj_angle = np.zeros([ndiag], "f")
|
476
|
+
self.diagnostic_zpix_transl = np.zeros([ndiag], "f")
|
477
|
+
self.diagnostic_zmm_transl = np.zeros([ndiag], "f")
|
478
|
+
|
449
479
|
self.diagnostic = {
|
450
480
|
"radios": self.diagnostic_radios,
|
451
481
|
"weights": self.diagnostic_weights,
|
452
482
|
"angles": self.diagnostic_proj_angle,
|
483
|
+
"zpix_transl": self.diagnostic_zpix_transl,
|
484
|
+
"zmm_trans": self.diagnostic_zmm_transl,
|
485
|
+
"pixel_size_mm": self.span_info.pix_size_mm,
|
486
|
+
"searched_rad": self.diagnostic_searched_angles_rad_clipped,
|
453
487
|
}
|
454
488
|
## -------
|
455
|
-
|
456
|
-
|
489
|
+
if self.diag_zpro_run == 0:
|
490
|
+
self.gridded_radios = np.zeros((self.n_gridded_angles,) + subradio_shape, np.float32)
|
491
|
+
self.gridded_cumulated_weights = np.zeros((self.n_gridded_angles,) + subradio_shape, np.float32)
|
492
|
+
else:
|
493
|
+
# only diagnostic will be cumulated. No need to keep the full size for diagnostic runs.
|
494
|
+
# The gridder is initialised passing also the two buffer below,
|
495
|
+
# and the two first dimensions are used to allocate auxiliaries,
|
496
|
+
# so we shorten only the last dimension, but this is already a good cut
|
497
|
+
self.gridded_radios = np.zeros((self.n_gridded_angles,) + (subradio_shape[0], 2), np.float32)
|
498
|
+
self.gridded_cumulated_weights = np.zeros((self.n_gridded_angles,) + (subradio_shape[0], 2), np.float32)
|
499
|
+
|
457
500
|
self.radios_subset = np.zeros((self.reading_granularity,) + subradio_shape, np.float32)
|
458
501
|
self.radios_weights_subset = np.zeros((self.reading_granularity,) + subradio_shape, np.float32)
|
459
502
|
|
460
|
-
|
461
|
-
|
462
|
-
|
503
|
+
if not self.diag_zpro_run:
|
504
|
+
self.radios = np.zeros(
|
505
|
+
(self.n_gridded_angles,) + ((end_z - down_margin) - (start_z + up_margin), shp_h), np.float32
|
506
|
+
)
|
507
|
+
else:
|
508
|
+
# place holder
|
509
|
+
self.radios = np.zeros((self.n_gridded_angles,) + (1, 1), np.float32)
|
510
|
+
|
463
511
|
self.radios_weights = np.zeros_like(self.radios)
|
464
512
|
|
465
513
|
self.radios_slim = self._allocate_array(self._get_shape("one_sino_slim"), "f", name="radios_slim")
|
@@ -479,6 +527,13 @@ class HelicalChunkedRegriddedPipeline:
|
|
479
527
|
# Pipeline initialization
|
480
528
|
#
|
481
529
|
|
530
|
+
def _reset_diagnostics(self):
|
531
|
+
self.diagnostic_radios[:] = 0
|
532
|
+
self.diagnostic_weights[:] = 0
|
533
|
+
self.diagnostic_zpix_transl[:] = 0
|
534
|
+
self.diagnostic_zmm_transl[:] = 0
|
535
|
+
self.diagnostic_proj_angle[:] = np.nan
|
536
|
+
|
482
537
|
def _init_pipeline(self):
|
483
538
|
self._get_size_of_a_raw_radio()
|
484
539
|
|
@@ -514,19 +569,35 @@ class HelicalChunkedRegriddedPipeline:
|
|
514
569
|
)
|
515
570
|
|
516
571
|
def _configure_regular_accumulator(self):
|
517
|
-
|
572
|
+
##
|
573
|
+
# keeping these freshly numpyed objects referenced by self
|
574
|
+
# ensures that their buffer info, conserved by c++ implementation of GriddedAccumulator
|
575
|
+
# will always point to existing data, which could otherwise be garbage collected by python
|
576
|
+
#
|
577
|
+
|
578
|
+
if self.process_config.nabu_config["preproc"]["normalize_srcurrent"]:
|
579
|
+
self.radios_srcurrent = np.array(self.dataset_info.projections_srcurrent, "f")
|
580
|
+
self.flats_srcurrent = np.array(self.dataset_info.flats_srcurrent, "f")
|
581
|
+
else:
|
582
|
+
self.radios_srcurrent = None
|
583
|
+
self.flats_srcurrent = None
|
518
584
|
|
519
|
-
self.regular_accumulator =
|
585
|
+
self.regular_accumulator = GriddedAccumulator(
|
520
586
|
gridded_radios=self.gridded_radios,
|
521
587
|
gridded_weights=self.gridded_cumulated_weights,
|
522
588
|
diagnostic_radios=self.diagnostic_radios,
|
523
589
|
diagnostic_weights=self.diagnostic_weights,
|
524
590
|
diagnostic_angles=self.diagnostic_proj_angle,
|
591
|
+
diagnostic_zpix_transl=self.diagnostic_zpix_transl,
|
592
|
+
diagnostic_searched_angles_rad_clipped=self.diagnostic_searched_angles_rad_clipped,
|
525
593
|
dark=self.flatfield.get_dark(),
|
526
594
|
flat_indexes=self.flatfield._sorted_flat_indices,
|
527
595
|
flats=self.flatfield.flats_stack,
|
528
596
|
weights=self.weights_field.data,
|
529
597
|
double_flat=self.double_flatfield.data,
|
598
|
+
diag_zpro_run=self.diag_zpro_run,
|
599
|
+
radios_srcurrent=self.radios_srcurrent,
|
600
|
+
flats_srcurrent=self.flats_srcurrent,
|
530
601
|
)
|
531
602
|
|
532
603
|
return
|
@@ -689,7 +760,7 @@ class HelicalChunkedRegriddedPipeline:
|
|
689
760
|
pixel_size=options["pixel_size_m"],
|
690
761
|
padding=options["padding_type"],
|
691
762
|
margin=margin,
|
692
|
-
|
763
|
+
fft_num_threads=True, # TODO tune in advanced params of nabu config file
|
693
764
|
)
|
694
765
|
if self.phase_retrieval.use_fftw:
|
695
766
|
self.logger.debug(
|
@@ -752,8 +823,13 @@ class HelicalChunkedRegriddedPipeline:
|
|
752
823
|
x_s, x_e = options["start_x"], options["end_x"]
|
753
824
|
y_s, y_e = options["start_y"], options["end_y"]
|
754
825
|
|
755
|
-
self.
|
756
|
-
|
826
|
+
if not self.diag_zpro_run:
|
827
|
+
self._rec_roi = (x_s, x_e + 1, y_s, y_e + 1)
|
828
|
+
self._allocate_recs(y_e - y_s + 1, x_e - x_s + 1)
|
829
|
+
else:
|
830
|
+
## Dummy 1x1 place holder
|
831
|
+
self._rec_roi = (x_s, x_s + 1, y_s, y_s + 1)
|
832
|
+
self._allocate_recs(y_s - y_s + 1, x_s - x_s + 1)
|
757
833
|
|
758
834
|
@use_options("reconstruction", "reconstruction")
|
759
835
|
def _init_reconstruction(self):
|
@@ -769,7 +845,7 @@ class HelicalChunkedRegriddedPipeline:
|
|
769
845
|
|
770
846
|
start_y, end_y, start_x, end_x = self._rec_roi
|
771
847
|
|
772
|
-
if self.HBPClass is not None:
|
848
|
+
if self.HBPClass is not None and self.process_config.nabu_config["reconstruction"]["use_hbp"]:
|
773
849
|
fan_source_distance_meters = self.process_config.nabu_config["reconstruction"]["fan_source_distance_meters"]
|
774
850
|
|
775
851
|
self.reconstruction_hbp = self.HBPClass(
|
@@ -780,7 +856,8 @@ class HelicalChunkedRegriddedPipeline:
|
|
780
856
|
extra_options={"axis_correction": np.zeros(self.radios.shape[0], "f")},
|
781
857
|
axis_source_meters=fan_source_distance_meters,
|
782
858
|
voxel_size_microns=options["voxel_size_cm"][0] * 1.0e4,
|
783
|
-
scale_factor=
|
859
|
+
scale_factor=2.0 / options["voxel_size_cm"][0],
|
860
|
+
clip_outer_circle=options["clip_outer_circle"],
|
784
861
|
)
|
785
862
|
|
786
863
|
else:
|
@@ -793,10 +870,10 @@ class HelicalChunkedRegriddedPipeline:
|
|
793
870
|
filter_name=options["fbp_filter_type"],
|
794
871
|
slice_roi=self._rec_roi,
|
795
872
|
# slice_shape = ( end_y-start_y, end_x- start_x ),
|
796
|
-
scale_factor=
|
873
|
+
scale_factor=2.0 / options["voxel_size_cm"][0],
|
797
874
|
padding_mode=options["padding_type"],
|
798
875
|
extra_options={
|
799
|
-
"scale_factor":
|
876
|
+
"scale_factor": 2.0 / options["voxel_size_cm"][0],
|
800
877
|
"axis_correction": np.zeros(self.radios.shape[0], "f"),
|
801
878
|
"clip_outer_circle": options["clip_outer_circle"],
|
802
879
|
}, # "padding_mode": options["padding_type"], },
|
@@ -850,9 +927,10 @@ class HelicalChunkedRegriddedPipeline:
|
|
850
927
|
"entry": entry,
|
851
928
|
}
|
852
929
|
self._histogram_processing_index = nx_info["processing_index"] + 1
|
853
|
-
elif options["file_format"] in ["tif", "tiff"]:
|
930
|
+
elif options["file_format"] in ["tif", "tiff", "edf"]:
|
854
931
|
fname_start_index = self._get_slice_start_index()
|
855
932
|
self._histogram_processing_index = 1
|
933
|
+
|
856
934
|
self._writer_configurator = WriterConfigurator(
|
857
935
|
output_dir,
|
858
936
|
file_prefix,
|
@@ -884,94 +962,57 @@ class HelicalChunkedRegriddedPipeline:
|
|
884
962
|
result_slice = slice(start, stop, step)
|
885
963
|
return result_slice
|
886
964
|
|
887
|
-
def
|
888
|
-
"""Read, and apply dark+ff to, a small angular domain corresponding to the slice argument sub_total_prange_slice
|
889
|
-
without refilling the holes.
|
890
|
-
|
891
|
-
"""
|
892
|
-
|
893
|
-
if self.chunk_reader.dataset_subsampling > 1:
|
894
|
-
subsampling_file_slice = self._expand_slice(sub_total_prange_slice)
|
895
|
-
else:
|
896
|
-
subsampling_file_slice = sub_total_prange_slice
|
897
|
-
|
965
|
+
def _read_data_and_apply_flats(self, sub_total_prange_slice, subchunk_slice, chunk_info):
|
898
966
|
my_integer_shifts_v = chunk_info.integer_shift_v[subchunk_slice]
|
899
967
|
fract_complement_shifts_v = chunk_info.fract_complement_to_integer_shift_v[subchunk_slice]
|
900
|
-
|
901
968
|
x_shifts_list = chunk_info.x_pix_per_proj[subchunk_slice]
|
902
|
-
|
903
969
|
(subr_start_x, subr_end_x, subr_start_z, subr_end_z) = self.sub_region
|
904
970
|
subr_start_z_list = subr_start_z - my_integer_shifts_v
|
905
971
|
subr_end_z_list = subr_end_z - my_integer_shifts_v + 1
|
906
|
-
floating_start_z = subr_start_z_list.min()
|
907
|
-
floating_end_z = subr_end_z_list.max()
|
908
|
-
|
909
|
-
floating_subregion = None, None, floating_start_z, floating_end_z
|
910
|
-
|
911
|
-
self._reset_reader_subregion(floating_subregion)
|
912
|
-
|
913
|
-
self.chunk_reader.load_data(overwrite=True, sub_total_prange_slice=sub_total_prange_slice)
|
914
|
-
my_indexes = self.chunk_reader._sorted_files_indices[subsampling_file_slice]
|
915
|
-
data_raw = self.chunk_reader.data[: len(my_indexes)]
|
916
972
|
|
917
|
-
|
918
|
-
sub_regions_per_radio = [self.trimmed_floating_subregion] * len(my_indexes)
|
919
|
-
|
920
|
-
if self.flatfield is not None:
|
921
|
-
self.flatfield.normalize_radios(data_raw, my_indexes, sub_regions_per_radio)
|
973
|
+
self._reset_reader_subregion((None, None, subr_start_z_list.min(), subr_end_z_list.max()))
|
922
974
|
|
923
|
-
|
924
|
-
self.double_flatfield.apply_double_flatfield_for_sub_regions(data_raw, sub_regions_per_radio)
|
975
|
+
dtasrc_start_x, dtasrc_end_x, dtasrc_start_z, dtasrc_end_z = self.trimmed_floating_subregion
|
925
976
|
|
926
|
-
|
977
|
+
if self.diag_zpro_run:
|
978
|
+
searched_angles = self.diagnostic_searched_angles_rad_clipped
|
927
979
|
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
data_raw, subr_start_z_list, subr_end_z_list, fract_complement_shifts_v, x_shifts_list, output
|
935
|
-
):
|
936
|
-
_fill_in_chunk_by_shift_crop_data(
|
937
|
-
data_target,
|
938
|
-
data_read,
|
939
|
-
fract_shit,
|
940
|
-
list_subr_start_z,
|
941
|
-
list_subr_end_z,
|
942
|
-
source_start_z,
|
943
|
-
sources_end_z,
|
944
|
-
x_shift=x_shift,
|
945
|
-
)
|
946
|
-
|
947
|
-
def _read_data_and_apply_flats(self, sub_total_prange_slice, subchunk_slice, chunk_info):
|
948
|
-
my_integer_shifts_v = chunk_info.integer_shift_v[subchunk_slice]
|
949
|
-
fract_complement_shifts_v = chunk_info.fract_complement_to_integer_shift_v[subchunk_slice]
|
950
|
-
x_shifts_list = chunk_info.x_pix_per_proj[subchunk_slice]
|
951
|
-
(subr_start_x, subr_end_x, subr_start_z, subr_end_z) = self.sub_region
|
952
|
-
subr_start_z_list = subr_start_z - my_integer_shifts_v
|
953
|
-
subr_end_z_list = subr_end_z - my_integer_shifts_v + 1
|
980
|
+
these_angles = chunk_info.angles_rad[subchunk_slice]
|
981
|
+
if len(these_angles) > 1:
|
982
|
+
# these_angles are the projection angles
|
983
|
+
# if no diagnostic angle falls close to them we skip to the next angular subchunk
|
984
|
+
# (here slice refers to angular slicing)
|
985
|
+
# We like hdf5 but we that is not a reason to read them all the time, so we spare time
|
954
986
|
|
955
|
-
|
987
|
+
a_step = abs(these_angles[1:] - these_angles[:-1]).mean()
|
988
|
+
distance = abs(np.mod(these_angles, np.pi * 2) - searched_angles[:, None]).min()
|
989
|
+
distance_l = abs(np.mod(these_angles, np.pi * 2) - searched_angles[:, None] - a_step).min()
|
990
|
+
distance_h = abs(np.mod(these_angles, np.pi * 2) - searched_angles[:, None] + a_step).min()
|
991
|
+
distance = np.array([distance, distance_h, distance_l]).min()
|
956
992
|
|
957
|
-
|
993
|
+
if distance > 2 * a_step:
|
994
|
+
return
|
958
995
|
|
959
996
|
self.chunk_reader.load_data(overwrite=True, sub_total_prange_slice=sub_total_prange_slice)
|
960
|
-
|
961
997
|
if self.chunk_reader.dataset_subsampling > 1:
|
962
|
-
|
998
|
+
radios_angular_range_slicing = self._expand_slice(sub_total_prange_slice)
|
963
999
|
else:
|
964
|
-
|
965
|
-
my_subsampled_indexes = self.chunk_reader._sorted_files_indices[
|
1000
|
+
radios_angular_range_slicing = sub_total_prange_slice
|
1001
|
+
my_subsampled_indexes = self.chunk_reader._sorted_files_indices[radios_angular_range_slicing]
|
966
1002
|
data_raw = self.chunk_reader.data[: len(my_subsampled_indexes)]
|
967
1003
|
|
968
1004
|
self.regular_accumulator.extract_preprocess_with_flats(
|
969
1005
|
subchunk_slice,
|
970
|
-
my_subsampled_indexes,
|
1006
|
+
my_subsampled_indexes, # these are indexes pointing within the global domain sequence which is composed of darks flats radios
|
971
1007
|
chunk_info,
|
972
1008
|
np.array((subr_start_z, subr_end_z), "i"),
|
973
1009
|
np.array((dtasrc_start_z, dtasrc_end_z), "i"),
|
974
1010
|
data_raw,
|
1011
|
+
radios_angular_range_slicing # my_subsampled_indexes is important in order to compare the
|
1012
|
+
# radios positions with respect to the flat position, and these position
|
1013
|
+
# are given as the sequential acquisition number which counts everything ( flats, darks, radios )
|
1014
|
+
# Insteqd, in order to access array which spans only the radios, we need to have an idea of where we are.
|
1015
|
+
# this is provided by radios_angular_range_slicing which addresses the radios domain
|
975
1016
|
)
|
976
1017
|
|
977
1018
|
def binning_expanded(self, region):
|
@@ -1116,14 +1157,24 @@ class HelicalChunkedRegriddedPipeline:
|
|
1116
1157
|
self.recs_histogram = self.histogram.merge_histograms(self.histo_stack)
|
1117
1158
|
self.histo_stack.clear()
|
1118
1159
|
|
1119
|
-
def _write_data(self, data=None
|
1160
|
+
def _write_data(self, data=None):
|
1120
1161
|
if data is None:
|
1121
1162
|
data = self.recs_stack
|
1122
1163
|
my_kw_args = copy.copy(self._writer_exec_kwargs)
|
1123
1164
|
if "config" in my_kw_args:
|
1124
|
-
self.logger.info(
|
1125
|
-
|
1126
|
-
|
1165
|
+
self.logger.info(
|
1166
|
+
"omitting config in writer because of too slow nexus writer. Just writing the diagnostics, if any "
|
1167
|
+
)
|
1168
|
+
|
1169
|
+
# diagnostic are saved here, with the Nabu mechanism for config
|
1170
|
+
self.diagnostic_zpix_transl[:] = np.interp(
|
1171
|
+
self.diagnostic_proj_angle,
|
1172
|
+
np.deg2rad(self.span_info.projection_angles_deg_internally),
|
1173
|
+
self.span_info.z_pix_per_proj,
|
1174
|
+
)
|
1175
|
+
self.diagnostic_zmm_transl[:] = self.diagnostic_zpix_transl * self.span_info.pix_size_mm
|
1176
|
+
|
1177
|
+
my_kw_args["config"] = self.diagnostic
|
1127
1178
|
|
1128
1179
|
self.writer.write(data, *self._writer_exec_args, **my_kw_args)
|
1129
1180
|
self.logger.info("Wrote %s" % self.writer.get_filename())
|
@@ -1181,12 +1232,6 @@ class HelicalChunkedRegriddedPipeline:
|
|
1181
1232
|
"""This will be used in the derived class to transfer data to gpu"""
|
1182
1233
|
self.radios_slim[:] = self.radios[:, i_slice, :]
|
1183
1234
|
|
1184
|
-
def reset_translation_diagnostics_accumulators(self):
|
1185
|
-
self.diagnostic_radios[:] = 0
|
1186
|
-
self.diagnostic_weights[:] = 0
|
1187
|
-
self.diagnostic_proj_angle[1] = (2**30) * 3.14
|
1188
|
-
self.diagnostic_proj_angle[0] = (2**30 - 1) * 3.14
|
1189
|
-
|
1190
1235
|
def process_chunk(self, sub_region=None):
|
1191
1236
|
self._private_process_chunk(sub_region=sub_region)
|
1192
1237
|
self._process_finalize()
|
@@ -1194,11 +1239,11 @@ class HelicalChunkedRegriddedPipeline:
|
|
1194
1239
|
def _private_process_chunk(self, sub_region=None):
|
1195
1240
|
assert sub_region is not None, "sub_region argument is mandatory in helical pipeline"
|
1196
1241
|
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1242
|
+
# Every chunk has its diagnostic, that is good to follow the trends in helical scans
|
1243
|
+
# or zstages
|
1244
|
+
self._reset_diagnostics()
|
1200
1245
|
|
1201
|
-
|
1246
|
+
self.set_subregion(sub_region)
|
1202
1247
|
|
1203
1248
|
(subr_start_x, subr_end_x, subr_start_z, subr_end_z) = self.sub_region
|
1204
1249
|
|
@@ -1218,8 +1263,13 @@ class HelicalChunkedRegriddedPipeline:
|
|
1218
1263
|
|
1219
1264
|
my_first_pnum = proj_num_start
|
1220
1265
|
|
1221
|
-
self.
|
1222
|
-
|
1266
|
+
if self.diag_zpro_run == 0:
|
1267
|
+
# It may seem anodine, but setting a huge vector to zero
|
1268
|
+
# takes time.
|
1269
|
+
# In diagnostic collection mode we can spare it. On the other hand nothing has would be allocated for the data
|
1270
|
+
# in such case
|
1271
|
+
self.gridded_cumulated_weights[:] = 0
|
1272
|
+
self.gridded_radios[:] = 0
|
1223
1273
|
|
1224
1274
|
for pnum_start, pnum_end in zip(pnum_start_list, pnum_end_list):
|
1225
1275
|
start_in_chunk = pnum_start - my_first_pnum
|
@@ -1229,60 +1279,102 @@ class HelicalChunkedRegriddedPipeline:
|
|
1229
1279
|
slice(pnum_start, pnum_end), slice(start_in_chunk, end_in_chunk), chunk_info
|
1230
1280
|
)
|
1231
1281
|
|
1232
|
-
|
1282
|
+
if not self.diag_zpro_run:
|
1283
|
+
# when we collect diagnostics we dont collect all the data
|
1284
|
+
# so there would be nothing to process here
|
1233
1285
|
|
1234
|
-
|
1235
|
-
paganin_margin = self._phase_margin_up
|
1236
|
-
if paganin_margin:
|
1237
|
-
data_to_dump = self.gridded_radios[:, paganin_margin:-paganin_margin, :]
|
1238
|
-
else:
|
1239
|
-
data_to_dump = self.gridded_radios
|
1240
|
-
self._dump_data_to_file("flatfield", data_to_dump)
|
1241
|
-
if self.process_config.nabu_config["pipeline"]["skip_after_flatfield_dump"]:
|
1242
|
-
return
|
1286
|
+
self.gridded_radios[:] /= self.gridded_cumulated_weights
|
1243
1287
|
|
1244
|
-
|
1245
|
-
self._ccd_corrections()
|
1288
|
+
self.correct_for_missing_angles()
|
1246
1289
|
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1290
|
+
linea = self.gridded_cumulated_weights.sum(axis=(1, 2))
|
1291
|
+
i_zero_list = np.where(linea == 0)[0]
|
1292
|
+
for i_zero in i_zero_list:
|
1293
|
+
if i_zero > linea.shape[0] // 2:
|
1294
|
+
direction = -1
|
1295
|
+
else:
|
1296
|
+
direction = 1
|
1297
|
+
i = i_zero
|
1298
|
+
while ((i >= 0 and direction == -1) or ((i < linea.shape[0] - 1) and direction == 1)) and linea[i] == 0:
|
1299
|
+
i += direction
|
1300
|
+
if linea[i]:
|
1301
|
+
self.gridded_radios[i_zero] = self.gridded_radios[i]
|
1302
|
+
self.gridded_cumulated_weights[i_zero] = self.gridded_cumulated_weights[i]
|
1303
|
+
|
1304
|
+
if "flatfield" in self._data_dump:
|
1305
|
+
paganin_margin = self._phase_margin_up
|
1306
|
+
if paganin_margin:
|
1307
|
+
data_to_dump = self.gridded_radios[:, paganin_margin:-paganin_margin, :]
|
1308
|
+
else:
|
1309
|
+
data_to_dump = self.gridded_radios
|
1310
|
+
self._dump_data_to_file("flatfield", data_to_dump)
|
1311
|
+
if self.process_config.nabu_config["pipeline"]["skip_after_flatfield_dump"]:
|
1312
|
+
return
|
1313
|
+
|
1314
|
+
if "ccd_correction" in self.processing_steps:
|
1315
|
+
self._ccd_corrections()
|
1316
|
+
|
1317
|
+
if cxx_paganin is None:
|
1318
|
+
if ("phase" in self.processing_steps) or ("unsharp_mask" in self.processing_steps):
|
1319
|
+
self._retrieve_phase()
|
1320
|
+
if "unsharp_mask" in self.processing_steps:
|
1321
|
+
self._apply_unsharp()
|
1322
|
+
else:
|
1323
|
+
self._nophase_put_to_radios(self.radios, self.gridded_radios)
|
1324
|
+
else:
|
1325
|
+
if "phase" in self.processing_steps:
|
1326
|
+
pr = self.phase_retrieval
|
1327
|
+
paganin_l_micron = math.sqrt(pr.wavelength_micron * pr.distance_micron * pr.delta_beta * math.pi)
|
1328
|
+
cxx_paganin.paganin_pyhst(
|
1329
|
+
data_raw=self.gridded_radios,
|
1330
|
+
output=self.radios,
|
1331
|
+
num_of_threads=-1,
|
1332
|
+
paganin_marge=self._phase_margin_up,
|
1333
|
+
paganin_l_micron=paganin_l_micron / pr.pixel_size_micron,
|
1334
|
+
image_pixel_size_y=1.0,
|
1335
|
+
image_pixel_size_x=1.0,
|
1336
|
+
unsharp_sigma=self.unsharp_sigma,
|
1337
|
+
unsharp_coeff=self.unsharp_coeff,
|
1338
|
+
unsharp_LoG=int((self.unsharp_method == "log")),
|
1339
|
+
)
|
1340
|
+
else:
|
1341
|
+
self._nophase_put_to_radios(self.radios, self.gridded_radios)
|
1253
1342
|
|
1254
|
-
|
1255
|
-
|
1343
|
+
self.logger.info(" LOG ")
|
1344
|
+
self._nophase_put_to_radios(self.radios_weights, self.gridded_cumulated_weights)
|
1256
1345
|
|
1257
|
-
|
1258
|
-
|
1346
|
+
# print( " processing steps ", self.processing_steps )
|
1347
|
+
# ['read_chunk', 'flatfield', 'double_flatfield', 'take_log', 'reconstruction', 'save']
|
1259
1348
|
|
1260
|
-
|
1261
|
-
|
1349
|
+
if "take_log" in self.processing_steps:
|
1350
|
+
self._take_log()
|
1262
1351
|
|
1263
|
-
|
1352
|
+
self.logger.info(" BALANCE ")
|
1264
1353
|
|
1265
|
-
|
1354
|
+
self.balance_weights()
|
1266
1355
|
|
1267
|
-
|
1356
|
+
num_slices = self.radios.shape[1]
|
1268
1357
|
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1358
|
+
self.logger.info(" NORMALIZE")
|
1359
|
+
self._normalize_sinos()
|
1360
|
+
self._dump_sinogram()
|
1272
1361
|
|
1273
1362
|
if "reconstruction" in self.processing_steps:
|
1274
|
-
|
1275
|
-
|
1363
|
+
if not self.diag_zpro_run:
|
1364
|
+
# otherwise, when collecting diagnostic, we are not interested in the remaining steps
|
1365
|
+
# on the other hand there would be nothing to process because only diagnostics have been collected
|
1366
|
+
for i_slice in range(num_slices):
|
1367
|
+
self._post_primary_data_reduction(i_slice) # charge on self.radios_slim
|
1276
1368
|
|
1277
|
-
|
1369
|
+
self._filter()
|
1278
1370
|
|
1279
|
-
|
1371
|
+
self.apply_weights(i_slice)
|
1280
1372
|
|
1281
|
-
|
1373
|
+
self._build_sino()
|
1282
1374
|
|
1283
|
-
|
1375
|
+
self._reconstruct(chunk_info=chunk_info, i_slice=i_slice)
|
1284
1376
|
|
1285
|
-
|
1377
|
+
self._compute_histogram(i_slice=i_slice, num_slices=num_slices)
|
1286
1378
|
|
1287
1379
|
self._write_data()
|
1288
1380
|
|
@@ -1299,9 +1391,27 @@ class HelicalChunkedRegriddedPipeline:
|
|
1299
1391
|
: end_angle_index - first_angle_index
|
1300
1392
|
]
|
1301
1393
|
|
1394
|
+
def correct_for_missing_angles(self):
|
1395
|
+
"""For non helical scan, the rotation is often incomplete ( < 360)
|
1396
|
+
here we complement the missing angles
|
1397
|
+
"""
|
1398
|
+
linea = self.gridded_cumulated_weights.sum(axis=(1, 2))
|
1399
|
+
i_zero_list = np.where(linea == 0)[0]
|
1400
|
+
for i_zero in i_zero_list:
|
1401
|
+
if i_zero > linea.shape[0] // 2:
|
1402
|
+
direction = -1
|
1403
|
+
else:
|
1404
|
+
direction = 1
|
1405
|
+
i = i_zero
|
1406
|
+
while ((i >= 0 and direction == -1) or ((i < linea.shape[0] - 1) and direction == 1)) and linea[i] == 0:
|
1407
|
+
i += direction
|
1408
|
+
if linea[i]:
|
1409
|
+
self.gridded_radios[i_zero] = self.gridded_radios[i]
|
1410
|
+
self.gridded_cumulated_weights[i_zero] = self.gridded_cumulated_weights[i]
|
1411
|
+
|
1302
1412
|
@classmethod
|
1303
1413
|
def estimate_required_memory(
|
1304
|
-
cls, process_config, reading_granularity=None, chunk_size=None, margin_v=0, span_info=None
|
1414
|
+
cls, process_config, reading_granularity=None, chunk_size=None, margin_v=0, span_info=None, diag_zpro_run=0
|
1305
1415
|
):
|
1306
1416
|
"""
|
1307
1417
|
Estimate the memory (RAM) needed for a reconstruction.
|
@@ -1340,11 +1450,12 @@ class HelicalChunkedRegriddedPipeline:
|
|
1340
1450
|
binning_z = nabu_config["dataset"]["binning_z"]
|
1341
1451
|
projections_subsampling = nabu_config["dataset"]["projections_subsampling"]
|
1342
1452
|
|
1343
|
-
|
1344
|
-
|
1453
|
+
if not diag_zpro_run:
|
1454
|
+
# the gridded target
|
1455
|
+
total_memory_needed += Nx * (2 * margin_v + chunk_size) * n_gridded_angles * 4
|
1345
1456
|
|
1346
|
-
|
1347
|
-
|
1457
|
+
# the gridded weights
|
1458
|
+
total_memory_needed += Nx * (2 * margin_v + chunk_size) * n_gridded_angles * 4
|
1348
1459
|
|
1349
1460
|
# the read grain
|
1350
1461
|
total_memory_needed += (
|
@@ -1383,7 +1494,7 @@ class HelicalChunkedRegriddedPipeline:
|
|
1383
1494
|
# Reconstruction
|
1384
1495
|
# ---------------
|
1385
1496
|
reconstructed_volume_size = 0
|
1386
|
-
if "reconstruction" in processing_steps:
|
1497
|
+
if "reconstruction" in processing_steps and not diag_zpro_run:
|
1387
1498
|
## radios_slim is used to process one slice at once, It will be on the gpu
|
1388
1499
|
## and cannot be reduced further, therefore no need to estimate it.
|
1389
1500
|
## Either it passes or it does not.
|