nabu 2024.2.14__py3-none-any.whl → 2025.1.0__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.
Files changed (197) hide show
  1. doc/doc_config.py +32 -0
  2. nabu/__init__.py +1 -1
  3. nabu/app/bootstrap_stitching.py +4 -2
  4. nabu/app/cast_volume.py +16 -14
  5. nabu/app/cli_configs.py +102 -9
  6. nabu/app/compare_volumes.py +1 -1
  7. nabu/app/composite_cor.py +2 -4
  8. nabu/app/diag_to_pix.py +5 -6
  9. nabu/app/diag_to_rot.py +10 -11
  10. nabu/app/double_flatfield.py +18 -5
  11. nabu/app/estimate_motion.py +75 -0
  12. nabu/app/multicor.py +28 -15
  13. nabu/app/parse_reconstruction_log.py +1 -0
  14. nabu/app/pcaflats.py +122 -0
  15. nabu/app/prepare_weights_double.py +1 -2
  16. nabu/app/reconstruct.py +1 -7
  17. nabu/app/reconstruct_helical.py +5 -9
  18. nabu/app/reduce_dark_flat.py +5 -4
  19. nabu/app/rotate.py +3 -1
  20. nabu/app/stitching.py +7 -2
  21. nabu/app/tests/test_reduce_dark_flat.py +2 -2
  22. nabu/app/validator.py +1 -4
  23. nabu/cuda/convolution.py +1 -1
  24. nabu/cuda/fft.py +1 -1
  25. nabu/cuda/medfilt.py +1 -1
  26. nabu/cuda/padding.py +1 -1
  27. nabu/cuda/src/backproj.cu +6 -6
  28. nabu/cuda/src/cone.cu +4 -0
  29. nabu/cuda/src/hierarchical_backproj.cu +14 -0
  30. nabu/cuda/utils.py +2 -2
  31. nabu/estimation/alignment.py +17 -31
  32. nabu/estimation/cor.py +27 -33
  33. nabu/estimation/cor_sino.py +2 -8
  34. nabu/estimation/focus.py +4 -8
  35. nabu/estimation/motion.py +557 -0
  36. nabu/estimation/tests/test_alignment.py +2 -0
  37. nabu/estimation/tests/test_motion_estimation.py +471 -0
  38. nabu/estimation/tests/test_tilt.py +1 -1
  39. nabu/estimation/tilt.py +6 -5
  40. nabu/estimation/translation.py +47 -1
  41. nabu/io/cast_volume.py +108 -18
  42. nabu/io/detector_distortion.py +5 -6
  43. nabu/io/reader.py +45 -6
  44. nabu/io/reader_helical.py +5 -4
  45. nabu/io/tests/test_cast_volume.py +2 -2
  46. nabu/io/tests/test_readers.py +41 -38
  47. nabu/io/tests/test_remove_volume.py +152 -0
  48. nabu/io/tests/test_writers.py +2 -2
  49. nabu/io/utils.py +8 -4
  50. nabu/io/writer.py +1 -2
  51. nabu/misc/fftshift.py +1 -1
  52. nabu/misc/fourier_filters.py +1 -1
  53. nabu/misc/histogram.py +1 -1
  54. nabu/misc/histogram_cuda.py +1 -1
  55. nabu/misc/padding_base.py +1 -1
  56. nabu/misc/rotation.py +1 -1
  57. nabu/misc/rotation_cuda.py +1 -1
  58. nabu/misc/tests/test_binning.py +1 -1
  59. nabu/misc/transpose.py +1 -1
  60. nabu/misc/unsharp.py +1 -1
  61. nabu/misc/unsharp_cuda.py +1 -1
  62. nabu/misc/unsharp_opencl.py +1 -1
  63. nabu/misc/utils.py +1 -1
  64. nabu/opencl/fft.py +1 -1
  65. nabu/opencl/padding.py +1 -1
  66. nabu/opencl/src/backproj.cl +6 -6
  67. nabu/opencl/utils.py +8 -8
  68. nabu/pipeline/config.py +2 -2
  69. nabu/pipeline/config_validators.py +46 -46
  70. nabu/pipeline/datadump.py +3 -3
  71. nabu/pipeline/estimators.py +271 -11
  72. nabu/pipeline/fullfield/chunked.py +103 -67
  73. nabu/pipeline/fullfield/chunked_cuda.py +5 -2
  74. nabu/pipeline/fullfield/computations.py +4 -1
  75. nabu/pipeline/fullfield/dataset_validator.py +0 -1
  76. nabu/pipeline/fullfield/get_double_flatfield.py +147 -0
  77. nabu/pipeline/fullfield/nabu_config.py +36 -17
  78. nabu/pipeline/fullfield/processconfig.py +41 -7
  79. nabu/pipeline/fullfield/reconstruction.py +14 -10
  80. nabu/pipeline/helical/dataset_validator.py +3 -4
  81. nabu/pipeline/helical/fbp.py +4 -4
  82. nabu/pipeline/helical/filtering.py +5 -4
  83. nabu/pipeline/helical/gridded_accumulator.py +10 -11
  84. nabu/pipeline/helical/helical_chunked_regridded.py +1 -0
  85. nabu/pipeline/helical/helical_reconstruction.py +12 -9
  86. nabu/pipeline/helical/helical_utils.py +1 -2
  87. nabu/pipeline/helical/nabu_config.py +2 -1
  88. nabu/pipeline/helical/span_strategy.py +1 -0
  89. nabu/pipeline/helical/weight_balancer.py +2 -3
  90. nabu/pipeline/params.py +20 -3
  91. nabu/pipeline/tests/__init__.py +0 -0
  92. nabu/pipeline/tests/test_estimators.py +240 -3
  93. nabu/pipeline/utils.py +1 -1
  94. nabu/pipeline/writer.py +1 -1
  95. nabu/preproc/alignment.py +0 -10
  96. nabu/preproc/ccd.py +53 -3
  97. nabu/preproc/ctf.py +8 -8
  98. nabu/preproc/ctf_cuda.py +1 -1
  99. nabu/preproc/double_flatfield_cuda.py +2 -2
  100. nabu/preproc/double_flatfield_variable_region.py +0 -1
  101. nabu/preproc/flatfield.py +307 -2
  102. nabu/preproc/flatfield_cuda.py +1 -2
  103. nabu/preproc/flatfield_variable_region.py +3 -3
  104. nabu/preproc/phase.py +2 -4
  105. nabu/preproc/phase_cuda.py +2 -2
  106. nabu/preproc/shift.py +4 -2
  107. nabu/preproc/shift_cuda.py +0 -1
  108. nabu/preproc/tests/test_ctf.py +4 -4
  109. nabu/preproc/tests/test_double_flatfield.py +1 -1
  110. nabu/preproc/tests/test_flatfield.py +1 -1
  111. nabu/preproc/tests/test_paganin.py +1 -3
  112. nabu/preproc/tests/test_pcaflats.py +154 -0
  113. nabu/preproc/tests/test_vshift.py +4 -1
  114. nabu/processing/azim.py +9 -5
  115. nabu/processing/convolution_cuda.py +6 -4
  116. nabu/processing/fft_base.py +7 -3
  117. nabu/processing/fft_cuda.py +25 -164
  118. nabu/processing/fft_opencl.py +28 -6
  119. nabu/processing/fftshift.py +1 -1
  120. nabu/processing/histogram.py +1 -1
  121. nabu/processing/muladd.py +0 -1
  122. nabu/processing/padding_base.py +1 -1
  123. nabu/processing/padding_cuda.py +0 -2
  124. nabu/processing/processing_base.py +12 -6
  125. nabu/processing/rotation_cuda.py +3 -1
  126. nabu/processing/tests/test_fft.py +2 -64
  127. nabu/processing/tests/test_fftshift.py +1 -1
  128. nabu/processing/tests/test_medfilt.py +1 -3
  129. nabu/processing/tests/test_padding.py +1 -1
  130. nabu/processing/tests/test_roll.py +1 -1
  131. nabu/processing/tests/test_rotation.py +4 -2
  132. nabu/processing/unsharp_opencl.py +1 -1
  133. nabu/reconstruction/astra.py +245 -0
  134. nabu/reconstruction/cone.py +39 -9
  135. nabu/reconstruction/fbp.py +7 -0
  136. nabu/reconstruction/fbp_base.py +36 -5
  137. nabu/reconstruction/filtering.py +59 -25
  138. nabu/reconstruction/filtering_cuda.py +22 -21
  139. nabu/reconstruction/filtering_opencl.py +10 -14
  140. nabu/reconstruction/hbp.py +26 -13
  141. nabu/reconstruction/mlem.py +55 -16
  142. nabu/reconstruction/projection.py +3 -5
  143. nabu/reconstruction/sinogram.py +1 -1
  144. nabu/reconstruction/sinogram_cuda.py +0 -1
  145. nabu/reconstruction/tests/test_cone.py +37 -2
  146. nabu/reconstruction/tests/test_deringer.py +4 -4
  147. nabu/reconstruction/tests/test_fbp.py +36 -15
  148. nabu/reconstruction/tests/test_filtering.py +27 -7
  149. nabu/reconstruction/tests/test_halftomo.py +28 -2
  150. nabu/reconstruction/tests/test_mlem.py +94 -64
  151. nabu/reconstruction/tests/test_projector.py +7 -2
  152. nabu/reconstruction/tests/test_reconstructor.py +1 -1
  153. nabu/reconstruction/tests/test_sino_normalization.py +0 -1
  154. nabu/resources/dataset_analyzer.py +210 -24
  155. nabu/resources/gpu.py +4 -4
  156. nabu/resources/logger.py +4 -4
  157. nabu/resources/nxflatfield.py +103 -37
  158. nabu/resources/tests/test_dataset_analyzer.py +37 -0
  159. nabu/resources/tests/test_extract.py +11 -0
  160. nabu/resources/tests/test_nxflatfield.py +5 -5
  161. nabu/resources/utils.py +16 -10
  162. nabu/stitching/alignment.py +8 -11
  163. nabu/stitching/config.py +44 -35
  164. nabu/stitching/definitions.py +2 -2
  165. nabu/stitching/frame_composition.py +8 -10
  166. nabu/stitching/overlap.py +4 -4
  167. nabu/stitching/sample_normalization.py +5 -5
  168. nabu/stitching/slurm_utils.py +2 -2
  169. nabu/stitching/stitcher/base.py +2 -0
  170. nabu/stitching/stitcher/dumper/base.py +0 -1
  171. nabu/stitching/stitcher/dumper/postprocessing.py +1 -1
  172. nabu/stitching/stitcher/post_processing.py +11 -9
  173. nabu/stitching/stitcher/pre_processing.py +37 -31
  174. nabu/stitching/stitcher/single_axis.py +2 -3
  175. nabu/stitching/stitcher_2D.py +2 -1
  176. nabu/stitching/tests/test_config.py +10 -11
  177. nabu/stitching/tests/test_sample_normalization.py +1 -1
  178. nabu/stitching/tests/test_slurm_utils.py +1 -2
  179. nabu/stitching/tests/test_y_preprocessing_stitching.py +11 -8
  180. nabu/stitching/tests/test_z_postprocessing_stitching.py +3 -3
  181. nabu/stitching/tests/test_z_preprocessing_stitching.py +27 -24
  182. nabu/stitching/utils/tests/__init__.py +0 -0
  183. nabu/stitching/utils/tests/test_post-processing.py +1 -0
  184. nabu/stitching/utils/utils.py +16 -18
  185. nabu/tests.py +0 -3
  186. nabu/testutils.py +62 -9
  187. nabu/utils.py +50 -20
  188. {nabu-2024.2.14.dist-info → nabu-2025.1.0.dist-info}/METADATA +7 -7
  189. nabu-2025.1.0.dist-info/RECORD +328 -0
  190. {nabu-2024.2.14.dist-info → nabu-2025.1.0.dist-info}/WHEEL +1 -1
  191. {nabu-2024.2.14.dist-info → nabu-2025.1.0.dist-info}/entry_points.txt +2 -1
  192. nabu/app/correct_rot.py +0 -70
  193. nabu/io/tests/test_detector_distortion.py +0 -178
  194. nabu-2024.2.14.dist-info/RECORD +0 -317
  195. /nabu/{stitching → app}/tests/__init__.py +0 -0
  196. {nabu-2024.2.14.dist-info → nabu-2025.1.0.dist-info}/licenses/LICENSE +0 -0
  197. {nabu-2024.2.14.dist-info → nabu-2025.1.0.dist-info}/top_level.txt +0 -0
