nabu 2024.2.4__py3-none-any.whl → 2025.1.0.dev4__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/doc_config.py +32 -0
- nabu/__init__.py +1 -1
- nabu/app/bootstrap_stitching.py +4 -2
- nabu/app/cast_volume.py +7 -13
- nabu/app/cli_configs.py +0 -5
- nabu/app/compare_volumes.py +1 -1
- nabu/app/composite_cor.py +2 -4
- nabu/app/correct_rot.py +0 -8
- nabu/app/diag_to_pix.py +5 -6
- nabu/app/diag_to_rot.py +10 -11
- nabu/app/multicor.py +1 -1
- nabu/app/parse_reconstruction_log.py +1 -0
- nabu/app/prepare_weights_double.py +1 -2
- nabu/app/reconstruct_helical.py +1 -5
- nabu/app/reduce_dark_flat.py +0 -2
- nabu/app/rotate.py +3 -1
- nabu/app/tests/test_reduce_dark_flat.py +2 -2
- nabu/app/validator.py +1 -4
- nabu/cuda/convolution.py +1 -1
- nabu/cuda/fft.py +1 -1
- nabu/cuda/medfilt.py +1 -1
- nabu/cuda/padding.py +1 -1
- nabu/cuda/src/cone.cu +19 -9
- nabu/cuda/src/hierarchical_backproj.cu +16 -0
- nabu/cuda/utils.py +2 -2
- nabu/estimation/alignment.py +17 -31
- nabu/estimation/cor.py +23 -29
- nabu/estimation/cor_sino.py +2 -8
- nabu/estimation/focus.py +4 -8
- nabu/estimation/tests/test_alignment.py +2 -0
- nabu/estimation/tests/test_tilt.py +1 -1
- nabu/estimation/tilt.py +5 -4
- nabu/io/cast_volume.py +5 -5
- nabu/io/detector_distortion.py +5 -6
- nabu/io/reader.py +3 -3
- nabu/io/reader_helical.py +5 -4
- nabu/io/tests/test_cast_volume.py +2 -2
- nabu/io/tests/test_readers.py +4 -4
- nabu/io/tests/test_writers.py +2 -2
- nabu/io/utils.py +8 -4
- nabu/io/writer.py +1 -2
- nabu/misc/fftshift.py +1 -1
- nabu/misc/fourier_filters.py +1 -1
- nabu/misc/histogram.py +1 -1
- nabu/misc/histogram_cuda.py +1 -1
- nabu/misc/padding_base.py +1 -1
- nabu/misc/rotation.py +1 -1
- nabu/misc/rotation_cuda.py +1 -1
- nabu/misc/tests/test_binning.py +1 -1
- nabu/misc/transpose.py +1 -1
- nabu/misc/unsharp.py +1 -1
- nabu/misc/unsharp_cuda.py +1 -1
- nabu/misc/unsharp_opencl.py +1 -1
- nabu/misc/utils.py +1 -1
- nabu/opencl/fft.py +1 -1
- nabu/opencl/padding.py +1 -1
- nabu/opencl/utils.py +8 -8
- nabu/pipeline/config.py +2 -2
- nabu/pipeline/config_validators.py +4 -3
- nabu/pipeline/datadump.py +3 -3
- nabu/pipeline/estimators.py +6 -6
- nabu/pipeline/fullfield/chunked.py +4 -5
- nabu/pipeline/fullfield/dataset_validator.py +0 -1
- nabu/pipeline/fullfield/nabu_config.py +2 -1
- nabu/pipeline/fullfield/reconstruction.py +9 -8
- nabu/pipeline/helical/dataset_validator.py +3 -4
- nabu/pipeline/helical/fbp.py +4 -4
- nabu/pipeline/helical/filtering.py +5 -4
- nabu/pipeline/helical/gridded_accumulator.py +9 -10
- nabu/pipeline/helical/helical_chunked_regridded.py +1 -0
- nabu/pipeline/helical/helical_reconstruction.py +10 -7
- nabu/pipeline/helical/helical_utils.py +1 -2
- nabu/pipeline/helical/nabu_config.py +1 -0
- nabu/pipeline/helical/span_strategy.py +1 -0
- nabu/pipeline/helical/weight_balancer.py +1 -2
- nabu/pipeline/tests/__init__.py +0 -0
- nabu/pipeline/utils.py +1 -1
- nabu/pipeline/writer.py +1 -1
- nabu/preproc/alignment.py +0 -10
- nabu/preproc/ctf.py +8 -8
- nabu/preproc/ctf_cuda.py +1 -1
- nabu/preproc/double_flatfield_cuda.py +2 -2
- nabu/preproc/double_flatfield_variable_region.py +0 -1
- nabu/preproc/flatfield.py +1 -1
- nabu/preproc/flatfield_cuda.py +1 -2
- nabu/preproc/flatfield_variable_region.py +3 -3
- nabu/preproc/phase.py +2 -4
- nabu/preproc/phase_cuda.py +2 -2
- nabu/preproc/shift_cuda.py +0 -1
- nabu/preproc/tests/test_ctf.py +3 -3
- nabu/preproc/tests/test_double_flatfield.py +1 -1
- nabu/preproc/tests/test_flatfield.py +1 -1
- nabu/preproc/tests/test_vshift.py +4 -1
- nabu/processing/azim.py +2 -2
- nabu/processing/convolution_cuda.py +6 -4
- nabu/processing/fft_base.py +1 -1
- nabu/processing/fft_cuda.py +19 -8
- nabu/processing/fft_opencl.py +9 -4
- nabu/processing/fftshift.py +1 -1
- nabu/processing/histogram.py +1 -1
- nabu/processing/muladd.py +0 -1
- nabu/processing/padding_base.py +1 -1
- nabu/processing/padding_cuda.py +0 -1
- nabu/processing/processing_base.py +1 -1
- nabu/processing/tests/test_fft.py +1 -1
- nabu/processing/tests/test_fftshift.py +1 -1
- nabu/processing/tests/test_medfilt.py +1 -3
- nabu/processing/tests/test_padding.py +1 -1
- nabu/processing/tests/test_roll.py +1 -1
- nabu/processing/unsharp_opencl.py +1 -1
- nabu/reconstruction/astra.py +245 -0
- nabu/reconstruction/cone.py +9 -4
- nabu/reconstruction/fbp_base.py +2 -2
- nabu/reconstruction/filtering_cuda.py +1 -1
- nabu/reconstruction/hbp.py +16 -3
- nabu/reconstruction/mlem.py +0 -1
- nabu/reconstruction/projection.py +3 -5
- nabu/reconstruction/sinogram.py +1 -1
- nabu/reconstruction/sinogram_cuda.py +0 -1
- nabu/reconstruction/tests/test_cone.py +76 -3
- nabu/reconstruction/tests/test_deringer.py +2 -2
- nabu/reconstruction/tests/test_fbp.py +1 -1
- nabu/reconstruction/tests/test_halftomo.py +27 -1
- nabu/reconstruction/tests/test_mlem.py +3 -2
- nabu/reconstruction/tests/test_projector.py +7 -2
- nabu/reconstruction/tests/test_sino_normalization.py +0 -1
- nabu/resources/dataset_analyzer.py +4 -4
- nabu/resources/gpu.py +4 -4
- nabu/resources/logger.py +4 -4
- nabu/resources/nxflatfield.py +2 -2
- nabu/resources/tests/test_nxflatfield.py +4 -4
- nabu/stitching/alignment.py +1 -4
- nabu/stitching/config.py +19 -16
- nabu/stitching/frame_composition.py +8 -10
- nabu/stitching/overlap.py +2 -2
- nabu/stitching/slurm_utils.py +2 -2
- nabu/stitching/stitcher/base.py +2 -0
- nabu/stitching/stitcher/dumper/base.py +0 -1
- nabu/stitching/stitcher/dumper/postprocessing.py +1 -1
- nabu/stitching/stitcher/post_processing.py +6 -6
- nabu/stitching/stitcher/pre_processing.py +13 -11
- nabu/stitching/stitcher/single_axis.py +3 -4
- nabu/stitching/stitcher_2D.py +2 -1
- nabu/stitching/tests/test_config.py +7 -8
- nabu/stitching/tests/test_sample_normalization.py +1 -1
- nabu/stitching/tests/test_slurm_utils.py +1 -2
- nabu/stitching/tests/test_z_postprocessing_stitching.py +1 -1
- nabu/stitching/tests/test_z_preprocessing_stitching.py +4 -4
- nabu/stitching/utils/tests/__init__.py +0 -0
- nabu/stitching/utils/tests/test_post-processing.py +1 -0
- nabu/stitching/utils/utils.py +10 -12
- nabu/tests.py +0 -3
- nabu/testutils.py +30 -8
- nabu/utils.py +28 -18
- {nabu-2024.2.4.dist-info → nabu-2025.1.0.dev4.dist-info}/METADATA +25 -25
- nabu-2025.1.0.dev4.dist-info/RECORD +320 -0
- {nabu-2024.2.4.dist-info → nabu-2025.1.0.dev4.dist-info}/WHEEL +1 -1
- nabu/io/tests/test_detector_distortion.py +0 -178
- nabu/resources/tests/test_extract.py +0 -9
- nabu-2024.2.4.dist-info/RECORD +0 -318
- /nabu/{stitching → app}/tests/__init__.py +0 -0
- {nabu-2024.2.4.dist-info → nabu-2025.1.0.dev4.dist-info}/LICENSE +0 -0
- {nabu-2024.2.4.dist-info → nabu-2025.1.0.dev4.dist-info}/entry_points.txt +0 -0
- {nabu-2024.2.4.dist-info → nabu-2025.1.0.dev4.dist-info}/top_level.txt +0 -0
nabu/resources/gpu.py
CHANGED
@@ -131,7 +131,7 @@ def pick_gpus_auto(cuda_gpus, opencl_platforms, n_gpus):
|
|
131
131
|
return (gpu1["device_id"] == gpu2["device_id"]) and (gpu1["name"] == gpu2["name"])
|
132
132
|
|
133
133
|
def is_in_gpus(avail_gpus, query_gpu):
|
134
|
-
for gpu in avail_gpus:
|
134
|
+
for gpu in avail_gpus: # noqa: SIM110
|
135
135
|
if gpu_equal(gpu, query_gpu):
|
136
136
|
return True
|
137
137
|
return False
|
@@ -142,8 +142,8 @@ def pick_gpus_auto(cuda_gpus, opencl_platforms, n_gpus):
|
|
142
142
|
chosen_gpus = list(cuda_gpus.values())
|
143
143
|
if len(chosen_gpus) >= n_gpus:
|
144
144
|
return chosen_gpus
|
145
|
-
for platform, gpus in opencl_platforms.items():
|
146
|
-
for gpu_id, gpu in gpus.items():
|
145
|
+
for platform, gpus in opencl_platforms.items(): # noqa: PERF102
|
146
|
+
for gpu_id, gpu in gpus.items(): # noqa: PERF102
|
147
147
|
if not (is_in_gpus(chosen_gpus, gpu)):
|
148
148
|
# TODO prioritize some OpenCL implementations ?
|
149
149
|
chosen_gpus.append(gpu)
|
@@ -166,5 +166,5 @@ def pick_gpus_nvidia(cuda_gpus, n_gpus):
|
|
166
166
|
gpus_cc_sorted = sorted(gpus_cc, key=lambda x: x[1], reverse=True)
|
167
167
|
res = []
|
168
168
|
for i in range(n_gpus):
|
169
|
-
res.append(cuda_gpus[gpus_cc_sorted[i][0]])
|
169
|
+
res.append(cuda_gpus[gpus_cc_sorted[i][0]]) # noqa: PERF401
|
170
170
|
return res
|
nabu/resources/logger.py
CHANGED
@@ -2,7 +2,7 @@ import logging
|
|
2
2
|
import logging.config
|
3
3
|
|
4
4
|
|
5
|
-
class Logger
|
5
|
+
class Logger:
|
6
6
|
def __init__(self, loggername, level="DEBUG", logfile="logger.log", console=True):
|
7
7
|
"""
|
8
8
|
Configure a Logger object.
|
@@ -30,7 +30,7 @@ class Logger(object):
|
|
30
30
|
|
31
31
|
def _configure_logger(self):
|
32
32
|
conf = self._get_default_config_dict()
|
33
|
-
for handler in conf["handlers"]
|
33
|
+
for handler in conf["handlers"]:
|
34
34
|
conf["handlers"][handler]["level"] = self.level.upper()
|
35
35
|
conf["loggers"][self.loggername]["level"] = self.level.upper()
|
36
36
|
if not (self.console):
|
@@ -103,7 +103,7 @@ def LoggerOrPrint(logger):
|
|
103
103
|
return logger
|
104
104
|
|
105
105
|
|
106
|
-
class PrinterLogger
|
106
|
+
class PrinterLogger:
|
107
107
|
def __init__(self):
|
108
108
|
methods = [
|
109
109
|
"debug",
|
@@ -122,7 +122,7 @@ LogLevel = {
|
|
122
122
|
"notset": logging.NOTSET,
|
123
123
|
"debug": logging.DEBUG,
|
124
124
|
"info": logging.INFO,
|
125
|
-
"warn": logging.
|
125
|
+
"warn": logging.WARNING,
|
126
126
|
"warning": logging.WARNING,
|
127
127
|
"error": logging.ERROR,
|
128
128
|
"critical": logging.CRITICAL,
|
nabu/resources/nxflatfield.py
CHANGED
@@ -202,7 +202,7 @@ def update_dataset_info_flats_darks(dataset_info, flatfield_mode, output_dir=Non
|
|
202
202
|
elif _can_load_from("output"):
|
203
203
|
where_to_load_from = "output"
|
204
204
|
|
205
|
-
if where_to_load_from
|
205
|
+
if where_to_load_from is None and flatfield_mode == "force-load":
|
206
206
|
raise ValueError("Could not load darks/flats (using 'force-load')")
|
207
207
|
|
208
208
|
if where_to_load_from is not None:
|
@@ -218,7 +218,7 @@ def update_dataset_info_flats_darks(dataset_info, flatfield_mode, output_dir=Non
|
|
218
218
|
setattr(
|
219
219
|
dataset_info,
|
220
220
|
frame_type,
|
221
|
-
{k: get_data(red_frames_dict[k]) for k in red_frames_dict
|
221
|
+
{k: get_data(red_frames_dict[k]) for k in red_frames_dict},
|
222
222
|
)
|
223
223
|
if frame_type == "flats":
|
224
224
|
dataset_info.flats_srcurrent = red_frames_info.machine_electric_current
|
@@ -83,8 +83,8 @@ class TestNXFlatField:
|
|
83
83
|
update_dataset_info_flats_darks(dataset_info, True, output_dir=output_dir)
|
84
84
|
# After reduction (median/mean), the flats/darks are located in another file.
|
85
85
|
# median(series_1) goes to entry/flats/idx1, mean(series_2) goes to entry/flats/idx2, etc.
|
86
|
-
assert set(dataset_info.flats.keys()) == set(s.start for s in self.params["flats_pos"])
|
87
|
-
assert set(dataset_info.darks.keys()) == set(s.start for s in self.params["darks_pos"])
|
86
|
+
assert set(dataset_info.flats.keys()) == set(s.start for s in self.params["flats_pos"]) # noqa: C401
|
87
|
+
assert set(dataset_info.darks.keys()) == set(s.start for s in self.params["darks_pos"]) # noqa: C401
|
88
88
|
|
89
89
|
# Check that the computations were correct
|
90
90
|
# Loads the entire volume in memory ! So keep the data volume small for the tests
|
@@ -97,8 +97,8 @@ class TestNXFlatField:
|
|
97
97
|
expected_darks[s.start] = self._reduction_func["darks"](data_volume[s.start : s.stop], axis=0)
|
98
98
|
|
99
99
|
flats = dataset_info.flats
|
100
|
-
for idx in flats
|
100
|
+
for idx in flats:
|
101
101
|
assert np.allclose(flats[idx], expected_flats[idx])
|
102
102
|
darks = dataset_info.darks
|
103
|
-
for idx in darks
|
103
|
+
for idx in darks:
|
104
104
|
assert np.allclose(darks[idx], expected_darks[idx])
|
nabu/stitching/alignment.py
CHANGED
@@ -2,9 +2,6 @@ import h5py
|
|
2
2
|
import numpy
|
3
3
|
from typing import Union
|
4
4
|
from silx.utils.enum import Enum as _Enum
|
5
|
-
from tomoscan.volumebase import VolumeBase
|
6
|
-
from tomoscan.esrf.volume.hdf5volume import HDF5Volume
|
7
|
-
from nabu.io.utils import DatasetReader
|
8
5
|
|
9
6
|
|
10
7
|
class AlignmentAxis2(_Enum):
|
@@ -151,7 +148,7 @@ class PaddedRawData:
|
|
151
148
|
@property
|
152
149
|
def shape(self):
|
153
150
|
if self._shape is None:
|
154
|
-
self._shape = tuple(
|
151
|
+
self._shape = tuple( # noqa: C409
|
155
152
|
(
|
156
153
|
self._raw_data_shape[0],
|
157
154
|
numpy.sum(
|
nabu/stitching/config.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from math import ceil
|
2
|
-
from typing import
|
2
|
+
from typing import Optional, Union
|
3
|
+
from collections.abc import Iterable, Sized
|
3
4
|
from dataclasses import dataclass
|
4
5
|
import numpy
|
5
6
|
from pyunitsystem.metricsystem import MetricSystem
|
@@ -12,13 +13,12 @@ from ..pipeline.config_validators import (
|
|
12
13
|
convert_to_bool,
|
13
14
|
)
|
14
15
|
from ..utils import concatenate_dict, convert_str_to_tuple
|
15
|
-
from ..io.utils import get_output_volume
|
16
16
|
from .overlap import OverlapStitchingStrategy
|
17
17
|
from .utils.utils import ShiftAlgorithm
|
18
18
|
from .definitions import StitchingType
|
19
19
|
from .alignment import AlignmentAxis1, AlignmentAxis2
|
20
|
-
from pyunitsystem.metricsystem import MetricSystem
|
21
20
|
|
21
|
+
# ruff: noqa: S105
|
22
22
|
|
23
23
|
KEY_IMG_REG_METHOD = "img_reg_method"
|
24
24
|
|
@@ -205,7 +205,7 @@ def _str_to_dict(my_str: Union[str, dict]):
|
|
205
205
|
|
206
206
|
|
207
207
|
def _dict_to_str(ddict: dict):
|
208
|
-
return ";".join([f"{
|
208
|
+
return ";".join([f"{key!s}={value!s}" for key, value in ddict.items()])
|
209
209
|
|
210
210
|
|
211
211
|
def str_to_shifts(my_str: Optional[str]) -> Union[str, tuple]:
|
@@ -235,8 +235,8 @@ def _valid_stitching_kernels_params(my_dict: Union[dict, str]):
|
|
235
235
|
my_dict = _str_to_dict(my_str=my_dict)
|
236
236
|
|
237
237
|
valid_keys = (KEY_THRESHOLD_FREQUENCY, KEY_SIDE)
|
238
|
-
for key in my_dict
|
239
|
-
if not
|
238
|
+
for key in my_dict:
|
239
|
+
if key not in valid_keys:
|
240
240
|
raise KeyError(f"{key} is a unrecognized key")
|
241
241
|
return my_dict
|
242
242
|
|
@@ -253,8 +253,8 @@ def _valid_shifts_params(my_dict: Union[dict, str]):
|
|
253
253
|
KEY_LOW_PASS_FILTER,
|
254
254
|
KEY_SIDE,
|
255
255
|
)
|
256
|
-
for key in my_dict
|
257
|
-
if not
|
256
|
+
for key in my_dict:
|
257
|
+
if key not in valid_keys:
|
258
258
|
raise KeyError(f"{key} is a unrecognized key")
|
259
259
|
return my_dict
|
260
260
|
|
@@ -401,16 +401,17 @@ class NormalizationBySample:
|
|
401
401
|
NORMALIZATION_BY_SAMPLE_WIDTH: self.width,
|
402
402
|
}
|
403
403
|
|
404
|
-
def __eq__(self,
|
405
|
-
if not isinstance(
|
404
|
+
def __eq__(self, value: object, /) -> bool:
|
405
|
+
if not isinstance(value, NormalizationBySample):
|
406
406
|
return False
|
407
407
|
else:
|
408
|
-
return self.to_dict() ==
|
408
|
+
return self.to_dict() == value.to_dict()
|
409
409
|
|
410
410
|
|
411
411
|
@dataclass
|
412
412
|
class SlurmConfig:
|
413
|
-
"configuration for slurm jobs"
|
413
|
+
"""configuration for slurm jobs"""
|
414
|
+
|
414
415
|
partition: str = "" # note: must stay empty to make by default we don't use slurm (use by the configuration file)
|
415
416
|
mem: str = "128"
|
416
417
|
n_jobs: int = 1
|
@@ -429,7 +430,7 @@ class SlurmConfig:
|
|
429
430
|
)
|
430
431
|
|
431
432
|
def to_dict(self) -> dict:
|
432
|
-
"dump configuration to dict"
|
433
|
+
"""dump configuration to dict"""
|
433
434
|
return {
|
434
435
|
SLURM_PARTITION: self.partition if self.partition is not None else "",
|
435
436
|
SLURM_MEM: self.mem,
|
@@ -459,15 +460,17 @@ class SlurmConfig:
|
|
459
460
|
)
|
460
461
|
|
461
462
|
|
462
|
-
def _cast_shift_to_str(shifts: Union[tuple, str, None]) -> str:
|
463
|
+
def _cast_shift_to_str(shifts: Union[tuple, numpy.ndarray, str, None]) -> str:
|
463
464
|
if shifts is None:
|
464
465
|
return ""
|
465
466
|
elif isinstance(shifts, ShiftAlgorithm):
|
466
467
|
return shifts.value
|
467
468
|
elif isinstance(shifts, str):
|
468
469
|
return shifts
|
469
|
-
elif isinstance(shifts, (tuple, list)):
|
470
|
+
elif isinstance(shifts, (tuple, list, numpy.ndarray)):
|
470
471
|
return ";".join([str(value) for value in shifts])
|
472
|
+
else:
|
473
|
+
raise TypeError(f"unexpected type: {type(shifts)}")
|
471
474
|
|
472
475
|
|
473
476
|
@dataclass
|
@@ -771,7 +774,7 @@ class SingleAxisConfigMetaClass(type):
|
|
771
774
|
warning: this class is used by tomwer as well
|
772
775
|
"""
|
773
776
|
|
774
|
-
def __new__(mcls, name, bases, attrs, axis=None):
|
777
|
+
def __new__(mcls, name, bases, attrs, axis=None): # noqa: N804
|
775
778
|
# assert axis is not None
|
776
779
|
mcls = super().__new__(mcls, name, bases, attrs)
|
777
780
|
mcls._axis = axis
|
@@ -31,7 +31,7 @@ class FrameComposition:
|
|
31
31
|
)
|
32
32
|
|
33
33
|
def compose(self, output_frame: numpy.ndarray, input_frames: tuple):
|
34
|
-
if
|
34
|
+
if output_frame.ndim not in (2, 3):
|
35
35
|
raise TypeError(
|
36
36
|
f"output_frame is expected to be 2D (gray scale) or 3D (RGB(A)) and not {output_frame.ndim}"
|
37
37
|
)
|
@@ -74,9 +74,10 @@ class FrameComposition:
|
|
74
74
|
local_start_indices.extend(
|
75
75
|
[ceil(key_line[1] + kernel.overlap_size / 2) for (key_line, kernel) in zip(key_lines, overlap_kernels)]
|
76
76
|
)
|
77
|
-
local_end_indices =
|
78
|
-
|
79
|
-
|
77
|
+
local_end_indices = [
|
78
|
+
ceil(key_line[0] - kernel.overlap_size / 2) for (key_line, kernel) in zip(key_lines, overlap_kernels)
|
79
|
+
]
|
80
|
+
|
80
81
|
local_end_indices.append(frames[-1].shape[stitching_axis])
|
81
82
|
|
82
83
|
for (
|
@@ -155,9 +156,6 @@ class FrameComposition:
|
|
155
156
|
print(
|
156
157
|
f"stitch_frame[{stitch_global_start}:{stitch_global_end}] = stitched_frame_{i_frame}[{stitch_local_start}:{stitch_local_end}]"
|
157
158
|
)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
print(
|
162
|
-
f"stitch_frame[{raw_global_start}:{raw_global_end}] = frame_{i_frame}[{raw_local_start}:{raw_local_end}]"
|
163
|
-
)
|
159
|
+
i_frame += 1
|
160
|
+
raw_local_start, raw_local_end, raw_global_start, raw_global_end = list(raw_composition.browse())[-1]
|
161
|
+
print(f"stitch_frame[{raw_global_start}:{raw_global_end}] = frame_{i_frame}[{raw_local_start}:{raw_local_end}]")
|
nabu/stitching/overlap.py
CHANGED
@@ -64,7 +64,7 @@ class ImageStichOverlapKernel(OverlapKernelBase):
|
|
64
64
|
f"frame_width is expected to be a positive int, {frame_unstitched_axis_size} - not {frame_unstitched_axis_size} ({type(frame_unstitched_axis_size)})"
|
65
65
|
)
|
66
66
|
|
67
|
-
if not
|
67
|
+
if stitching_axis not in (0, 1):
|
68
68
|
raise ValueError(
|
69
69
|
"stitching_axis is expected to be the axis along which stitching must be done. It should be '0' or '1'"
|
70
70
|
)
|
@@ -391,7 +391,7 @@ def check_overlaps(frames: Union[tuple, numpy.ndarray], positions: tuple, axis:
|
|
391
391
|
|
392
392
|
:return: (tested_bounding_box, bounding_boxes_to_test)
|
393
393
|
"""
|
394
|
-
my_bounding_boxes = {bb_index: bb for bb_index, bb in enumerate(my_bounding_boxes)}
|
394
|
+
my_bounding_boxes = {bb_index: bb for bb_index, bb in enumerate(my_bounding_boxes)} # noqa: C416
|
395
395
|
bounding_boxes = dict(
|
396
396
|
filter(
|
397
397
|
lambda pair: pair[0] not in (index - 1, index, index + 1),
|
nabu/stitching/slurm_utils.py
CHANGED
@@ -177,7 +177,7 @@ def split_slices(slices: Union[slice, tuple], n_parts: int):
|
|
177
177
|
raise TypeError(f"slices type ({type(slices)}) is not handled. Must be a slice or an Iterable")
|
178
178
|
|
179
179
|
|
180
|
-
def get_working_directory(obj: TomoObject) -> Optional[str]:
|
180
|
+
def get_working_directory(obj: TomoObject) -> Optional[str]: # noqa: PLR0911
|
181
181
|
"""
|
182
182
|
return working directory for a specific TomoObject
|
183
183
|
"""
|
@@ -201,4 +201,4 @@ def get_working_directory(obj: TomoObject) -> Optional[str]:
|
|
201
201
|
else:
|
202
202
|
return os.path.abspath(os.path.dirname(obj.master_file))
|
203
203
|
else:
|
204
|
-
raise RuntimeError(f"obj type not handled ({type(obj)})")
|
204
|
+
raise RuntimeError(f"obj type not handled ({type(obj)})") # noqa: TRY004
|
nabu/stitching/stitcher/base.py
CHANGED
@@ -21,6 +21,8 @@ def get_obj_constant_side_length(obj: Union[NXtomoScan, VolumeBase], axis: int)
|
|
21
21
|
return obj.dim_1
|
22
22
|
elif axis in (1, 2):
|
23
23
|
return obj.dim_2
|
24
|
+
else:
|
25
|
+
raise ValueError(f"Axis ({axis}) not handled. Should be in (0, 1, 2)")
|
24
26
|
elif isinstance(obj, VolumeBase) and axis == 0:
|
25
27
|
return obj.get_volume_shape()[-1]
|
26
28
|
else:
|
@@ -96,7 +96,7 @@ class OutputVolumeContext(AbstractContextManager):
|
|
96
96
|
if self._file_handler is not None:
|
97
97
|
return self._file_handler.close()
|
98
98
|
else:
|
99
|
-
self._volume.save_data()
|
99
|
+
self._volume.save_data() # noqa: RET503
|
100
100
|
|
101
101
|
|
102
102
|
class OutputVolumeNoDDContext(OutputVolumeContext):
|
@@ -13,11 +13,10 @@ from tomoscan.esrf import NXtomoScan
|
|
13
13
|
from tomoscan.series import Series
|
14
14
|
from tomoscan.volumebase import VolumeBase
|
15
15
|
from tomoscan.esrf.volume import HDF5Volume
|
16
|
-
from
|
16
|
+
from collections.abc import Iterable
|
17
17
|
from contextlib import AbstractContextManager
|
18
18
|
from pyunitsystem.metricsystem import MetricSystem
|
19
19
|
from nabu.stitching.config import (
|
20
|
-
PostProcessedSingleAxisStitchingConfiguration,
|
21
20
|
KEY_IMG_REG_METHOD,
|
22
21
|
)
|
23
22
|
from nabu.stitching.utils.utils import find_volumes_relative_shifts
|
@@ -422,7 +421,7 @@ class PostProcessingStitching(SingleAxisStitcher):
|
|
422
421
|
[(abs(overlap), n_slices, self._stitching_constant_length) for overlap in self._axis_0_rel_final_shifts]
|
423
422
|
)
|
424
423
|
|
425
|
-
with self.dumper.OutputDatasetContext(**output_dataset_args):
|
424
|
+
with self.dumper.OutputDatasetContext(**output_dataset_args): # noqa: SIM117
|
426
425
|
# note: output_dataset is a HDF5 dataset if final volume is an HDF5 volume else is a numpy array
|
427
426
|
with _RawDatasetsContext(
|
428
427
|
self._input_volumes,
|
@@ -518,7 +517,8 @@ class _RawDatasetsContext(AbstractContextManager):
|
|
518
517
|
else:
|
519
518
|
data = volume.load_data(store=False)
|
520
519
|
if data is None:
|
521
|
-
|
520
|
+
# TODO
|
521
|
+
raise ValueError(f"No data found for volume {volume.get_identifier()}") # noqa: TRY301
|
522
522
|
if axis_1_need_padding:
|
523
523
|
data = self.add_padding(data=data, axis_1_dim=axis_1_dim, alignment=self.alignment_axis_1)
|
524
524
|
datasets.append(data)
|
@@ -526,7 +526,7 @@ class _RawDatasetsContext(AbstractContextManager):
|
|
526
526
|
# if some errors happen during loading HDF5
|
527
527
|
for file_handled in self.__file_handlers:
|
528
528
|
file_handled.close()
|
529
|
-
raise e
|
529
|
+
raise e # noqa: TRY201
|
530
530
|
|
531
531
|
return datasets
|
532
532
|
|
@@ -534,7 +534,7 @@ class _RawDatasetsContext(AbstractContextManager):
|
|
534
534
|
success = True
|
535
535
|
for file_handler in self.__file_handlers:
|
536
536
|
success = success and file_handler.close()
|
537
|
-
if exc_type is None:
|
537
|
+
if exc_type is None: # noqa: RET503
|
538
538
|
return success
|
539
539
|
|
540
540
|
def add_padding(self, data: Union[h5py.Dataset, numpy.ndarray], axis_1_dim, alignment: AlignmentAxis1):
|
@@ -2,7 +2,7 @@ import numpy
|
|
2
2
|
import logging
|
3
3
|
import h5py
|
4
4
|
import os
|
5
|
-
from
|
5
|
+
from collections.abc import Iterable
|
6
6
|
from silx.io.url import DataUrl
|
7
7
|
from silx.io.utils import get_data
|
8
8
|
from datetime import datetime
|
@@ -40,7 +40,6 @@ class PreProcessingStitching(SingleAxisStitcher):
|
|
40
40
|
"""
|
41
41
|
|
42
42
|
def __init__(self, configuration, progress=None) -> None:
|
43
|
-
""" """
|
44
43
|
if not isinstance(configuration, PreProcessedSingleAxisStitchingConfiguration):
|
45
44
|
raise TypeError(
|
46
45
|
f"configuration is expected to be an instance of {PreProcessedSingleAxisStitchingConfiguration}. Get {type(configuration)} instead"
|
@@ -526,9 +525,9 @@ class PreProcessingStitching(SingleAxisStitcher):
|
|
526
525
|
if isinstance(slices, slice):
|
527
526
|
return array[slices.start : slices.stop : 1]
|
528
527
|
elif isinstance(slices, Iterable):
|
529
|
-
return
|
528
|
+
return [array[index] for index in slices]
|
530
529
|
else:
|
531
|
-
raise RuntimeError("slices must be instance of a slice or of an iterable")
|
530
|
+
raise RuntimeError("slices must be instance of a slice or of an iterable") # noqa: TRY004
|
532
531
|
|
533
532
|
nx_tomo.sample.rotation_angle = apply_slices_selection(
|
534
533
|
array=nx_tomo.sample.rotation_angle, slices=self._slices_to_stitch
|
@@ -794,7 +793,7 @@ class PreProcessingStitching(SingleAxisStitcher):
|
|
794
793
|
):
|
795
794
|
i_frame = 0
|
796
795
|
_, set_of_compacted_slices = get_compacted_dataslices(scan_urls, return_url_set=True)
|
797
|
-
for
|
796
|
+
for url in set_of_compacted_slices.values():
|
798
797
|
scan = scans[i_scan]
|
799
798
|
url = DataUrl(
|
800
799
|
file_path=url.file_path(),
|
@@ -886,6 +885,9 @@ class PreProcessingStitching(SingleAxisStitcher):
|
|
886
885
|
"""
|
887
886
|
make sure reduced dark and flats are existing otherwise compute them
|
888
887
|
"""
|
888
|
+
# TODO
|
889
|
+
# ruff: noqa: SIM105, S110
|
890
|
+
# --
|
889
891
|
for scan in self.series:
|
890
892
|
try:
|
891
893
|
reduced_darks, darks_infos = scan.load_reduced_darks(return_info=True)
|
@@ -896,7 +898,7 @@ class PreProcessingStitching(SingleAxisStitcher):
|
|
896
898
|
try:
|
897
899
|
# if we don't have write in the folder containing the .nx for example
|
898
900
|
scan.save_reduced_darks(reduced_darks, darks_infos=darks_infos)
|
899
|
-
except Exception
|
901
|
+
except Exception:
|
900
902
|
pass
|
901
903
|
scan.set_reduced_darks(reduced_darks, darks_infos=darks_infos)
|
902
904
|
|
@@ -909,7 +911,7 @@ class PreProcessingStitching(SingleAxisStitcher):
|
|
909
911
|
try:
|
910
912
|
# if we don't have write in the folder containing the .nx for example
|
911
913
|
scan.save_reduced_flats(reduced_flats, flats_infos=flats_infos)
|
912
|
-
except Exception
|
914
|
+
except Exception:
|
913
915
|
pass
|
914
916
|
scan.set_reduced_flats(reduced_flats, flats_infos=flats_infos)
|
915
917
|
|
@@ -979,7 +981,7 @@ class PreProcessingStitching(SingleAxisStitcher):
|
|
979
981
|
):
|
980
982
|
i_frame = 0
|
981
983
|
_, set_of_compacted_slices = get_compacted_dataslices(scan_urls, return_url_set=True)
|
982
|
-
for
|
984
|
+
for url in set_of_compacted_slices.values():
|
983
985
|
scan = scans[i_scan]
|
984
986
|
url = DataUrl(
|
985
987
|
file_path=url.file_path(),
|
@@ -1000,12 +1002,12 @@ class PreProcessingStitching(SingleAxisStitcher):
|
|
1000
1002
|
|
1001
1003
|
missing = []
|
1002
1004
|
if len(scan.reduced_flats) == 0:
|
1003
|
-
missing
|
1005
|
+
missing.append("flats")
|
1004
1006
|
if len(scan.reduced_darks) == 0:
|
1005
|
-
missing
|
1007
|
+
missing.append("darks")
|
1006
1008
|
|
1007
1009
|
if len(missing) > 0:
|
1008
|
-
_logger.warning(f"missing {'and'.join(missing)}. Unable to do flat field correction")
|
1010
|
+
_logger.warning(f"missing {' and '.join(missing)}. Unable to do flat field correction")
|
1009
1011
|
ff_arrays = None
|
1010
1012
|
data = raw_radios
|
1011
1013
|
else:
|
@@ -1,8 +1,7 @@
|
|
1
|
-
import h5py
|
2
1
|
import numpy
|
3
2
|
import logging
|
4
|
-
from
|
5
|
-
from
|
3
|
+
from typing import Optional, Union
|
4
|
+
from collections.abc import Iterable
|
6
5
|
from tomoscan.series import Series
|
7
6
|
from tomoscan.identifier import BaseIdentifier
|
8
7
|
from nabu.stitching.stitcher.base import _StitcherBase, get_obj_constant_side_length
|
@@ -38,7 +37,7 @@ class _SingleAxisMetaClass(type):
|
|
38
37
|
Metaclass for single axis stitcher in order to aggregate dumper class and axis
|
39
38
|
"""
|
40
39
|
|
41
|
-
def __new__(mcls, name, bases, attrs, axis=None, dumper_cls=None):
|
40
|
+
def __new__(mcls, name, bases, attrs, axis=None, dumper_cls=None): # noqa: N804
|
42
41
|
mcls = super().__new__(mcls, name, bases, attrs)
|
43
42
|
mcls._axis = axis
|
44
43
|
mcls._dumperCls = dumper_cls
|
nabu/stitching/stitcher_2D.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# ruff: noqa: N999
|
1
2
|
import numpy
|
2
3
|
from math import ceil
|
3
4
|
from typing import Union, Optional
|
@@ -19,7 +20,7 @@ def stitch_raw_frames(
|
|
19
20
|
pad_mode="constant",
|
20
21
|
new_unstitched_axis_size: Optional[int] = None,
|
21
22
|
) -> numpy.ndarray:
|
22
|
-
"""
|
23
|
+
r"""
|
23
24
|
stitches raw frames (already shifted and flat fielded !!!) together using
|
24
25
|
raw stitching (no pixel interpolation, y_overlap_in_px is expected to be a int).
|
25
26
|
Sttiching depends on the kernel used.
|
@@ -43,12 +43,10 @@ def test_stitching_config(stitching_type, option_level):
|
|
43
43
|
stiching_config.dict_to_config_obj(config),
|
44
44
|
stiching_config.PostProcessedSingleAxisStitchingConfiguration,
|
45
45
|
)
|
46
|
-
elif
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
)
|
51
|
-
elif stitching_type is stiching_config.StitchingType.Y_PREPROC:
|
46
|
+
elif (
|
47
|
+
stitching_type is stiching_config.StitchingType.Z_PREPROC
|
48
|
+
or stitching_type is stiching_config.StitchingType.Y_PREPROC
|
49
|
+
):
|
52
50
|
assert isinstance(
|
53
51
|
stiching_config.dict_to_config_obj(config),
|
54
52
|
stiching_config.PreProcessedSingleAxisStitchingConfiguration,
|
@@ -92,7 +90,7 @@ def test_stitching_config(stitching_type, option_level):
|
|
92
90
|
"",
|
93
91
|
None,
|
94
92
|
"None",
|
95
|
-
"",
|
93
|
+
"", # noqa: PT014
|
96
94
|
"skimage",
|
97
95
|
"nabu-fft",
|
98
96
|
),
|
@@ -176,7 +174,8 @@ def test_PreProcessedZStitchingConfiguration(
|
|
176
174
|
|
177
175
|
from_dict = stiching_config.PreProcessedZStitchingConfiguration.from_dict(pre_process_config.to_dict())
|
178
176
|
# workaround for scans because a new object is created each time
|
179
|
-
|
177
|
+
# ???
|
178
|
+
pre_process_config.settle_inputs # noqa: B018
|
180
179
|
assert len(from_dict.input_scans) == len(pre_process_config.input_scans)
|
181
180
|
from_dict.input_scans = None
|
182
181
|
pre_process_config.input_scans = None
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import os
|
2
|
-
import numpy
|
3
2
|
import pytest
|
4
3
|
from tomoscan.esrf import NXtomoScan
|
5
4
|
from tomoscan.esrf.volume import HDF5Volume
|
@@ -14,7 +13,7 @@ from nabu.stitching.slurm_utils import (
|
|
14
13
|
from tomoscan.esrf.mock import MockNXtomo
|
15
14
|
|
16
15
|
try:
|
17
|
-
import sluurp
|
16
|
+
import sluurp # noqa: F401
|
18
17
|
except ImportError:
|
19
18
|
has_sluurp = False
|
20
19
|
else:
|
@@ -770,6 +770,6 @@ def test_data_duplication(tmp_path, data_duplication):
|
|
770
770
|
if not data_duplication:
|
771
771
|
# make sure an error is raised if we try to ask for no data duplication and if we get some flips
|
772
772
|
z_stich_config.flip_ud = (False, True, False)
|
773
|
-
with pytest.raises(ValueError):
|
773
|
+
with pytest.raises(ValueError): # noqa: PT012
|
774
774
|
stitcher = PostProcessZStitcherNoDD(z_stich_config, progress=None)
|
775
775
|
stitcher.stitch()
|
@@ -5,11 +5,11 @@ import numpy
|
|
5
5
|
import pytest
|
6
6
|
from nabu.stitching.config import PreProcessedZStitchingConfiguration
|
7
7
|
from nabu.stitching.config import KEY_IMG_REG_METHOD
|
8
|
-
from nabu.stitching.overlap import
|
8
|
+
from nabu.stitching.overlap import OverlapStitchingStrategy
|
9
9
|
from nabu.stitching.z_stitching import (
|
10
10
|
PreProcessZStitcher,
|
11
11
|
)
|
12
|
-
from nabu.stitching.stitcher_2D import
|
12
|
+
from nabu.stitching.stitcher_2D import get_overlap_areas
|
13
13
|
from nxtomo.nxobject.nxdetector import ImageKey
|
14
14
|
from nxtomo.utils.transformation import DetYFlipTransformation, DetZFlipTransformation
|
15
15
|
from nxtomo.application.nxtomo import NXtomo
|
@@ -301,11 +301,11 @@ def test_DistributePreProcessZStitcher(tmp_path, configuration_dist):
|
|
301
301
|
)
|
302
302
|
|
303
303
|
if complete:
|
304
|
-
len(final_nx_tomo.instrument.detector.data) ==
|
304
|
+
assert len(final_nx_tomo.instrument.detector.data) == 100
|
305
305
|
# test middle
|
306
306
|
numpy.testing.assert_array_almost_equal(raw_data[1], final_nx_tomo.instrument.detector.data[1, :, :])
|
307
307
|
else:
|
308
|
-
len(final_nx_tomo.instrument.detector.data) == 3
|
308
|
+
assert len(final_nx_tomo.instrument.detector.data) == 3
|
309
309
|
# test middle
|
310
310
|
numpy.testing.assert_array_almost_equal(raw_data[49], final_nx_tomo.instrument.detector.data[1, :, :])
|
311
311
|
# in the case of first, middle and last frames
|
File without changes
|