tomwer 1.3.0.dev2__py3-none-any.whl → 1.3.0rc10__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 (156) hide show
  1. orangecontrib/tomwer/widgets/__init__.py +11 -12
  2. orangecontrib/tomwer/widgets/control/DataListenerOW.py +6 -6
  3. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +6 -6
  4. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +3 -3
  5. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +10 -8
  6. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +6 -6
  7. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +1 -1
  8. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -98
  9. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -129
  10. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +13 -12
  11. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +11 -9
  12. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +11 -9
  13. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +12 -15
  14. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +9 -9
  15. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  16. orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -1
  17. tomwer/__main__.py +0 -10
  18. tomwer/app/canvas_launcher/config.py +3 -3
  19. tomwer/app/canvas_launcher/environ.py +1 -0
  20. tomwer/app/intensitynormalization.py +12 -11
  21. tomwer/app/nabuapp.py +0 -11
  22. tomwer/app/zstitching.py +11 -1
  23. tomwer/core/process/control/datalistener/datalistener.py +15 -10
  24. tomwer/core/process/control/nxtomomill.py +1 -1
  25. tomwer/core/process/control/scantransfer.py +8 -32
  26. tomwer/core/process/edit/darkflatpatch.py +8 -9
  27. tomwer/core/process/edit/imagekeyeditor.py +20 -22
  28. tomwer/core/process/icat/screenshots.py +1 -0
  29. tomwer/core/process/reconstruction/axis/axis.py +263 -59
  30. tomwer/core/process/reconstruction/axis/mode.py +161 -50
  31. tomwer/core/process/reconstruction/axis/params.py +23 -20
  32. tomwer/core/process/reconstruction/darkref/darkrefs.py +12 -13
  33. tomwer/core/process/reconstruction/nabu/castvolume.py +3 -3
  34. tomwer/core/process/reconstruction/nabu/nabucommon.py +43 -19
  35. tomwer/core/process/reconstruction/nabu/nabuscores.py +34 -7
  36. tomwer/core/process/reconstruction/nabu/nabuslices.py +81 -26
  37. tomwer/core/process/reconstruction/nabu/nabuvolume.py +31 -26
  38. tomwer/core/process/reconstruction/nabu/plane.py +9 -0
  39. tomwer/core/process/reconstruction/nabu/utils.py +32 -9
  40. tomwer/core/process/reconstruction/saaxis/saaxis.py +4 -1
  41. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +9 -1
  42. tomwer/core/process/reconstruction/scores/params.py +3 -3
  43. tomwer/core/process/reconstruction/test/test_darkref_copy.py +4 -4
  44. tomwer/core/process/stitching/nabustitcher.py +11 -10
  45. tomwer/core/process/task.py +33 -27
  46. tomwer/core/process/test/test_axis.py +7 -6
  47. tomwer/core/process/test/test_data_transfer.py +3 -3
  48. tomwer/core/process/test/test_nabu.py +10 -2
  49. tomwer/core/process/test/test_normalization.py +2 -2
  50. tomwer/core/scan/blissscan.py +3 -3
  51. tomwer/core/scan/edfscan.py +9 -9
  52. tomwer/core/scan/nxtomoscan.py +11 -11
  53. tomwer/core/scan/scanbase.py +31 -24
  54. tomwer/core/scan/test/test_future_scan.py +1 -1
  55. tomwer/core/scan/test/test_h5.py +4 -4
  56. tomwer/core/scan/test/test_process_registration.py +2 -2
  57. tomwer/core/scan/test/test_scan.py +1 -75
  58. tomwer/core/settings.py +3 -3
  59. tomwer/core/test/test_utils.py +2 -2
  60. tomwer/core/volume/edfvolume.py +6 -6
  61. tomwer/core/volume/hdf5volume.py +6 -6
  62. tomwer/core/volume/jp2kvolume.py +6 -6
  63. tomwer/core/volume/rawvolume.py +6 -6
  64. tomwer/core/volume/tiffvolume.py +12 -12
  65. tomwer/gui/cluster/slurm.py +14 -9
  66. tomwer/gui/cluster/supervisor.py +12 -0
  67. tomwer/gui/cluster/test/test_cluster.py +1 -2
  68. tomwer/gui/cluster/test/test_supervisor.py +1 -1
  69. tomwer/gui/control/datalist.py +5 -0
  70. tomwer/gui/control/datawatcher/controlwidget.py +2 -4
  71. tomwer/gui/control/reducedarkflatselector.py +8 -8
  72. tomwer/gui/control/test/test_single_tomo_obj.py +1 -1
  73. tomwer/gui/edit/dkrfpatch.py +4 -4
  74. tomwer/gui/edit/nxtomowarmer.py +2 -2
  75. tomwer/gui/edit/test/test_dkrf_patch.py +6 -6
  76. tomwer/gui/imagefromfile.py +2 -2
  77. tomwer/gui/qfolderdialog.py +5 -0
  78. tomwer/gui/reconstruction/axis/CompareImages.py +94 -168
  79. tomwer/gui/reconstruction/axis/radioaxis.py +58 -182
  80. tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -1
  81. tomwer/gui/reconstruction/nabu/castvolume.py +8 -1
  82. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +54 -21
  83. tomwer/gui/reconstruction/normalization/intensity.py +3 -25
  84. tomwer/gui/reconstruction/saaxis/corrangeselector.py +1 -1
  85. tomwer/gui/reconstruction/saaxis/saaxis.py +1 -11
  86. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +0 -10
  87. tomwer/gui/reconstruction/scores/scoreplot.py +1 -6
  88. tomwer/gui/reconstruction/test/test_axis.py +18 -4
  89. tomwer/gui/reconstruction/test/test_nabu.py +3 -0
  90. tomwer/gui/stitching/stitching.py +2 -2
  91. tomwer/gui/stitching/stitching_preview.py +7 -53
  92. tomwer/gui/stitching/stitching_raw.py +3 -3
  93. tomwer/gui/utils/inputwidget.py +12 -2
  94. tomwer/gui/utils/lineselector/lineselector.py +1 -1
  95. tomwer/gui/visualization/dataviewer.py +47 -17
  96. tomwer/gui/visualization/sinogramviewer.py +19 -26
  97. tomwer/gui/visualization/test/test_volumeviewer.py +64 -66
  98. tomwer/gui/visualization/volumeviewer.py +105 -105
  99. tomwer/io/utils/h5pyutils.py +7 -3
  100. tomwer/io/utils/utils.py +3 -3
  101. tomwer/resources/gui/icons/parameters.svg +1 -1
  102. tomwer/resources/gui/illustrations/no_rot.svg +1 -1
  103. tomwer/synctools/stacks/edit/darkflatpatch.py +17 -12
  104. tomwer/tests/test_scripts.py +0 -3
  105. tomwer/third_part/WaitingOverlay.py +110 -0
  106. tomwer/third_part/__init__.py +0 -0
  107. tomwer/version.py +2 -2
  108. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/METADATA +32 -31
  109. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/RECORD +115 -153
  110. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/WHEEL +1 -1
  111. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +0 -197
  112. orangecontrib/tomwer/widgets/reconstruction/icons/XY_lamino.svg +0 -168
  113. orangecontrib/tomwer/widgets/reconstruction/icons/XZ_lamino.svg +0 -275
  114. orangecontrib/tomwer/widgets/reconstruction/icons/YZ_lamino.svg +0 -182
  115. tomwer/app/lamino.py +0 -143
  116. tomwer/core/process/reconstruction/lamino/__init__.py +0 -1
  117. tomwer/core/process/reconstruction/lamino/tofu.py +0 -1000
  118. tomwer/core/process/test/test_lamino.py +0 -76
  119. tomwer/core/test/test_lamino.py +0 -92
  120. tomwer/gui/reconstruction/lamino/__init__.py +0 -31
  121. tomwer/gui/reconstruction/lamino/tofu/TofuOptionLoader.py +0 -107
  122. tomwer/gui/reconstruction/lamino/tofu/__init__.py +0 -1
  123. tomwer/gui/reconstruction/lamino/tofu/misc.py +0 -148
  124. tomwer/gui/reconstruction/lamino/tofu/projections.py +0 -896
  125. tomwer/gui/reconstruction/lamino/tofu/settings.py +0 -75
  126. tomwer/gui/reconstruction/lamino/tofu/tofu.py +0 -432
  127. tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +0 -567
  128. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +0 -757
  129. tomwer/gui/reconstruction/test/test_lamino.py +0 -194
  130. tomwer/resources/gui/icons/lamino_parameters.svg +0 -70
  131. tomwer/resources/gui/illustrations/lamino_angle.png +0 -0
  132. tomwer/resources/gui/illustrations/lamino_angle.svg +0 -509
  133. tomwer/resources/gui/illustrations/lamino_beta_angle.png +0 -0
  134. tomwer/resources/gui/illustrations/lamino_beta_angle.svg +0 -97
  135. tomwer/resources/gui/illustrations/lamino_theta_angle.png +0 -0
  136. tomwer/resources/gui/illustrations/lamino_theta_angle.svg +0 -368
  137. tomwer/resources/gui/illustrations/manual_slice.png +0 -0
  138. tomwer/resources/gui/illustrations/manual_slice.svg +0 -221
  139. tomwer/resources/gui/illustrations/psi_angle.png +0 -0
  140. tomwer/resources/gui/illustrations/psi_angle.svg +0 -479
  141. tomwer/resources/gui/illustrations/rotation_center.png +0 -0
  142. tomwer/resources/gui/illustrations/rotation_center.svg +0 -276
  143. tomwer/resources/gui/illustrations/slice_stack.png +0 -0
  144. tomwer/resources/gui/illustrations/slice_stack.svg +0 -266
  145. tomwer/resources/gui/illustrations/xy_slice.png +0 -0
  146. tomwer/resources/gui/illustrations/xy_slice.svg +0 -269
  147. tomwer/resources/gui/illustrations/xz_slice.png +0 -0
  148. tomwer/resources/gui/illustrations/xz_slice.svg +0 -270
  149. tomwer/resources/gui/illustrations/yz_slice.png +0 -0
  150. tomwer/resources/gui/illustrations/yz_slice.svg +0 -270
  151. tomwer/synctools/stacks/reconstruction/lamino.py +0 -233
  152. /tomwer-1.3.0.dev2-py3.11-nspkg.pth → /tomwer-1.3.0rc10-py3.11-nspkg.pth +0 -0
  153. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/LICENSE +0 -0
  154. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/entry_points.txt +0 -0
  155. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/namespace_packages.txt +0 -0
  156. {tomwer-1.3.0.dev2.dist-info → tomwer-1.3.0rc10.dist-info}/top_level.txt +0 -0