nabu/app/pcaflats.py ADDED
@@ -0,0 +1,122 @@
1
+ import sys
2
+ import os
3
+ import numpy as np
4
+ import h5py
5
+ from .utils import parse_params_values
6
+ from ..utils import is_writeable
7
+ from .cli_configs import PCAFlatsConfig
8
+ from .. import version
9
+ from ..preproc.flatfield import PCAFlatsDecomposer
10
+ from ..io.reader import NXDarksFlats
11
+
12
+
13
+ def get_flats_darks_in_nx(filename):
14
+ dfreader = NXDarksFlats(filename)
15
+ darks = np.concatenate([d for d in dfreader.get_raw_darks()], axis=0)
16
+ flats = np.concatenate([f for f in dfreader.get_raw_flats()], axis=0)
17
+ entry = dfreader.flats_reader.data_path.lstrip("/").split("/")[0]
18
+ return flats, darks, entry
19
+
20
+
21
+ def get_flats_darks_from_h5(filename):
22
+ flats = []
23
+ darks = []
24
+ with h5py.File(filename, "r") as f:
25
+ for k, v in f.items():
26
+ if k == "1.1":
27
+ detector_name = decode_bytes(f["1.1/technique/tomoconfig/detector"][()][0])
28
+ else:
29
+ try:
30
+ image_key = v["technique/image_key"][()]
31
+ except:
32
+ raise NotImplementedError(
33
+ "Legacy h5 file format is not handled. The entry of the h5 file should contain a 'technique/image_key' group."
34
+ )
35
+ if image_key == 2: # Darks
36
+ darks.append(v[f"instrument/{detector_name}/data"][()])
37
+ elif image_key == 1: # Flats
38
+ flats.append(v[f"instrument/{detector_name}/data"][()])
39
+
40
+ flats = np.concatenate([f for f in flats], axis=0)
41
+ darks = np.concatenate([d for d in darks], axis=0)
42
+ return flats, darks, "entry0000" # TODO this will be problematic on the reconstruction side
43
+
44
+
45
+ def pcaflats_decomposition(
46
+ flats, darks, pcaflats_filename="PCAFlats.h5", overwrite=False, entry="entry0000", nsigma=3.0
47
+ ):
48
+ """Compute the PCS decomposition of a series of flats and darks, possibly taken from various scans."""
49
+ try:
50
+ decomposer = PCAFlatsDecomposer(flats, darks, nsigma=nsigma)
51
+ decomposer.save_decomposition(pcaflats_filename, overwrite=overwrite, entry=entry)
52
+ success = True
53
+ except:
54
+ success = False
55
+ raise ValueError("An error occured in the PCA deccomposition.")
56
+ return success
57
+
58
+
59
+ def decode_bytes(content):
60
+ if isinstance(content, bytes):
61
+ return content.decode()
62
+ else:
63
+ return content
64
+
65
+
66
+ def main(argv=None):
67
+ """Compute PCA Flats on a series of datasets (h5 or NX)."""
68
+ if argv is None:
69
+ argv = sys.argv[1:]
70
+
71
+ args = parse_params_values(
72
+ PCAFlatsConfig,
73
+ parser_description=f"Compute a PCA Decomposition of flats acquired from various datasets..",
74
+ program_version="nabu " + version,
75
+ user_args=argv,
76
+ )
77
+
78
+ # Get "where to write".
79
+ if args["output_filename"] is None:
80
+ abspath = os.path.abspath("./PCAFlats.hdf5")
81
+ else:
82
+ abspath = os.path.abspath(args["output_filename"])
83
+
84
+ pcaflats_dir = os.path.dirname(abspath)
85
+ pcaflats_filename = os.path.basename(abspath)
86
+
87
+ if is_writeable(pcaflats_dir):
88
+ output_path = os.path.join(pcaflats_dir, pcaflats_filename)
89
+ else:
90
+ raise ValueError(f"Output dir {pcaflats_dir} is not writeable.")
91
+
92
+ # raise error if file exists and overwrite=False
93
+ if not args["overwrite"] and os.path.exists(output_path):
94
+ raise FileExistsError(f"Output file {output_path} already exists. Use --overwrite to overwrite it.")
95
+
96
+ # Collect raw darks and flats
97
+ flats_stack = []
98
+ darks_stack = []
99
+
100
+ for dataset in args["datasets"]:
101
+ filename = os.path.basename(dataset)
102
+ kind = filename.split(".")[-1]
103
+ if kind == "nx":
104
+ flats, darks, entry = get_flats_darks_in_nx(dataset)
105
+ elif kind in ("h5", "hdf5"):
106
+ flats, darks, entry = get_flats_darks_from_h5(dataset)
107
+
108
+ flats_stack.append(flats)
109
+ darks_stack.append(darks)
110
+
111
+ flats = np.concatenate(flats_stack, axis=0)
112
+ darks = np.concatenate(darks_stack, axis=0)
113
+
114
+ exit(
115
+ pcaflats_decomposition(
116
+ flats, darks, pcaflats_filename=output_path, overwrite=args["overwrite"], entry=entry, nsigma=args["nsigma"]
117
+ )
118
+ )
119
+
120
+
121
+ if __name__ == "__main__":
122
+ main()
@@ -4,7 +4,6 @@ from scipy.special import erf # pylint: disable=all
4
4
  import sys
