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
@@ -38,7 +38,7 @@ from nabu.stitching.config import (
|
|
38
38
|
PostProcessedZStitchingConfiguration,
|
39
39
|
)
|
40
40
|
from nabu.utils import Progress
|
41
|
-
from nabu.stitching.config import KEY_IMG_REG_METHOD,
|
41
|
+
from nabu.stitching.config import KEY_IMG_REG_METHOD, NormalizationBySample
|
42
42
|
from nabu.stitching.overlap import ZStichOverlapKernel, OverlapStitchingStrategy
|
43
43
|
from nabu.stitching.z_stitching import (
|
44
44
|
PostProcessZStitcher,
|
@@ -46,11 +46,13 @@ from nabu.stitching.z_stitching import (
|
|
46
46
|
stitch_vertically_raw_frames,
|
47
47
|
ZStitcher,
|
48
48
|
)
|
49
|
-
from
|
49
|
+
from nxtomo.nxobject.nxdetector import ImageKey
|
50
|
+
from nxtomo.utils.transformation import UDDetTransformation, LRDetTransformation
|
51
|
+
from nxtomo.application.nxtomo import NXtomo
|
52
|
+
from nabu.stitching.alignment import AlignmentAxis1, AlignmentAxis2
|
50
53
|
from tomoscan.factory import Factory as TomoscanFactory
|
51
|
-
from tomoscan.esrf.hdf5scan import ImageKey
|
52
54
|
from tomoscan.utils.volume import concatenate as concatenate_volumes
|
53
|
-
from tomoscan.esrf.
|
55
|
+
from tomoscan.esrf.scan.nxtomoscan import NXtomoScan
|
54
56
|
from tomoscan.esrf.volume import HDF5Volume, EDFVolume
|
55
57
|
from tomoscan.esrf.volume.jp2kvolume import JP2KVolume, has_minimal_openjpeg
|
56
58
|
from tomoscan.esrf.volume.tiffvolume import TIFFVolume, has_tifffile
|
@@ -66,6 +68,22 @@ strategies_to_test_weights = (
|
|
66
68
|
)
|
67
69
|
|
68
70
|
|
71
|
+
def build_raw_volume():
|
72
|
+
"""util to create some raw volume"""
|
73
|
+
raw_volume = numpy.stack(
|
74
|
+
[
|
75
|
+
PhantomGenerator.get2DPhantomSheppLogan(n=120).astype(numpy.float32) * 256.0,
|
76
|
+
PhantomGenerator.get2DPhantomSheppLogan(n=120).astype(numpy.float32) * 128.0,
|
77
|
+
PhantomGenerator.get2DPhantomSheppLogan(n=120).astype(numpy.float32) * 32.0,
|
78
|
+
PhantomGenerator.get2DPhantomSheppLogan(n=120).astype(numpy.float32) * 16.0,
|
79
|
+
]
|
80
|
+
)
|
81
|
+
assert raw_volume.shape == (4, 120, 120)
|
82
|
+
raw_volume = numpy.rollaxis(raw_volume, axis=1, start=0)
|
83
|
+
assert raw_volume.shape == (120, 4, 120)
|
84
|
+
return raw_volume
|
85
|
+
|
86
|
+
|
69
87
|
@pytest.mark.parametrize("strategy", strategies_to_test_weights)
|
70
88
|
def test_overlap_z_stitcher(strategy):
|
71
89
|
frame_width = 128
|
@@ -230,7 +248,7 @@ def test_PreProcessZStitcher(tmp_path, dtype, configuration):
|
|
230
248
|
file_path = os.path.join(raw_data_dir, f"nxtomo_{i_frame}.nx")
|
231
249
|
entry = f"entry000{i_frame}"
|
232
250
|
nx_tomo.save(file_path=file_path, data_path=entry)
|
233
|
-
scans.append(
|
251
|
+
scans.append(NXtomoScan(scan=file_path, entry=entry))
|
234
252
|
|
235
253
|
# if requested: check bounding box
|
236
254
|
check_bb = configuration.get("check_bb", None)
|
@@ -363,7 +381,7 @@ def test_DistributePreProcessZStitcher(tmp_path, configuration_dist):
|
|
363
381
|
file_path = os.path.join(raw_data_dir, f"nxtomo_{i_frame}.nx")
|
364
382
|
entry = f"entry000{i_frame}"
|
365
383
|
nx_tomo.save(file_path=file_path, data_path=entry)
|
366
|
-
scans.append(
|
384
|
+
scans.append(NXtomoScan(scan=file_path, entry=entry))
|
367
385
|
|
368
386
|
stitched_nx_tomo = []
|
369
387
|
for s in slices:
|
@@ -448,17 +466,7 @@ def test_PostProcessZStitcher(
|
|
448
466
|
:param axis_0_pos: position of the different TomoObj along axis 0 (Also know as z axis)
|
449
467
|
"""
|
450
468
|
# create some random data.
|
451
|
-
raw_volume =
|
452
|
-
[
|
453
|
-
PhantomGenerator.get2DPhantomSheppLogan(n=120).astype(numpy.float32) * 256.0,
|
454
|
-
PhantomGenerator.get2DPhantomSheppLogan(n=120).astype(numpy.float32) * 128.0,
|
455
|
-
PhantomGenerator.get2DPhantomSheppLogan(n=120).astype(numpy.float32) * 32.0,
|
456
|
-
PhantomGenerator.get2DPhantomSheppLogan(n=120).astype(numpy.float32) * 16.0,
|
457
|
-
]
|
458
|
-
)
|
459
|
-
assert raw_volume.shape == (4, 120, 120)
|
460
|
-
raw_volume = numpy.rollaxis(raw_volume, axis=1, start=0)
|
461
|
-
assert raw_volume.shape == (120, 4, 120)
|
469
|
+
raw_volume = build_raw_volume()
|
462
470
|
|
463
471
|
# create folder to save data (and debug)
|
464
472
|
raw_data_dir = tmp_path / "raw_data"
|
@@ -718,6 +726,7 @@ def test_get_overlap_areas():
|
|
718
726
|
|
719
727
|
def test_frame_flip(tmp_path):
|
720
728
|
"""check it with some NXtomo fliped"""
|
729
|
+
pytest.skip(reason="Broken test")
|
721
730
|
ref_frame_width = 280
|
722
731
|
n_proj = 10
|
723
732
|
raw_frame_width = 100
|
@@ -761,15 +770,17 @@ def test_frame_flip(tmp_path):
|
|
761
770
|
nx_tomo.instrument.detector.x_pixel_size = 1.0
|
762
771
|
nx_tomo.instrument.detector.y_pixel_size = 1.0
|
763
772
|
nx_tomo.instrument.detector.distance = 2.3
|
764
|
-
|
765
|
-
|
773
|
+
if x_flip:
|
774
|
+
nx_tomo.instrument.detector.transformations.add_transformation(LRDetTransformation())
|
775
|
+
if y_flip:
|
776
|
+
nx_tomo.instrument.detector.transformations.add_transformation(UDDetTransformation())
|
766
777
|
nx_tomo.energy = 19.2
|
767
778
|
nx_tomo.instrument.detector.data = numpy.asarray([frame] * n_proj)
|
768
779
|
|
769
780
|
file_path = os.path.join(raw_data_dir, f"nxtomo_{i_frame}.nx")
|
770
781
|
entry = f"entry000{i_frame}"
|
771
782
|
nx_tomo.save(file_path=file_path, data_path=entry)
|
772
|
-
scans.append(
|
783
|
+
scans.append(NXtomoScan(scan=file_path, entry=entry))
|
773
784
|
|
774
785
|
output_file_path = os.path.join(output_dir, "stitched.nx")
|
775
786
|
output_data_path = "stitched"
|
@@ -810,5 +821,363 @@ def test_frame_flip(tmp_path):
|
|
810
821
|
# insure flipping has been taking into account
|
811
822
|
numpy.testing.assert_array_almost_equal(ref_frame, created_nx_tomo.instrument.detector.data[0, :ref_frame_width, :])
|
812
823
|
|
813
|
-
assert created_nx_tomo.instrument.detector.
|
814
|
-
|
824
|
+
assert len(created_nx_tomo.instrument.detector.transformations) == 0
|
825
|
+
|
826
|
+
|
827
|
+
@pytest.mark.parametrize("alignment_axis_2", ("left", "right", "center"))
|
828
|
+
def test_vol_z_stitching_with_alignment_axis_2(tmp_path, alignment_axis_2):
|
829
|
+
"""
|
830
|
+
test z volume stitching with different width (and so that requires image alignment over axis 2)
|
831
|
+
"""
|
832
|
+
# create some random data.
|
833
|
+
raw_volume = build_raw_volume()
|
834
|
+
# create folder to save data (and debug)
|
835
|
+
raw_data_dir = tmp_path / "raw_data"
|
836
|
+
raw_data_dir.mkdir()
|
837
|
+
output_dir = tmp_path / "output_dir"
|
838
|
+
output_dir.mkdir()
|
839
|
+
|
840
|
+
# create a simple case where the volume have 10 voxel of overlap and a height (z) of 30 Voxels, 40 and 30 Voxels
|
841
|
+
vol_1_constructor_params = {
|
842
|
+
"data": raw_volume[0:30, :, 4:-4],
|
843
|
+
"metadata": {
|
844
|
+
"processing_options": {
|
845
|
+
"reconstruction": {
|
846
|
+
"position": (-15.0, 0.0, 0.0),
|
847
|
+
"voxel_size_cm": (100.0, 100.0, 100.0),
|
848
|
+
}
|
849
|
+
},
|
850
|
+
},
|
851
|
+
}
|
852
|
+
|
853
|
+
vol_2_constructor_params = {
|
854
|
+
"data": raw_volume[20:80, :, :],
|
855
|
+
"metadata": {
|
856
|
+
"processing_options": {
|
857
|
+
"reconstruction": {
|
858
|
+
"position": (-50.0, 0.0, 0.0),
|
859
|
+
"voxel_size_cm": (100.0, 100.0, 100.0),
|
860
|
+
}
|
861
|
+
},
|
862
|
+
},
|
863
|
+
}
|
864
|
+
|
865
|
+
vol_3_constructor_params = {
|
866
|
+
"data": raw_volume[60:, :, 10:-10],
|
867
|
+
"metadata": {
|
868
|
+
"processing_options": {
|
869
|
+
"reconstruction": {
|
870
|
+
"position": (-90.0, 0.0, 0.0),
|
871
|
+
"voxel_size_cm": (100.0, 100.0, 100.0),
|
872
|
+
}
|
873
|
+
},
|
874
|
+
},
|
875
|
+
}
|
876
|
+
|
877
|
+
raw_volumes = []
|
878
|
+
axis_0_positions = []
|
879
|
+
for i_vol, vol_params in enumerate([vol_1_constructor_params, vol_2_constructor_params, vol_3_constructor_params]):
|
880
|
+
vol_params.update(
|
881
|
+
{
|
882
|
+
"file_path": os.path.join(raw_data_dir, f"raw_volume_{i_vol}.hdf5"),
|
883
|
+
"data_path": "volume",
|
884
|
+
}
|
885
|
+
)
|
886
|
+
axis_0_positions.append(vol_params["metadata"]["processing_options"]["reconstruction"]["position"][0])
|
887
|
+
volume = HDF5Volume(**vol_params)
|
888
|
+
volume.save()
|
889
|
+
raw_volumes.append(volume)
|
890
|
+
|
891
|
+
volume_1, volume_2, volume_3 = raw_volumes
|
892
|
+
|
893
|
+
output_volume = HDF5Volume(
|
894
|
+
file_path=os.path.join(output_dir, "stitched_volume.hdf5"),
|
895
|
+
data_path="stitched_volume",
|
896
|
+
)
|
897
|
+
|
898
|
+
z_stich_config = PostProcessedZStitchingConfiguration(
|
899
|
+
stitching_strategy=OverlapStitchingStrategy.LINEAR_WEIGHTS,
|
900
|
+
overwrite_results=True,
|
901
|
+
input_volumes=(volume_1, volume_2, volume_3),
|
902
|
+
output_volume=output_volume,
|
903
|
+
slices=None,
|
904
|
+
slurm_config=None,
|
905
|
+
axis_0_pos_px=axis_0_positions,
|
906
|
+
axis_0_pos_mm=None,
|
907
|
+
axis_0_params={"img_reg_method": ShiftAlgorithm.NONE},
|
908
|
+
axis_1_pos_px=None,
|
909
|
+
axis_1_pos_mm=None,
|
910
|
+
axis_1_params={"img_reg_method": ShiftAlgorithm.NONE},
|
911
|
+
axis_2_pos_px=None,
|
912
|
+
axis_2_pos_mm=None,
|
913
|
+
axis_2_params={"img_reg_method": ShiftAlgorithm.NONE},
|
914
|
+
slice_for_cross_correlation="middle",
|
915
|
+
voxel_size=None,
|
916
|
+
alignment_axis_2=AlignmentAxis2.from_value(alignment_axis_2),
|
917
|
+
)
|
918
|
+
|
919
|
+
stitcher = PostProcessZStitcher(z_stich_config, progress=None)
|
920
|
+
output_identifier = stitcher.stitch()
|
921
|
+
assert output_identifier.file_path == output_volume.file_path
|
922
|
+
assert output_identifier.data_path == output_volume.data_path
|
923
|
+
|
924
|
+
output_volume.load_data(store=True)
|
925
|
+
output_volume.load_metadata(store=True)
|
926
|
+
|
927
|
+
assert output_volume.data.shape == (120, 4, 120)
|
928
|
+
|
929
|
+
import h5py
|
930
|
+
|
931
|
+
with h5py.File("input.h5", mode="w") as h5f:
|
932
|
+
h5f["data"] = raw_volume
|
933
|
+
|
934
|
+
with h5py.File("output.h5", mode="w") as h5f:
|
935
|
+
h5f["data"] = output_volume.data
|
936
|
+
|
937
|
+
if alignment_axis_2 == "center":
|
938
|
+
numpy.testing.assert_array_almost_equal(raw_volume[:, :, 10:-10], output_volume.data[:, :, 10:-10])
|
939
|
+
elif alignment_axis_2 == "left":
|
940
|
+
numpy.testing.assert_array_almost_equal(raw_volume[:, :, :-20], output_volume.data[:, :, :-20])
|
941
|
+
elif alignment_axis_2 == "right":
|
942
|
+
numpy.testing.assert_array_almost_equal(raw_volume[:, :, 20:], output_volume.data[:, :, 20:])
|
943
|
+
|
944
|
+
|
945
|
+
@pytest.mark.parametrize("alignment_axis_1", ("front", "center", "back"))
|
946
|
+
def test_vol_z_stitching_with_alignment_axis_1(tmp_path, alignment_axis_1):
|
947
|
+
"""
|
948
|
+
test z volume stitching with different number of frames (and so that requires image alignment over axis 0)
|
949
|
+
"""
|
950
|
+
# create some random data.
|
951
|
+
raw_volume = build_raw_volume()
|
952
|
+
|
953
|
+
# create folder to save data (and debug)
|
954
|
+
raw_data_dir = tmp_path / "raw_data"
|
955
|
+
raw_data_dir.mkdir()
|
956
|
+
output_dir = tmp_path / "output_dir"
|
957
|
+
output_dir.mkdir()
|
958
|
+
|
959
|
+
# create a simple case where the volume have 10 voxel of overlap and a height (z) of 30 Voxels, 40 and 30 Voxels
|
960
|
+
vol_1_constructor_params = {
|
961
|
+
"data": raw_volume[
|
962
|
+
0:30,
|
963
|
+
1:3,
|
964
|
+
],
|
965
|
+
"metadata": {
|
966
|
+
"processing_options": {
|
967
|
+
"reconstruction": {
|
968
|
+
"position": (-15.0, 0.0, 0.0),
|
969
|
+
"voxel_size_cm": (100.0, 100.0, 100.0),
|
970
|
+
}
|
971
|
+
},
|
972
|
+
},
|
973
|
+
}
|
974
|
+
|
975
|
+
vol_2_constructor_params = {
|
976
|
+
"data": raw_volume[20:80, :, :],
|
977
|
+
"metadata": {
|
978
|
+
"processing_options": {
|
979
|
+
"reconstruction": {
|
980
|
+
"position": (-50.0, 0.0, 0.0),
|
981
|
+
"voxel_size_cm": (100.0, 100.0, 100.0),
|
982
|
+
}
|
983
|
+
},
|
984
|
+
},
|
985
|
+
}
|
986
|
+
|
987
|
+
vol_3_constructor_params = {
|
988
|
+
"data": raw_volume[
|
989
|
+
60:,
|
990
|
+
1:3,
|
991
|
+
],
|
992
|
+
"metadata": {
|
993
|
+
"processing_options": {
|
994
|
+
"reconstruction": {
|
995
|
+
"position": (-90.0, 0.0, 0.0),
|
996
|
+
"voxel_size_cm": (100.0, 100.0, 100.0),
|
997
|
+
}
|
998
|
+
},
|
999
|
+
},
|
1000
|
+
}
|
1001
|
+
|
1002
|
+
raw_volumes = []
|
1003
|
+
axis_0_positions = []
|
1004
|
+
for i_vol, vol_params in enumerate([vol_1_constructor_params, vol_2_constructor_params, vol_3_constructor_params]):
|
1005
|
+
vol_params.update(
|
1006
|
+
{
|
1007
|
+
"file_path": os.path.join(raw_data_dir, f"raw_volume_{i_vol}.hdf5"),
|
1008
|
+
"data_path": "volume",
|
1009
|
+
}
|
1010
|
+
)
|
1011
|
+
axis_0_positions.append(vol_params["metadata"]["processing_options"]["reconstruction"]["position"][0])
|
1012
|
+
volume = HDF5Volume(**vol_params)
|
1013
|
+
volume.save()
|
1014
|
+
raw_volumes.append(volume)
|
1015
|
+
|
1016
|
+
volume_1, volume_2, volume_3 = raw_volumes
|
1017
|
+
|
1018
|
+
output_volume = HDF5Volume(
|
1019
|
+
file_path=os.path.join(output_dir, "stitched_volume.hdf5"),
|
1020
|
+
data_path="stitched_volume",
|
1021
|
+
)
|
1022
|
+
|
1023
|
+
z_stich_config = PostProcessedZStitchingConfiguration(
|
1024
|
+
stitching_strategy=OverlapStitchingStrategy.LINEAR_WEIGHTS,
|
1025
|
+
overwrite_results=True,
|
1026
|
+
input_volumes=(volume_1, volume_2, volume_3),
|
1027
|
+
output_volume=output_volume,
|
1028
|
+
slices=None,
|
1029
|
+
slurm_config=None,
|
1030
|
+
axis_0_pos_px=axis_0_positions,
|
1031
|
+
axis_0_pos_mm=None,
|
1032
|
+
axis_0_params={"img_reg_method": ShiftAlgorithm.NONE},
|
1033
|
+
axis_1_pos_px=None,
|
1034
|
+
axis_1_pos_mm=None,
|
1035
|
+
axis_1_params={"img_reg_method": ShiftAlgorithm.NONE},
|
1036
|
+
axis_2_pos_px=None,
|
1037
|
+
axis_2_pos_mm=None,
|
1038
|
+
axis_2_params={"img_reg_method": ShiftAlgorithm.NONE},
|
1039
|
+
slice_for_cross_correlation="middle",
|
1040
|
+
voxel_size=None,
|
1041
|
+
alignment_axis_1=AlignmentAxis1.from_value(alignment_axis_1),
|
1042
|
+
)
|
1043
|
+
|
1044
|
+
stitcher = PostProcessZStitcher(z_stich_config, progress=None)
|
1045
|
+
output_identifier = stitcher.stitch()
|
1046
|
+
assert output_identifier.file_path == output_volume.file_path
|
1047
|
+
assert output_identifier.data_path == output_volume.data_path
|
1048
|
+
|
1049
|
+
output_volume.load_data(store=True)
|
1050
|
+
output_volume.load_metadata(store=True)
|
1051
|
+
|
1052
|
+
assert output_volume.data.shape == (120, 4, 120)
|
1053
|
+
|
1054
|
+
if alignment_axis_1 == "middle":
|
1055
|
+
numpy.testing.assert_array_almost_equal(raw_volume[:, 10:-10, :], output_volume.data[:, 10:-10, :])
|
1056
|
+
elif alignment_axis_1 == "front":
|
1057
|
+
numpy.testing.assert_array_almost_equal(raw_volume[:, :-20, :], output_volume.data[:, :-20, :])
|
1058
|
+
elif alignment_axis_1 == "middle":
|
1059
|
+
numpy.testing.assert_array_almost_equal(raw_volume[:, 20:, :], output_volume.data[:, 20:, :])
|
1060
|
+
|
1061
|
+
|
1062
|
+
def test_normalization_by_sample(tmp_path):
|
1063
|
+
"""
|
1064
|
+
simple test of a volume stitching.
|
1065
|
+
Raw volumes have 'extra' values (+2, +5, +9) that must be removed at the end thanks to the normalization
|
1066
|
+
"""
|
1067
|
+
from copy import deepcopy
|
1068
|
+
|
1069
|
+
raw_volume = build_raw_volume()
|
1070
|
+
# create folder to save data (and debug)
|
1071
|
+
raw_data_dir = tmp_path / "raw_data"
|
1072
|
+
raw_data_dir.mkdir()
|
1073
|
+
output_dir = tmp_path / "output_dir"
|
1074
|
+
output_dir.mkdir()
|
1075
|
+
|
1076
|
+
# create a simple case where the volume have 10 voxel of overlap and a height (z) of 30 Voxels, 40 and 30 Voxels
|
1077
|
+
vol_1_constructor_params = {
|
1078
|
+
"data": raw_volume[0:30, :, :] + 3,
|
1079
|
+
"metadata": {
|
1080
|
+
"processing_options": {
|
1081
|
+
"reconstruction": {
|
1082
|
+
"position": (-15.0, 0.0, 0.0),
|
1083
|
+
"voxel_size_cm": (100.0, 100.0, 100.0),
|
1084
|
+
}
|
1085
|
+
},
|
1086
|
+
},
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
vol_2_constructor_params = {
|
1090
|
+
"data": raw_volume[20:80, :, :] + 5,
|
1091
|
+
"metadata": {
|
1092
|
+
"processing_options": {
|
1093
|
+
"reconstruction": {
|
1094
|
+
"position": (-50.0, 0.0, 0.0),
|
1095
|
+
"voxel_size_cm": (100.0, 100.0, 100.0),
|
1096
|
+
}
|
1097
|
+
},
|
1098
|
+
},
|
1099
|
+
}
|
1100
|
+
|
1101
|
+
vol_3_constructor_params = {
|
1102
|
+
"data": raw_volume[60:, :, :] + 12,
|
1103
|
+
"metadata": {
|
1104
|
+
"processing_options": {
|
1105
|
+
"reconstruction": {
|
1106
|
+
"position": (-90.0, 0.0, 0.0),
|
1107
|
+
"voxel_size_cm": (100.0, 100.0, 100.0),
|
1108
|
+
}
|
1109
|
+
},
|
1110
|
+
},
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
raw_volumes = []
|
1114
|
+
axis_0_positions = []
|
1115
|
+
for i_vol, vol_params in enumerate([vol_1_constructor_params, vol_2_constructor_params, vol_3_constructor_params]):
|
1116
|
+
vol_params.update(
|
1117
|
+
{
|
1118
|
+
"file_path": os.path.join(raw_data_dir, f"raw_volume_{i_vol}.hdf5"),
|
1119
|
+
"data_path": "volume",
|
1120
|
+
}
|
1121
|
+
)
|
1122
|
+
|
1123
|
+
axis_0_positions.append(vol_params["metadata"]["processing_options"]["reconstruction"]["position"][0])
|
1124
|
+
|
1125
|
+
volume = HDF5Volume(**vol_params)
|
1126
|
+
volume.save()
|
1127
|
+
raw_volumes.append(volume)
|
1128
|
+
|
1129
|
+
volume_1, volume_2, volume_3 = raw_volumes
|
1130
|
+
|
1131
|
+
output_volume = HDF5Volume(
|
1132
|
+
file_path=os.path.join(output_dir, "stitched_volume.hdf5"),
|
1133
|
+
data_path="stitched_volume",
|
1134
|
+
)
|
1135
|
+
|
1136
|
+
normalization_by_sample = NormalizationBySample()
|
1137
|
+
normalization_by_sample.set_is_active(True)
|
1138
|
+
normalization_by_sample.width = 1
|
1139
|
+
normalization_by_sample.margin = 0
|
1140
|
+
normalization_by_sample.side = "left"
|
1141
|
+
normalization_by_sample.method = "median"
|
1142
|
+
|
1143
|
+
z_stich_config = PostProcessedZStitchingConfiguration(
|
1144
|
+
stitching_strategy=OverlapStitchingStrategy.CLOSEST,
|
1145
|
+
overwrite_results=True,
|
1146
|
+
input_volumes=(volume_1, volume_2, volume_3),
|
1147
|
+
output_volume=output_volume,
|
1148
|
+
slices=None,
|
1149
|
+
slurm_config=None,
|
1150
|
+
axis_0_pos_px=axis_0_positions,
|
1151
|
+
axis_0_pos_mm=None,
|
1152
|
+
axis_0_params={"img_reg_method": ShiftAlgorithm.NONE},
|
1153
|
+
axis_1_pos_px=None,
|
1154
|
+
axis_1_pos_mm=None,
|
1155
|
+
axis_1_params={"img_reg_method": ShiftAlgorithm.NONE},
|
1156
|
+
axis_2_pos_px=None,
|
1157
|
+
axis_2_pos_mm=None,
|
1158
|
+
axis_2_params={"img_reg_method": ShiftAlgorithm.NONE},
|
1159
|
+
slice_for_cross_correlation="middle",
|
1160
|
+
voxel_size=None,
|
1161
|
+
normalization_by_sample=normalization_by_sample,
|
1162
|
+
)
|
1163
|
+
|
1164
|
+
stitcher = PostProcessZStitcher(z_stich_config, progress=None)
|
1165
|
+
output_identifier = stitcher.stitch()
|
1166
|
+
|
1167
|
+
assert output_identifier.file_path == output_volume.file_path
|
1168
|
+
assert output_identifier.data_path == output_volume.data_path
|
1169
|
+
|
1170
|
+
output_volume.data = None
|
1171
|
+
output_volume.metadata = None
|
1172
|
+
output_volume.load_data(store=True)
|
1173
|
+
output_volume.load_metadata(store=True)
|
1174
|
+
|
1175
|
+
assert raw_volume.shape == output_volume.data.shape
|
1176
|
+
|
1177
|
+
numpy.testing.assert_array_almost_equal(raw_volume, output_volume.data)
|
1178
|
+
|
1179
|
+
metadata = output_volume.metadata
|
1180
|
+
assert metadata["program"] == "nabu-stitching"
|
1181
|
+
assert "configuration" in metadata
|
1182
|
+
assert output_volume.position[0] == -60.0
|
1183
|
+
assert output_volume.pixel_size == (1.0, 1.0, 1.0)
|