tomwer 1.2.1__py3-none-any.whl → 1.3.12__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 (334) hide show
  1. orangecontrib/tomwer/tutorials/icat_publication.ows +58 -0
  2. orangecontrib/tomwer/widgets/__init__.py +11 -11
  3. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +2 -2
  4. orangecontrib/tomwer/widgets/control/DataListOW.py +9 -7
  5. orangecontrib/tomwer/widgets/control/DataListenerOW.py +6 -6
  6. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +21 -10
  7. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +6 -6
  8. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +24 -7
  9. orangecontrib/tomwer/widgets/control/EmailOW.py +4 -4
  10. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +3 -3
  11. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +64 -23
  12. orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +20 -8
  13. orangecontrib/tomwer/widgets/control/NotifierOW.py +1 -0
  14. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +6 -6
  15. orangecontrib/tomwer/widgets/control/VolumeSelector.py +7 -4
  16. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +182 -182
  17. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +5 -5
  18. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +4 -4
  19. orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +3 -3
  20. orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +8 -1
  21. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +3 -3
  22. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +3 -3
  23. orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +115 -0
  24. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -0
  25. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -0
  26. orangecontrib/tomwer/widgets/icat/__init__.py +13 -0
  27. orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
  28. orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +82 -0
  29. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.png +0 -0
  30. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.svg +95 -0
  31. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
  32. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +143 -0
  33. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.png +0 -0
  34. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.svg +76 -0
  35. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +22 -20
  36. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +19 -3
  37. orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +184 -169
  38. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +23 -0
  39. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +39 -5
  40. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +18 -22
  41. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +18 -26
  42. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +15 -19
  43. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +9 -9
  44. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  45. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +1 -1
  46. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +3 -3
  47. orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -1
  48. orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +3 -29
  49. tomwer/__main__.py +7 -64
  50. tomwer/app/axis.py +3 -3
  51. tomwer/app/canvas.py +8 -0
  52. tomwer/app/canvas_launcher/config.py +16 -14
  53. tomwer/app/canvas_launcher/environ.py +1 -0
  54. tomwer/app/canvas_launcher/mainwindow.py +4 -1
  55. tomwer/app/darkref.py +1 -1
  56. tomwer/app/darkrefpatch.py +1 -1
  57. tomwer/app/diffframe.py +3 -3
  58. tomwer/app/imagekeyeditor.py +5 -5
  59. tomwer/app/imagekeyupgrader.py +5 -5
  60. tomwer/app/intensitynormalization.py +14 -13
  61. tomwer/app/{saaxis.py → multicor.py} +3 -3
  62. tomwer/app/{sadeltabeta.py → multipag.py} +3 -3
  63. tomwer/app/nabuapp.py +0 -11
  64. tomwer/app/radiostack.py +6 -4
  65. tomwer/app/samplemoved.py +3 -2
  66. tomwer/app/scanviewer.py +4 -2
  67. tomwer/app/sinogramviewer.py +3 -2
  68. tomwer/app/slicestack.py +3 -2
  69. tomwer/app/zstitching.py +88 -6
  70. tomwer/core/cluster/cluster.py +26 -0
  71. tomwer/core/log/logger.py +7 -5
  72. tomwer/core/process/conditions/filters.py +1 -1
  73. tomwer/core/process/control/datalistener/datalistener.py +19 -14
  74. tomwer/core/process/control/datawatcher/edfdwprocess.py +0 -9
  75. tomwer/core/process/control/nxtomoconcatenate.py +13 -13
  76. tomwer/core/process/control/nxtomomill.py +92 -34
  77. tomwer/core/process/control/scantransfer.py +20 -43
  78. tomwer/core/process/control/scanvalidator.py +3 -2
  79. tomwer/core/process/control/test/test_concatenate_nxtomos.py +9 -9
  80. tomwer/core/process/control/test/test_email.py +4 -4
  81. tomwer/core/process/control/test/test_h52nx_process.py +59 -7
  82. tomwer/core/process/control/test/test_volume_link.py +64 -64
  83. tomwer/core/process/control/timer.py +1 -1
  84. tomwer/core/process/control/volumesymlink.py +200 -200
  85. tomwer/core/process/edit/darkflatpatch.py +14 -15
  86. tomwer/core/process/edit/imagekeyeditor.py +41 -39
  87. tomwer/core/process/icat/__init__.py +0 -0
  88. tomwer/core/process/icat/createscreenshots.py +100 -0
  89. tomwer/core/process/icat/gallery.py +377 -0
  90. tomwer/core/process/icat/icatbase.py +36 -0
  91. tomwer/core/process/icat/publish.py +228 -0
  92. tomwer/core/process/icat/screenshots.py +27 -0
  93. tomwer/core/process/output.py +52 -0
  94. tomwer/core/process/reconstruction/axis/axis.py +280 -69
  95. tomwer/core/process/reconstruction/axis/mode.py +163 -48
  96. tomwer/core/process/reconstruction/axis/params.py +29 -21
  97. tomwer/core/process/reconstruction/darkref/darkrefs.py +41 -127
  98. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +4 -3
  99. tomwer/core/process/reconstruction/darkref/params.py +1 -1
  100. tomwer/core/process/reconstruction/nabu/castvolume.py +4 -4
  101. tomwer/core/process/reconstruction/nabu/helical.py +9 -5
  102. tomwer/core/process/reconstruction/nabu/nabucommon.py +71 -78
  103. tomwer/core/process/reconstruction/nabu/nabuscores.py +425 -53
  104. tomwer/core/process/reconstruction/nabu/nabuslices.py +114 -93
  105. tomwer/core/process/reconstruction/nabu/nabuvolume.py +54 -27
  106. tomwer/core/process/reconstruction/nabu/plane.py +9 -0
  107. tomwer/core/process/reconstruction/nabu/settings.py +2 -2
  108. tomwer/core/process/reconstruction/nabu/utils.py +164 -26
  109. tomwer/core/process/reconstruction/output.py +108 -0
  110. tomwer/core/process/reconstruction/saaxis/saaxis.py +238 -264
  111. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +151 -87
  112. tomwer/core/process/reconstruction/scores/params.py +7 -4
  113. tomwer/core/process/reconstruction/scores/scores.py +13 -0
  114. tomwer/core/process/reconstruction/test/test_axis_params.py +2 -2
  115. tomwer/core/process/reconstruction/test/test_darkref.py +3 -3
  116. tomwer/core/process/reconstruction/test/test_darkref_copy.py +7 -7
  117. tomwer/core/process/reconstruction/test/test_saaxis.py +3 -4
  118. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +2 -2
  119. tomwer/core/process/stitching/nabustitcher.py +13 -12
  120. tomwer/core/process/task.py +34 -26
  121. tomwer/core/process/test/test_axis.py +13 -12
  122. tomwer/core/process/test/test_dark_and_flat.py +10 -7
  123. tomwer/core/process/test/test_data_transfer.py +10 -8
  124. tomwer/core/process/test/test_nabu.py +14 -6
  125. tomwer/core/process/test/test_normalization.py +4 -4
  126. tomwer/core/scan/blissscan.py +3 -3
  127. tomwer/core/scan/edfscan.py +13 -10
  128. tomwer/core/scan/hdf5scan.py +19 -530
  129. tomwer/core/scan/nxtomoscan.py +534 -0
  130. tomwer/core/scan/scanbase.py +72 -44
  131. tomwer/core/scan/scanfactory.py +13 -13
  132. tomwer/core/scan/test/test_edf.py +2 -2
  133. tomwer/core/scan/test/test_future_scan.py +3 -3
  134. tomwer/core/scan/test/test_h5.py +18 -16
  135. tomwer/core/scan/test/test_process_registration.py +4 -40
  136. tomwer/core/scan/test/test_scan.py +5 -78
  137. tomwer/core/settings.py +22 -2
  138. tomwer/core/test/test_scanutils.py +8 -7
  139. tomwer/core/test/test_utils.py +35 -28
  140. tomwer/core/tomwer_object.py +1 -1
  141. tomwer/core/utils/__init__.py +0 -466
  142. tomwer/core/utils/deprecation.py +1 -1
  143. tomwer/core/utils/dictutils.py +14 -0
  144. tomwer/core/utils/lbsram.py +35 -0
  145. tomwer/core/utils/nxtomoutils.py +1 -1
  146. tomwer/core/utils/scanutils.py +6 -6
  147. tomwer/core/utils/spec.py +263 -0
  148. tomwer/core/volume/edfvolume.py +6 -6
  149. tomwer/core/volume/hdf5volume.py +6 -6
  150. tomwer/core/volume/jp2kvolume.py +6 -6
  151. tomwer/core/volume/rawvolume.py +6 -6
  152. tomwer/core/volume/tiffvolume.py +12 -12
  153. tomwer/core/volume/volumefactory.py +2 -2
  154. tomwer/gui/cluster/slurm.py +274 -65
  155. tomwer/gui/cluster/supervisor.py +12 -0
  156. tomwer/gui/cluster/test/test_cluster.py +14 -2
  157. tomwer/gui/cluster/test/test_supervisor.py +3 -3
  158. tomwer/gui/configuration/__init__.py +0 -0
  159. tomwer/gui/{reconstruction/nabu → configuration}/action.py +1 -32
  160. tomwer/gui/configuration/level.py +22 -0
  161. tomwer/gui/control/actions.py +54 -0
  162. tomwer/gui/control/datalist.py +83 -16
  163. tomwer/gui/control/datalistener.py +4 -16
  164. tomwer/gui/control/datawatcher/controlwidget.py +2 -4
  165. tomwer/gui/control/datawatcher/datawatcher.py +1 -24
  166. tomwer/gui/control/{email.py → emailnotifier.py} +9 -18
  167. tomwer/gui/control/history.py +2 -2
  168. tomwer/gui/control/observations.py +2 -2
  169. tomwer/gui/control/reducedarkflatselector.py +9 -9
  170. tomwer/gui/control/selectorwidgetbase.py +36 -9
  171. tomwer/gui/control/serie/seriecreator.py +5 -22
  172. tomwer/gui/control/test/test_email.py +1 -1
  173. tomwer/gui/control/test/test_scanvalidator.py +6 -5
  174. tomwer/gui/control/test/test_single_tomo_obj.py +3 -3
  175. tomwer/gui/control/tomoobjdisplaymode.py +8 -0
  176. tomwer/gui/debugtools/datasetgenerator.py +3 -3
  177. tomwer/gui/edit/dkrfpatch.py +20 -26
  178. tomwer/gui/edit/imagekeyeditor.py +11 -12
  179. tomwer/gui/edit/nxtomoeditor.py +111 -44
  180. tomwer/gui/edit/nxtomowarmer.py +7 -6
  181. tomwer/gui/edit/test/test_dkrf_patch.py +13 -13
  182. tomwer/gui/edit/test/test_image_key_editor.py +3 -3
  183. tomwer/gui/edit/test/test_nx_editor.py +40 -16
  184. tomwer/gui/icat/__init__.py +0 -0
  185. tomwer/gui/icat/createscreenshots.py +80 -0
  186. tomwer/gui/icat/gallery.py +214 -0
  187. tomwer/gui/icat/publish.py +187 -0
  188. tomwer/gui/imagefromfile.py +2 -2
  189. tomwer/gui/qfolderdialog.py +24 -1
  190. tomwer/gui/reconstruction/axis/CompareImages.py +88 -168
  191. tomwer/gui/reconstruction/axis/axis.py +171 -57
  192. tomwer/gui/reconstruction/axis/radioaxis.py +122 -257
  193. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +3 -2
  194. tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -1
  195. tomwer/gui/reconstruction/nabu/castvolume.py +14 -3
  196. tomwer/gui/reconstruction/nabu/check.py +9 -9
  197. tomwer/gui/reconstruction/nabu/helical.py +29 -12
  198. tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
  199. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +2 -1
  200. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +126 -35
  201. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +39 -32
  202. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +222 -31
  203. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +57 -27
  204. tomwer/gui/reconstruction/nabu/nabuflow.py +12 -20
  205. tomwer/gui/reconstruction/nabu/slices.py +10 -11
  206. tomwer/gui/reconstruction/nabu/volume.py +22 -10
  207. tomwer/gui/reconstruction/normalization/intensity.py +18 -48
  208. tomwer/gui/reconstruction/saaxis/corrangeselector.py +8 -24
  209. tomwer/gui/reconstruction/saaxis/dimensionwidget.py +1 -1
  210. tomwer/gui/reconstruction/saaxis/saaxis.py +9 -21
  211. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +45 -12
  212. tomwer/gui/reconstruction/scores/control.py +2 -9
  213. tomwer/gui/reconstruction/scores/scoreplot.py +13 -11
  214. tomwer/gui/reconstruction/test/test_axis.py +41 -16
  215. tomwer/gui/reconstruction/test/test_nabu.py +31 -9
  216. tomwer/gui/reconstruction/test/test_saaxis.py +3 -3
  217. tomwer/gui/reconstruction/test/test_sadeltabeta.py +12 -2
  218. tomwer/gui/settings.py +5 -28
  219. tomwer/gui/stackplot.py +2 -5
  220. tomwer/gui/stitching/action.py +49 -0
  221. tomwer/gui/stitching/config/axisparams.py +7 -24
  222. tomwer/gui/stitching/config/output.py +10 -8
  223. tomwer/gui/stitching/config/positionoveraxis.py +22 -23
  224. tomwer/gui/stitching/normalization.py +117 -0
  225. tomwer/gui/stitching/stitchandbackground.py +4 -6
  226. tomwer/gui/stitching/stitching.py +267 -45
  227. tomwer/gui/stitching/stitching_preview.py +62 -55
  228. tomwer/gui/stitching/stitching_raw.py +13 -12
  229. tomwer/gui/stitching/z_stitching/fineestimation.py +0 -60
  230. tomwer/gui/utils/buttons.py +112 -29
  231. tomwer/gui/utils/inputwidget.py +43 -25
  232. tomwer/gui/utils/lineselector/lineselector.py +1 -1
  233. tomwer/gui/utils/scandescription.py +4 -0
  234. tomwer/gui/utils/step.py +144 -0
  235. tomwer/gui/utils/unitsystem.py +2 -5
  236. tomwer/gui/utils/vignettes.py +176 -15
  237. tomwer/gui/visualization/dataviewer.py +48 -35
  238. tomwer/gui/visualization/diffviewer/diffviewer.py +7 -16
  239. tomwer/gui/visualization/diffviewer/shiftwidget.py +2 -5
  240. tomwer/gui/visualization/scanoverview.py +1 -1
  241. tomwer/gui/visualization/sinogramviewer.py +20 -36
  242. tomwer/gui/visualization/test/test_diffviewer.py +3 -3
  243. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +4 -4
  244. tomwer/gui/visualization/test/test_sinogramviewer.py +2 -2
  245. tomwer/gui/visualization/test/test_stacks.py +3 -3
  246. tomwer/gui/visualization/test/test_volumeviewer.py +65 -67
  247. tomwer/gui/visualization/volumeviewer.py +114 -113
  248. tomwer/io/utils/h5pyutils.py +3 -3
  249. tomwer/io/utils/raw_and_processed_data.py +84 -0
  250. tomwer/io/utils/tomoobj.py +4 -6
  251. tomwer/io/utils/utils.py +7 -7
  252. tomwer/resources/gui/icons/parameters.svg +1 -1
  253. tomwer/resources/gui/icons/ruler.png +0 -0
  254. tomwer/resources/gui/icons/ruler.svg +273 -0
  255. tomwer/resources/gui/icons/short_description.png +0 -0
  256. tomwer/resources/gui/icons/short_description.svg +58 -0
  257. tomwer/resources/gui/icons/url.png +0 -0
  258. tomwer/resources/gui/icons/url.svg +58 -0
  259. tomwer/resources/gui/illustrations/no_rot.svg +1 -1
  260. tomwer/synctools/stacks/edit/darkflatpatch.py +19 -14
  261. tomwer/synctools/stacks/edit/imagekeyeditor.py +2 -2
  262. tomwer/synctools/stacks/reconstruction/axis.py +4 -4
  263. tomwer/synctools/stacks/reconstruction/castvolume.py +22 -7
  264. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +25 -20
  265. tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
  266. tomwer/synctools/stacks/reconstruction/normalization.py +2 -2
  267. tomwer/synctools/stacks/reconstruction/saaxis.py +2 -2
  268. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -2
  269. tomwer/synctools/test/test_darkRefs.py +7 -58
  270. tomwer/synctools/test/test_foldertransfer.py +6 -6
  271. tomwer/synctools/utils/scanstages.py +6 -6
  272. tomwer/tests/conftest.py +34 -0
  273. tomwer/tests/datasets.py +13 -0
  274. tomwer/tests/test_scripts.py +91 -41
  275. tomwer/tests/utils.py +5 -0
  276. tomwer/third_part/WaitingOverlay.py +110 -0
  277. tomwer/third_part/__init__.py +0 -0
  278. tomwer/version.py +2 -2
  279. tomwer-1.3.12-py3.11-nspkg.pth +1 -0
  280. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/METADATA +73 -58
  281. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/RECORD +287 -286
  282. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/WHEEL +1 -1
  283. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +0 -197
  284. orangecontrib/tomwer/widgets/reconstruction/icons/XY_lamino.svg +0 -168
  285. orangecontrib/tomwer/widgets/reconstruction/icons/XZ_lamino.svg +0 -275
  286. orangecontrib/tomwer/widgets/reconstruction/icons/YZ_lamino.svg +0 -182
  287. tomwer/app/lamino.py +0 -143
  288. tomwer/core/process/reconstruction/lamino/__init__.py +0 -1
  289. tomwer/core/process/reconstruction/lamino/tofu.py +0 -1000
  290. tomwer/core/process/test/test_lamino.py +0 -76
  291. tomwer/core/test/test_lamino.py +0 -92
  292. tomwer/gui/reconstruction/lamino/__init__.py +0 -31
  293. tomwer/gui/reconstruction/lamino/tofu/TofuOptionLoader.py +0 -107
  294. tomwer/gui/reconstruction/lamino/tofu/__init__.py +0 -1
  295. tomwer/gui/reconstruction/lamino/tofu/misc.py +0 -148
  296. tomwer/gui/reconstruction/lamino/tofu/projections.py +0 -896
  297. tomwer/gui/reconstruction/lamino/tofu/settings.py +0 -75
  298. tomwer/gui/reconstruction/lamino/tofu/tofu.py +0 -432
  299. tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +0 -567
  300. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +0 -760
  301. tomwer/gui/reconstruction/test/test_lamino.py +0 -189
  302. tomwer/resources/gui/icons/esrf_1.svg +0 -307
  303. tomwer/resources/gui/icons/lamino_parameters.svg +0 -70
  304. tomwer/resources/gui/icons/triangle.svg +0 -80
  305. tomwer/resources/gui/illustrations/lamino_angle.png +0 -0
  306. tomwer/resources/gui/illustrations/lamino_angle.svg +0 -509
  307. tomwer/resources/gui/illustrations/lamino_beta_angle.png +0 -0
  308. tomwer/resources/gui/illustrations/lamino_beta_angle.svg +0 -97
  309. tomwer/resources/gui/illustrations/lamino_theta_angle.png +0 -0
  310. tomwer/resources/gui/illustrations/lamino_theta_angle.svg +0 -368
  311. tomwer/resources/gui/illustrations/manual_slice.png +0 -0
  312. tomwer/resources/gui/illustrations/manual_slice.svg +0 -221
  313. tomwer/resources/gui/illustrations/psi_angle.png +0 -0
  314. tomwer/resources/gui/illustrations/psi_angle.svg +0 -479
  315. tomwer/resources/gui/illustrations/rotation_center.png +0 -0
  316. tomwer/resources/gui/illustrations/rotation_center.svg +0 -276
  317. tomwer/resources/gui/illustrations/slice_stack.png +0 -0
  318. tomwer/resources/gui/illustrations/slice_stack.svg +0 -266
  319. tomwer/resources/gui/illustrations/xy_slice.png +0 -0
  320. tomwer/resources/gui/illustrations/xy_slice.svg +0 -269
  321. tomwer/resources/gui/illustrations/xz_slice.png +0 -0
  322. tomwer/resources/gui/illustrations/xz_slice.svg +0 -270
  323. tomwer/resources/gui/illustrations/yz_slice.png +0 -0
  324. tomwer/resources/gui/illustrations/yz_slice.svg +0 -270
  325. tomwer/synctools/stacks/reconstruction/lamino.py +0 -233
  326. tomwer/synctools/test/test_scanstages.py +0 -162
  327. tomwer/tests/utils/__init__.py +0 -247
  328. tomwer/tests/utils/utilstest.py +0 -220
  329. tomwer-1.2.1-py3.11-nspkg.pth +0 -1
  330. /tomwer/core/process/control/{email.py → emailnotifier.py} +0 -0
  331. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/LICENSE +0 -0
  332. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/entry_points.txt +0 -0
  333. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/namespace_packages.txt +0 -0
  334. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/top_level.txt +0 -0