5
5
  import os
6
6
  from scipy.ndimage import gaussian_filter
7
- from nxtomo.nxobject.nxdetector import ImageKey
8
7
  from nabu.resources.nxflatfield import update_dataset_info_flats_darks
9
8
  from nabu.resources.dataset_analyzer import HDF5DatasetAnalyzer
10
9
  from ..io.reader import load_images_from_dataurl_dict
@@ -57,7 +56,7 @@ def main(argv=None):
57
56
  beam_profile = 0
58
57
  my_flats = load_images_from_dataurl_dict(dataset_info.flats)
59
58
 
60
- for key, flat in my_flats.items():
59
+ for flat in my_flats.values():
61
60
  beam_profile += flat
62
61
  beam_profile = beam_profile / len(list(dataset_info.flats.keys()))
63
62
 
nabu/app/reconstruct.py CHANGED
@@ -1,5 +1,5 @@
1
- from tomoscan.io import HDF5File
2
1
  from .. import version
2
+ from ..io.reader import list_hdf5_entries
3
3
  from ..utils import list_match_queries
4
4
  from ..pipeline.config import parse_nabu_config_file
5
5
  from ..pipeline.config_validators import convert_to_int
@@ -92,12 +92,6 @@ def get_reconstructor(args, overwrite_options=None):
92
92
  return reconstructor
