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
@@ -28,16 +28,16 @@ __license__ = "MIT"
|
|
28
28
|
__date__ = "26/10/2020"
|
29
29
|
|
30
30
|
|
31
|
+
import copy
|
31
32
|
import logging
|
32
33
|
|
33
34
|
import nxtomomill.version
|
34
35
|
from nxtomomill.utils import change_image_key_control as _change_image_key_control
|
35
|
-
from
|
36
|
-
from
|
37
|
-
from silx.utils.deprecation import deprecated_warning, deprecated
|
36
|
+
from nxtomo.nxobject.nxdetector import ImageKey
|
37
|
+
from tomwer.core.utils.deprecation import deprecated_warning, deprecated
|
38
38
|
|
39
39
|
from tomwer.core.process.task import Task
|
40
|
-
from tomwer.core.scan.
|
40
|
+
from tomwer.core.scan.nxtomoscan import NXtomoScan
|
41
41
|
from tomwer.core.scan.scanbase import TomwerScanBase
|
42
42
|
from tomwer.core.utils.scanutils import data_identifier_to_scan
|
43
43
|
|
@@ -45,14 +45,14 @@ _logger = logging.getLogger(__name__)
|
|
45
45
|
|
46
46
|
|
47
47
|
IMAGE_KEYS = {
|
48
|
-
"projection":
|
49
|
-
"invalid":
|
50
|
-
"dark":
|
51
|
-
"flat":
|
48
|
+
"projection": ImageKey.PROJECTION,
|
49
|
+
"invalid": ImageKey.INVALID,
|
50
|
+
"dark": ImageKey.DARK_FIELD,
|
51
|
+
"flat": ImageKey.FLAT_FIELD,
|
52
52
|
}
|
53
53
|
|
54
54
|
|
55
|
-
def change_image_key_control(scan:
|
55
|
+
def change_image_key_control(scan: NXtomoScan, config: dict) -> TomwerScanBase:
|
56
56
|
"""
|
57
57
|
|
58
58
|
:param scan:
|
@@ -63,9 +63,9 @@ def change_image_key_control(scan: HDF5TomoScan, config: dict) -> TomwerScanBase
|
|
63
63
|
"""
|
64
64
|
if scan is None:
|
65
65
|
return
|
66
|
-
elif not isinstance(scan,
|
66
|
+
elif not isinstance(scan, NXtomoScan):
|
67
67
|
raise ValueError(
|
68
|
-
f"Image key control only handle
|
68
|
+
f"Image key control only handle NXtomoScan and not {type(scan)}"
|
69
69
|
)
|
70
70
|
|
71
71
|
if "modifications" not in config:
|
@@ -84,7 +84,7 @@ def change_image_key_control(scan: HDF5TomoScan, config: dict) -> TomwerScanBase
|
|
84
84
|
filter(lambda item: item[1] is image_key_type, modifications.items())
|
85
85
|
)
|
86
86
|
frame_indexes = tuple(frame_indexes_dict.keys())
|
87
|
-
_logger.
|
87
|
+
_logger.info(f"will modify {frame_indexes} to {image_key_type}")
|
88
88
|
_change_image_key_control(
|
89
89
|
file_path=scan.master_file,
|
90
90
|
entry=scan.entry,
|
@@ -125,28 +125,31 @@ class ImageKeyEditorTask(
|
|
125
125
|
raise TypeError(
|
126
126
|
f"scan is expected to be a dict or an instance of TomwerScanBase. Not {type(scan)}"
|
127
127
|
)
|
128
|
-
if not isinstance(scan,
|
128
|
+
if not isinstance(scan, NXtomoScan):
|
129
129
|
raise ValueError(f"input type of {scan}: {(type(scan))} is not managed")
|
130
130
|
|
131
131
|
config = self.inputs.configuration
|
132
132
|
if not isinstance(config, dict):
|
133
133
|
raise TypeError
|
134
|
+
# apply configuration
|
134
135
|
change_image_key_control(scan=scan, config=config)
|
135
136
|
modif_keys = list(config.get("modifications", {}).keys())
|
137
|
+
# dump modifications to the tomwer processes file
|
138
|
+
# note: we need to cast image keys to str to save it
|
139
|
+
config = copy.deepcopy(config)
|
136
140
|
new_modif = {}
|
137
141
|
for key in modif_keys:
|
138
142
|
value = config["modifications"][key]
|
139
143
|
config["modifications"][str(key)] = value
|
140
144
|
config["modifications"] = new_modif
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
)
|
145
|
+
self.register_process(
|
146
|
+
process_file=scan.process_file,
|
147
|
+
entry=scan.entry,
|
148
|
+
configuration=config,
|
149
|
+
results={},
|
150
|
+
process_index=scan.pop_process_index(),
|
151
|
+
overwrite=True,
|
152
|
+
)
|
150
153
|
if self.get_input_value("serialize_output_data", True):
|
151
154
|
self.outputs.data = scan.to_dict()
|
152
155
|
else:
|
@@ -178,8 +181,8 @@ class ImageKeyUpgraderTask(
|
|
178
181
|
|
179
182
|
def run(self):
|
180
183
|
scan = data_identifier_to_scan(self.inputs.data)
|
181
|
-
if not isinstance(scan,
|
182
|
-
raise TypeError(f"scan is expected to be an instance of {
|
184
|
+
if not isinstance(scan, NXtomoScan):
|
185
|
+
raise TypeError(f"scan is expected to be an instance of {NXtomoScan}")
|
183
186
|
operations = self.inputs.operations
|
184
187
|
if not isinstance(operations, dict):
|
185
188
|
raise TypeError("operations is expected to be an dict")
|
@@ -199,18 +202,17 @@ class ImageKeyUpgraderTask(
|
|
199
202
|
|
200
203
|
# apply modification using tomoscan
|
201
204
|
change_image_key_control(scan=scan, config=configuration)
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
)
|
205
|
+
self.register_process(
|
206
|
+
process_file=scan.process_file,
|
207
|
+
entry=scan.entry,
|
208
|
+
configuration={
|
209
|
+
ImageKey.from_value(key).value: ImageKey.from_value(value).value
|
210
|
+
for key, value in operations.items()
|
211
|
+
},
|
212
|
+
results={},
|
213
|
+
process_index=scan.pop_process_index(),
|
214
|
+
overwrite=True,
|
215
|
+
)
|
214
216
|
if self.get_input_value("serialize_output_data", True):
|
215
217
|
self.outputs.data = scan.to_dict()
|
216
218
|
else:
|
@@ -218,13 +220,13 @@ class ImageKeyUpgraderTask(
|
|
218
220
|
|
219
221
|
@staticmethod
|
220
222
|
def from_operation_to_config(
|
221
|
-
scan: TomwerScanBase, from_image_key:
|
223
|
+
scan: TomwerScanBase, from_image_key: ImageKey, to_image_key: ImageKey
|
222
224
|
):
|
223
225
|
"""
|
224
226
|
retrieve frame indices to be updated
|
225
227
|
"""
|
226
|
-
from_image_key =
|
227
|
-
to_image_key =
|
228
|
+
from_image_key = ImageKey.from_value(from_image_key)
|
229
|
+
to_image_key = ImageKey.from_value(to_image_key)
|
228
230
|
|
229
231
|
config = {}
|
230
232
|
for i_frame, frame in enumerate(scan.frames):
|
File without changes
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import numpy
|
2
|
+
from tomwer.core.process.task import Task
|
3
|
+
from tomwer.core.scan.scanbase import TomwerScanBase
|
4
|
+
from tomwer.core.utils.dictutils import concatenate_dict
|
5
|
+
from tomwer.core.process.icat.screenshots import IcatScreenshots
|
6
|
+
from tomwer.core.process.icat.gallery import deduce_dataset_gallery_location
|
7
|
+
|
8
|
+
from processview.core.manager import DatasetState, ProcessManager
|
9
|
+
|
10
|
+
|
11
|
+
def get_closest_projection(angle, angles_list):
|
12
|
+
idx_closest = numpy.argmin(numpy.abs(angles_list - angle))
|
13
|
+
return angles_list[idx_closest]
|
14
|
+
|
15
|
+
|
16
|
+
def select_angles(angles_list: tuple, each_angle: int) -> tuple:
|
17
|
+
angles_list = sorted(angles_list)
|
18
|
+
if len(angles_list) > 0:
|
19
|
+
start_angle = angles_list[0]
|
20
|
+
stop_angle = angles_list[-1]
|
21
|
+
picked_angle = numpy.arange(start_angle, stop_angle + 1, step=each_angle)
|
22
|
+
return tuple(
|
23
|
+
[get_closest_projection(angle, angles_list) for angle in picked_angle]
|
24
|
+
)
|
25
|
+
else:
|
26
|
+
return tuple()
|
27
|
+
|
28
|
+
|
29
|
+
class CreateRawDataScreenshotsTask(
|
30
|
+
Task,
|
31
|
+
input_names=("data",), # screenshots as instance of :class:Screenshots
|
32
|
+
optional_input_names=(
|
33
|
+
"__process__",
|
34
|
+
"raw_projections_required",
|
35
|
+
"raw_projections_each",
|
36
|
+
"raw_darks_required",
|
37
|
+
"raw_flats_required",
|
38
|
+
),
|
39
|
+
output_names=("screenshots",),
|
40
|
+
):
|
41
|
+
"""
|
42
|
+
simple task to create screenshots from raw data
|
43
|
+
One raw projection will be picked each 'raw_projections_each' angle (expected in degree)
|
44
|
+
If 'with_flat' then will pick the first flat of each serie
|
45
|
+
If 'with_dark' then will pick the first dark of each serie
|
46
|
+
"""
|
47
|
+
|
48
|
+
def run(self):
|
49
|
+
process = self.get_input_value("__process__", None)
|
50
|
+
scan = self.inputs.data
|
51
|
+
if not isinstance(scan, TomwerScanBase):
|
52
|
+
raise TypeError(
|
53
|
+
f"scan is expected to be an instance of {TomwerScanBase}. Get {type(scan)} instead"
|
54
|
+
)
|
55
|
+
if process is not None:
|
56
|
+
ProcessManager().notify_dataset_state(
|
57
|
+
dataset=scan,
|
58
|
+
process=process(),
|
59
|
+
state=DatasetState.ON_GOING,
|
60
|
+
)
|
61
|
+
|
62
|
+
raw_projections_required = self.get_input_value(
|
63
|
+
"raw_projections_required", True
|
64
|
+
)
|
65
|
+
proj_each = self.get_input_value("raw_projections_each", 90)
|
66
|
+
raw_flats_required = self.get_input_value("raw_flats_required", True)
|
67
|
+
raw_darks_required = self.get_input_value("raw_darks_required", True)
|
68
|
+
|
69
|
+
screenshots_urls = {}
|
70
|
+
# dict with screenshot name as key and DataUrl as value
|
71
|
+
if raw_darks_required and len(scan.darks) > 0:
|
72
|
+
screenshots_urls["dark"] = next(iter(scan.darks.values()))
|
73
|
+
if raw_flats_required and len(scan.flats) > 0:
|
74
|
+
screenshots_urls["flat"] = next(iter(scan.flats.values()))
|
75
|
+
if raw_projections_required:
|
76
|
+
projections = scan.projections_with_angle()
|
77
|
+
picked_angles = select_angles(
|
78
|
+
angles_list=sorted(projections.keys()),
|
79
|
+
each_angle=proj_each,
|
80
|
+
)
|
81
|
+
screenshots_urls = concatenate_dict(
|
82
|
+
screenshots_urls,
|
83
|
+
{
|
84
|
+
f"projection_{angle:.1f}": projections[angle]
|
85
|
+
for angle in picked_angles
|
86
|
+
},
|
87
|
+
)
|
88
|
+
|
89
|
+
self.outputs.screenshots = IcatScreenshots(
|
90
|
+
data_dir=deduce_dataset_gallery_location(scan),
|
91
|
+
screenshots=screenshots_urls,
|
92
|
+
scan=scan,
|
93
|
+
)
|
94
|
+
|
95
|
+
if process is not None:
|
96
|
+
ProcessManager().notify_dataset_state(
|
97
|
+
dataset=scan,
|
98
|
+
process=process(),
|
99
|
+
state=DatasetState.SUCCEED,
|
100
|
+
)
|
@@ -0,0 +1,377 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
from math import floor
|
4
|
+
|
5
|
+
import numpy
|
6
|
+
from nxtomomill.converter.hdf5.utils import PROCESSED_DATA_DIR_NAME, RAW_DATA_DIR_NAME
|
7
|
+
from PIL import Image
|
8
|
+
from silx.io.url import DataUrl
|
9
|
+
from silx.utils.enum import Enum as _Enum
|
10
|
+
from tomoscan.esrf.scan.utils import get_data
|
11
|
+
from tomoscan.esrf.volume.hdf5volume import HDF5Volume
|
12
|
+
from tomoscan.esrf.volume.singleframebase import VolumeSingleFrameBase
|
13
|
+
|
14
|
+
from tomwer.io.utils.raw_and_processed_data import to_processed_data_path
|
15
|
+
from tomwer.core.scan.edfscan import EDFTomoScan
|
16
|
+
from tomwer.core.process.task import Task
|
17
|
+
from tomwer.core.scan.scanbase import TomwerScanBase
|
18
|
+
from tomwer.core.volume.volumebase import TomwerVolumeBase
|
19
|
+
from tomwer.core.process.icat.screenshots import IcatScreenshots
|
20
|
+
from tomwer.core.process.icat.publish import (
|
21
|
+
PublishProcessedDataFolderTask,
|
22
|
+
from_bliss_original_file_to_raw,
|
23
|
+
)
|
24
|
+
from processview.core.manager import DatasetState, ProcessManager
|
25
|
+
|
26
|
+
_logger = logging.getLogger(__name__)
|
27
|
+
|
28
|
+
PROPOSAL_GALLERY_DIR_NAME = "GALLERY"
|
29
|
+
DATASET_GALLERY_DIR_NAME = "gallery"
|
30
|
+
|
31
|
+
|
32
|
+
class OutputFormat(_Enum):
|
33
|
+
"""possible output format to save screenshots"""
|
34
|
+
|
35
|
+
PNG = "png"
|
36
|
+
JPEG = "jpg"
|
37
|
+
|
38
|
+
|
39
|
+
class Binning(_Enum):
|
40
|
+
ONE_BY_ONE = "1x1"
|
41
|
+
TWO_BY_TWO = "2x2"
|
42
|
+
FOUR_BY_FOUR = "4x4"
|
43
|
+
HEIGHT_BY_HEIGHT = "8x8"
|
44
|
+
SIXTEEN_BY_SIXTEEN = "16x16"
|
45
|
+
THIRTY_TWO_BY_THIRTY_TWO = "32x32"
|
46
|
+
SIXTY_FOUR_BY_SIXTY_FOUR = "64x64"
|
47
|
+
ONE_HUNDRED_TWENTY_HEIGHT_BY_ONE_HUNDRED_TWENTY_HEIGHT = "128x128"
|
48
|
+
|
49
|
+
@staticmethod
|
50
|
+
def _bin_data(data, binning):
|
51
|
+
if not isinstance(data, numpy.ndarray):
|
52
|
+
raise TypeError("data should be a numpy array")
|
53
|
+
if not data.ndim == 2:
|
54
|
+
raise ValueError("data is expected to be 2d")
|
55
|
+
binning = Binning.from_value(binning)
|
56
|
+
if binning is Binning.ONE_BY_ONE:
|
57
|
+
return data
|
58
|
+
elif binning is Binning.TWO_BY_TWO:
|
59
|
+
return data[::2, ::2]
|
60
|
+
elif binning is Binning.FOUR_BY_FOUR:
|
61
|
+
return data[::4, ::4]
|
62
|
+
elif binning is Binning.HEIGHT_BY_HEIGHT:
|
63
|
+
return data[::8, ::8]
|
64
|
+
elif binning is Binning.SIXTEEN_BY_SIXTEEN:
|
65
|
+
return data[::16, ::16]
|
66
|
+
elif binning is Binning.THIRTY_TWO_BY_THIRTY_TWO:
|
67
|
+
return data[::32, ::32]
|
68
|
+
elif binning is Binning.SIXTY_FOUR_BY_SIXTY_FOUR:
|
69
|
+
return data[::64, ::64]
|
70
|
+
else:
|
71
|
+
raise NotImplementedError
|
72
|
+
|
73
|
+
|
74
|
+
def deduce_dataset_gallery_location(scan_obj: TomwerScanBase) -> str:
|
75
|
+
"""
|
76
|
+
From scan path deduce the 'dataset' path to the gallery.
|
77
|
+
Warning: dataset gallery is different then the 'proposal' gallery
|
78
|
+
"""
|
79
|
+
if not isinstance(scan_obj, TomwerScanBase):
|
80
|
+
raise TypeError(f"'scan_obj' is expected to be an instance of {TomwerScanBase}")
|
81
|
+
|
82
|
+
file_path = os.path.abspath(scan_obj.path)
|
83
|
+
|
84
|
+
split_path = file_path.split(os.sep)
|
85
|
+
# reverse it to find the lower level value of 'PROCESSED_DATA_DIR_NAME' or 'RAW_DATA_DIR_NAME' if by any 'chance' has several in the path
|
86
|
+
# then we will replace the 'lower one' in the string. This is where the GALLERY will be added
|
87
|
+
split_path = split_path[::-1]
|
88
|
+
# check if already contain in a "PROCESSED_DATA" directory
|
89
|
+
try:
|
90
|
+
index_processed_data = split_path.index(PROCESSED_DATA_DIR_NAME)
|
91
|
+
except ValueError:
|
92
|
+
pass
|
93
|
+
index_processed_data = None
|
94
|
+
try:
|
95
|
+
index_raw_data = split_path.index(RAW_DATA_DIR_NAME)
|
96
|
+
except ValueError:
|
97
|
+
# if the value is not in the list
|
98
|
+
index_raw_data = None
|
99
|
+
|
100
|
+
if index_processed_data is None and index_raw_data is None:
|
101
|
+
# if not in any "PROCESSED_DATA" or "RAW_DATA" directory
|
102
|
+
return scan_obj.get_relative_file(
|
103
|
+
file_name=DATASET_GALLERY_DIR_NAME, with_dataset_prefix=False
|
104
|
+
)
|
105
|
+
elif index_processed_data is not None and index_raw_data is not None:
|
106
|
+
if index_raw_data > index_processed_data:
|
107
|
+
# if PROCESSED_DATA lower in the path than RAW_DATA
|
108
|
+
split_path[index_processed_data] = RAW_DATA_DIR_NAME
|
109
|
+
# reorder path to original
|
110
|
+
split_path = list(split_path[::-1])
|
111
|
+
split_path.append(DATASET_GALLERY_DIR_NAME)
|
112
|
+
# move it to PPROCESSED_DATA when possible
|
113
|
+
path = os.sep.join(split_path)
|
114
|
+
path = to_processed_data_path(path)
|
115
|
+
return path
|
116
|
+
|
117
|
+
|
118
|
+
def deduce_proposal_GALLERY_location(scan_obj: TomwerScanBase) -> str:
|
119
|
+
"""
|
120
|
+
Policy: look if the scan_obj.path is in 'PROCESSED_DATA_DIR_NAME' or 'RAW_DATA_DIR_NAME' directories.
|
121
|
+
If find any (before any 'GALLERY_DIR_NAME' directory) replace it "GALLERY_DIR_NAME".
|
122
|
+
If none of those are found then create it at the same level as the scan
|
123
|
+
|
124
|
+
:param TomwerScanBase scan_obj: scan_obj for which we want the GALLERY directory
|
125
|
+
:return: gallery path (to save screeshots for example)
|
126
|
+
"""
|
127
|
+
if not isinstance(scan_obj, TomwerScanBase):
|
128
|
+
raise TypeError(f"'scan_obj' is expected to be an instance of {TomwerScanBase}")
|
129
|
+
|
130
|
+
file_path = os.path.abspath(scan_obj.path)
|
131
|
+
|
132
|
+
split_path = file_path.split(os.sep)
|
133
|
+
# reverse it to find the lower level value of 'PROCESSED_DATA_DIR_NAME' or 'RAW_DATA_DIR_NAME' if by any 'chance' has several in the path
|
134
|
+
# then we will replace the 'lower one' in the string. This is where the GALLERY will be added
|
135
|
+
split_path = split_path[::-1]
|
136
|
+
# check if already contain in a "PROCESSED_DATA" directory
|
137
|
+
try:
|
138
|
+
index_processed_data = split_path.index(PROCESSED_DATA_DIR_NAME)
|
139
|
+
except ValueError:
|
140
|
+
pass
|
141
|
+
index_processed_data = None
|
142
|
+
try:
|
143
|
+
index_raw_data = split_path.index(RAW_DATA_DIR_NAME)
|
144
|
+
except ValueError:
|
145
|
+
# if the value is not in the list
|
146
|
+
index_raw_data = None
|
147
|
+
|
148
|
+
if index_processed_data is None and index_raw_data is None:
|
149
|
+
# if not in any "PROCESSED_DATA" or "RAW_DATA" directory
|
150
|
+
return scan_obj.get_relative_file(
|
151
|
+
file_name=PROPOSAL_GALLERY_DIR_NAME, with_dataset_prefix=False
|
152
|
+
)
|
153
|
+
elif index_processed_data is not None and index_raw_data is not None:
|
154
|
+
if index_raw_data > index_processed_data:
|
155
|
+
# if PROCESSED_DATA lower in the path than RAW_DATA
|
156
|
+
split_path[index_processed_data] = PROPOSAL_GALLERY_DIR_NAME
|
157
|
+
else:
|
158
|
+
# if RAW_DATA lower in the path than PROCESSED_DATA
|
159
|
+
split_path[index_raw_data] = PROPOSAL_GALLERY_DIR_NAME
|
160
|
+
elif index_raw_data is not None:
|
161
|
+
# if the path contains only PROCESSED_DATA or RAW_DATA (expected behavior for online acquistion)
|
162
|
+
split_path[index_raw_data] = PROPOSAL_GALLERY_DIR_NAME
|
163
|
+
else:
|
164
|
+
assert index_processed_data is not None, "index_processed_data is None"
|
165
|
+
split_path[index_processed_data] = PROPOSAL_GALLERY_DIR_NAME
|
166
|
+
|
167
|
+
# reorder path to original
|
168
|
+
split_path = split_path[::-1]
|
169
|
+
return os.sep.join(split_path)
|
170
|
+
|
171
|
+
|
172
|
+
def select_screenshot_from_volume(volume: TomwerVolumeBase) -> dict:
|
173
|
+
"""
|
174
|
+
return a subset of url for a volume.
|
175
|
+
Warning: this function will be called each time a nabu slice or a nabu volume is executer from orangecontrig.
|
176
|
+
So it must stay 'low processing' function to avoid slowing down everything
|
177
|
+
"""
|
178
|
+
if not isinstance(volume, TomwerVolumeBase):
|
179
|
+
raise TypeError(
|
180
|
+
f"volume is expected to be an instance of {TomwerVolumeBase}. Get {type(volume)}"
|
181
|
+
)
|
182
|
+
|
183
|
+
if isinstance(volume, VolumeSingleFrameBase):
|
184
|
+
volume_urls = tuple(volume.browse_data_urls())
|
185
|
+
# lets take the three equally spaced slices (3/6, 4/6 and 5/6)
|
186
|
+
n_slices = len(volume_urls)
|
187
|
+
# note: using a dict ensure to get a set of DataUrl in case an Url is used several time
|
188
|
+
# workaround: DataUrl is not hashable
|
189
|
+
screenshots = {
|
190
|
+
f"{os.path.splitext(os.path.basename(volume_urls[floor(i * n_slices / 6)].file_path()))}": volume_urls[
|
191
|
+
floor(i * n_slices / 6)
|
192
|
+
]
|
193
|
+
for i in range(2, 5)
|
194
|
+
}
|
195
|
+
return screenshots
|
196
|
+
elif isinstance(volume, HDF5Volume):
|
197
|
+
volume_url = next(volume.browse_data_urls())
|
198
|
+
n_slices = volume.get_volume_shape()[0]
|
199
|
+
# note: using a dict ensure to get a set of DataUrl in case an Url is used several time
|
200
|
+
# workaround: DataUrl is not hashable
|
201
|
+
screenshots = {
|
202
|
+
f"{os.path.splitext(os.path.basename(volume_url.file_path()))[0]}_{floor(i / 6 * n_slices)}": DataUrl(
|
203
|
+
file_path=volume_url.file_path(),
|
204
|
+
data_path=volume_url.data_path(),
|
205
|
+
scheme="silx",
|
206
|
+
data_slice=floor(i / 6 * n_slices),
|
207
|
+
)
|
208
|
+
for i in range(2, 5)
|
209
|
+
}
|
210
|
+
return screenshots
|
211
|
+
else:
|
212
|
+
_logger.warning(
|
213
|
+
f"volume {type(volume)} does not allow to create screenshot for now"
|
214
|
+
)
|
215
|
+
return ()
|
216
|
+
|
217
|
+
|
218
|
+
class SaveScreenshotsToGalleryTask(
|
219
|
+
Task,
|
220
|
+
input_names=("screenshots",), # screenshots as instance of :class:Screenshots
|
221
|
+
optional_input_names=("format", "overwrite", "binning"),
|
222
|
+
):
|
223
|
+
"""simple task to do the binding between orange design and 'SaveScreenshotsTask'"""
|
224
|
+
|
225
|
+
def run(self):
|
226
|
+
if not isinstance(self.inputs.screenshots, IcatScreenshots):
|
227
|
+
raise TypeError(
|
228
|
+
f"'screenshots' is expected to be an instance of {IcatScreenshots}. get {type(self.inputs.screenshots)}"
|
229
|
+
)
|
230
|
+
|
231
|
+
inputs = self.inputs.screenshots.to_dict()
|
232
|
+
if self.get_input_value("overwrite", None) is not None:
|
233
|
+
inputs["overwrite"] = self.inputs.overwrite
|
234
|
+
if self.get_input_value("format", None) is not None:
|
235
|
+
inputs["format"] = self.inputs.format
|
236
|
+
if self.get_input_value("binning", None) is not None:
|
237
|
+
inputs["binning"] = self.inputs.binning
|
238
|
+
task = SaveScreenshotsTask(
|
239
|
+
inputs=inputs,
|
240
|
+
)
|
241
|
+
task.run()
|
242
|
+
|
243
|
+
|
244
|
+
class SaveScreenshotsTask(
|
245
|
+
Task,
|
246
|
+
input_names=("screenshots_as_dict", "output_dir"),
|
247
|
+
optional_input_names=("format", "overwrite", "binning"),
|
248
|
+
):
|
249
|
+
"""Task which save a set of screenshot to the output_dir under required format"""
|
250
|
+
|
251
|
+
def run(self):
|
252
|
+
format = OutputFormat.from_value(
|
253
|
+
self.get_input_value("format", OutputFormat.PNG)
|
254
|
+
)
|
255
|
+
overwrite = self.get_input_value("overwrite", False)
|
256
|
+
binning = self.get_input_value("binning", Binning.ONE_BY_ONE)
|
257
|
+
|
258
|
+
os.makedirs(self.inputs.output_dir, exist_ok=True)
|
259
|
+
for screenshot_name, data_url in self.inputs.screenshots_as_dict.items():
|
260
|
+
if not isinstance(data_url, DataUrl):
|
261
|
+
raise TypeError("screenshot values are expected to DataUrl")
|
262
|
+
data = get_data(data_url)
|
263
|
+
if not isinstance(data, numpy.ndarray) and data.ndim != 2:
|
264
|
+
raise TypeError("screenshot are expected to be 2D numpy arrays")
|
265
|
+
if not isinstance(screenshot_name, str):
|
266
|
+
raise TypeError(
|
267
|
+
f"screenshot keys are expected to be str. Get {type(screenshot_name)}"
|
268
|
+
)
|
269
|
+
|
270
|
+
if data.ndim == 3 and data.shape[0] == 1:
|
271
|
+
data = data.reshape(data.shape[1:])
|
272
|
+
elif data.ndim != 2:
|
273
|
+
raise ValueError(
|
274
|
+
f"only 2D grayscale image are handled. Get {data.shape}"
|
275
|
+
)
|
276
|
+
# if qt is available use it as this is simpler and more powerful
|
277
|
+
# clamp data in 0-256
|
278
|
+
data = data.astype(numpy.float32)
|
279
|
+
data = Binning._bin_data(data=data, binning=binning)
|
280
|
+
data *= 255.0 / data.max()
|
281
|
+
|
282
|
+
# do a rescale else use qt instead
|
283
|
+
img = Image.fromarray(data, mode=None)
|
284
|
+
img = img.convert("L")
|
285
|
+
output_file = os.path.join(
|
286
|
+
self.inputs.output_dir,
|
287
|
+
".".join([screenshot_name, format.value]),
|
288
|
+
)
|
289
|
+
if not overwrite and os.path.exists(output_file):
|
290
|
+
raise OSError(f"File already exists ({output_file})")
|
291
|
+
img.save(output_file)
|
292
|
+
|
293
|
+
|
294
|
+
class SaveToGalleryAndPublishTask(
|
295
|
+
Task,
|
296
|
+
input_names=(
|
297
|
+
"screenshots",
|
298
|
+
"beamline",
|
299
|
+
"dataset",
|
300
|
+
"proposal",
|
301
|
+
"format",
|
302
|
+
),
|
303
|
+
optional_input_names=(
|
304
|
+
"dry_run",
|
305
|
+
"__process__",
|
306
|
+
),
|
307
|
+
):
|
308
|
+
def __init__(
|
309
|
+
self, varinfo=None, inputs=None, node_id=None, node_attrs=None, execinfo=None
|
310
|
+
):
|
311
|
+
super().__init__(varinfo, inputs, node_id, node_attrs, execinfo)
|
312
|
+
self._process = self.get_input_value("__process__", None)
|
313
|
+
self._task_save = SaveScreenshotsToGalleryTask(
|
314
|
+
inputs=inputs,
|
315
|
+
)
|
316
|
+
|
317
|
+
scan = self.inputs.screenshots.scan
|
318
|
+
if isinstance(scan, EDFTomoScan):
|
319
|
+
raise TypeError(
|
320
|
+
"scan is an EDFTomoScan. Not handled for publishing processed data to icat"
|
321
|
+
)
|
322
|
+
raw = from_bliss_original_file_to_raw(scan.get_bliss_orginal_files())
|
323
|
+
|
324
|
+
path = self.inputs.screenshots.data_dir
|
325
|
+
# path = os.path.dirname(self.inputs.screenshots.data_dir)
|
326
|
+
|
327
|
+
self._task_publish = PublishProcessedDataFolderTask(
|
328
|
+
inputs={
|
329
|
+
"beamline": self.inputs.beamline,
|
330
|
+
"dataset": self.inputs.dataset,
|
331
|
+
"proposal": self.inputs.proposal,
|
332
|
+
"path": path, # must be the dataset path
|
333
|
+
"raw": raw,
|
334
|
+
"dry_run": self.get_input_value("dry_run", False),
|
335
|
+
"metadata": self.inputs.screenshots.metadata,
|
336
|
+
},
|
337
|
+
)
|
338
|
+
|
339
|
+
def run(self):
|
340
|
+
if self._process is not None:
|
341
|
+
ProcessManager().notify_dataset_state(
|
342
|
+
dataset=self.inputs.screenshots.scan,
|
343
|
+
process=self._process(),
|
344
|
+
state=DatasetState.ON_GOING,
|
345
|
+
)
|
346
|
+
# save screenshots to gallery
|
347
|
+
try:
|
348
|
+
self._task_save.run()
|
349
|
+
except Exception as e:
|
350
|
+
if self._process is not None:
|
351
|
+
ProcessManager().notify_dataset_state(
|
352
|
+
dataset=self.inputs.screenshots.scan,
|
353
|
+
process=self._process(),
|
354
|
+
state=DatasetState.FAILED,
|
355
|
+
details=str(e),
|
356
|
+
)
|
357
|
+
raise e
|
358
|
+
|
359
|
+
# publication
|
360
|
+
try:
|
361
|
+
self._task_publish.run()
|
362
|
+
except Exception as e:
|
363
|
+
if self._process is not None:
|
364
|
+
ProcessManager().notify_dataset_state(
|
365
|
+
dataset=self.inputs.screenshots.scan,
|
366
|
+
process=self._process(),
|
367
|
+
state=DatasetState.FAILED,
|
368
|
+
details=str(e),
|
369
|
+
)
|
370
|
+
raise e
|
371
|
+
else:
|
372
|
+
if self._process is not None:
|
373
|
+
ProcessManager().notify_dataset_state(
|
374
|
+
dataset=self.inputs.screenshots.scan,
|
375
|
+
process=self._process(),
|
376
|
+
state=DatasetState.SUCCEED,
|
377
|
+
)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import weakref
|
2
|
+
from tomwer.core.scan.scanbase import TomwerScanBase
|
3
|
+
|
4
|
+
|
5
|
+
class IcatProcessedDataBase:
|
6
|
+
"""
|
7
|
+
Container class to get all the information about processed data to be publish
|
8
|
+
"""
|
9
|
+
|
10
|
+
def __init__(self, data_dir: str, scan: TomwerScanBase) -> None:
|
11
|
+
self._output_dir = data_dir
|
12
|
+
# output dir for the screenshots. The default is expected to be provided by the task creating the screenshot.
|
13
|
+
# it can be overwrite by the 'SaveScreenshotTask' if an output dir is provided
|
14
|
+
self._scan = weakref.ref(scan) if scan is not None else scan
|
15
|
+
|
16
|
+
@property
|
17
|
+
def data_dir(self):
|
18
|
+
return self._output_dir
|
19
|
+
|
20
|
+
@property
|
21
|
+
def scan(self):
|
22
|
+
return self._scan() if self._scan is not None else None
|
23
|
+
|
24
|
+
@property
|
25
|
+
def metadata(self) -> dict:
|
26
|
+
scan = self.scan
|
27
|
+
if scan is not None:
|
28
|
+
return scan.build_icat_metadata()
|
29
|
+
else:
|
30
|
+
return {}
|
31
|
+
|
32
|
+
def to_dict(self):
|
33
|
+
return {
|
34
|
+
"output_dir": self.data_dir,
|
35
|
+
"icat_metadata": self.metadata,
|
36
|
+
}
|