@@ -41,6 +41,7 @@ from processview.core.manager.manager import ProcessManager, DatasetState
41
41
  from tomwer.core.futureobject import FutureTomwerObject
42
42
  from tomwer.core.utils.scanutils import data_identifier_to_scan
43
43
  from tomwer.core.utils.slurm import is_slurm_available
44
+ from tomwer.core.process.reconstruction.nabu.plane import NabuPlane
44
45
  from tomwer.core.process.reconstruction.nabu.utils import slice_index_to_int
45
46
  from tomwer.core.process.icat.gallery import (
46
47
  IcatScreenshots,
@@ -83,7 +84,7 @@ from processview.core.superviseprocess import SuperviseProcess
83
84
  from silx.io.dictdump import h5todict
84
85
  from silx.io.utils import h5py_read_dataset
85
86
  from silx.utils.enum import Enum as _Enum
86
- from tomoscan.io import HDF5File
87
+ from tomoscan.io import HDF5File, get_swmr_mode
87
88
 
88
89
  from tomwer.core.process.task import Task
89
90
  from tomwer.core.scan.edfscan import EDFTomoScan
@@ -201,6 +202,7 @@ def run_slices_reconstruction(
201
202
  ask_sinogram_registration=ask_sinogram_registration,
202
203
  ask_sinogram_load=ask_sinogram_load,
203
204
  instanciate_class_only=instanciate_classes_only,
205
+ axis=config.get("reconstruction", {}).get("slice_plane", "XY"),
204
206
  )
205
207
 
206
208
  # specific treatments of results
@@ -443,15 +445,14 @@ class NabuSlicesTask(
443
445
  )
444
446
 
445
447
  # register result
446
- with scan.acquire_process_file_lock():
447
- self.register_process(
448
- process_file=scan.process_file,
449
- entry=entry,
450
- configuration=configuration,
451
- results={},
452
- process_index=process_index,
453
- overwrite=True,
454
- )
448
+ self.register_process(
449
+ process_file=scan.process_file,
450
+ entry=entry,
451
+ configuration=configuration,
452
+ results={},
453
+ process_index=process_index,
454
+ overwrite=True,
455
+ )
455
456
  if self.get_input_value("serialize_output_data", True):
456
457
  self.outputs.data = scan.to_dict()
457
458
  else:
@@ -493,7 +494,7 @@ class NabuSlicesTask(
493
494
  :rtype:dict
494
495
  """
495
496
  if entry is None:
496
- with HDF5File(process_file, "r", swmr=True) as h5f:
497
+ with HDF5File(process_file, "r", swmr=get_swmr_mode()) as h5f:
497
498
  entries = NabuSlicesTask._get_process_nodes(
498
499
  root_node=h5f, process=NabuSlicesTask
499
500
  )
@@ -509,7 +510,7 @@ class NabuSlicesTask(
509
510
  configuration_path = None
510
511
  res = {}
511
512
 
512
- with HDF5File(process_file, "r", swmr=True) as h5f:
513
+ with HDF5File(process_file, "r", swmr=get_swmr_mode()) as h5f:
513
514
  nabu_nodes = NabuSlicesTask._get_process_nodes(
514
515
  root_node=h5f[entry], process=NabuSlicesTask
515
516
  )
@@ -587,7 +588,13 @@ def interpret_tomwer_configuration(
587
588
  return nabu_config
588
589
 
589
590
  if "tomwer_slices" in config and scan is not None:
590
- slices = list(NabuSliceMode.getSlices(config["tomwer_slices"], scan))
591
+ slices = list(
592
+ NabuSliceMode.getSlices(
593
+ config["tomwer_slices"],
594
+ scan=scan,
595
+ axis=config.get("reconstruction", {}).get("slice_plane", "XY"),
596
+ )
597
+ )
591
598
  else:
592
599
  slices = []
593
600
 
@@ -631,6 +638,7 @@ class SingleSliceRunner(_NabuBaseReconstructor):
631
638
  config: dict,
632
639
  dry_run: bool,
633
640
  slice_index: Union[int, str, None],
641
+ axis: NabuPlane,
634
642
  target: Target,
635
643
  ask_sinogram_registration: bool,
636
644
  ask_sinogram_load: bool,
@@ -644,6 +652,7 @@ class SingleSliceRunner(_NabuBaseReconstructor):
644
652
  target=target,
645
653
  cluster_config=cluster_config,
646
654
  process_name=process_name,
655
+ axis=axis,
647
656
  )
648
657
  self._slice_index = slice_index
649
658
  if not isinstance(config, dict):
@@ -686,7 +695,9 @@ class SingleSliceRunner(_NabuBaseReconstructor):
686
695
  """
687
696
  if isinstance(self.slice_index, str):
688
697
  self._slice_index = slice_index_to_int(
689
- slice_index=self.slice_index, scan=self.scan
698
+ slice_index=self.slice_index,
699
+ scan=self.scan,
700
+ axis=self.axis,
690
701
  )
691
702
  elif (
692
703
  isinstance(self.slice_index, float)
@@ -734,8 +745,19 @@ class SingleSliceRunner(_NabuBaseReconstructor):
734
745
  # the policy is to save nabu .cfg file at the same location as the
735
746
  # force overwrite results
736
747
  if self.slice_index is not None:
737
- config["reconstruction"]["start_z"] = self.slice_index
738
- config["reconstruction"]["end_z"] = self.slice_index
748
+ if self.axis is NabuPlane.YZ:
749
+ config["reconstruction"]["start_x"] = self.slice_index
750
+ config["reconstruction"]["end_x"] = self.slice_index
751
+ elif self.axis is NabuPlane.XZ:
752
+ config["reconstruction"]["start_y"] = self.slice_index
753
+ config["reconstruction"]["end_y"] = self.slice_index
754
+ elif self.axis is NabuPlane.XY:
755
+ config["reconstruction"]["start_z"] = self.slice_index
756
+ config["reconstruction"]["end_z"] = self.slice_index
757
+ else:
758
+ raise ValueError(
759
+ f"self.axis has an invalid value: {self.axis} when expected to be in {NabuPlane.values()}"
760
+ )
739
761
 
740
762
  if self.slice_index is not None:
741
763
  os.makedirs(config["output"]["location"], exist_ok=True)
@@ -763,8 +785,6 @@ class SingleSliceRunner(_NabuBaseReconstructor):
763
785
  self._process_config(
764
786
  config_to_dump=config_to_dump,
765
787
  config_file=conf_file,
766
- start_z=None,
767
- end_z=None,
768
788
  info="nabu slice reconstruction",
769
789
  file_format=config_to_dump["output"]["file_format"],
770
790
  process_name=self.process_name,
@@ -798,6 +818,7 @@ class SingleSliceRunner(_NabuBaseReconstructor):
798
818
  file_format=file_format,
799
819
  scan=self.scan,
800
820
  slice_index=None,
821
+ axis=self.axis,
801
822
  )
802
823
 
803
824
  return (CallBack(callback, self.scan),)
@@ -806,19 +827,25 @@ class SingleSliceRunner(_NabuBaseReconstructor):
806
827
 
807
828
  @staticmethod
808
829
  def get_file_basename_reconstruction(
809
- scan, pag, ctf, db, slice_index: Union[str, int]
830
+ scan,
831
+ pag,
832
+ ctf,
833
+ db,
834
+ slice_index: Union[str, int],
835
+ axis: NabuPlane,
810
836
  ):
837
+ axis = NabuPlane.from_value(axis)
811
838
  if pag:
812
839
  assert db is not None, "if paganin defined, db should not be None"
813
840
  if slice_index is not None:
814
- slice_index = slice_index_to_int(slice_index, scan=scan)
841
+ slice_index = slice_index_to_int(slice_index, scan=scan, axis=axis)
815
842
 
816
843
  assert type(db) in (int, type(None))
817
844
  if isinstance(scan, NXtomoScan):
818
845
  basename, _ = os.path.splitext(scan.master_file)
819
846
  basename = os.path.basename(basename)
820
847
  try:
821
- with HDF5File(scan.master_file, mode="r") as h5f:
848
+ with HDF5File(scan.master_file, mode="r", swmr=get_swmr_mode()) as h5f:
822
849
  if len(h5f.keys()) > 1:
823
850
  # if there is more than one entry in the file append the entry name to the file basename
824
851
  basename = "_".join((basename, scan.entry.lstrip("/")))
@@ -840,6 +867,8 @@ class SingleSliceRunner(_NabuBaseReconstructor):
840
867
  basename + "slice_pag",
841
868
  str(slice_index).zfill(6),
842
869
  "db" + str(db).zfill(4),
870
+ "plane",
871
+ axis.value,
843
872
  )
844
873
  )
845
874
  elif ctf:
@@ -848,13 +877,22 @@ class SingleSliceRunner(_NabuBaseReconstructor):
848
877
  basename + "slice_ctf",
849
878
  str(slice_index).zfill(6),
850
879
  "db" + str(db).zfill(4),
880
+ "plane",
881
+ axis.value,
851
882
  )