93
93
 
94
94
 
95
- def list_hdf5_entries(fname):
96
- with HDF5File(fname, "r") as f:
97
- entries = list(f.keys())
98
- return entries
99
-
100
-
101
95
  def main():
102
96
  args = parse_params_values(
103
97
  ReconstructConfig,
@@ -1,7 +1,6 @@
1
1
  from .. import version
2
2
  from ..resources.utils import is_hdf5_extension
3
3
  from ..pipeline.config import parse_nabu_config_file
4
- from ..pipeline.config_validators import convert_to_int
5
4
  from .cli_configs import ReconstructConfig
6
5
  from .utils import parse_params_values
7
6
  from .reconstruct import update_reconstruction_start_end, get_log_file
@@ -39,7 +38,7 @@ def main_helical():
39
38
  try:
40
39
  from silx.math.fft.cufft import CUFFT
41
40
  except: # can't catch narrower - cublasNotInitialized requires cublas !
42
- CUFFT = None
41
+ CUFFT = None # noqa: F841
43
42
  #
44
43
 
45
44
  logfile = get_log_file(args["logfile"], args["log_file"], forbidden=[args["input_file"]])
@@ -58,9 +57,6 @@ def main_helical():
58
57
 
59
58
  # Determine which reconstructor to use
60
59
  reconstructor_cls = None
61
- phase_method = None
62
- if "phase" in proc.processing_steps:
63
- phase_method = proc.processing_options["phase"]["method"]
64
60
 
65
61
  # fix the reconstruction roi if not given
66
62
  if "reconstruction" in proc.processing_steps:
@@ -71,11 +67,11 @@ def main_helical():
71
67
 
72
68
  if proc.nabu_config["reconstruction"]["auto_size"]:
73
69
  if 2 * rot_center > Nx:
74
- w = int(round(2 * rot_center))
70
+ w = round(2 * rot_center)
75
71
  else:
76
- w = int(round(2 * Nx - 2 * rot_center))
77
- rec_config["start_x"] = int(round(rot_center - w / 2))
78
- rec_config["end_x"] = int(round(rot_center + w / 2))
72
+ w = round(2 * Nx - 2 * rot_center)
73
+ rec_config["start_x"] = round(rot_center - w / 2)
74
+ rec_config["end_x"] = round(rot_center + w / 2)
79
75
 
80
76
  rec_config["start_y"] = rec_config["start_x"]
81
77
  rec_config["end_y"] = rec_config["end_x"]
@@ -1,6 +1,4 @@
1
1
  import sys
2
- import logging
3
- import argparse
4
2
  from typing import Optional
5
3
 
6
4
  from nabu.app.cli_configs import ReduceDarkFlatConfig
@@ -14,6 +12,9 @@ from tomoscan.factory import Factory
14
12
  from silx.io.url import DataUrl
15
13
 
16
14
 
15
+ reduce_methods = tuple(member.value for member in ReduceMethod)
16
+
17
+
17
18
  def _create_data_urls(output_file: Optional[str], output_data_path: Optional[str], name: str):
18
19
  """
19
20
  util function to compute reduced Data and metadata url(s)
@@ -67,8 +68,8 @@ def reduce_dark_flat(
67
68
  """
68
69
  calculation of the darks / flats calling tomoscan utils function
69
70
  """
70
- dark_method = ReduceMethod.from_value(dark_method) if dark_method is not None else None
71
- flat_method = ReduceMethod.from_value(flat_method) if flat_method is not None else None
71
+ dark_method = ReduceMethod(dark_method) if dark_method is not None else None
72
+ flat_method = ReduceMethod(flat_method) if flat_method is not None else None
72
73
 
73
74
  # 1. define url where to save the file
74
75
  ## 1.1 for darks
nabu/app/rotate.py CHANGED
@@ -7,6 +7,8 @@ from multiprocessing.pool import ThreadPool
7
7
  import numpy as np
8
8
  from tomoscan.io import HDF5File
9
9
  from tomoscan.esrf.scan.nxtomoscan import NXtomoScan
10
+
11
+ from nabu.utils import first_generator_item
10
12
  from ..io.utils import get_first_hdf5_entry
11
13
  from ..processing.rotation import Rotation
12
14
  from ..resources.logger import Logger, LoggerOrPrint
@@ -61,7 +63,7 @@ class HDF5ImagesStackRotation:
61
63
  self.output_file = output_file
62
64
  copy(self.input_file, output_file)
63
65
 
64
- first_proj_url = self.dataset_info.projections[list(self.dataset_info.projections.keys())[0]]
66
+ first_proj_url = self.dataset_info.projections[first_generator_item(self.dataset_info.projections.keys())]
65
67
  self.data_path = first_proj_url.data_path()
66
68
  dirname, basename = posixpath.split(self.data_path)
67
69
  self._data_path_dirname = dirname
nabu/app/stitching.py CHANGED
@@ -67,12 +67,17 @@ def main():
67
67
  futures = {}
68
68
  # 2.1 launch jobs
69
69
  slurm_job_progress_bars: dict = {}
70
+
71
+ # set job name
72
+ final_output_object_identifier = stitching_config.get_output_object().get_identifier().to_str()
73
+ stitching_config.slurm_config.job_name = f"stitching-{final_output_object_identifier}"
74
+
70
75
  for i_job, (job, sub_config) in enumerate(
71
76
  split_stitching_configuration_to_slurm_job(stitching_config, yield_configuration=True)
72
77
  ):
73
78
  _logger.info(f"submit job nb {i_job}: handles {sub_config.slices}")
74
- output_volume = sub_config.get_output_object().get_identifier().to_str()
75
- futures[output_volume] = submit(job, timeout=999999)
79
+ output_object = sub_config.get_output_object().get_identifier().to_str()
80
+ futures[output_object] = submit(job, timeout=999999)
76
81
  # note on total=100: we only consider percentage in this case (providing advancement from slurm jobs)
77
82
  slurm_job_progress_bars[job] = tqdm(
78
83
  total=100,
@@ -9,7 +9,7 @@ except ImportError:
9
9
  from tomoscan.test.utils import NXtomoMockContext
10
10
 
11
11
 
12
- @pytest.fixture(scope="function")
12
+ @pytest.fixture
13
13
  def hdf5_scan(tmp_path):
14
14
  """simple fixture to create a scan and provide it to another function"""
15
15
  test_dir = tmp_path / "my_hdf5_scan"
@@ -26,7 +26,7 @@ def hdf5_scan(tmp_path):
26
26
 
27
27
  @pytest.mark.parametrize("dark_method", (None, "first", "mean"))
28
28
  @pytest.mark.parametrize("flat_method", (None, "last", "median"))
29
- def test_reduce_dark_flat_hdf5(tmp_path, hdf5_scan, dark_method, flat_method): # noqa F811
29
+ def test_reduce_dark_flat_hdf5(tmp_path, hdf5_scan, dark_method, flat_method):
30
30
  """simply test output - processing is tested at tomoscan side"""
31
31
  # test with default url
32
32
  default_darks_path = os.path.join(hdf5_scan.path, hdf5_scan.get_dataset_basename() + "_darks.hdf5")
nabu/app/validator.py CHANGED
@@ -1,6 +1,3 @@
1
- #!/usr/bin/env python
2
- # -*- coding: utf-8 -*-
3
-
4
1
  import argparse
5
2
  import sys
6
3
  import os
@@ -19,7 +16,7 @@ def get_scans(path, entries: str):
19
16
  if entries == "__all__":
20
17
  entries = NXtomoScan.get_valid_entries(path)
21
18
  for entry in entries:
22
- res.append(NXtomoScan(path, entry))
19
+ res.append(NXtomoScan(path, entry)) # noqa: PERF401
23
20
  else:
24
21
  raise TypeError(f"{path} does not looks like a folder containing .EDF or a valid nexus file ")
25
22
  return res
nabu/cuda/convolution.py CHANGED
@@ -1,4 +1,4 @@
1
- from ..processing.convolution_cuda import *
1
+ from ..processing.convolution_cuda import * # noqa: F403
2
2
  from ..utils import deprecation_warning
3
3
 
4
4
  deprecation_warning(
nabu/cuda/fft.py CHANGED
@@ -1,4 +1,4 @@
1
- from ..processing.fft_cuda import *
1
+ from ..processing.fft_cuda import * # noqa: F403
2
2
  from ..utils import deprecation_warning
3
3
 
4
4
  deprecation_warning("nabu.cuda.fft has been moved to nabu.processing.fft_cuda", do_print=True, func_name="fft_cuda")
nabu/cuda/medfilt.py CHANGED
@@ -1,4 +1,4 @@
1
- from ..processing.medfilt_cuda import *
1
+ from ..processing.medfilt_cuda import * # noqa: F403
2
2
  from ..utils import deprecation_warning
3
3
 
4
4
  deprecation_warning(
nabu/cuda/padding.py CHANGED
@@ -1,4 +1,4 @@
1
- from ..processing.padding_cuda import *
1
+ from ..processing.padding_cuda import * # noqa: F403
2
2
  from ..utils import deprecation_warning
3
3
 
4
4
  deprecation_warning(
nabu/cuda/src/backproj.cu CHANGED
@@ -18,8 +18,8 @@ inline __device__ int is_in_circle(int x, int y, float center_x, float center_y,
18
18
  This will return arr[y][x] where y is an int (exact access) and x is a float (linear interp horizontally)
19
19
  */
20
20
  static inline __device__ float linear_interpolation(float* arr, int Nx, float x, int y) {
21
- // check commented to gain a bit of speed - the check was done before function call
22
- // if (x < 0 || x >= Nx) return 0.0f; // texture address mode CLAMP_TO_EDGE
21
+ // if (x < 0 || x > Nx-1) return 0.0f; // texture address mode BORDER (CLAMP_TO_EDGE continues with edge)
22
+ if (x <= -0.5f || x >= Nx - 0.5f) return 0.0f; // texture address mode BORDER (CLAMP_TO_EDGE continues with edge)
23
23
  int xm = (int) floorf(x);
24
24
  int xp = (int) ceilf(x);
25
25
  if ((xm == xp) || (xp >= Nx)) return arr[y*Nx+xm];
@@ -127,10 +127,10 @@ __global__ void backproj(
127
127
  #endif
128
128
 
129
129
  #ifdef USE_TEXTURES
130
- if (h1 >= 0 && h1 < num_bins) sum1 += tex2D(tex_projections, h1 + 0.5f, proj + 0.5f);
131
- if (h2 >= 0 && h2 < num_bins) sum2 += tex2D(tex_projections, h2 + 0.5f, proj + 0.5f);
132
- if (h3 >= 0 && h3 < num_bins) sum3 += tex2D(tex_projections, h3 + 0.5f, proj + 0.5f);
133
- if (h4 >= 0 && h4 < num_bins) sum4 += tex2D(tex_projections, h4 + 0.5f, proj + 0.5f);
130
+ sum1 += tex2D(tex_projections, h1 + 0.5f, proj + 0.5f);
131
+ sum2 += tex2D(tex_projections, h2 + 0.5f, proj + 0.5f);
132
+ sum3 += tex2D(tex_projections, h3 + 0.5f, proj + 0.5f);
133
+ sum4 += tex2D(tex_projections, h4 + 0.5f, proj + 0.5f);
134
134
  #else
135
135
  if (h1 >= 0 && h1 < num_bins) sum1 += linear_interpolation(d_sino, num_bins, h1, proj);
136
136
  if (h2 >= 0 && h2 < num_bins) sum2 += linear_interpolation(d_sino, num_bins, h2, proj);
nabu/cuda/src/cone.cu CHANGED
@@ -80,7 +80,11 @@ __global__ void devFDK_preweight(void* D_projData, unsigned int projPitch, unsig
80
80
 
81
81
  const float fWeight = fW / fRayLength;
82
82
 
83
+ #ifndef RADIOS_LAYOUT
83
84
  projData[(detectorV*iProjAngles+angle)*projPitch+detectorU] *= fWeight;
85
+ #else
86
+ projData[(angle*iProjV+detectorV)*projPitch+detectorU] *= fWeight;
87
+ #endif
84
88
 
85
89
  fV += fDetVSize;
86
90
  }
@@ -6,6 +6,20 @@
6
6
  Please cite :
7
7
  reference to be added...
8
8
 
9
+
10
+
11
+ # Generalized Hierarchical Backprojection (GHBP)
12
+ # for fast tomographic reconstruction from ultra high resolution images at non-negligible fan angles.
13
+ #
14
+ # Authors/Contributions:
15
+ # - Jonas Graetz, Fraunhofer IIS / Universitat Wurzburg: Algorithm Design and original OpenCL/Python implementation.
16
+ # - Alessandro Mirone, ESRF: CUDA translation, ESRF / BM18 integration, testing <mirone@esrf.fr>
17
+ # - Pierre Paleo, ESRF: ESRF / BM18 integration, testing <pierre.paleo@esrf.fr>
18
+ #
19
+ # JG was funded by the German Federal Ministry of Education and Research (BMBF), grant 05E2019,
20
+ # funding the development of BM18 at ESRF in collaboration with the Fraunhofer Gesellschaft,
21
+ # the Julius-Maximilians-Universitat Wurzburg, and the University of Passau
22
+
9
23
  """
10
24
  */
11
25
 
nabu/cuda/utils.py CHANGED
@@ -197,12 +197,12 @@ def cuarray_shape_dtype(cuarray):
197
197
 
198
198
 
199
199
  def get_shape_dtype(arr):
200
- if isinstance(arr, garray.GPUArray) or isinstance(arr, np.ndarray):
200
+ if isinstance(arr, (garray.GPUArray, np.ndarray)):
201
201
  return arr.shape, arr.dtype
202
202
  elif isinstance(arr, cuda.Array):
203
203
  return cuarray_shape_dtype(arr)
204
204
  else:
205
- raise ValueError("Unknown array type %s" % str(type(arr)))
205
+ raise TypeError("Unknown array type %s" % str(type(arr)))
206
206
 
207
207
 
208
208
  def copy_array(dst, src, check=False, src_dtype=None, dst_x_in_bytes=0, dst_y=0):
@@ -87,20 +87,20 @@ class AlignmentBase:
87
87
  if not len(shape_stack) == 3:
88
88
  raise ValueError(
89
89
  "A stack of 2-dimensional images is required. Shape of stack: %s"
90
- % (" ".join(("%d" % x for x in shape_stack)))
90
+ % (" ".join("%d" % x for x in shape_stack))
91
91
  )
92
92
  if not len(shape_pos) == 1:
93
93
  raise ValueError(
94
94
  "Positions need to be a 1-dimensional array. Shape of the positions variable: %s"
95
- % (" ".join(("%d" % x for x in shape_pos)))
95
+ % (" ".join("%d" % x for x in shape_pos))
96
96
  )
97
97
  if not shape_stack[0] == shape_pos[0]:
98
98
  raise ValueError(
99
99
  "The same number of images and positions is required."
100
100
  + " Shape of stack: %s, shape of positions variable: %s"
101
101
  % (
102
- " ".join(("%d" % x for x in shape_stack)),
103
- " ".join(("%d" % x for x in shape_pos)),
102
+ " ".join("%d" % x for x in shape_stack),
103
+ " ".join("%d" % x for x in shape_pos),
104
104
  )
105
105
  )
106
106
 
@@ -110,18 +110,18 @@ class AlignmentBase:
110
110
  shape_2 = np.squeeze(img_2).shape
111
111
  if not len(shape_1) == 2:
112
112
  raise ValueError(
113
- "Images need to be 2-dimensional. Shape of image #1: %s" % (" ".join(("%d" % x for x in shape_1)))
113
+ "Images need to be 2-dimensional. Shape of image #1: %s" % (" ".join("%d" % x for x in shape_1))
114
114
  )
115
115
  if not len(shape_2) == 2:
116
116
  raise ValueError(
117
- "Images need to be 2-dimensional. Shape of image #2: %s" % (" ".join(("%d" % x for x in shape_2)))
117
+ "Images need to be 2-dimensional. Shape of image #2: %s" % (" ".join("%d" % x for x in shape_2))
118
118
  )
119
119
  if not np.all(shape_1 == shape_2):
120
120
  raise ValueError(
121
121
  "Images need to be of the same shape. Shape of image #1: %s, image #2: %s"
122
122
  % (
123
- " ".join(("%d" % x for x in shape_1)),
124
- " ".join(("%d" % x for x in shape_2)),
123
+ " ".join("%d" % x for x in shape_1),
124
+ " ".join("%d" % x for x in shape_2),
125
125
  )
126
126
  )
127
127
 
@@ -153,7 +153,7 @@ class AlignmentBase:
153
153
  if not (len(f_vals.shape) == 2):
154
154
  raise ValueError(
155
155
  "The fitted values should form a 2-dimensional array. Array of shape: [%s] was given."
156
- % (" ".join(("%d" % s for s in f_vals.shape)))
156
+ % (" ".join("%d" % s for s in f_vals.shape))
157
157
  )
158
158
  if fy is None:
159
159
  fy_half_size = (f_vals.shape[0] - 1) / 2
@@ -161,7 +161,7 @@ class AlignmentBase:
161
161
  elif not (len(fy.shape) == 1 and np.all(fy.size == f_vals.shape[0])):
162
162
  raise ValueError(
163
163
  "Vertical coordinates should have the same length as values matrix. Sizes of fy: %d, f_vals: [%s]"
164
- % (fy.size, " ".join(("%d" % s for s in f_vals.shape)))
164
+ % (fy.size, " ".join("%d" % s for s in f_vals.shape))
165
165
  )
166
166
  if fx is None:
167
167
  fx_half_size = (f_vals.shape[1] - 1) / 2
@@ -169,7 +169,7 @@ class AlignmentBase:
169
169
  elif not (len(fx.shape) == 1 and np.all(fx.size == f_vals.shape[1])):
170
170
  raise ValueError(
171
171
  "Horizontal coordinates should have the same length as values matrix. Sizes of fx: %d, f_vals: [%s]"
172
- % (fx.size, " ".join(("%d" % s for s in f_vals.shape)))
172
+ % (fx.size, " ".join("%d" % s for s in f_vals.shape))
173
173
  )
174
174
 
175
175
  fy, fx = np.meshgrid(fy, fx, indexing="ij")
@@ -190,14 +190,7 @@ class AlignmentBase:
190
190
  vertex_max_yx = [np.max(fy), np.max(fx)]
191
191
  if np.any(vertex_yx < vertex_min_yx) or np.any(vertex_yx > vertex_max_yx):
192
192
  raise ValueError(
193
- "Fitted (y: {}, x: {}) positions are outside the input margins y: [{}, {}], and x: [{}, {}]".format(
194
- vertex_yx[0],
195
- vertex_yx[1],
196
- vertex_min_yx[0],
197
- vertex_max_yx[0],
198
- vertex_min_yx[1],
199
- vertex_max_yx[1],
200
- )
193
+ f"Fitted (y: {vertex_yx[0]}, x: {vertex_yx[1]}) positions are outside the input margins y: [{vertex_min_yx[0]}, {vertex_max_yx[0]}], and x: [{vertex_min_yx[1]}, {vertex_max_yx[1]}]"
201
194
  )
202
195
  return vertex_yx
203
196
 
@@ -225,10 +218,10 @@ class AlignmentBase:
225
218
  float
226
219
  Estimated function max, according to the coordinates in fx.
227
220
  """
228
- if not len(f_vals.shape) in (1, 2):
221
+ if len(f_vals.shape) not in (1, 2):
229
222
  raise ValueError(
230
223
  "The fitted values should be either one or a collection of 1-dimensional arrays. Array of shape: [%s] was given."
231
- % (" ".join(("%d" % s for s in f_vals.shape)))
224
+ % (" ".join("%d" % s for s in f_vals.shape))
232
225
  )
233
226
  num_vals = f_vals.shape[0]
234
227
 
@@ -264,16 +257,9 @@ class AlignmentBase:
264
257
  upper_bound_ok = vertex_x < vertex_max_x
265
258
  if not np.all(lower_bound_ok * upper_bound_ok):
266
259
  if len(f_vals.shape) == 1:
267
- message = "Fitted position {} is outide the input margins [{}, {}]".format(
268
- vertex_x, vertex_min_x, vertex_max_x
269
- )
260
+ message = f"Fitted position {vertex_x} is outide the input margins [{vertex_min_x}, {vertex_max_x}]"
270
261
  else:
271
- message = "Fitted positions outside the input margins [{}, {}]: {} below and {} above".format(
272
- vertex_min_x,
273
- vertex_max_x,
274
- np.sum(1 - lower_bound_ok),
275
- np.sum(1 - upper_bound_ok),
276
- )
262
+ message = f"Fitted positions outside the input margins [{vertex_min_x}, {vertex_max_x}]: {np.sum(1 - lower_bound_ok)} below and {np.sum(1 - upper_bound_ok)} above"
277
263
  raise ValueError(message)
278
264
  if return_vertex_val:
279
265
  vertex_val = coeffs[0, :] + vertex_x * coeffs[1, :] / 2
@@ -354,7 +340,7 @@ class AlignmentBase:
354
340
  if not (len(img_shape) == 2):
355
341
  raise ValueError(
356
342
  "The input image should be either a 1 or 2-dimensional array. Array of shape: [%s] was given."
357
- % (" ".join(("%d" % s for s in cc.shape)))
343
+ % (" ".join("%d" % s for s in cc.shape))
358
344
  )
359
345
  other_axis = (axis + 1) % 2
360
346
  # get pixel having the maximum value of the correlation array