@@ -1,1000 +0,0 @@
1
- # coding: utf-8
2
- # /*##########################################################################
3
- #
4
- # Copyright (c) 2016-2017 European Synchrotron Radiation Facility
5
- #
6
- # Permission is hereby granted, free of charge, to any person obtaining a copy
7
- # of this software and associated documentation files (the "Software"), to deal
8
- # in the Software without restriction, including without limitation the rights
9
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- # copies of the Software, and to permit persons to whom the Software is
11
- # furnished to do so, subject to the following conditions:
12
- #
13
- # The above copyright notice and this permission notice shall be included in
14
- # all copies or substantial portions of the Software.
15
- #
16
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
- # THE SOFTWARE.
23
- #
24
- # ###########################################################################*/
25
-
26
- __authors__ = ["H. Payno"]
27
- __license__ = "MIT"
28
- __date__ = "01/06/2018"
29
-
30
-
31
- import enum
32
- import glob
33
- import logging
34
- import os
35
- import shutil
36
- import subprocess
37
- import sys
38
- import tempfile
39
- from collections import OrderedDict
40
-
41
- import tomwer.version
42
- from tomwer.core.process.reconstruction.darkref.settings import (
43
- DARKHST_PREFIX,
44
- REFHST_PREFIX,
45
- )
46
- from tomwer.core.process.task import Task
47
- from tomwer.core.scan.hdf5scan import HDF5TomoScan
48
- from tomwer.core.scan.scanbase import TomwerScanBase
49
- from tomwer.core.scan.scanfactory import ScanFactory
50
- from tomwer.core.utils import getDim1Dim2
51
- from tomwer.core.utils.char import PSI_CHAR
52
- from silx.utils.deprecation import deprecated_warning
53
-
54
- logger = logging.getLogger(__name__)
55
-
56
-
57
- @enum.unique
58
- class FFCWhen(enum.Enum):
59
- on_the_fly = 0
60
- preprocessing = 1
61
-
62
-
63
- SLICE_STACK_TYPE = "slice stack"
64
- ROTATION_CENTER_TYPE = "rotation center"
65
- LAMINO_ANGLE_TYPE = "lamino angle"
66
- PSI_ANGLE_TYPE = " ".join((PSI_CHAR, "angle"))
67
-
68
- SCAN_TYPES = OrderedDict(
69
- (
70
- (SLICE_STACK_TYPE, "z"),
71
- (ROTATION_CENTER_TYPE, "center-position-x"),
72
- (LAMINO_ANGLE_TYPE, "axis-angle-x"),
73
- (PSI_ANGLE_TYPE, "axis-angle-y"),
74
- )
75
- )
76
-
77
- SCAN_TYPES_I = {}
78
- for s_type, value in SCAN_TYPES.items():
79
- SCAN_TYPES_I[value] = s_type
80
-
81
-
82
- def _retrieve_opts_recons_cmd(scan_id, recons_param, additional_opts, pre_proc_ffc):
83
- """Return the options command under the style '--name optValue' from the
84
- ToFuReconstructionParam and a string of additional options
85
-
86
- :param bool pre_proc_ffc: should we apply flat field correction on the fly
87
- or is it already done.
88
- """
89
- assert isinstance(recons_param, dict)
90
- options = [""]
91
- # deal with option that will can be None
92
- for opt in (
93
- "retry-timeout",
94
- "retries",
95
- "reduction-mode",
96
- "dark-scale",
97
- "darks",
98
- "slices-per-device",
99
- "slice-memory-coeff",
100
- "number",
101
- "volume-angle-x",
102
- "volume-angle-y",
103
- "volume-angle-z",
104
- "output",
105
- "center-position-x",
106
- "center-position-z",
107
- "axis-angle-x",
108
- "axis-angle-y",
109
- "z-parameter",
110
- "retrieval-method",
111
- "energy",
112
- "pixel-size",
113
- "propagation-distance",
114
- "regularization-rate",
115
- "thresholding-rate",
116
- "flats",
117
- "flats2",
118
- "z",
119
- ):
120
- if recons_param[opt] is not None:
121
- # special case for option that can contain Unix filename pattern matching (*)
122
- if opt in ("darks", "flats", "flats2", "dark-scale"):
123
- if pre_proc_ffc is True:
124
- # in this case flat field correction has already been run
125
- pass
126
- else:
127
- options = options + [
128
- " ".join([opt, '"' + str(recons_param[opt]) + '"'])
129
- ]
130
- else:
131
- options = options + [" ".join([opt, str(recons_param[opt])])]
132
-
133
- # deal with specific case of the scan
134
- if "projections" in recons_param:
135
- options = options + [recons_param["projections"]]
136
- elif scan_id is not None:
137
- concert_radio_path = os.path.join(scan_id, "radios")
138
- proj_file_fn = concert_radio_path + os.sep + "frame_*.tif"
139
- if os.path.exists(concert_radio_path) and len(glob.glob(proj_file_fn)) > 0:
140
- options = options + ['projections "' + proj_file_fn + '"']
141
- else:
142
- options = options + [
143
- 'projections "'
144
- + scan_id
145
- + os.sep
146
- + os.path.basename(scan_id)
147
- + '*.edf"'
148
- ]
149
-
150
- # deal with region
151
- assert "region" in recons_param
152
- if recons_param["region"] is not None:
153
- assert type(recons_param["region"]) in (list, tuple)
154
- assert len(recons_param["region"]) == 3
155
- options = options + ["region=%s,%s,%s" % recons_param["region"]]
156
-
157
- # deal with overallangle wich need a '-' for avoid staring by a numerical
158
- assert type(recons_param["overall-angle"]) is float
159
- options = options + ["overall-angle " + str(-1.0 * recons_param["overall-angle"])]
160
-
161
- # deal with x and y region
162
- for region in ("x-region", "y-region"):
163
- value = recons_param[region]
164
- if value is not None:
165
- value = str(value).replace(" ", "")
166
- value = value.lstrip("(").rstrip(")")
167
- options = options + ["=".join((region, value))]
168
-
169
- # deal with option that will be defined only if set to true
170
- for opt in ("verbose", "dry-run", "absorptivity"):
171
- assert opt in recons_param
172
- if recons_param[opt] is True:
173
- options.append(opt)
174
-
175
- return " --".join(options) + " " + additional_opts
176
-
177
-
178
- # TODO: additional opts should be specific to flat field correction.
179
- # reconstruction and ffc additional options should be separated
180
- def _retrieve_opts_ffc_cmd(scan, recons_param, additional_opts, output):
181
- """Return the options command under the style '--name optValue' from the
182
- ToFuReconstructionParam and a string of additional options
183
-
184
- :param str output: output directory
185
- """
186
- assert isinstance(scan, TomwerScanBase)
187
- options = [""]
188
-
189
- options = options + [" ".join(["output", output])]
190
-
191
- # deal with option that will can be None
192
- for opt in (
193
- "retry-timeout",
194
- "retries",
195
- "reduction-mode",
196
- "dark-scale",
197
- "darks",
198
- "number",
199
- "center-position-x",
200
- "center-position-z",
201
- "axis-angle-x",
202
- "retrieval-method",
203
- "energy",
204
- "pixel-size",
205
- "propagation-distance",
206
- "regularization-rate",
207
- "thresholding-rate",
208
- "flats",
209
- "flats2",
210
- ):
211
- if recons_param[opt] is not None:
212
- # special case for option that can contain Unix filename pattern matching (*)
213
- if opt in ("darks", "flats", "flats2"):
214
- options = options + [
215
- " ".join([opt, '"' + str(recons_param[opt] + '"')])
216
- ]
217
- else:
218
- options = options + [" ".join([opt, str(recons_param[opt])])]
219
-
220
- # deal with specific case of the scanID
221
- if scan.path is not None:
222
- options = options + [_get_projection_file_pattern(scan_path=scan.path)]
223
-
224
- # deal with option that will be defined only if set to true
225
- for opt in ("verbose", "dry-run", "absorptivity"):
226
- assert opt in recons_param
227
- # absorptivity should only be done in reconstruction
228
- if opt == "absorptivity":
229
- continue
230
- if recons_param[opt] is True:
231
- options.append(opt)
232
-
233
- return " --".join(options) + " " + additional_opts
234
-
235
-
236
- def _get_projection_file_pattern(scan_path, ffc_folder=None):
237
- """
238
-
239
- :param scan_path: original scan path
240
- :param ffc_folder: folder containing the preprocessed flat field corrected
241
- image
242
- :return: projection + name of the scan path
243
- """
244
- assert type(scan_path) is str
245
- if ffc_folder is None:
246
- root_folder = scan_path
247
- else:
248
- root_folder = ffc_folder
249
- assert root_folder is not None
250
- concert_radio_path = os.path.join(root_folder, "radios")
251
- proj_file_fn = concert_radio_path + os.sep + "frame_*.tif"
252
- if os.path.exists(concert_radio_path) and len(glob.glob(proj_file_fn)) > 0:
253
- return 'projections "' + proj_file_fn + '"'
254
- else:
255
- return (
256
- 'projections "'
257
- + root_folder
258
- + os.sep
259
- + os.path.basename(scan_path)
260
- + '*.edf"'
261
- )
262
-
263
-
264
- def _tofu_lamino_reconstruction(
265
- scan_id,
266
- recons_param,
267
- additional_options,
268
- delete_existing,
269
- exec_cmd=True,
270
- pre_proc_ffc=True,
271
- ):
272
- """Process a reconstruction for lamino using tofu
273
-
274
- :param str scan_id: path of the scan to reconstruct
275
- :param dict recons_param: parameters for the reconstruction
276
- :param str additional_options: additional options to be add at the tofu reco
277
- call.
278
- :param bool delete_existing: if True then remove output dir if given
279
- :param bool exec_cmd: if True, will run reconstruction, otherwise will only
280
- display the reconstruction parameters.
281
- :param bool pre_proc_ffc: should we apply flat field correction on the fly
282
- or is it already done. If flat field correction
283
- has been preprocessed, also deal with half
284
- acquisition case
285
- """
286
- assert "output" in recons_param
287
- outputdir = recons_param["output"]
288
-
289
- if exec_cmd is True:
290
- if has_tofu() is False:
291
- logger.error(
292
- "Cannot launch tofu reconstruction because " "tofu is not installed."
293
- )
294
- return
295
- if delete_existing is True and "output" in recons_param:
296
- logger.info("remove output dir: %s" % recons_param["output"])
297
- if exec_cmd:
298
- for _file in glob.glob(outputdir + "*.tif"):
299
- try:
300
- os.remove(_file)
301
- except Exception as e:
302
- logger.error(e)
303
- else:
304
- logger.info(("will remove all files" + outputdir + "*.tif"))
305
-
306
- options = _retrieve_opts_recons_cmd(
307
- scan_id=scan_id,
308
- recons_param=recons_param,
309
- additional_opts=additional_options,
310
- pre_proc_ffc=pre_proc_ffc,
311
- )
312
-
313
- try:
314
- logger.info("launch command : " + "tofu reco" + options)
315
- if exec_cmd is True:
316
- subprocess.call(
317
- "tofu reco " + options, shell=True, stderr=sys.stderr, stdout=sys.stdout
318
- )
319
- except OSError:
320
- return False
321
- else:
322
- return True
323
-
324
-
325
- def _preprocess_ffc(scan, recons_param, additional_options, output, exec_cmd=True):
326
- """Process a flat field reconstruction for given scan using tofu
327
-
328
- :param TomwerScanBase scan: path of the scan to reconstruct
329
- :param dict recons_param: parameters for the reconstruction
330
- :param str additional_options: additional options to be add at the tofu reco
331
- call.
332
- :param bool exec_cmd: if True, will run reconstruction, otherwise will only
333
- display the reconstruction parameters.
334
- """
335
- assert isinstance(scan, TomwerScanBase)
336
- if exec_cmd is True:
337
- if has_tofu() is False:
338
- logger.error(
339
- "Cannot launch tofu reconstruction because " "tofu is not installed."
340
- )
341
- return
342
-
343
- output_folder = os.path.join(output, "fc")
344
- if exec_cmd is True and os.path.exists(output_folder):
345
- logger.info("removing" + output_folder)
346
- shutil.rmtree(output_folder)
347
- output = output_folder + "-%04i.tif"
348
- options = _retrieve_opts_ffc_cmd(
349
- scan=scan,
350
- recons_param=recons_param,
351
- additional_opts=additional_options,
352
- output=output,
353
- )
354
- if output is not None:
355
- logger.info(
356
- "flat field correction preprocessing result will be store in " + output
357
- )
358
- logger.info("launch command : " + "tofu preprocess" + options)
359
- if exec_cmd is True:
360
- subprocess.call(
361
- "tofu preprocess " + options,
362
- shell=True,
363
- stderr=sys.stderr,
364
- stdout=sys.stdout,
365
- )
366
- return True
367
-
368
-
369
- def _preprocess_stitching(
370
- shift, blend, adjust_mean, fc_folder, fc1_folder, fc2_folder, dry_run
371
- ):
372
- """
373
- move half acquisition dataset to full within flips
374
-
375
- :param float shift: How much is second image shifted with respect to the
376
- first one. For example, shift 0 means that both images
377
- overlap perfectly and the stitching doesn’t actually
378
- broaden the image. Shift corresponding to image width
379
- makes for a stitched image with twice the width of the
380
- respective images.
381
- :param bool blend: Linearly interpolate between the two images in the
382
- overlapping region.
383
- :param bool adjust_mean: Compute the mean of the overlapping region in the
384
- two images and adjust the second image to match
385
- the mean of the first one.
386
- :param str fc_folder: original folder of the sinogram
387
- :param str fc1_folder:
388
- :param str fc2_folder:
389
- :param bool dry_run:
390
- """
391
- assert shift is not None
392
- assert blend is not None
393
- if dry_run is False:
394
- if os.path.exists(fc1_folder):
395
- logger.info("removing" + fc1_folder)
396
- shutil.rmtree(fc1_folder)
397
- os.makedirs(fc1_folder)
398
- if os.path.exists(fc2_folder):
399
- logger.info("removing" + fc2_folder)
400
- shutil.rmtree(fc2_folder)
401
- os.makedirs(fc2_folder)
402
-
403
- def move_tiff():
404
- n_file = len(os.listdir(fc_folder))
405
- if n_file % 2 != 0:
406
- logger.error("even number of projection file, unable to apply stitching")
407
- return False
408
- order_list_dir = os.listdir(fc_folder)
409
- order_list_dir.sort()
410
- for i_file, file_ in enumerate(list(order_list_dir)):
411
- if i_file < n_file / 2:
412
- dest = fc1_folder
413
- else:
414
- dest = fc2_folder
415
- file_path = os.path.join(fc_folder, file_)
416
- shutil.move(src=file_path, dst=dest)
417
- # now fc should be empty of any tif file
418
- return True
419
-
420
- def stitching(shift, blend, adjust_mean):
421
- work = f"[read path={fc1_folder}, read path={fc2_folder} ! flip direction=horizontal]"
422
- work = work + " ! stitch shift=%s " % shift
423
- work += "blend=%s " % str(int(blend))
424
- work += "adjust-mean=%s " % str(int(adjust_mean))
425
- work += "! write filename=%s" % os.path.join(fc_folder, "fc-%04i.tif")
426
- try:
427
- logger.info("launch command : " + "ufo-launch " + work)
428
- if not dry_run:
429
- subprocess.call(
430
- "ufo-launch " + work,
431
- shell=True,
432
- stderr=sys.stderr,
433
- stdout=sys.stdout,
434
- )
435
- except OSError:
436
- return False
437
- else:
438
- return True
439
-
440
- if not dry_run:
441
- move_res = move_tiff()
442
- else:
443
- logger.info("move tiff file from fc to fc1, fc2")
444
- move_res = True
445
- if move_res is True:
446
- stitching(shift, blend=blend, adjust_mean=adjust_mean)
447
- return fc_folder
448
-
449
-
450
- def has_tofu():
451
- """
452
-
453
- :return: true if the os knows the tofu command
454
- """
455
- if not sys.platform.startswith("linux"):
456
- return False
457
- try:
458
- subprocess.call(
459
- ["tofu", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
460
- )
461
- return True
462
- except Exception:
463
- return False
464
-
465
-
466
- class LaminoReconstructionTask(
467
- Task,
468
- input_names=("data",),
469
- optional_input_names=("serialize_output_data",),
470
- output_names=("data",),
471
- ):
472
- """
473
- Process to launch the lamino reconstruction
474
-
475
- TODO: setting parameters should be group with the gui/lamino/tofu/xxx files
476
- But this binding is probably overkilled as we should use the tofu python
477
- binding for it. But no python3 version at the moment.
478
- """
479
-
480
- DEFAULT_RECONSTRUCTION_PARAMETERS_VALS = {
481
- "region": None,
482
- "retry-timeout": None,
483
- "retries": None,
484
- "reduction-mode": None,
485
- "dark-scale": None,
486
- "darks": None,
487
- "slices-per-device": None,
488
- "slice-memory-coeff": 0.8,
489
- "number": None,
490
- "volume-angle-x": None,
491
- "volume-angle-y": None,
492
- "volume-angle-z": None,
493
- "output": None,
494
- "center-position-x": None,
495
- "center-position-z": None,
496
- "axis-angle-x": None,
497
- "axis-angle-y": None,
498
- "z-parameter": None,
499
- "retrieval-method": None,
500
- "energy": None,
501
- "pixel-size": None,
502
- "propagation-distance": None,
503
- "regularization-rate": None,
504
- "thresholding-rate": None,
505
- "flats": None,
506
- "flats2": None,
507
- "overall-angle": 360.0,
508
- "x-region": None,
509
- "y-region": None,
510
- "verbose": False,
511
- "dry-run": False,
512
- "absorptivity": False,
513
- "half-acquisition": False,
514
- "z": None,
515
- }
516
-
517
- def __init__(
518
- self, varinfo=None, inputs=None, node_id=None, node_attrs=None, execinfo=None
519
- ):
520
- Task.__init__(
521
- self,
522
- varinfo=varinfo,
523
- inputs=inputs,
524
- node_id=node_id,
525
- node_attrs=node_attrs,
526
- execinfo=execinfo,
527
- )
528
- if "recons_params" in inputs:
529
- raise KeyError(
530
- "Do not use recons_params but `lamino_recons_params` instead"
531
- )
532
-
533
- self._reconsparams = self.DEFAULT_RECONSTRUCTION_PARAMETERS_VALS
534
- recons_params = inputs.get("lamino_params", {})
535
- self._reconsparams.update(recons_params)
536
- self._additional_reco_options = ""
537
- self._additional_preprocess_options = ""
538
- self._delete_existing = False
539
- self._dry_run = False
540
- self.__ffc_has_been_preprocessed = False
541
- self.__ffc_tmp_dir = None
542
- self.__prepare_ffc_dir()
543
- # top level directory to make the stitching and flat field preprocessing
544
-
545
- self.__latest_ffc_prerecons = (None, None, None, None, None, self._dry_run)
546
- # Store the current existing flat field reconstruction as the 'ffc key'
547
- # (scan, x center, method, dark, ff, dry_run) because this should be
548
- # executed only if x center change or scan path and we want to keep the same
549
- # behavior with or without the dry-run option
550
- self.__latest_stitching = None, None
551
-
552
- def __del__(self):
553
- self.__remove_ffc_dir()
554
-
555
- def __remove_ffc_dir(self):
556
- if self.__ffc_tmp_dir is not None and os.path.exists(self.__ffc_tmp_dir):
557
- shutil.rmtree(self.__ffc_tmp_dir)
558
-
559
- def __prepare_ffc_dir(self):
560
- self.__remove_ffc_dir()
561
- self.__ffc_tmp_dir = tempfile.mkdtemp()
562
- os.makedirs(os.path.join(self.__ffc_tmp_dir, "fc"))
563
-
564
- @property
565
- def reconstruction_parameters(self):
566
- return self._reconsparams
567
-
568
- @reconstruction_parameters.setter
569
- def reconstruction_parameters(self, params):
570
- self._reconsparams = params
571
- if "output" in self._reconsparams:
572
- self.dry_run = self._reconsparams["output"] in (None, "")
573
-
574
- @property
575
- def additional_reco_options(self):
576
- return self._additional_reco_options
577
-
578
- @additional_reco_options.setter
579
- def additional_reco_options(self, opts):
580
- self._additional_reco_options = opts
581
- if "delete-existing" in self.additional_reco_options:
582
- self.delete_existing = self.additional_reco_options["delete-existing"]
583
-
584
- @property
585
- def additional_preprocess_options(self):
586
- return self._additional_preprocess_options
587
-
588
- @additional_preprocess_options.setter
589
- def additional_preprocess_options(self, opts):
590
- self._additional_preprocess_options = opts
591
-
592
- @property
593
- def delete_existing(self):
594
- return self._delete_existing
595
-
596
- @delete_existing.setter
597
- def delete_existing(self, _delete):
598
- assert type(_delete) is bool
599
- self._delete_existing = _delete
600
-
601
- @property
602
- def dry_run(self):
603
- return self._dry_run
604
-
605
- @dry_run.setter
606
- def dry_run(self, dryrun):
607
- assert type(dryrun) is bool
608
- self._dry_run = dryrun
609
-
610
- def preprocess_ff(self, scan):
611
- """preprocess flat field corrcetion on the given scan.
612
- Result will be stored in `__ffc_tmp_dir` and the tuple
613
- (scan, center-position-x) will be stored as a key to avoid repeating
614
- this preprocessing flat field if already process.
615
-
616
- :return: path of the flat field corrected file or None if cannot process
617
- """
618
- assert isinstance(scan, TomwerScanBase)
619
- if scan is None:
620
- return None
621
-
622
- def get_val_or_none(key):
623
- if key in self._reconsparams:
624
- return self._reconsparams[key]
625
- else:
626
- return None
627
-
628
- x_center = get_val_or_none("center-position-x")
629
-
630
- if "output" not in self._reconsparams:
631
- logger.error(
632
- "no output define, requested for pre processing the "
633
- "flat field correction in pre processing."
634
- )
635
- return None
636
- elif x_center is None:
637
- logger.error(
638
- "x center position not defined, unable to process flat "
639
- "field correction"
640
- )
641
- return None
642
-
643
- output_folder = self._reconsparams["output"]
644
- output_folder = self._get_fc_folder_frm_output_folder(output_folder)
645
- fc_folder = os.path.join(output_folder, "fc")
646
- # TODO: should not integrate xySlice ...
647
-
648
- # case data is already stored in ffc_preprocess_dir
649
- darks = get_val_or_none("darks")
650
- flats = get_val_or_none("flat")
651
- flats2 = get_val_or_none("flats2")
652
- method = get_val_or_none("reduction-mode")
653
- if (
654
- scan,
655
- x_center,
656
- method,
657
- darks,
658
- [flats, flats2],
659
- self._dry_run,
660
- ) == self.__latest_ffc_prerecons:
661
- logger.info("flat field correction already process, skip correction")
662
- return fc_folder
663
-
664
- recons_params = self.reconstruction_parameters
665
- recons_params["projections"] = _get_projection_file_pattern(scan_path=scan.path)
666
- _preprocess_ffc(
667
- scan=scan,
668
- recons_param=recons_params,
669
- additional_options=self.additional_preprocess_options,
670
- exec_cmd=(not self.dry_run),
671
- output=fc_folder,
672
- )
673
- recons_params["projections"] = os.path.join(fc_folder, "*.tif")
674
- self.__latest_ffc_prerecons = (
675
- scan,
676
- x_center,
677
- method,
678
- darks,
679
- [flats, flats2],
680
- self._dry_run,
681
- )
682
- return fc_folder
683
-
684
- def _get_fc_folder_frm_output_folder(self, output_folder):
685
- if output_folder is None:
686
- return "/ghost_folder"
687
- else:
688
- return os.path.dirname(output_folder)
689
-
690
- def is_ffc_has_been_preprocessed(self, scan, x_center, method, darks, ff):
691
- return (
692
- scan,
693
- x_center,
694
- method,
695
- darks,
696
- ff,
697
- self._dry_run,
698
- ) == self.__latest_ffc_prerecons
699
-
700
- def stitching_requested(self):
701
- """
702
-
703
- :return: True if stitching is requested
704
- """
705
- return (
706
- "half-acquisition" in self._reconsparams
707
- and self._reconsparams["half-acquisition"] is True
708
- )
709
-
710
- def need_reprocessing_stitching(
711
- self, shift, blend, adjust_mean, fc_folder, fc1_folder, fc2_folder, dry_run
712
- ):
713
- """
714
-
715
- :param float shift:
716
- :param bool blend:
717
- :param bool adjust_mean:
718
- :param str fc_folder:
719
- :param str fc1_folder:
720
- :param str fc2_folder:
721
- :param bool dry_run:
722
- :return: True if stitching need to be reprocess
723
- """
724
- return self.__latest_stitching != (
725
- self.__latest_ffc_prerecons,
726
- (shift, blend, adjust_mean, fc_folder, fc1_folder, fc2_folder, dry_run),
727
- )
728
-
729
- def preprocessing_requested(self):
730
- """
731
-
732
- :return: True if some preprocessing is require
733
- """
734
- return (
735
- "ffc-when" in self._reconsparams
736
- and self._reconsparams["ffc-when"] is FFCWhen.preprocessing
737
- )
738
-
739
- def need_reprocessing_ffc(self, scan, x_center, method, darks, ff, dry_run):
740
- """
741
-
742
- :param :class:`.TomoBase` scan: scan to process
743
- :param float x_center: x center
744
- :param str method: can be 'median' or 'average'
745
- :param str darks:
746
- :param list ff:
747
- :param bool dry_run:
748
- :return: True if flat field need to be reprocess
749
- """
750
- assert isinstance(scan, TomwerScanBase)
751
- return self.__latest_ffc_prerecons != (
752
- scan.path,
753
- x_center,
754
- method,
755
- darks,
756
- ff,
757
- dry_run,
758
- )
759
-
760
- def run(self):
761
- scan = self.inputs.data
762
- if scan is None:
763
- self.outputs.data = None
764
- return
765
- if type(scan) is dict:
766
- _scan = ScanFactory.create_scan_object_frm_dict(scan)
767
- elif type(scan) is str:
768
- _scan = ScanFactory.create_scan_object(scan_path=scan)
769
- else:
770
- _scan = scan
771
- if not isinstance(_scan, TomwerScanBase):
772
- raise TypeError(
773
- f"input data is expected to be dict, str or {TomwerScanBase} not {type(_scan)}"
774
- )
775
-
776
- # if need some preprocessing
777
- if self.preprocessing_requested():
778
- print("------------------------")
779
- print("request preprocessing ffc")
780
- self._ff_has_been_reprocess = False
781
- # if need to reprocess flat field
782
- ffc_key = {
783
- "scan": _scan,
784
- "x_center": self._reconsparams["center-position-x"],
785
- "method": self._reconsparams["reduction-mode"],
786
- "darks": self._reconsparams["darks"],
787
- "ff": [self._reconsparams["flats"], self._reconsparams["flats2"]],
788
- "dry_run": self._dry_run,
789
- }
790
- if self.need_reprocessing_ffc(**ffc_key):
791
- ffc_folder = self.preprocess_ff(scan=_scan)
792
- if ffc_folder is None:
793
- logger.error("Fail to process the flat field correction")
794
- return None
795
- self._reconsparams["projections"] = _get_projection_file_pattern(
796
- scan_path=scan.path, ffc_folder=ffc_folder
797
- )
798
- self._ff_has_been_reprocess = True
799
-
800
- if self.stitching_requested():
801
- output_folder = self._reconsparams["output"]
802
- output_folder = self._get_fc_folder_frm_output_folder(output_folder)
803
- fc_folder = os.path.join(output_folder, "fc")
804
- fc1_folder = os.path.join(output_folder, "fc1")
805
- fc2_folder = os.path.join(output_folder, "fc2")
806
- shift = self._reconsparams["center-position-x"]
807
- blend = self._reconsparams["blend"]
808
- adjust_mean = self._reconsparams["adjust-mean"]
809
- if self.need_reprocessing_stitching(
810
- shift=shift,
811
- fc_folder=fc_folder,
812
- fc1_folder=fc1_folder,
813
- fc2_folder=fc2_folder,
814
- dry_run=self.dry_run,
815
- blend=blend,
816
- adjust_mean=adjust_mean,
817
- ):
818
- print("------------------------")
819
- print("run stitching")
820
- image_width = getDim1Dim2(scan.path)[0]
821
- if image_width is None:
822
- logger.warning(
823
- "failed to find image width, set to " "2048 by default"
824
- )
825
- image_width = 2048
826
- shift = 2.0 * self._reconsparams["center-position-x"] - image_width
827
- _preprocess_stitching(
828
- shift=shift,
829
- fc_folder=fc_folder,
830
- fc1_folder=fc1_folder,
831
- fc2_folder=fc2_folder,
832
- dry_run=self.dry_run,
833
- blend=blend,
834
- adjust_mean=adjust_mean,
835
- )
836
-
837
- self.__latest_stitching = (
838
- self.__latest_ffc_prerecons,
839
- (
840
- shift,
841
- blend,
842
- adjust_mean,
843
- fc_folder,
844
- fc1_folder,
845
- fc2_folder,
846
- self.dry_run,
847
- ),
848
- )
849
- else:
850
- logger.info("data already stitch from previous process")
851
- fc_folder = os.path.join(output_folder, "fc")
852
- # stitching reduce the number of projections by a factor of 2
853
- self._reconsparams["number"] = int(self._reconsparams["number"] / 2)
854
- self._reconsparams["overall-angle"] = (
855
- self._reconsparams["overall-angle"] / 2.0
856
- )
857
- else:
858
- # TODO: remove, ffc folder is fc folder
859
- fc_folder = ffc_folder
860
- self._reconsparams["projections"] = 'projections "' + os.path.join(
861
- fc_folder, '*.tif"'
862
- )
863
-
864
- res = _tofu_lamino_reconstruction(
865
- scan_id=_scan.path,
866
- recons_param=self._reconsparams,
867
- additional_options=self.additional_reco_options,
868
- delete_existing=self.delete_existing,
869
- exec_cmd=(not self.dry_run),
870
- pre_proc_ffc=self.preprocessing_requested(),
871
- )
872
- if not self.dry_run:
873
- entry = "entry"
874
- if isinstance(scan, HDF5TomoScan):
875
- entry = scan.entry
876
- with scan.acquire_process_file_lock():
877
- self.register_process(
878
- process_file=scan.process_file,
879
- entry=entry,
880
- configuration=self._reconsparams,
881
- results={},
882
- process_index=scan.pop_process_index(),
883
- overwrite=True,
884
- )
885
-
886
- if res is False:
887
- logger.error(f"Reconstruction of {_scan.path} failed")
888
- if self.get_input_value("serialize_output_data", True):
889
- self.outputs.data = _scan.to_dict()
890
- else:
891
- self.outputs.data = _scan
892
-
893
- def set_configuration(self, configuration):
894
- assert isinstance(configuration, (tuple, list))
895
- self.additional_preprocess_options, self.additional_reco_options = configuration
896
-
897
- @staticmethod
898
- def program_name():
899
- return "lamino"
900
-
901
- @staticmethod
902
- def program_version():
903
- """version of the program used for this processing"""
904
- return tomwer.version.version
905
-
906
-
907
- def getDark(scan):
908
- """Return darks as a string for tofu from the scan path"""
909
- concert_projection_files = os.path.join(scan, "radios")
910
- concert_darks_path = os.path.join(scan, "darks")
911
- if (
912
- os.path.exists(concert_darks_path)
913
- and os.path.exists(concert_projection_files)
914
- and len(glob.glob(concert_darks_path + os.sep + "frame_*.tif")) > 0
915
- ):
916
- return concert_darks_path + os.sep + "frame_*.tif"
917
- else:
918
- files = os.listdir(scan)
919
- for thFile in (
920
- DARKHST_PREFIX,
921
- "dark.edf",
922
- "darkend0000.edf",
923
- "darkend000.edf",
924
- "darkHST.edf",
925
- ):
926
- if thFile in files:
927
- return os.path.join(scan, thFile)
928
- return None
929
-
930
-
931
- def getFlats(scan):
932
- """
933
- Return flats as a string for tofu from the scan path
934
-
935
- :return: tuple (flats, secondFlats)
936
- """
937
-
938
- def treatRawRef(rawRefFiles):
939
- ns = set()
940
- for _file in rawRefFiles:
941
- name = _file.rstrip(".edf")
942
- ns.add(name.split("_")[-1])
943
- ns = sorted(ns)
944
- if len(ns) == 0:
945
- return None, None
946
- elif len(ns) == 1:
947
- return os.path.join(scan, "ref*_" + ns[0] + ".edf")
948
- else:
949
- return (
950
- os.path.join(scan, "ref*_" + ns[0] + ".edf"),
951
- os.path.join(scan, "ref*_" + ns[-1] + ".edf"),
952
- )
953
-
954
- # deal with concert files
955
- concert_projection_files = os.path.join(scan, "radios")
956
- concert_flats_path = os.path.join(scan, "flats")
957
- if os.path.exists(concert_flats_path) and os.path.exists(concert_projection_files):
958
- flats = None
959
- flats2 = None
960
- if len(glob.glob(concert_flats_path + os.sep + "frame_*.tif")) > 0:
961
- flats = concert_flats_path + os.sep + "frame_*.tif"
962
- concert_flats2_path = os.path.join(scan, "flats_2")
963
- if len(glob.glob(concert_flats2_path + os.sep + "frame_*.tif")) > 0:
964
- flats2 = concert_flats2_path + os.sep + "frame_*.tif"
965
- if flats is not None or flats2 is not None:
966
- return flats, flats2
967
-
968
- # deal with classical files
969
- files = os.listdir(scan)
970
- refHSTFiles = [] # files starting with refHST (treated by darkRef)
971
- rawRefFiles = [] # files starting with ref only (raw ref files)
972
- for _file in files:
973
- if _file.startswith("ref") and _file.endswith(".edf"):
974
- if _file.startswith(REFHST_PREFIX):
975
- refHSTFiles.append(_file)
976
- else:
977
- rawRefFiles.append(_file)
978
-
979
- refHSTFiles = sorted(refHSTFiles)
980
- if len(refHSTFiles) == 0:
981
- return treatRawRef(rawRefFiles)
982
- elif len(refHSTFiles) == 1:
983
- return os.path.join(scan, refHSTFiles[0]), None
984
- else:
985
- logger.warning("Found more than two refHST files")
986
- return (os.path.join(scan, refHSTFiles[0]), os.path.join(scan, refHSTFiles[-1]))
987
-
988
-
989
- class LaminoReconstruction(LaminoReconstructionTask):
990
- def __init__(
991
- self, varinfo=None, inputs=None, node_id=None, node_attrs=None, execinfo=None
992
- ):
993
- deprecated_warning(
994
- name="tomwer.core.process.reconstruction.lamino.tofu.LaminoReconstruction",
995
- type_="class",
996
- reason="improve readibility",
997
- since_version="1.2",
998
- replacement="LaminoReconstructionTask",
999
- )
1000
- super().__init__(varinfo, inputs, node_id, node_attrs, execinfo)