852
883
  )
853
884
  else:
854
- return "_".join((basename + "slice", str(slice_index).zfill(6)))
885
+ return "_".join(
886
+ (
887
+ basename + "slice",
888
+ str(slice_index).zfill(6),
889
+ "plane",
890
+ axis.value,
891
+ )
892
+ )
855
893
 
856
894
  @docstring(_NabuBaseReconstructor)
857
- def _get_file_basename_reconstruction(self, pag, db, ctf):
895
+ def _get_file_basename_reconstruction(self, pag, db, ctf, axis):
858
896
  """
859
897
 
860
898
  :param TomwerScanBase scan: scan reconstructed
@@ -866,7 +904,12 @@ class SingleSliceRunner(_NabuBaseReconstructor):
866
904
  :return: basename of the file reconstructed (without any extension)
867
905
  """
868
906
  return self.get_file_basename_reconstruction(
869
- scan=self.scan, db=db, pag=pag, slice_index=self.slice_index, ctf=ctf
907
+ scan=self.scan,
908
+ db=db,
909
+ pag=pag,
910
+ slice_index=self.slice_index,
911
+ ctf=ctf,
912
+ axis=axis,
870
913
  )
871
914
 
872
915
 
@@ -881,6 +924,7 @@ def run_single_slice_reconstruction(
881
924
  cluster_config: Optional[dict] = None,
882
925
  add_to_latest_reconstructions=True,
883
926
  instanciate_class_only=False,
927
+ axis: NabuPlane = NabuPlane.XY,
884
928
  ) -> Optional[ResultsRun]:
