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.
- orangecontrib/tomwer/tutorials/icat_publication.ows +58 -0
- orangecontrib/tomwer/widgets/__init__.py +11 -11
- orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +2 -2
- orangecontrib/tomwer/widgets/control/DataListOW.py +9 -7
- orangecontrib/tomwer/widgets/control/DataListenerOW.py +6 -6
- orangecontrib/tomwer/widgets/control/DataSelectorOW.py +21 -10
- orangecontrib/tomwer/widgets/control/DataValidatorOW.py +6 -6
- orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +24 -7
- orangecontrib/tomwer/widgets/control/EmailOW.py +4 -4
- orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +3 -3
- orangecontrib/tomwer/widgets/control/NXTomomillOW.py +64 -23
- orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +20 -8
- orangecontrib/tomwer/widgets/control/NotifierOW.py +1 -0
- orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +6 -6
- orangecontrib/tomwer/widgets/control/VolumeSelector.py +7 -4
- orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +182 -182
- orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +5 -5
- orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +4 -4
- orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +3 -3
- orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +8 -1
- orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +3 -3
- orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +3 -3
- orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +115 -0
- orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -0
- orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -0
- orangecontrib/tomwer/widgets/icat/__init__.py +13 -0
- orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
- orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +82 -0
- orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.png +0 -0
- orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.svg +95 -0
- orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
- orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +143 -0
- orangecontrib/tomwer/widgets/icons/tomwer_data_portal.png +0 -0
- orangecontrib/tomwer/widgets/icons/tomwer_data_portal.svg +76 -0
- orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +22 -20
- orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +19 -3
- orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +184 -169
- orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +23 -0
- orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +39 -5
- orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +18 -22
- orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +18 -26
- orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +15 -19
- orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +9 -9
- orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
- orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +1 -1
- orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +3 -3
- orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -1
- orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +3 -29
- tomwer/__main__.py +7 -64
- tomwer/app/axis.py +3 -3
- tomwer/app/canvas.py +8 -0
- tomwer/app/canvas_launcher/config.py +16 -14
- tomwer/app/canvas_launcher/environ.py +1 -0
- tomwer/app/canvas_launcher/mainwindow.py +4 -1
- tomwer/app/darkref.py +1 -1
- tomwer/app/darkrefpatch.py +1 -1
- tomwer/app/diffframe.py +3 -3
- tomwer/app/imagekeyeditor.py +5 -5
- tomwer/app/imagekeyupgrader.py +5 -5
- tomwer/app/intensitynormalization.py +14 -13
- tomwer/app/{saaxis.py → multicor.py} +3 -3
- tomwer/app/{sadeltabeta.py → multipag.py} +3 -3
- tomwer/app/nabuapp.py +0 -11
- tomwer/app/radiostack.py +6 -4
- tomwer/app/samplemoved.py +3 -2
- tomwer/app/scanviewer.py +4 -2
- tomwer/app/sinogramviewer.py +3 -2
- tomwer/app/slicestack.py +3 -2
- tomwer/app/zstitching.py +88 -6
- tomwer/core/cluster/cluster.py +26 -0
- tomwer/core/log/logger.py +7 -5
- tomwer/core/process/conditions/filters.py +1 -1
- tomwer/core/process/control/datalistener/datalistener.py +19 -14
- tomwer/core/process/control/datawatcher/edfdwprocess.py +0 -9
- tomwer/core/process/control/nxtomoconcatenate.py +13 -13
- tomwer/core/process/control/nxtomomill.py +92 -34
- tomwer/core/process/control/scantransfer.py +20 -43
- tomwer/core/process/control/scanvalidator.py +3 -2
- tomwer/core/process/control/test/test_concatenate_nxtomos.py +9 -9
- tomwer/core/process/control/test/test_email.py +4 -4
- tomwer/core/process/control/test/test_h52nx_process.py +59 -7
- tomwer/core/process/control/test/test_volume_link.py +64 -64
- tomwer/core/process/control/timer.py +1 -1
- tomwer/core/process/control/volumesymlink.py +200 -200
- tomwer/core/process/edit/darkflatpatch.py +14 -15
- tomwer/core/process/edit/imagekeyeditor.py +41 -39
- tomwer/core/process/icat/__init__.py +0 -0
- tomwer/core/process/icat/createscreenshots.py +100 -0
- tomwer/core/process/icat/gallery.py +377 -0
- tomwer/core/process/icat/icatbase.py +36 -0
- tomwer/core/process/icat/publish.py +228 -0
- tomwer/core/process/icat/screenshots.py +27 -0
- tomwer/core/process/output.py +52 -0
- tomwer/core/process/reconstruction/axis/axis.py +280 -69
- tomwer/core/process/reconstruction/axis/mode.py +163 -48
- tomwer/core/process/reconstruction/axis/params.py +29 -21
- tomwer/core/process/reconstruction/darkref/darkrefs.py +41 -127
- tomwer/core/process/reconstruction/darkref/darkrefscopy.py +4 -3
- tomwer/core/process/reconstruction/darkref/params.py +1 -1
- tomwer/core/process/reconstruction/nabu/castvolume.py +4 -4
- tomwer/core/process/reconstruction/nabu/helical.py +9 -5
- tomwer/core/process/reconstruction/nabu/nabucommon.py +71 -78
- tomwer/core/process/reconstruction/nabu/nabuscores.py +425 -53
- tomwer/core/process/reconstruction/nabu/nabuslices.py +114 -93
- tomwer/core/process/reconstruction/nabu/nabuvolume.py +54 -27
- tomwer/core/process/reconstruction/nabu/plane.py +9 -0
- tomwer/core/process/reconstruction/nabu/settings.py +2 -2
- tomwer/core/process/reconstruction/nabu/utils.py +164 -26
- tomwer/core/process/reconstruction/output.py +108 -0
- tomwer/core/process/reconstruction/saaxis/saaxis.py +238 -264
- tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +151 -87
- tomwer/core/process/reconstruction/scores/params.py +7 -4
- tomwer/core/process/reconstruction/scores/scores.py +13 -0
- tomwer/core/process/reconstruction/test/test_axis_params.py +2 -2
- tomwer/core/process/reconstruction/test/test_darkref.py +3 -3
- tomwer/core/process/reconstruction/test/test_darkref_copy.py +7 -7
- tomwer/core/process/reconstruction/test/test_saaxis.py +3 -4
- tomwer/core/process/reconstruction/test/test_sadeltabeta.py +2 -2
- tomwer/core/process/stitching/nabustitcher.py +13 -12
- tomwer/core/process/task.py +34 -26
- tomwer/core/process/test/test_axis.py +13 -12
- tomwer/core/process/test/test_dark_and_flat.py +10 -7
- tomwer/core/process/test/test_data_transfer.py +10 -8
- tomwer/core/process/test/test_nabu.py +14 -6
- tomwer/core/process/test/test_normalization.py +4 -4
- tomwer/core/scan/blissscan.py +3 -3
- tomwer/core/scan/edfscan.py +13 -10
- tomwer/core/scan/hdf5scan.py +19 -530
- tomwer/core/scan/nxtomoscan.py +534 -0
- tomwer/core/scan/scanbase.py +72 -44
- tomwer/core/scan/scanfactory.py +13 -13
- tomwer/core/scan/test/test_edf.py +2 -2
- tomwer/core/scan/test/test_future_scan.py +3 -3
- tomwer/core/scan/test/test_h5.py +18 -16
- tomwer/core/scan/test/test_process_registration.py +4 -40
- tomwer/core/scan/test/test_scan.py +5 -78
- tomwer/core/settings.py +22 -2
- tomwer/core/test/test_scanutils.py +8 -7
- tomwer/core/test/test_utils.py +35 -28
- tomwer/core/tomwer_object.py +1 -1
- tomwer/core/utils/__init__.py +0 -466
- tomwer/core/utils/deprecation.py +1 -1
- tomwer/core/utils/dictutils.py +14 -0
- tomwer/core/utils/lbsram.py +35 -0
- tomwer/core/utils/nxtomoutils.py +1 -1
- tomwer/core/utils/scanutils.py +6 -6
- tomwer/core/utils/spec.py +263 -0
- tomwer/core/volume/edfvolume.py +6 -6
- tomwer/core/volume/hdf5volume.py +6 -6
- tomwer/core/volume/jp2kvolume.py +6 -6
- tomwer/core/volume/rawvolume.py +6 -6
- tomwer/core/volume/tiffvolume.py +12 -12
- tomwer/core/volume/volumefactory.py +2 -2
- tomwer/gui/cluster/slurm.py +274 -65
- tomwer/gui/cluster/supervisor.py +12 -0
- tomwer/gui/cluster/test/test_cluster.py +14 -2
- tomwer/gui/cluster/test/test_supervisor.py +3 -3
- tomwer/gui/configuration/__init__.py +0 -0
- tomwer/gui/{reconstruction/nabu → configuration}/action.py +1 -32
- tomwer/gui/configuration/level.py +22 -0
- tomwer/gui/control/actions.py +54 -0
- tomwer/gui/control/datalist.py +83 -16
- tomwer/gui/control/datalistener.py +4 -16
- tomwer/gui/control/datawatcher/controlwidget.py +2 -4
- tomwer/gui/control/datawatcher/datawatcher.py +1 -24
- tomwer/gui/control/{email.py → emailnotifier.py} +9 -18
- tomwer/gui/control/history.py +2 -2
- tomwer/gui/control/observations.py +2 -2
- tomwer/gui/control/reducedarkflatselector.py +9 -9
- tomwer/gui/control/selectorwidgetbase.py +36 -9
- tomwer/gui/control/serie/seriecreator.py +5 -22
- tomwer/gui/control/test/test_email.py +1 -1
- tomwer/gui/control/test/test_scanvalidator.py +6 -5
- tomwer/gui/control/test/test_single_tomo_obj.py +3 -3
- tomwer/gui/control/tomoobjdisplaymode.py +8 -0
- tomwer/gui/debugtools/datasetgenerator.py +3 -3
- tomwer/gui/edit/dkrfpatch.py +20 -26
- tomwer/gui/edit/imagekeyeditor.py +11 -12
- tomwer/gui/edit/nxtomoeditor.py +111 -44
- tomwer/gui/edit/nxtomowarmer.py +7 -6
- tomwer/gui/edit/test/test_dkrf_patch.py +13 -13
- tomwer/gui/edit/test/test_image_key_editor.py +3 -3
- tomwer/gui/edit/test/test_nx_editor.py +40 -16
- tomwer/gui/icat/__init__.py +0 -0
- tomwer/gui/icat/createscreenshots.py +80 -0
- tomwer/gui/icat/gallery.py +214 -0
- tomwer/gui/icat/publish.py +187 -0
- tomwer/gui/imagefromfile.py +2 -2
- tomwer/gui/qfolderdialog.py +24 -1
- tomwer/gui/reconstruction/axis/CompareImages.py +88 -168
- tomwer/gui/reconstruction/axis/axis.py +171 -57
- tomwer/gui/reconstruction/axis/radioaxis.py +122 -257
- tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +3 -2
- tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -1
- tomwer/gui/reconstruction/nabu/castvolume.py +14 -3
- tomwer/gui/reconstruction/nabu/check.py +9 -9
- tomwer/gui/reconstruction/nabu/helical.py +29 -12
- tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
- tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +2 -1
- tomwer/gui/reconstruction/nabu/nabuconfig/output.py +126 -35
- tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +39 -32
- tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +222 -31
- tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +57 -27
- tomwer/gui/reconstruction/nabu/nabuflow.py +12 -20
- tomwer/gui/reconstruction/nabu/slices.py +10 -11
- tomwer/gui/reconstruction/nabu/volume.py +22 -10
- tomwer/gui/reconstruction/normalization/intensity.py +18 -48
- tomwer/gui/reconstruction/saaxis/corrangeselector.py +8 -24
- tomwer/gui/reconstruction/saaxis/dimensionwidget.py +1 -1
- tomwer/gui/reconstruction/saaxis/saaxis.py +9 -21
- tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +45 -12
- tomwer/gui/reconstruction/scores/control.py +2 -9
- tomwer/gui/reconstruction/scores/scoreplot.py +13 -11
- tomwer/gui/reconstruction/test/test_axis.py +41 -16
- tomwer/gui/reconstruction/test/test_nabu.py +31 -9
- tomwer/gui/reconstruction/test/test_saaxis.py +3 -3
- tomwer/gui/reconstruction/test/test_sadeltabeta.py +12 -2
- tomwer/gui/settings.py +5 -28
- tomwer/gui/stackplot.py +2 -5
- tomwer/gui/stitching/action.py +49 -0
- tomwer/gui/stitching/config/axisparams.py +7 -24
- tomwer/gui/stitching/config/output.py +10 -8
- tomwer/gui/stitching/config/positionoveraxis.py +22 -23
- tomwer/gui/stitching/normalization.py +117 -0
- tomwer/gui/stitching/stitchandbackground.py +4 -6
- tomwer/gui/stitching/stitching.py +267 -45
- tomwer/gui/stitching/stitching_preview.py +62 -55
- tomwer/gui/stitching/stitching_raw.py +13 -12
- tomwer/gui/stitching/z_stitching/fineestimation.py +0 -60
- tomwer/gui/utils/buttons.py +112 -29
- tomwer/gui/utils/inputwidget.py +43 -25
- tomwer/gui/utils/lineselector/lineselector.py +1 -1
- tomwer/gui/utils/scandescription.py +4 -0
- tomwer/gui/utils/step.py +144 -0
- tomwer/gui/utils/unitsystem.py +2 -5
- tomwer/gui/utils/vignettes.py +176 -15
- tomwer/gui/visualization/dataviewer.py +48 -35
- tomwer/gui/visualization/diffviewer/diffviewer.py +7 -16
- tomwer/gui/visualization/diffviewer/shiftwidget.py +2 -5
- tomwer/gui/visualization/scanoverview.py +1 -1
- tomwer/gui/visualization/sinogramviewer.py +20 -36
- tomwer/gui/visualization/test/test_diffviewer.py +3 -3
- tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +4 -4
- tomwer/gui/visualization/test/test_sinogramviewer.py +2 -2
- tomwer/gui/visualization/test/test_stacks.py +3 -3
- tomwer/gui/visualization/test/test_volumeviewer.py +65 -67
- tomwer/gui/visualization/volumeviewer.py +114 -113
- tomwer/io/utils/h5pyutils.py +3 -3
- tomwer/io/utils/raw_and_processed_data.py +84 -0
- tomwer/io/utils/tomoobj.py +4 -6
- tomwer/io/utils/utils.py +7 -7
- tomwer/resources/gui/icons/parameters.svg +1 -1
- tomwer/resources/gui/icons/ruler.png +0 -0
- tomwer/resources/gui/icons/ruler.svg +273 -0
- tomwer/resources/gui/icons/short_description.png +0 -0
- tomwer/resources/gui/icons/short_description.svg +58 -0
- tomwer/resources/gui/icons/url.png +0 -0
- tomwer/resources/gui/icons/url.svg +58 -0
- tomwer/resources/gui/illustrations/no_rot.svg +1 -1
- tomwer/synctools/stacks/edit/darkflatpatch.py +19 -14
- tomwer/synctools/stacks/edit/imagekeyeditor.py +2 -2
- tomwer/synctools/stacks/reconstruction/axis.py +4 -4
- tomwer/synctools/stacks/reconstruction/castvolume.py +22 -7
- tomwer/synctools/stacks/reconstruction/dkrefcopy.py +25 -20
- tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
- tomwer/synctools/stacks/reconstruction/normalization.py +2 -2
- tomwer/synctools/stacks/reconstruction/saaxis.py +2 -2
- tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -2
- tomwer/synctools/test/test_darkRefs.py +7 -58
- tomwer/synctools/test/test_foldertransfer.py +6 -6
- tomwer/synctools/utils/scanstages.py +6 -6
- tomwer/tests/conftest.py +34 -0
- tomwer/tests/datasets.py +13 -0
- tomwer/tests/test_scripts.py +91 -41
- tomwer/tests/utils.py +5 -0
- tomwer/third_part/WaitingOverlay.py +110 -0
- tomwer/third_part/__init__.py +0 -0
- tomwer/version.py +2 -2
- tomwer-1.3.12-py3.11-nspkg.pth +1 -0
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/METADATA +73 -58
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/RECORD +287 -286
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/WHEEL +1 -1
- orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +0 -197
- orangecontrib/tomwer/widgets/reconstruction/icons/XY_lamino.svg +0 -168
- orangecontrib/tomwer/widgets/reconstruction/icons/XZ_lamino.svg +0 -275
- orangecontrib/tomwer/widgets/reconstruction/icons/YZ_lamino.svg +0 -182
- tomwer/app/lamino.py +0 -143
- tomwer/core/process/reconstruction/lamino/__init__.py +0 -1
- tomwer/core/process/reconstruction/lamino/tofu.py +0 -1000
- tomwer/core/process/test/test_lamino.py +0 -76
- tomwer/core/test/test_lamino.py +0 -92
- tomwer/gui/reconstruction/lamino/__init__.py +0 -31
- tomwer/gui/reconstruction/lamino/tofu/TofuOptionLoader.py +0 -107
- tomwer/gui/reconstruction/lamino/tofu/__init__.py +0 -1
- tomwer/gui/reconstruction/lamino/tofu/misc.py +0 -148
- tomwer/gui/reconstruction/lamino/tofu/projections.py +0 -896
- tomwer/gui/reconstruction/lamino/tofu/settings.py +0 -75
- tomwer/gui/reconstruction/lamino/tofu/tofu.py +0 -432
- tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +0 -567
- tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +0 -760
- tomwer/gui/reconstruction/test/test_lamino.py +0 -189
- tomwer/resources/gui/icons/esrf_1.svg +0 -307
- tomwer/resources/gui/icons/lamino_parameters.svg +0 -70
- tomwer/resources/gui/icons/triangle.svg +0 -80
- tomwer/resources/gui/illustrations/lamino_angle.png +0 -0
- tomwer/resources/gui/illustrations/lamino_angle.svg +0 -509
- tomwer/resources/gui/illustrations/lamino_beta_angle.png +0 -0
- tomwer/resources/gui/illustrations/lamino_beta_angle.svg +0 -97
- tomwer/resources/gui/illustrations/lamino_theta_angle.png +0 -0
- tomwer/resources/gui/illustrations/lamino_theta_angle.svg +0 -368
- tomwer/resources/gui/illustrations/manual_slice.png +0 -0
- tomwer/resources/gui/illustrations/manual_slice.svg +0 -221
- tomwer/resources/gui/illustrations/psi_angle.png +0 -0
- tomwer/resources/gui/illustrations/psi_angle.svg +0 -479
- tomwer/resources/gui/illustrations/rotation_center.png +0 -0
- tomwer/resources/gui/illustrations/rotation_center.svg +0 -276
- tomwer/resources/gui/illustrations/slice_stack.png +0 -0
- tomwer/resources/gui/illustrations/slice_stack.svg +0 -266
- tomwer/resources/gui/illustrations/xy_slice.png +0 -0
- tomwer/resources/gui/illustrations/xy_slice.svg +0 -269
- tomwer/resources/gui/illustrations/xz_slice.png +0 -0
- tomwer/resources/gui/illustrations/xz_slice.svg +0 -270
- tomwer/resources/gui/illustrations/yz_slice.png +0 -0
- tomwer/resources/gui/illustrations/yz_slice.svg +0 -270
- tomwer/synctools/stacks/reconstruction/lamino.py +0 -233
- tomwer/synctools/test/test_scanstages.py +0 -162
- tomwer/tests/utils/__init__.py +0 -247
- tomwer/tests/utils/utilstest.py +0 -220
- tomwer-1.2.1-py3.11-nspkg.pth +0 -1
- /tomwer/core/process/control/{email.py → emailnotifier.py} +0 -0
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/LICENSE +0 -0
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/entry_points.txt +0 -0
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/namespace_packages.txt +0 -0
- {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)
|