885
929
  """
886
930
  # TODO: might need something like a context or an option "keep" slice in memory
@@ -904,6 +948,12 @@ def run_single_slice_reconstruction(
904
948
  """
905
949
  # TODO: remove local from the function signature
906
950
  target = Target.SLURM if cluster_config not in ({}, None) else Target.LOCAL
951
+ axis = NabuPlane.from_value(axis)
952
+ # FIXEME: nabu fails if outer_circle activated and if the axis != z
953
+ if axis != NabuPlane.XY and nabu_config.get("reconstruction", {}).get(
954
+ "clip_outer_circle", False
955
+ ):
956
+ nabu_config["reconstruction"]["clip_outer_circle"] = False
907
957
 
908
958
  if process_id is not None:
909
959
  try:
@@ -918,6 +968,7 @@ def run_single_slice_reconstruction(
918
968
  config=nabu_config,
919
969
  dry_run=dry_run,
920
970
  slice_index=slice_index,
971
+ axis=axis,
921
972
  target=target,
922
973
  ask_sinogram_registration=ask_sinogram_registration,
923
974
  ask_sinogram_load=ask_sinogram_load,
@@ -943,7 +994,7 @@ class NabuSliceMode(_Enum):
943
994
  OTHER = "other"
944
995
 
945
996
  @staticmethod
946
- def getSlices(slices, scan) -> tuple:
997
+ def getSlices(slices, scan, axis=NabuPlane.XY) -> tuple:
947
998
  res = []
948
999
  try:
949
1000
  mode = NabuSliceMode.from_value(slices)
@@ -954,7 +1005,11 @@ class NabuSliceMode(_Enum):
954
1005
  pass
955
1006
  else:
956
1007
  if mode == mode.MIDDLE:
957
- n_slice = scan.dim_2 or 2048
1008
+ axis = NabuPlane.from_value(axis)
1009
+ if axis is NabuPlane.XY:
1010
+ n_slice = scan.dim_2 or 2048
1011
+ elif axis in (NabuPlane.YZ, NabuPlane.XZ):
1012
+ n_slice = scan.dim_1 or 2048
958
1013
  res.append(n_slice // 2)
959
1014
  else:
960
1015
  raise ValueError(
@@ -45,7 +45,7 @@ from processview.core.superviseprocess import SuperviseProcess
45
45
  from silx.io.utils import h5py_read_dataset
46
46
  from tomwer.core.utils.deprecation import deprecated_warning
47
47
 
48
- from tomoscan.io import HDF5File
48
+ from tomoscan.io import HDF5File, get_swmr_mode
49
49
 
50
50
  from tomwer.core.cluster.cluster import SlurmClusterConfiguration
51
51
  from tomwer.core.futureobject import FutureTomwerObject
@@ -275,8 +275,6 @@ class VolumeRunner(_NabuBaseReconstructor):
275
275
  self._process_config(
276
276
  config_to_dump=config_to_dump,
277
277
  config_file=conf_file,
278
- start_z=config_to_dump["reconstruction"]["start_z"],
279
- end_z=config_to_dump["reconstruction"]["end_z"],
280
278
  info="nabu volume reconstruction",
281
279
  file_format=config_slices["output"]["file_format"],
282
280
  process_name=self.process_name,
@@ -326,17 +324,16 @@ class VolumeRunner(_NabuBaseReconstructor):
326
324
  if "postproc" in config:
327
325
  config["postproc"] = config["postproc"]
328
326
 
329
- if "start_z" in config:
330
- config["reconstruction"]["start_z"] = config["start_z"]
331
- del config["start_z"]
332
- if "end_z" in config:
333
- config["reconstruction"]["end_z"] = config["end_z"]
334
- del config["end_z"]
327
+ # make sure start_[x] and end_[x] come from config
328
+ for key in ("start_x", "end_x", "start_y", "end_y", "start_z", "end_z"):
329
+ if key in config:
330
+ config["reconstruction"][key] = config[key]
331
+ del config[key]
335
332
 
336
333
  return config, nabu_cfg_folder
337
334
 
338
335
  @docstring(_NabuBaseReconstructor)
339
- def _get_file_basename_reconstruction(self, pag, db, ctf):
336
+ def _get_file_basename_reconstruction(self, pag, db, ctf, axis):
340
337
  """
341
338
 
342
339
  :param TomwerScanBase scan: scan reconstructed
@@ -345,6 +342,7 @@ class VolumeRunner(_NabuBaseReconstructor):
345
342
  entire volume
346
343
  :param bool pag: is it a paganin reconstruction
347
344
  :param int db: delta / beta parameter
345
+ :param axis: axis over which the reconstruction goes. For volume always expected to be z. So ignored in the function
348
346
  :return: basename of the file reconstructed (without any extension)
349
347
  """
350
348
  assert type(db) in (int, type(None))
@@ -354,7 +352,9 @@ class VolumeRunner(_NabuBaseReconstructor):
354
352
  basename = os.path.basename(basename)
355
353
  try:
356
354
  # if there is more than one entry in the file append the entry name to the file basename
357
- with HDF5File(self.scan.master_file, mode="r") as h5f:
355
+ with HDF5File(
356
+ self.scan.master_file, mode="r", swmr=get_swmr_mode()
357
+ ) as h5f:
358
358
  if len(h5f.keys()) > 1:
359
359
  basename = "_".join((basename, self.scan.entry.strip("/")))
360
360
  except Exception:
@@ -531,24 +531,29 @@ class NabuVolumeTask(
531
531
  self.outputs.future_tomo_obj = future_tomo_obj
532
532
 
533
533
  # build screenshots
534
- try:
535
- screenshots = {}
536
- [
537
- screenshots.update(
538
- select_screenshot_from_volume(
539
- VolumeFactory.create_tomo_object_from_identifier(rec_identifier)
534
+ if scan.latest_vol_reconstructions is not None:
535
+ try:
536
+ screenshots = {}
537
+ [
538
+ screenshots.update(
539
+ select_screenshot_from_volume(
540
+ VolumeFactory.create_tomo_object_from_identifier(
541
+ rec_identifier
542
+ )
543
+ )
540
544
  )
545
+ for rec_identifier in scan.latest_vol_reconstructions
546
+ ]
547
+ except Exception as e:
548
+ _logger.error(f"screenshot creation failed. Error is {e}")
549
+ else:
550
+ self.outputs.screenshots = IcatScreenshots(
551
+ data_dir=deduce_dataset_gallery_location(scan),
552
+ screenshots=screenshots,
553
+ scan=scan,
541
554
  )
542
- for rec_identifier in scan.latest_vol_reconstructions
543
- ]
544
- except Exception as e:
545
- _logger.error(f"screenshot creation failed. Error is {e}")
546
555
  else:
547
- self.outputs.screenshots = IcatScreenshots(
548
- data_dir=deduce_dataset_gallery_location(scan),
549
- screenshots=screenshots,
550
- scan=scan,
551
- )
556
+ self.outputs.screenshots = tuple()
552
557
 
553
558
  def set_configuration(self, configuration: dict) -> None:
554
559
  Task.set_configuration(self, configuration=configuration)
@@ -0,0 +1,9 @@
1
+ """Define nabu Axis"""
2
+
3
+ from silx.utils.enum import Enum as _Enum
4
+
5
+
6
+ class NabuPlane(_Enum):
7
+ YZ = "YZ"
8
+ XZ = "XZ"
9
+ XY = "XY"
@@ -41,6 +41,7 @@ from nabu.pipeline.fullfield.nabu_config import (
41
41
  from silx.utils.enum import Enum as _Enum
42
42
 
43
43
  import tomwer.version
44
+ from tomwer.core.process.reconstruction.nabu.plane import NabuPlane
44
45
  from tomwer.core.scan.edfscan import EDFTomoScan
45
46
  from tomwer.core.scan.nxtomoscan import NXtomoScan
46
47
  from tomwer.core.scan.scanbase import TomwerScanBase
@@ -148,15 +149,19 @@ def get_recons_volume_identifier(
148
149
  file_format: str,
149
150
  scan: TomwerScanBase,
150
151
  slice_index: typing.Union[int, None],
152
+ axis: NabuPlane,
151
153
  ) -> tuple:
152
154
  """
153
155
  return tuple of DataUrl for existings slices
154
156
  """
157
+ axis = NabuPlane.from_value(axis)
155
158
  file_format = file_format.lower()
156
159
  if file_format in ("hdf5", "h5", "hdf"):
157
160
  if slice_index is not None:
158
161
  # case of a single hdf5 file
159
- file_name = "_".join((file_prefix, str(slice_index).zfill(6)))
162
+ file_name = "_".join(
163
+ (file_prefix, "plane", axis.value, str(slice_index).zfill(6))
164
+ )
160
165
  else:
161
166
  file_name = file_prefix
162
167
  file_name = ".".join((file_name, file_format))
@@ -176,7 +181,9 @@ def get_recons_volume_identifier(
176
181
  elif file_format in ("vol", "raw"):
177
182
  if slice_index is not None:
178
183
  # case of a single hdf5 file
179
- file_name = "_".join((file_prefix, str(slice_index).zfill(6)))
184
+ file_name = "_".join(
185
+ (file_prefix, "plane", axis.value, str(slice_index).zfill(6))
186
+ )
180
187
  else:
181
188
  file_name = file_prefix
182
189
  file_name = ".".join((file_name, file_format))
@@ -227,6 +234,7 @@ def get_multi_cor_recons_volume_identifiers(
227
234
  file_prefix: str,
228
235
  cors: tuple,
229
236
  file_format: str,
237
+ axis=NabuPlane.XY,
230
238
  ) -> dict:
231
239
  """
232
240
  util to retrieve Volumes (identifier) reconstructed by nabu-multicor
@@ -241,7 +249,11 @@ def get_multi_cor_recons_volume_identifiers(
241
249
  """
242
250
  _logger.info("Deduce volume identifiers for nabu-multicor")
243
251
  if isinstance(slice_index, str):
244
- slice_index = slice_index_to_int(slice_index=slice_index, scan=scan)
252
+ slice_index = slice_index_to_int(
253
+ slice_index=slice_index,
254
+ scan=scan,
255
+ axis=axis, # for now we always expect the multicor to be done along Z
256
+ )
245
257
  assert isinstance(
246
258
  slice_index, int
247
259
  ), "slice_index is expected to be an int or to be converted to it"
@@ -260,7 +272,6 @@ def get_multi_cor_recons_volume_identifiers(
260
272
  if file_format in ("hdf5", "h5", "hdf"):
261
273
  file_path = os.path.join(
262
274
  location,
263
- f"{file_prefix}_{cor:.3f}", # this level is expected to be removed on the future nabu versions
264
275
  f"{file_prefix}_{cor:.3f}_{str(slice_index).zfill(5)}.{file_format}",
265
276
  )
266
277
  volume = HDF5Volume(
@@ -493,11 +504,14 @@ def update_cfg_file_after_transfer(config_file_path, old_path, new_path):
493
504
 
494
505
 
495
506
  def slice_index_to_int(
496
- slice_index: typing.Union[int, str], scan: TomwerScanBase
507
+ slice_index: typing.Union[int, str],
508
+ scan: TomwerScanBase,
509
+ axis=NabuPlane.XY,
497
510
  ) -> int:
498
511
  """
499
512
  cast a slice to an index. The slice can be a string in ['first', 'last', 'middle']
500
513
  """
514
+ axis = NabuPlane.from_value(axis)
501
515
  if slice_index == "fisrt":
502
516
  return 0
503
517
  elif slice_index == "last":
@@ -518,11 +532,20 @@ def slice_index_to_int(
518
532
  _logger.warning("Scan not provided. Consider the 1024 width detector")
519
533
  # default middle.
520
534
  return 1024
521
- elif scan.dim_2 is None:
522
- _logger.warning("unable to get dim size, guess this is 2048 width")
523
- return 1024
535
+ elif axis is NabuPlane.XY:
536
+ if scan.dim_2 is None:
537
+ _logger.warning("unable to get dim size, guess this is 2048 height")
538
+ return 1024
539
+ else:
540
+ return scan.dim_2 // 2
541
+ elif axis in (NabuPlane.YZ, NabuPlane.XZ):
542
+ if scan.dim_1 is None:
543
+ _logger.warning("unable to get dim size, guess this is 2048 width")
544
+ return 1024
545
+ else:
546
+ return scan.dim_1 // 2
524
547
  else:
525
- return scan.dim_2 // 2
548
+ raise ValueError(f"axis {axis} is not handled")
526
549
  else:
527
550
  return int(slice_index)
528
551
 
@@ -520,7 +520,10 @@ class SAAxisTask(
520
520
  cors_res, rois = self._post_processing(
521
521
  scan=scan,
522
522
  slice_index=slice_index,
523
- cor_reconstructions=cor_reconstructions,
523
+ cor_reconstructions={
524
+ cor: recons
525
+ for cor, recons in zip(params.cors, cor_reconstructions)
526
+ },
524
527
  )
525
528
  except Exception as e:
526
529
  _logger.error(e)
@@ -87,7 +87,7 @@ from .params import SADeltaBetaParams
87
87
  _logger = logging.getLogger(__name__)
88
88
 
89
89
 
90
- DEFAULT_RECONS_FOLDER = "sadeltabeta_results"
90
+ DEFAULT_RECONS_FOLDER = "multi_delta_beta_results"
91
91
 
92
92
 
93
93
  def one_slice_several_db(
@@ -250,6 +250,7 @@ class SADeltaBetaTask(
250
250
  raise ValueError(f"input type of {scan}: {type(scan)} is not managed")
251
251
 
252
252
  config = copy(self.inputs.sa_delta_beta_params)
253
+ axis = config.get("reconstruction", {}).get("slice_plane", "XY")
253
254
  params = SADeltaBetaParams.from_dict(config)
254
255
 
255
256
  # insure scan contains some parameter regarding sa delta / beta
@@ -303,6 +304,7 @@ class SADeltaBetaTask(
303
304
  slice_index=slice_index,
304
305
  dry_run=dry_run,
305
306
  cluster_config=cluster_config,
307
+ axis=axis,
306
308
  )
307
309
  except Exception as e:
308
310
  _logger.error(e)
@@ -318,6 +320,7 @@ class SADeltaBetaTask(
318
320
  slice_index=slice_index,
319
321
  db_reconstructions=dbs_res,
320
322
  future_tomo_objs=future_tomo_objs,
323
+ axis=axis,
321
324
  )
322
325
 
323
326
  # step 4: run post processing (compute score for each slice)
@@ -424,6 +427,7 @@ class SADeltaBetaTask(
424
427
  slice_index,
425
428
  advancement,
426
429
  dry_run,
430
+ axis,
427
431
  cluster_config: Optional[dict],
428
432
  ):
429
433
  future_tomo_objs = {}
@@ -448,6 +452,7 @@ class SADeltaBetaTask(
448
452
  process_id=self.process_id,
449
453
  instanciate_classes_only=True,
450
454
  output_file_prefix_pattern=None,
455
+ axis=axis,
451
456
  )
452
457
 
453
458
  for runner in runners:
@@ -510,6 +515,7 @@ class SADeltaBetaTask(
510
515
  nabu_config: dict,
511
516
  db_reconstructions,
512
517
  slice_index,
518
+ axis,
513
519
  future_tomo_objs: dict,
514
520
  ):
515
521
  assert isinstance(nabu_config, dict)
@@ -536,6 +542,7 @@ class SADeltaBetaTask(
536
542
  pag=pag,
537
543
  db=int(db) if db is not None else None,
538
544
  ctf=ctf,
545
+ axis=axis,
539
546
  )
540
547
  # retrieve url
541
548
  volume_identifier = nabu_utils.get_recons_volume_identifier(
@@ -544,6 +551,7 @@ class SADeltaBetaTask(
544
551
  file_format=nabu_config.get("file_format", "hdf5"),
545
552
  scan=scan,
546
553
  slice_index=None,
554
+ axis=axis,
547
555
  )
548
556
 
549
557
  assert len(volume_identifier) <= 1, "only one slice expected"
@@ -179,9 +179,9 @@ class SABaseParams:
179
179
  "dry_run": self.dry_run,
180
180
  "output_dir": self.output_dir or "",
181
181
  "score_method": self.score_method.value,
182
- "cluster_config": self.cluster_config
183
- if self.cluster_config is not None
184
- else "",
182
+ "cluster_config": (
183
+ self.cluster_config if self.cluster_config is not None else ""
184
+ ),
185
185
  }
186
186
 
187
187
  def load_from_dict(self, dict_: dict):
@@ -35,8 +35,8 @@ def test_register_and_copy_darks_and_flats(
35
35
  scan_with_raw = MockNXtomo(
36
36
  scan_path=scan_folder_with_raw,
37
37
  create_ini_dark=True,
38
- create_ini_ref=True,
39
- create_final_ref=False,
38
+ create_ini_flat=True,
39
+ create_final_flat=False,
40
40
  n_proj=10,
41
41
  n_ini_proj=10,
42
42
  dim=12,
@@ -44,8 +44,8 @@ def test_register_and_copy_darks_and_flats(
44
44
  scan_without_raw = MockNXtomo(
45
45
  scan_path=scan_folder_without_raw,
46
46
  create_ini_dark=False,
47
- create_ini_ref=False,
48
- create_final_ref=False,
47
+ create_ini_flat=False,
48
+ create_final_flat=False,
49
49
  n_proj=10,
50
50
  n_ini_proj=10,
51
51
  dim=12,
@@ -139,32 +139,33 @@ class StitchingPostProcAggregation(_StitchingPostProcAggregation):
139
139
  once stitching is done remotly we need to 'aggregate' the different part in the correct order
140
140
  """
141
141
 
142
- def __init__(self, futures, stitching_config) -> None:
142
+ def __init__(self, *args, **kwargs) -> None:
143
+ super().__init__(*args, **kwargs)
143
144
  self._n_finished = 0
144
- self._futures = futures
145
- if isinstance(stitching_config, dict):
145
+
146
+ if isinstance(self.stitching_config, dict):
146
147
  stitching_type = StitchingType.from_value(
147
- stitching_config["stitching"]["type"]
148
+ self.stitching_config["stitching"]["type"]
148
149
  )
149
150
  if stitching_type is StitchingType.Z_PREPROC:
150
151
  self._stitching_config = PreProcessedZStitchingConfiguration.from_dict(
151
- stitching_config
152
+ self.stitching_config
152
153
  )
153
154
  elif stitching_type is StitchingType.Z_POSTPROC:
154
155
  self._stitching_config = PostProcessedZStitchingConfiguration.from_dict(
155
- stitching_config
156
+ self.stitching_config
156
157
  )
157
158
  else:
158
159
  raise NotImplementedError("stitching type not handled")
159
- elif not isinstance(stitching_config, StitchingConfiguration):
160
+ elif not isinstance(self.stitching_config, StitchingConfiguration):
160
161
  raise TypeError(
161
- f"stitching_config is expected to be an instance of {StitchingConfiguration}. {type(stitching_config)} provided instead"
162
+ f"stitching_config is expected to be an instance of {StitchingConfiguration}. {type(self.stitching_config)} provided instead"
162
163
  )
163
- else:
164
- self._stitching_config = stitching_config
165
164
 
166
165
  def onePartFinished(self, *args, **kwargs):
167
166
  self._n_finished += 1
167
+ # note: for now we only consider the user of the futures.
168
+ # if users want to only run post-processing agregation then they will use the nabu CLI for it.
168
169
  if self._n_finished == len(self._futures):
169
170
  _logger.info("All slurm job finished. Will aggregate results")
170
171
  self.process()