python-microscopy 25.6.5__cp39-cp39-win_amd64.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.
- PYME/Acquire/ActionManager.py +450 -0
- PYME/Acquire/ExecTools.py +235 -0
- PYME/Acquire/Hardware/AAOptoelectronics/MDS.py +175 -0
- PYME/Acquire/Hardware/AAOptoelectronics/__init__.py +0 -0
- PYME/Acquire/Hardware/ARCoptix/__init__.py +0 -0
- PYME/Acquire/Hardware/ARCoptix/lcdriver.py +149 -0
- PYME/Acquire/Hardware/ARCoptix/lcserver32.py +123 -0
- PYME/Acquire/Hardware/AndorIXon/AndorCam.py +1095 -0
- PYME/Acquire/Hardware/AndorIXon/AndorControlFrame.py +312 -0
- PYME/Acquire/Hardware/AndorIXon/AndorIXon.py +737 -0
- PYME/Acquire/Hardware/AndorIXon/__init__.py +27 -0
- PYME/Acquire/Hardware/AndorNeo/AndorNeo.py +644 -0
- PYME/Acquire/Hardware/AndorNeo/AndorNeoControlFrame.py +161 -0
- PYME/Acquire/Hardware/AndorNeo/AndorZyla.py +761 -0
- PYME/Acquire/Hardware/AndorNeo/SDK3.py +307 -0
- PYME/Acquire/Hardware/AndorNeo/SDK3Cam.py +171 -0
- PYME/Acquire/Hardware/AndorNeo/ZylaControlPanel.py +105 -0
- PYME/Acquire/Hardware/AndorNeo/__init__.py +22 -0
- PYME/Acquire/Hardware/AndorNeo/parseAtdebug.py +46 -0
- PYME/Acquire/Hardware/AndorNeo/plotTimings.py +73 -0
- PYME/Acquire/Hardware/AndorNeo/testNeo.py +62 -0
- PYME/Acquire/Hardware/Camera.py +1199 -0
- PYME/Acquire/Hardware/CameraSkeleton.py +642 -0
- PYME/Acquire/Hardware/Coherent/OBIS.py +147 -0
- PYME/Acquire/Hardware/Coherent/Sapphire.py +162 -0
- PYME/Acquire/Hardware/Coherent/__init__.py +0 -0
- PYME/Acquire/Hardware/DMDGui.py +198 -0
- PYME/Acquire/Hardware/DigiData/DigiData.py +95 -0
- PYME/Acquire/Hardware/DigiData/DigiDataClient.py +30 -0
- PYME/Acquire/Hardware/DigiData/RemoteDigiData.py +51 -0
- PYME/Acquire/Hardware/DigiData/__init__.py +24 -0
- PYME/Acquire/Hardware/DigiData/axDD132x.py +348 -0
- PYME/Acquire/Hardware/EMCCDTheory.py +41 -0
- PYME/Acquire/Hardware/ExciterWheel.py +167 -0
- PYME/Acquire/Hardware/FilterWheel.py +184 -0
- PYME/Acquire/Hardware/FocCorr.py +106 -0
- PYME/Acquire/Hardware/FocCorrR.py +196 -0
- PYME/Acquire/Hardware/FrFilter.py +51 -0
- PYME/Acquire/Hardware/GCS/GCS_DLL.py +370 -0
- PYME/Acquire/Hardware/GCS/__init__.py +0 -0
- PYME/Acquire/Hardware/GCS/gcs.py +210 -0
- PYME/Acquire/Hardware/HamamatsuDCAM/HamamatsuDCAM.py +662 -0
- PYME/Acquire/Hardware/HamamatsuDCAM/HamamatsuORCA.py +602 -0
- PYME/Acquire/Hardware/HamamatsuDCAM/Hamamatsu_control_panel.py +54 -0
- PYME/Acquire/Hardware/HamamatsuDCAM/__init__.py +23 -0
- PYME/Acquire/Hardware/LaserControlFrame.py +160 -0
- PYME/Acquire/Hardware/MPBCommunications/MPBCW.py +132 -0
- PYME/Acquire/Hardware/MPBCommunications/__init__.py +0 -0
- PYME/Acquire/Hardware/Mercury/Mercury.py +269 -0
- PYME/Acquire/Hardware/Mercury/PI_Mercury_GCS_DLL.py +857 -0
- PYME/Acquire/Hardware/Mercury/__init__.py +22 -0
- PYME/Acquire/Hardware/Mercury/mercuryStepper.py +203 -0
- PYME/Acquire/Hardware/Mercury/mercuryStepperGCS.py +281 -0
- PYME/Acquire/Hardware/NikonTE2000.py +68 -0
- PYME/Acquire/Hardware/NikonTi.py +214 -0
- PYME/Acquire/Hardware/NikonTiGUI.py +81 -0
- PYME/Acquire/Hardware/OrielCornerstone.py +125 -0
- PYME/Acquire/Hardware/PM100USB.py +51 -0
- PYME/Acquire/Hardware/Piezos/__init__.py +22 -0
- PYME/Acquire/Hardware/Piezos/base_piezo.py +108 -0
- PYME/Acquire/Hardware/Piezos/offsetPiezo.py +162 -0
- PYME/Acquire/Hardware/Piezos/offsetPiezoREST.py +308 -0
- PYME/Acquire/Hardware/Piezos/piezo_c867.py +486 -0
- PYME/Acquire/Hardware/Piezos/piezo_e255.py +68 -0
- PYME/Acquire/Hardware/Piezos/piezo_e662.py +61 -0
- PYME/Acquire/Hardware/Piezos/piezo_e709.py +434 -0
- PYME/Acquire/Hardware/Piezos/piezo_e816.py +452 -0
- PYME/Acquire/Hardware/Piezos/piezo_e816_corr.py +70 -0
- PYME/Acquire/Hardware/Piezos/piezo_e816_dll.py +518 -0
- PYME/Acquire/Hardware/Piezos/piezo_e816b.py +70 -0
- PYME/Acquire/Hardware/Piezos/piezo_pipython_gcs.py +560 -0
- PYME/Acquire/Hardware/Piezos/piezo_test.py +55 -0
- PYME/Acquire/Hardware/Simulator/EmpiricalHist.py +273 -0
- PYME/Acquire/Hardware/Simulator/__init__.py +22 -0
- PYME/Acquire/Hardware/Simulator/dSimControl.py +961 -0
- PYME/Acquire/Hardware/Simulator/fakeCam.py +651 -0
- PYME/Acquire/Hardware/Simulator/fakePiezo.py +95 -0
- PYME/Acquire/Hardware/Simulator/fluor.py +336 -0
- PYME/Acquire/Hardware/Simulator/illuminate.cp39-win_amd64.dll.a +0 -0
- PYME/Acquire/Hardware/Simulator/illuminate.cp39-win_amd64.pyd +0 -0
- PYME/Acquire/Hardware/Simulator/illuminate.pyx +93 -0
- PYME/Acquire/Hardware/Simulator/lasersliders.py +90 -0
- PYME/Acquire/Hardware/Simulator/rend_im.py +446 -0
- PYME/Acquire/Hardware/Simulator/setup.py +69 -0
- PYME/Acquire/Hardware/Simulator/simcontrol.py +348 -0
- PYME/Acquire/Hardware/Simulator/simui_wx.py +794 -0
- PYME/Acquire/Hardware/Tango/__init__.py +0 -0
- PYME/Acquire/Hardware/Tango/marzhauser_tango.py +583 -0
- PYME/Acquire/Hardware/TiLightCrafter.py +388 -0
- PYME/Acquire/Hardware/__init__.py +22 -0
- PYME/Acquire/Hardware/aotf.py +271 -0
- PYME/Acquire/Hardware/arclampshutterpanel.py +71 -0
- PYME/Acquire/Hardware/cameraSoftwareBuffer.py +215 -0
- PYME/Acquire/Hardware/camera_noise.py +270 -0
- PYME/Acquire/Hardware/ccdAdjPanel.py +188 -0
- PYME/Acquire/Hardware/ccdCalibrator.py +161 -0
- PYME/Acquire/Hardware/cobaltLaser.py +108 -0
- PYME/Acquire/Hardware/cobaltLaser561.py +94 -0
- PYME/Acquire/Hardware/comports.py +26 -0
- PYME/Acquire/Hardware/driftTrackGUI.py +488 -0
- PYME/Acquire/Hardware/driftTracking.py +595 -0
- PYME/Acquire/Hardware/fakeShutters.py +52 -0
- PYME/Acquire/Hardware/focusKeys.py +204 -0
- PYME/Acquire/Hardware/focus_locks/__init__.py +0 -0
- PYME/Acquire/Hardware/focus_locks/reflection_focus_lock.py +684 -0
- PYME/Acquire/Hardware/frZStage.py +117 -0
- PYME/Acquire/Hardware/fw102.py +38 -0
- PYME/Acquire/Hardware/ids_peak_cam.py +561 -0
- PYME/Acquire/Hardware/ioslave.py +305 -0
- PYME/Acquire/Hardware/lasers.py +292 -0
- PYME/Acquire/Hardware/matchboxLaser.py +58 -0
- PYME/Acquire/Hardware/microscope_adapter.py +104 -0
- PYME/Acquire/Hardware/mpd_picosecond_delayer.py +395 -0
- PYME/Acquire/Hardware/multiview.py +287 -0
- PYME/Acquire/Hardware/olympusix81.py +59 -0
- PYME/Acquire/Hardware/pco/__init__.py +0 -0
- PYME/Acquire/Hardware/pco/pco_cam.py +348 -0
- PYME/Acquire/Hardware/pco/pco_edge_42_lt.py +34 -0
- PYME/Acquire/Hardware/pco/pco_sdk.py +1645 -0
- PYME/Acquire/Hardware/pco/pco_sdk_cam.py +518 -0
- PYME/Acquire/Hardware/pco/pco_sdk_cam_control_panel.py +54 -0
- PYME/Acquire/Hardware/phoxxLaser.py +199 -0
- PYME/Acquire/Hardware/phoxxLaserOLD.py +242 -0
- PYME/Acquire/Hardware/priorLumen.py +102 -0
- PYME/Acquire/Hardware/priorarclampshutter.py +52 -0
- PYME/Acquire/Hardware/setup.py +50 -0
- PYME/Acquire/Hardware/spacenav.py +148 -0
- PYME/Acquire/Hardware/splitter.py +394 -0
- PYME/Acquire/Hardware/thorlabsPiezo.py +98 -0
- PYME/Acquire/Hardware/thorlabs_cam.py +136 -0
- PYME/Acquire/Hardware/thorlabs_mff_flipper.py +400 -0
- PYME/Acquire/Hardware/toptica_ibeam.py +51 -0
- PYME/Acquire/Hardware/uc480/AndorControlFrame.py +308 -0
- PYME/Acquire/Hardware/uc480/__init__.py +27 -0
- PYME/Acquire/Hardware/uc480/uCam480.py +695 -0
- PYME/Acquire/Hardware/uc480/uc480.py +189 -0
- PYME/Acquire/Hardware/uc480/uc480Deprecated.py +87 -0
- PYME/Acquire/Hardware/uc480/uc480_h.py +767 -0
- PYME/Acquire/Hardware/uc480/uc480_h_gen.py +62 -0
- PYME/Acquire/Hardware/uc480/ucCamControlFrame.py +83 -0
- PYME/Acquire/Hardware/ueye.py +702 -0
- PYME/Acquire/PYMEAcquire.py +186 -0
- PYME/Acquire/Protocols/DMDMFM.py +74 -0
- PYME/Acquire/Protocols/__init__.py +22 -0
- PYME/Acquire/Protocols/darkCalibrate.py +56 -0
- PYME/Acquire/Protocols/dual671_470.py +45 -0
- PYME/Acquire/Protocols/dual671_488.py +46 -0
- PYME/Acquire/Protocols/gainCal.py +53 -0
- PYME/Acquire/Protocols/htsms-cal-psf.py +32 -0
- PYME/Acquire/Protocols/htsms-cal-registration.py +52 -0
- PYME/Acquire/Protocols/htsms-flow.py +29 -0
- PYME/Acquire/Protocols/htsms-staggered.py +31 -0
- PYME/Acquire/Protocols/htsms-tile.py +68 -0
- PYME/Acquire/Protocols/htsms-two-color.py +29 -0
- PYME/Acquire/Protocols/htsms-widefield.py +32 -0
- PYME/Acquire/Protocols/paint671.py +61 -0
- PYME/Acquire/Protocols/photoconversion.py +52 -0
- PYME/Acquire/Protocols/photoconversion2.py +55 -0
- PYME/Acquire/Protocols/prebleach488.py +70 -0
- PYME/Acquire/Protocols/prebleach490.py +69 -0
- PYME/Acquire/Protocols/prebleach561.py +72 -0
- PYME/Acquire/Protocols/prebleach561NeomEos2.py +70 -0
- PYME/Acquire/Protocols/prebleach561NeomEos2ND1.py +70 -0
- PYME/Acquire/Protocols/prebleach642.py +70 -0
- PYME/Acquire/Protocols/prebleach671.py +69 -0
- PYME/Acquire/Protocols/prebleach671ND1.py +58 -0
- PYME/Acquire/Protocols/prebleach671ND2.py +57 -0
- PYME/Acquire/Protocols/prebleach671Neo.py +69 -0
- PYME/Acquire/Protocols/recover671.py +62 -0
- PYME/Acquire/Protocols/sequence642_488.py +85 -0
- PYME/Acquire/Protocols/sequence642_561.py +87 -0
- PYME/Acquire/Protocols/shiftfield.py +198 -0
- PYME/Acquire/Protocols/simul488.py +59 -0
- PYME/Acquire/Protocols/simul642.py +63 -0
- PYME/Acquire/Protocols/simul642HTSMS.py +63 -0
- PYME/Acquire/Protocols/simulPA.py +37 -0
- PYME/Acquire/Protocols/simulSIM.py +76 -0
- PYME/Acquire/Protocols/simulStep.py +62 -0
- PYME/Acquire/Protocols/spdscan.py +178 -0
- PYME/Acquire/Protocols/standard470.py +45 -0
- PYME/Acquire/Protocols/standard488.py +47 -0
- PYME/Acquire/Protocols/standard671.py +48 -0
- PYME/Acquire/Protocols/standard671_no532.py +47 -0
- PYME/Acquire/Protocols/standardArclamp.py +46 -0
- PYME/Acquire/Protocols/strip_tile.py +204 -0
- PYME/Acquire/Protocols/tile.py +204 -0
- PYME/Acquire/Protocols/tile3D.py +192 -0
- PYME/Acquire/Protocols/tile671.py +190 -0
- PYME/Acquire/Protocols/tile_triggered.py +189 -0
- PYME/Acquire/Scripts/init.py +232 -0
- PYME/Acquire/Scripts/init_N1_Ti_Exeter_ZIx.py +374 -0
- PYME/Acquire/Scripts/init_N2_Ti_Zyla_lasers.py +290 -0
- PYME/Acquire/Scripts/init_Neo.py +266 -0
- PYME/Acquire/Scripts/init_NeoSim.py +159 -0
- PYME/Acquire/Scripts/init_TIRF.py +232 -0
- PYME/Acquire/Scripts/init_TIRF_Neo.py +242 -0
- PYME/Acquire/Scripts/init_TIRF_NeoO.py +241 -0
- PYME/Acquire/Scripts/init_TIRF_onecam.py +233 -0
- PYME/Acquire/Scripts/init_Ti.py +257 -0
- PYME/Acquire/Scripts/init_UOA_n.py +256 -0
- PYME/Acquire/Scripts/init_UOA_n2.py +246 -0
- PYME/Acquire/Scripts/init_Y1.py +249 -0
- PYME/Acquire/Scripts/init_Zyla.py +260 -0
- PYME/Acquire/Scripts/init_drift_tracking.py +80 -0
- PYME/Acquire/Scripts/init_emccd_basic.py +277 -0
- PYME/Acquire/Scripts/init_htsms.py +277 -0
- PYME/Acquire/Scripts/init_htsms_focus_lock.py +132 -0
- PYME/Acquire/Scripts/init_micrpi.py +45 -0
- PYME/Acquire/Scripts/init_orca.py +70 -0
- PYME/Acquire/Scripts/init_pco.py +49 -0
- PYME/Acquire/Scripts/init_rev.py +74 -0
- PYME/Acquire/Scripts/init_sim100.py +169 -0
- PYME/Acquire/Scripts/init_sim103.py +169 -0
- PYME/Acquire/Scripts/init_sim2.py +211 -0
- PYME/Acquire/Scripts/init_sim50.py +169 -0
- PYME/Acquire/Scripts/init_sim75.py +169 -0
- PYME/Acquire/Scripts/init_sim_drift_tracking.py +166 -0
- PYME/Acquire/Scripts/init_sim_htsms.py +224 -0
- PYME/Acquire/Scripts/init_sim_htsms_n.py +233 -0
- PYME/Acquire/Scripts/init_sim_main.py +229 -0
- PYME/Acquire/Scripts/init_sim_min.py +197 -0
- PYME/Acquire/Scripts/init_sim_rem.py +176 -0
- PYME/Acquire/Scripts/init_smi1.py +110 -0
- PYME/Acquire/Scripts/init_spectro.py +78 -0
- PYME/Acquire/Scripts/init_spim.py +116 -0
- PYME/Acquire/Scripts/init_thorcam.py +62 -0
- PYME/Acquire/Scripts/init_twophoton.py +112 -0
- PYME/Acquire/Scripts/init_uc480.py +262 -0
- PYME/Acquire/Scripts/init_ueye.py +261 -0
- PYME/Acquire/SpoolController.py +958 -0
- PYME/Acquire/Utils/MultiPointScanner.py +80 -0
- PYME/Acquire/Utils/__init__.py +13 -0
- PYME/Acquire/Utils/failsafe.py +157 -0
- PYME/Acquire/Utils/fastTiler.py +153 -0
- PYME/Acquire/Utils/pointScanner.py +496 -0
- PYME/Acquire/Utils/sarcSpacing.py +86 -0
- PYME/Acquire/Utils/strip_tiler.py +93 -0
- PYME/Acquire/Utils/tiler.py +464 -0
- PYME/Acquire/Utils/vibrationAnalysis.py +65 -0
- PYME/Acquire/__init__.py +44 -0
- PYME/Acquire/acquire_client.py +90 -0
- PYME/Acquire/acquire_server.py +316 -0
- PYME/Acquire/acquirebase.py +139 -0
- PYME/Acquire/acquiremainframe.py +716 -0
- PYME/Acquire/acquirewx.py +61 -0
- PYME/Acquire/acquisition_base.py +148 -0
- PYME/Acquire/actions.py +209 -0
- PYME/Acquire/autofocus.py +54 -0
- PYME/Acquire/eventLog.py +95 -0
- PYME/Acquire/event_loop.py +119 -0
- PYME/Acquire/frameWrangler.py +530 -0
- PYME/Acquire/htsms/__init__.py +8 -0
- PYME/Acquire/htsms/rule_ui.py +970 -0
- PYME/Acquire/htsms/rule_ui_v2.py +573 -0
- PYME/Acquire/htsms/tweeter.py +222 -0
- PYME/Acquire/microscope.py +1050 -0
- PYME/Acquire/positionTracker.py +181 -0
- PYME/Acquire/protocol.py +299 -0
- PYME/Acquire/protocol_acquisition.py +404 -0
- PYME/Acquire/sampleInformation.py +591 -0
- PYME/Acquire/sampleInformationDjangoDirect.py +446 -0
- PYME/Acquire/setup.py +52 -0
- PYME/Acquire/stackSettings.py +295 -0
- PYME/Acquire/stage_leveling.py +285 -0
- PYME/Acquire/ui/AnalysisSettingsUI.py +184 -0
- PYME/Acquire/ui/HDFSpoolFrame.py +584 -0
- PYME/Acquire/ui/__init__.py +0 -0
- PYME/Acquire/ui/actionUI.py +892 -0
- PYME/Acquire/ui/focus_lock_gui.py +149 -0
- PYME/Acquire/ui/intensity_trace.py +143 -0
- PYME/Acquire/ui/intsliders.py +222 -0
- PYME/Acquire/ui/lasersliders.py +329 -0
- PYME/Acquire/ui/mpd_picosecond_delay_panel.py +69 -0
- PYME/Acquire/ui/multiview_select.py +77 -0
- PYME/Acquire/ui/positionUI.py +519 -0
- PYME/Acquire/ui/preflight.py +58 -0
- PYME/Acquire/ui/scanner_panel.py +41 -0
- PYME/Acquire/ui/selectCameraPanel.py +46 -0
- PYME/Acquire/ui/seqdialog.py +501 -0
- PYME/Acquire/ui/splashScreen.py +175 -0
- PYME/Acquire/ui/spool_panel.py +830 -0
- PYME/Acquire/ui/tile_panel.py +556 -0
- PYME/Acquire/ui/tilesettingsui.py +91 -0
- PYME/Acquire/ui/voxelSizeDialog.py +149 -0
- PYME/Acquire/webui/__init__.py +36 -0
- PYME/Acquire/webui/ipy.py +361 -0
- PYME/Acquire/webui/static/css/pyme-bootstrap.css +9908 -0
- PYME/Acquire/webui/static/css/pymeacquire.css +85 -0
- PYME/Acquire/webui/static/js/pymeacquire.js +375 -0
- PYME/Acquire/webui/static/js/pzf.js +95 -0
- PYME/Acquire/webui/templates/PYMEAcquire.html +232 -0
- PYME/Acquire/webui/templates/login.html +84 -0
- PYME/Acquire/webui/templates/xtermpage.html +106 -0
- PYME/Acquire/xyztc.py +418 -0
- PYME/Acquire/zScanner.py +287 -0
- PYME/Analysis/BleachProfile/__init__.py +26 -0
- PYME/Analysis/BleachProfile/deMod.py +91 -0
- PYME/Analysis/BleachProfile/fitRecover.py +90 -0
- PYME/Analysis/BleachProfile/intensProf.py +519 -0
- PYME/Analysis/BleachProfile/kinModels.py +507 -0
- PYME/Analysis/BleachProfile/rawIntensity.py +227 -0
- PYME/Analysis/BleachProfile/rollins.py +555 -0
- PYME/Analysis/Colocalisation/__init__.py +27 -0
- PYME/Analysis/Colocalisation/colocScaleDep.py +126 -0
- PYME/Analysis/Colocalisation/correlationCoeffs.py +198 -0
- PYME/Analysis/Colocalisation/distColoc.py +110 -0
- PYME/Analysis/Colocalisation/edtColoc.py +191 -0
- PYME/Analysis/MetaData.py +278 -0
- PYME/Analysis/PSFEst/__init__.py +27 -0
- PYME/Analysis/PSFEst/dec.py +283 -0
- PYME/Analysis/PSFEst/extractImages.py +316 -0
- PYME/Analysis/PSFEst/fit_psf_zernikes.py +194 -0
- PYME/Analysis/PSFEst/psfQuality.py +225 -0
- PYME/Analysis/PSFGen/__init__.py +40 -0
- PYME/Analysis/PSFGen/fourier.py +138 -0
- PYME/Analysis/PSFGen/fourierHNA.py +1108 -0
- PYME/Analysis/PSFGen/fourierHNA2.py +979 -0
- PYME/Analysis/PSFGen/genCRBcurves.py +185 -0
- PYME/Analysis/PSFGen/nijboer_zernike.py +79 -0
- PYME/Analysis/PSFGen/ps_app.cp39-win_amd64.dll.a +0 -0
- PYME/Analysis/PSFGen/ps_app.cp39-win_amd64.pyd +0 -0
- PYME/Analysis/PSFGen/test_ps_app.py +24 -0
- PYME/Analysis/Tracking/__init__.py +3 -0
- PYME/Analysis/Tracking/msd.py +57 -0
- PYME/Analysis/Tracking/trackUtils.py +528 -0
- PYME/Analysis/Tracking/tracking.py +152 -0
- PYME/Analysis/__init__.py +30 -0
- PYME/Analysis/_fithelpers.py +161 -0
- PYME/Analysis/angleFilter.py +287 -0
- PYME/Analysis/annealThresh.py +157 -0
- PYME/Analysis/autoencoder_classifier.py +45 -0
- PYME/Analysis/binAvg.py +144 -0
- PYME/Analysis/composite.py +183 -0
- PYME/Analysis/cramerRao.py +259 -0
- PYME/Analysis/deTile.py +441 -0
- PYME/Analysis/density_filter.py +45 -0
- PYME/Analysis/distributed_pyramid.py +426 -0
- PYME/Analysis/fiducial_matching.py +220 -0
- PYME/Analysis/gen_sCMOS_maps.py +355 -0
- PYME/Analysis/graphing_filters.py +311 -0
- PYME/Analysis/implicitFilter.py +359 -0
- PYME/Analysis/keep_pairs.py +69 -0
- PYME/Analysis/optic_flow.py +108 -0
- PYME/Analysis/piecewise.py +36 -0
- PYME/Analysis/piecewiseMapping.py +304 -0
- PYME/Analysis/piezo_movement_correction.py +153 -0
- PYME/Analysis/points/DeClump/__init__.py +78 -0
- PYME/Analysis/points/DeClump/deClump.cp39-win_amd64.dll.a +0 -0
- PYME/Analysis/points/DeClump/deClump.cp39-win_amd64.pyd +0 -0
- PYME/Analysis/points/DeClump/deClumpGUI.py +87 -0
- PYME/Analysis/points/DeClump/pyDeClump.py +284 -0
- PYME/Analysis/points/DeClump/testClump.py +40 -0
- PYME/Analysis/points/DistHist/__init__.py +25 -0
- PYME/Analysis/points/DistHist/distHist.cp39-win_amd64.dll.a +0 -0
- PYME/Analysis/points/DistHist/distHist.cp39-win_amd64.pyd +0 -0
- PYME/Analysis/points/DistHist/distHistThreaded.py +71 -0
- PYME/Analysis/points/DistHist/setup.py +54 -0
- PYME/Analysis/points/EdgeDB/__init__.py +24 -0
- PYME/Analysis/points/EdgeDB/edgeDB.cp39-win_amd64.dll.a +0 -0
- PYME/Analysis/points/EdgeDB/edgeDB.cp39-win_amd64.pyd +0 -0
- PYME/Analysis/points/EdgeDB/edgeTest.py +66 -0
- PYME/Analysis/points/EdgeDB/edges.py +102 -0
- PYME/Analysis/points/EdgeDB/segment.py +75 -0
- PYME/Analysis/points/EdgeDB/setup.py +54 -0
- PYME/Analysis/points/QuadTree/QTrend.py +232 -0
- PYME/Analysis/points/QuadTree/__init__.py +25 -0
- PYME/Analysis/points/QuadTree/pointQT.py +227 -0
- PYME/Analysis/points/QuadTree/pointQTF.py +254 -0
- PYME/Analysis/points/QuadTree/qtUtils.py +36 -0
- PYME/Analysis/points/QuadTree/rendQT.py +118 -0
- PYME/Analysis/points/SoftRend/__init__.py +24 -0
- PYME/Analysis/points/SoftRend/setup.py +61 -0
- PYME/Analysis/points/SoftRend/testTetrahedra.py +41 -0
- PYME/Analysis/points/SoftRend/triRend.cp39-win_amd64.dll.a +0 -0
- PYME/Analysis/points/SoftRend/triRend.cp39-win_amd64.pyd +0 -0
- PYME/Analysis/points/__init__.py +3 -0
- PYME/Analysis/points/arcfit/__init__.py +1 -0
- PYME/Analysis/points/arcfit/arcmf.cp39-win_amd64.dll.a +0 -0
- PYME/Analysis/points/arcfit/arcmf.cp39-win_amd64.pyd +0 -0
- PYME/Analysis/points/arcfit/setup.py +51 -0
- PYME/Analysis/points/astigmatism/__init__.py +0 -0
- PYME/Analysis/points/astigmatism/astigTools.py +338 -0
- PYME/Analysis/points/astigmatism/astiglookup.cp39-win_amd64.dll.a +0 -0
- PYME/Analysis/points/astigmatism/astiglookup.cp39-win_amd64.pyd +0 -0
- PYME/Analysis/points/astigmatism/setup.py +54 -0
- PYME/Analysis/points/cluster_morphology.py +199 -0
- PYME/Analysis/points/coordinate_tools.py +278 -0
- PYME/Analysis/points/features/__init__.py +0 -0
- PYME/Analysis/points/features/metal.py +46 -0
- PYME/Analysis/points/features/shaders/vect_feat_3d.metal +49 -0
- PYME/Analysis/points/fiducials.py +142 -0
- PYME/Analysis/points/gen3DTriangs.py +796 -0
- PYME/Analysis/points/moments.py +143 -0
- PYME/Analysis/points/multiview.py +458 -0
- PYME/Analysis/points/objectMeasure.py +206 -0
- PYME/Analysis/points/qpaint.py +97 -0
- PYME/Analysis/points/rings.py +372 -0
- PYME/Analysis/points/ripleys.py +427 -0
- PYME/Analysis/points/segmentLL.py +134 -0
- PYME/Analysis/points/setup.py +56 -0
- PYME/Analysis/points/spherical_harmonics.py +909 -0
- PYME/Analysis/points/surfit.py +829 -0
- PYME/Analysis/points/tCluster.py +75 -0
- PYME/Analysis/points/traveling_salesperson/__init__.py +0 -0
- PYME/Analysis/points/traveling_salesperson/queue_opt.py +117 -0
- PYME/Analysis/points/traveling_salesperson/sectioned_two_opt.py +327 -0
- PYME/Analysis/points/traveling_salesperson/setup.py +40 -0
- PYME/Analysis/points/traveling_salesperson/sort.py +78 -0
- PYME/Analysis/points/traveling_salesperson/two_opt.py +189 -0
- PYME/Analysis/points/traveling_salesperson/two_opt_utils.cp39-win_amd64.dll.a +0 -0
- PYME/Analysis/points/traveling_salesperson/two_opt_utils.cp39-win_amd64.pyd +0 -0
- PYME/Analysis/points/traveling_salesperson/two_opt_utils.pyx +41 -0
- PYME/Analysis/points/twoColour.py +572 -0
- PYME/Analysis/points/twoColourPlot.py +107 -0
- PYME/Analysis/processLogger.py +129 -0
- PYME/Analysis/profile_extraction.py +72 -0
- PYME/Analysis/setup.py +60 -0
- PYME/Analysis/splitting.py +372 -0
- PYME/Analysis/svmSegment.py +214 -0
- PYME/Analysis/thresholding.py +192 -0
- PYME/Analysis/tile_pyramid.py +938 -0
- PYME/Analysis/widefieldify.py +54 -0
- PYME/DSView/DisplayOptionsPanel.py +503 -0
- PYME/DSView/LUT/__init__.py +33 -0
- PYME/DSView/LUT/lut.cp39-win_amd64.dll.a +0 -0
- PYME/DSView/LUT/lut.cp39-win_amd64.pyd +0 -0
- PYME/DSView/LUT/setup.py +54 -0
- PYME/DSView/OpenSequenceDialog.py +83 -0
- PYME/DSView/OverlaysPanel.py +111 -0
- PYME/DSView/__init__.py +32 -0
- PYME/DSView/arrayViewPanel.py +1035 -0
- PYME/DSView/displayOptions.py +453 -0
- PYME/DSView/displaySettingsPanel.py +271 -0
- PYME/DSView/dsviewer.py +542 -0
- PYME/DSView/eventLogViewer.py +805 -0
- PYME/DSView/fitInfo.py +486 -0
- PYME/DSView/htmlServe.py +72 -0
- PYME/DSView/logparser.py +100 -0
- PYME/DSView/modules/LMAnalysis.py +1053 -0
- PYME/DSView/modules/LMDisplay.py +194 -0
- PYME/DSView/modules/__init__.py +162 -0
- PYME/DSView/modules/_base.py +39 -0
- PYME/DSView/modules/annotation.py +725 -0
- PYME/DSView/modules/arrayView.py +71 -0
- PYME/DSView/modules/blobFinding.py +455 -0
- PYME/DSView/modules/blobMeasure.py +1093 -0
- PYME/DSView/modules/camera_maps.py +38 -0
- PYME/DSView/modules/coloc.py +414 -0
- PYME/DSView/modules/composite.py +715 -0
- PYME/DSView/modules/convnet.py +24 -0
- PYME/DSView/modules/cropping.py +133 -0
- PYME/DSView/modules/dark_and_flatfield.py +31 -0
- PYME/DSView/modules/deconvolution.py +499 -0
- PYME/DSView/modules/eventView.py +80 -0
- PYME/DSView/modules/extractBlobs.py +46 -0
- PYME/DSView/modules/fastGraphPanel.py +85 -0
- PYME/DSView/modules/filtering.py +335 -0
- PYME/DSView/modules/flowView.py +268 -0
- PYME/DSView/modules/fourierRingCorrelation.py +172 -0
- PYME/DSView/modules/gl_image_view.py +12 -0
- PYME/DSView/modules/graphViewPanel.py +272 -0
- PYME/DSView/modules/measure/__init__.py +1 -0
- PYME/DSView/modules/measure/filterPan.py +239 -0
- PYME/DSView/modules/metadataView.py +28 -0
- PYME/DSView/modules/multithreshold.py +98 -0
- PYME/DSView/modules/neuroglancer.py +129 -0
- PYME/DSView/modules/object_tagging.py +157 -0
- PYME/DSView/modules/particleTracking.py +463 -0
- PYME/DSView/modules/playback.py +256 -0
- PYME/DSView/modules/prebleachExtraction.py +75 -0
- PYME/DSView/modules/profileFitting.py +137 -0
- PYME/DSView/modules/profilePlotting.py +408 -0
- PYME/DSView/modules/projections.py +48 -0
- PYME/DSView/modules/psfExtraction.py +570 -0
- PYME/DSView/modules/psfGeneration.py +138 -0
- PYME/DSView/modules/psfTools.py +661 -0
- PYME/DSView/modules/pupilTools.py +204 -0
- PYME/DSView/modules/pupilView.py +139 -0
- PYME/DSView/modules/recipes.py +266 -0
- PYME/DSView/modules/redimension.py +75 -0
- PYME/DSView/modules/segmentation.py +192 -0
- PYME/DSView/modules/sheetView.py +24 -0
- PYME/DSView/modules/shell.py +39 -0
- PYME/DSView/modules/shiftfield_estimation.py +433 -0
- PYME/DSView/modules/sparkprepare.py +94 -0
- PYME/DSView/modules/splitter.py +148 -0
- PYME/DSView/modules/synchronise.py +64 -0
- PYME/DSView/modules/templates/astigCal.html +42 -0
- PYME/DSView/modules/templates/measureView.html +69 -0
- PYME/DSView/modules/templates/measureView2.html +48 -0
- PYME/DSView/modules/templates/measureView3.html +98 -0
- PYME/DSView/modules/templates/trackView.html +139 -0
- PYME/DSView/modules/templates/tracksOverview.html +21 -0
- PYME/DSView/modules/tiling.py +147 -0
- PYME/DSView/modules/unmixing.py +148 -0
- PYME/DSView/modules/vis3D.py +320 -0
- PYME/DSView/modules/visgui.py +163 -0
- PYME/DSView/modules/webview.py +14 -0
- PYME/DSView/overlays.py +295 -0
- PYME/DSView/scrolledImagePanel.py +188 -0
- PYME/DSView/setup.py +53 -0
- PYME/DSView/splashScreen.py +158 -0
- PYME/DSView/voxSizeDialog.py +84 -0
- PYME/Deconv/__init__.py +35 -0
- PYME/Deconv/beadGen.py +55 -0
- PYME/Deconv/dec.py +659 -0
- PYME/Deconv/decTask.py +65 -0
- PYME/Deconv/decThread.py +113 -0
- PYME/Deconv/dec_test.py +47 -0
- PYME/Deconv/deconvDialogs.py +417 -0
- PYME/Deconv/fftwWisdom.py +62 -0
- PYME/Deconv/richardsonLucy.py +141 -0
- PYME/Deconv/richardsonLucyMV.py +151 -0
- PYME/Deconv/richardsonLucyMVM.py +218 -0
- PYME/Deconv/test_block_dec.py +46 -0
- PYME/Deconv/tq_block_dec.py +331 -0
- PYME/Deconv/wiener.py +163 -0
- PYME/IO/DataSources/AlignDataSource.py +91 -0
- PYME/IO/DataSources/ArrayDataSource.py +260 -0
- PYME/IO/DataSources/BGSDataSource.py +353 -0
- PYME/IO/DataSources/BaseDataSource.py +457 -0
- PYME/IO/DataSources/BioformatsDataSource.py +178 -0
- PYME/IO/DataSources/BufferedDataSource.py +99 -0
- PYME/IO/DataSources/ClusterPZFDataSource.py +124 -0
- PYME/IO/DataSources/ConcatenatedDataSource.py +56 -0
- PYME/IO/DataSources/CropDataSource.py +243 -0
- PYME/IO/DataSources/DcimgDataSource.py +54 -0
- PYME/IO/DataSources/ElectronsPerSecondDataSource.py +61 -0
- PYME/IO/DataSources/FlatFieldDataSource.py +77 -0
- PYME/IO/DataSources/HDFDataSource.py +160 -0
- PYME/IO/DataSources/HTTPDataSource.py +80 -0
- PYME/IO/DataSources/ImageSeriesDataSource.py +123 -0
- PYME/IO/DataSources/MultiviewDataSource.py +99 -0
- PYME/IO/DataSources/OBFDataSource.py +67 -0
- PYME/IO/DataSources/RandomDataSource.py +23 -0
- PYME/IO/DataSources/SupertileDatasource.py +148 -0
- PYME/IO/DataSources/TQDataSource.py +41 -0
- PYME/IO/DataSources/TiffDataSource.py +193 -0
- PYME/IO/DataSources/TileDataSource.py +91 -0
- PYME/IO/DataSources/UnsplitDataSource.py +92 -0
- PYME/IO/DataSources/__init__.py +59 -0
- PYME/IO/FileUtils/IJLut.py +43 -0
- PYME/IO/FileUtils/RawToHdf5.py +105 -0
- PYME/IO/FileUtils/SPAD.py +54 -0
- PYME/IO/FileUtils/TiffSeqToHdf5.py +87 -0
- PYME/IO/FileUtils/__init__.py +24 -0
- PYME/IO/FileUtils/compHdf.py +39 -0
- PYME/IO/FileUtils/fileID.py +246 -0
- PYME/IO/FileUtils/fnlGen.py +40 -0
- PYME/IO/FileUtils/freeSpace.py +59 -0
- PYME/IO/FileUtils/h5-thumbnailer.py +85 -0
- PYME/IO/FileUtils/h5ExFrames.py +98 -0
- PYME/IO/FileUtils/h5ToTiffSeries.py +50 -0
- PYME/IO/FileUtils/h5r-thumbnailer.py +118 -0
- PYME/IO/FileUtils/h5rFormatDef.py +99 -0
- PYME/IO/FileUtils/h5rToTxt.py +77 -0
- PYME/IO/FileUtils/nameUtils.py +300 -0
- PYME/IO/FileUtils/obf_support.py +538 -0
- PYME/IO/FileUtils/ply.py +147 -0
- PYME/IO/FileUtils/readSpeckle.py +78 -0
- PYME/IO/FileUtils/readTiff.py +64 -0
- PYME/IO/FileUtils/saveTiffStack.py +137 -0
- PYME/IO/FileUtils/setup.py +36 -0
- PYME/IO/FileUtils/sf-thumbnailer.py +76 -0
- PYME/IO/FileUtils/stl.py +44 -0
- PYME/IO/FileUtils/thumbnailDatabase.py +104 -0
- PYME/IO/HTTPSpooler_v2.py +141 -0
- PYME/IO/MetaDataHandler.py +1072 -0
- PYME/IO/PZFFormat.py +375 -0
- PYME/IO/Spooler.py +279 -0
- PYME/IO/__init__.py +12 -0
- PYME/IO/acquisition_backends.py +353 -0
- PYME/IO/buffer_helpers.cp39-win_amd64.dll.a +0 -0
- PYME/IO/buffer_helpers.cp39-win_amd64.pyd +0 -0
- PYME/IO/buffer_helpers.pyx +190 -0
- PYME/IO/buffers.py +291 -0
- PYME/IO/clusterDuplication.py +204 -0
- PYME/IO/clusterExport.py +152 -0
- PYME/IO/clusterGlob.py +108 -0
- PYME/IO/clusterIO.py +1076 -0
- PYME/IO/clusterListing.py +232 -0
- PYME/IO/clusterResults.py +206 -0
- PYME/IO/clusterUpload.py +49 -0
- PYME/IO/cluster_directory.py +419 -0
- PYME/IO/cluster_streaming.py +216 -0
- PYME/IO/compatibility.py +12 -0
- PYME/IO/countdir/__init__.py +5 -0
- PYME/IO/countdir/setup.py +58 -0
- PYME/IO/csv_flavours.py +251 -0
- PYME/IO/dataExporter.py +470 -0
- PYME/IO/dataWrap.py +108 -0
- PYME/IO/dcimg.py +167 -0
- PYME/IO/events.py +161 -0
- PYME/IO/h5File.py +148 -0
- PYME/IO/h5rFile.py +323 -0
- PYME/IO/image.py +1447 -0
- PYME/IO/load_psf.py +26 -0
- PYME/IO/pyme_h5ls.py +58 -0
- PYME/IO/ragged.py +138 -0
- PYME/IO/rgb_image.py +94 -0
- PYME/IO/tabular.py +1338 -0
- PYME/IO/testClusterSpooling.py +199 -0
- PYME/IO/unifiedIO.py +301 -0
- PYME/LMVis/Extras/QPobjectSegment.py +220 -0
- PYME/LMVis/Extras/__init__.py +55 -0
- PYME/LMVis/Extras/animation.py +359 -0
- PYME/LMVis/Extras/annotation.py +78 -0
- PYME/LMVis/Extras/clusterAnalysis.py +460 -0
- PYME/LMVis/Extras/dockedPanel.py +69 -0
- PYME/LMVis/Extras/extra_layers.py +373 -0
- PYME/LMVis/Extras/fiducial.py +230 -0
- PYME/LMVis/Extras/flagZMotionArtifacts.py +60 -0
- PYME/LMVis/Extras/fusionUtils.py +9 -0
- PYME/LMVis/Extras/multiColourAnalysis.py +103 -0
- PYME/LMVis/Extras/multiviewMapping.py +489 -0
- PYME/LMVis/Extras/objectMeasurements.py +246 -0
- PYME/LMVis/Extras/particleTracking.py +272 -0
- PYME/LMVis/Extras/perFrameVariable.py +77 -0
- PYME/LMVis/Extras/photophysics.py +72 -0
- PYME/LMVis/Extras/pointSetGeneration.py +232 -0
- PYME/LMVis/Extras/pointwiseColoc.py +54 -0
- PYME/LMVis/Extras/saved_views.py +125 -0
- PYME/LMVis/Extras/scaleBox.py +115 -0
- PYME/LMVis/Extras/sequentialSpecies.py +125 -0
- PYME/LMVis/Extras/setBGImage.py +74 -0
- PYME/LMVis/Extras/shiftmapGenerator.py +138 -0
- PYME/LMVis/Extras/showShortcuts.py +69 -0
- PYME/LMVis/Extras/snapshot.py +135 -0
- PYME/LMVis/Extras/spherical_harmonics.py +98 -0
- PYME/LMVis/Extras/subsets.py +15 -0
- PYME/LMVis/Extras/surface_fitting.py +52 -0
- PYME/LMVis/Extras/temperaturePlot.py +53 -0
- PYME/LMVis/Extras/vibration.py +112 -0
- PYME/LMVis/VisGUI.py +506 -0
- PYME/LMVis/__init__.py +39 -0
- PYME/LMVis/colourFilterGUI.py +77 -0
- PYME/LMVis/colourPanel.py +491 -0
- PYME/LMVis/displayPane.py +462 -0
- PYME/LMVis/dyeRatios.py +116 -0
- PYME/LMVis/genImageDialog.py +298 -0
- PYME/LMVis/gl_offScreenHandler.py +134 -0
- PYME/LMVis/gl_render.py +1302 -0
- PYME/LMVis/gl_render3D.py +1453 -0
- PYME/LMVis/gl_render3D_shaders.py +1213 -0
- PYME/LMVis/glcanvas_core.py +1157 -0
- PYME/LMVis/imageView.py +752 -0
- PYME/LMVis/imageView2.py +372 -0
- PYME/LMVis/importTextDialog.py +342 -0
- PYME/LMVis/layer_panel.py +231 -0
- PYME/LMVis/layers/AxesOverlayLayer.py +126 -0
- PYME/LMVis/layers/LUTOverlayLayer.py +346 -0
- PYME/LMVis/layers/OverlayLayer.py +46 -0
- PYME/LMVis/layers/ScaleBarOverlayLayer.py +97 -0
- PYME/LMVis/layers/ScaleBoxOverlayLayer.py +181 -0
- PYME/LMVis/layers/SelectionOverlayLayer.py +124 -0
- PYME/LMVis/layers/__init__.py +53 -0
- PYME/LMVis/layers/base.py +319 -0
- PYME/LMVis/layers/image_layer.py +363 -0
- PYME/LMVis/layers/labels.py +186 -0
- PYME/LMVis/layers/layer_defaults.py +97 -0
- PYME/LMVis/layers/mesh.py +378 -0
- PYME/LMVis/layers/octree.py +194 -0
- PYME/LMVis/layers/pointcloud.py +372 -0
- PYME/LMVis/layers/quiver.py +271 -0
- PYME/LMVis/layers/text.py +250 -0
- PYME/LMVis/layers/tracks.py +309 -0
- PYME/LMVis/mv_math.py +78 -0
- PYME/LMVis/objectDarkMeasure.py +120 -0
- PYME/LMVis/pipeline.py +1227 -0
- PYME/LMVis/pipelineView.py +115 -0
- PYME/LMVis/pointSettingsPanel.py +135 -0
- PYME/LMVis/progGraph.py +59 -0
- PYME/LMVis/quadTreeSettings.py +90 -0
- PYME/LMVis/rendGauss.py +158 -0
- PYME/LMVis/renderers.py +576 -0
- PYME/LMVis/sessionpaths.py +136 -0
- PYME/LMVis/setup.py +39 -0
- PYME/LMVis/setup_exe.py +71 -0
- PYME/LMVis/shader_programs/DefaultShaderProgram.py +149 -0
- PYME/LMVis/shader_programs/GLProgram.py +119 -0
- PYME/LMVis/shader_programs/GouraudShaderProgram.py +157 -0
- PYME/LMVis/shader_programs/PointSpriteShaderProgram.py +163 -0
- PYME/LMVis/shader_programs/ShaderProgramFactory.py +54 -0
- PYME/LMVis/shader_programs/TesselShaderProgram.py +47 -0
- PYME/LMVis/shader_programs/WireFrameShaderProgram.py +50 -0
- PYME/LMVis/shader_programs/__init__.py +20 -0
- PYME/LMVis/shader_programs/oit_compositor.py +23 -0
- PYME/LMVis/shader_programs/shader_loader.py +84 -0
- PYME/LMVis/shader_programs/shader_program.py +168 -0
- PYME/LMVis/shader_programs/shaders/bigflatpoints_fs.glsl +16 -0
- PYME/LMVis/shader_programs/shaders/bigpoints_gs.glsl +40 -0
- PYME/LMVis/shader_programs/shaders/bigpointsprites_fs.glsl +20 -0
- PYME/LMVis/shader_programs/shaders/bigspheres_fs.glsl +53 -0
- PYME/LMVis/shader_programs/shaders/compose_fs.glsl +41 -0
- PYME/LMVis/shader_programs/shaders/compose_vs.glsl +25 -0
- PYME/LMVis/shader_programs/shaders/default_fs.glsl +27 -0
- PYME/LMVis/shader_programs/shaders/default_vs.glsl +88 -0
- PYME/LMVis/shader_programs/shaders/flatpoints_fs.glsl +27 -0
- PYME/LMVis/shader_programs/shaders/gouraud_flatpoints_fs.glsl +30 -0
- PYME/LMVis/shader_programs/shaders/gouraud_fs.glsl +26 -0
- PYME/LMVis/shader_programs/shaders/gouraud_oit_fs.glsl +68 -0
- PYME/LMVis/shader_programs/shaders/gouraud_vs.glsl +135 -0
- PYME/LMVis/shader_programs/shaders/image_fs.glsl +60 -0
- PYME/LMVis/shader_programs/shaders/image_vs.glsl +79 -0
- PYME/LMVis/shader_programs/shaders/phong_fs.glsl +43 -0
- PYME/LMVis/shader_programs/shaders/phong_vs.glsl +45 -0
- PYME/LMVis/shader_programs/shaders/pointsprites_fs.glsl +33 -0
- PYME/LMVis/shader_programs/shaders/pointsprites_vs.glsl +77 -0
- PYME/LMVis/shader_programs/shaders/spheres_fs.glsl +102 -0
- PYME/LMVis/shader_programs/shaders/tessel_fs.glsl +23 -0
- PYME/LMVis/shader_programs/shaders/tessel_vs.glsl +37 -0
- PYME/LMVis/shader_programs/shaders/text_fs.glsl +42 -0
- PYME/LMVis/shader_programs/shaders/text_vs.glsl +84 -0
- PYME/LMVis/shader_programs/shaders/widelines_fs.glsl +19 -0
- PYME/LMVis/shader_programs/shaders/widelines_gs.glsl +56 -0
- PYME/LMVis/shader_programs/shaders/widelines_vs.glsl +51 -0
- PYME/LMVis/statusLog.py +57 -0
- PYME/LMVis/triBlobs.py +142 -0
- PYME/LMVis/view_clipping_pane.py +429 -0
- PYME/LMVis/views.py +275 -0
- PYME/LMVis/visCore.py +1005 -0
- PYME/LMVis/visHelpers.py +880 -0
- PYME/LMVis/visHelpersMin.py +230 -0
- PYME/LMVis/workspaceTree.py +238 -0
- PYME/__init__.py +22 -0
- PYME/cluster/HTTPDataServer.py +1063 -0
- PYME/cluster/HTTPRulePusher.py +382 -0
- PYME/cluster/HTTPTaskPusher.py +377 -0
- PYME/cluster/PYMERuleNodeServer.py +192 -0
- PYME/cluster/PYMERuleServer.py +109 -0
- PYME/cluster/__init__.py +0 -0
- PYME/cluster/clusterUI/accounts/__init__.py +0 -0
- PYME/cluster/clusterUI/accounts/admin.py +85 -0
- PYME/cluster/clusterUI/accounts/forms.py +8 -0
- PYME/cluster/clusterUI/accounts/migrations/0001_initial.py +31 -0
- PYME/cluster/clusterUI/accounts/migrations/__init__.py +0 -0
- PYME/cluster/clusterUI/accounts/models.py +44 -0
- PYME/cluster/clusterUI/accounts/templates/accounts/user_form.html +13 -0
- PYME/cluster/clusterUI/accounts/templates/registration/initial_complete.html +6 -0
- PYME/cluster/clusterUI/accounts/templates/registration/initial_confirm.html +13 -0
- PYME/cluster/clusterUI/accounts/templates/registration/initial_done.html +7 -0
- PYME/cluster/clusterUI/accounts/templates/registration/verification.html +2 -0
- PYME/cluster/clusterUI/accounts/templates/registration/verification_subject.txt +1 -0
- PYME/cluster/clusterUI/accounts/urls.py +19 -0
- PYME/cluster/clusterUI/accounts/views.py +36 -0
- PYME/cluster/clusterUI/clusterUI/__init__.py +0 -0
- PYME/cluster/clusterUI/clusterUI/settings.py +147 -0
- PYME/cluster/clusterUI/clusterUI/urls.py +31 -0
- PYME/cluster/clusterUI/clusterUI/wsgi.py +16 -0
- PYME/cluster/clusterUI/clusterbrowser/__init__.py +0 -0
- PYME/cluster/clusterUI/clusterbrowser/admin.py +3 -0
- PYME/cluster/clusterUI/clusterbrowser/apps.py +7 -0
- PYME/cluster/clusterUI/clusterbrowser/migrations/__init__.py +0 -0
- PYME/cluster/clusterUI/clusterbrowser/models.py +5 -0
- PYME/cluster/clusterUI/clusterbrowser/templates/base.html +129 -0
- PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/dirlisting.html +231 -0
- PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/form_new_folder.html +18 -0
- PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/form_upload_file.html +17 -0
- PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/lightlisting.html +25 -0
- PYME/cluster/clusterUI/clusterbrowser/templates/registration/login.html +38 -0
- PYME/cluster/clusterUI/clusterbrowser/urls.py +10 -0
- PYME/cluster/clusterUI/clusterbrowser/views.py +148 -0
- PYME/cluster/clusterUI/clusterstatus/__init__.py +0 -0
- PYME/cluster/clusterUI/clusterstatus/admin.py +3 -0
- PYME/cluster/clusterUI/clusterstatus/apps.py +7 -0
- PYME/cluster/clusterUI/clusterstatus/migrations/__init__.py +0 -0
- PYME/cluster/clusterUI/clusterstatus/models.py +5 -0
- PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/load_content.html +29 -0
- PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/queue_info.html +28 -0
- PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/queue_info_content.html +50 -0
- PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/status_dash.html +29 -0
- PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/status_dash_content.html +90 -0
- PYME/cluster/clusterUI/clusterstatus/urls.py +9 -0
- PYME/cluster/clusterUI/clusterstatus/views.py +69 -0
- PYME/cluster/clusterUI/clusterui_tornado.py +117 -0
- PYME/cluster/clusterUI/deploy.py +62 -0
- PYME/cluster/clusterUI/localization/__init__.py +0 -0
- PYME/cluster/clusterUI/localization/admin.py +3 -0
- PYME/cluster/clusterUI/localization/apps.py +7 -0
- PYME/cluster/clusterUI/localization/migrations/__init__.py +0 -0
- PYME/cluster/clusterUI/localization/models.py +5 -0
- PYME/cluster/clusterUI/localization/templates/localization/settings_form.html +67 -0
- PYME/cluster/clusterUI/localization/urls.py +10 -0
- PYME/cluster/clusterUI/localization/views.py +170 -0
- PYME/cluster/clusterUI/manage.py +17 -0
- PYME/cluster/clusterUI/recipes/__init__.py +0 -0
- PYME/cluster/clusterUI/recipes/admin.py +3 -0
- PYME/cluster/clusterUI/recipes/migrations/__init__.py +0 -0
- PYME/cluster/clusterUI/recipes/models.py +3 -0
- PYME/cluster/clusterUI/recipes/templates/recipes/extra_inputs.html +35 -0
- PYME/cluster/clusterUI/recipes/templates/recipes/form_recipe.html +82 -0
- PYME/cluster/clusterUI/recipes/templates/recipes/input_list.html +7 -0
- PYME/cluster/clusterUI/recipes/templates/recipes/recipe_editor.html +28 -0
- PYME/cluster/clusterUI/recipes/templates/recipes/recipe_editor_common.html +46 -0
- PYME/cluster/clusterUI/recipes/templates/recipes/recipe_editrun.html +93 -0
- PYME/cluster/clusterUI/recipes/templates/recipes/recipe_standalone.html +100 -0
- PYME/cluster/clusterUI/recipes/templates/recipes/recipe_template.html +109 -0
- PYME/cluster/clusterUI/recipes/urls.py +17 -0
- PYME/cluster/clusterUI/recipes/views.py +109 -0
- PYME/cluster/cluster_of_one.py +200 -0
- PYME/cluster/dataserver_wsgi.py +615 -0
- PYME/cluster/distribution.py +71 -0
- PYME/cluster/rulenodeserver.py +487 -0
- PYME/cluster/rules.py +979 -0
- PYME/cluster/ruleserver.py +1106 -0
- PYME/cluster/status.py +101 -0
- PYME/cluster/taskWorkerHTTP.py +522 -0
- PYME/cluster/webdav.py +1102 -0
- PYME/config.py +659 -0
- PYME/contrib/TextCtrlAutoComplete.py +582 -0
- PYME/contrib/__init__.py +7 -0
- PYME/contrib/aniso/__init__.py +2 -0
- PYME/contrib/aniso/aniso.py +268 -0
- PYME/contrib/auiFloatBook.py +94 -0
- PYME/contrib/congrid.py +127 -0
- PYME/contrib/dispatch/__init__.py +19 -0
- PYME/contrib/dispatch/dispatcher.py +289 -0
- PYME/contrib/gohlke/__init__.py +1 -0
- PYME/contrib/gohlke/_tifffile.cp39-win_amd64.dll.a +0 -0
- PYME/contrib/gohlke/_tifffile.cp39-win_amd64.pyd +0 -0
- PYME/contrib/gohlke/czifile.py +1289 -0
- PYME/contrib/gohlke/fcsfiles.py +514 -0
- PYME/contrib/gohlke/setup.py +54 -0
- PYME/contrib/gohlke/tifffile.py +3095 -0
- PYME/contrib/listctrlMixins.py +896 -0
- PYME/contrib/lzw.py +838 -0
- PYME/contrib/pad/__init__.py +13 -0
- PYME/contrib/pad/info_pad.py +18 -0
- PYME/contrib/pad/pad.py +670 -0
- PYME/contrib/setup.py +38 -0
- PYME/contrib/wxPlotPanel.py +137 -0
- PYME/experimental/SimReconstruction.py +49 -0
- PYME/experimental/__init__.pxd +0 -0
- PYME/experimental/__init__.py +4 -0
- PYME/experimental/_octree.cp39-win_amd64.dll.a +0 -0
- PYME/experimental/_octree.cp39-win_amd64.pyd +0 -0
- PYME/experimental/_octree.pyx +477 -0
- PYME/experimental/_treap.cp39-win_amd64.dll.a +0 -0
- PYME/experimental/_treap.cp39-win_amd64.pyd +0 -0
- PYME/experimental/_treap.pyx +267 -0
- PYME/experimental/_triangle_mesh.cp39-win_amd64.dll.a +0 -0
- PYME/experimental/_triangle_mesh.cp39-win_amd64.pyd +0 -0
- PYME/experimental/_triangle_mesh.pxd +149 -0
- PYME/experimental/_triangle_mesh.pyx +2787 -0
- PYME/experimental/aqavg.py +40 -0
- PYME/experimental/dcimgFileChucker.py +375 -0
- PYME/experimental/dcimgSpoolShim.py +174 -0
- PYME/experimental/dcimg_chuck_test.py +78 -0
- PYME/experimental/dual_marching_cubes.py +801 -0
- PYME/experimental/dual_marching_cubes_v2.py +645 -0
- PYME/experimental/expectationMaximisation.py +298 -0
- PYME/experimental/func_octree.cp39-win_amd64.dll.a +0 -0
- PYME/experimental/func_octree.cp39-win_amd64.pyd +0 -0
- PYME/experimental/func_octree.pyx +381 -0
- PYME/experimental/fusionRadial.py +520 -0
- PYME/experimental/fusionUtils.py +522 -0
- PYME/experimental/isosurface.py +147 -0
- PYME/experimental/labview_spooling_hacks.py +48 -0
- PYME/experimental/marching_cubes.py +811 -0
- PYME/experimental/marching_tetrahedra.py +343 -0
- PYME/experimental/modified_marching_cubes.py +1245 -0
- PYME/experimental/music.py +25 -0
- PYME/experimental/octree.py +188 -0
- PYME/experimental/pSpaceMap.py +34 -0
- PYME/experimental/pointFitConfoc.py +112 -0
- PYME/experimental/scanner_control.py +107 -0
- PYME/experimental/setup.py +109 -0
- PYME/experimental/spark.py +31 -0
- PYME/experimental/tcHist.py +71 -0
- PYME/experimental/treap.py +262 -0
- PYME/experimental/triangle_mesh.py +2287 -0
- PYME/experimental/triangle_mesh_utils.cp39-win_amd64.dll.a +0 -0
- PYME/experimental/triangle_mesh_utils.cp39-win_amd64.pyd +0 -0
- PYME/experimental/triangle_utils.py +87 -0
- PYME/experimental/triangular_mesh.py +2255 -0
- PYME/experimental/zMotionArtifactUtils.py +55 -0
- PYME/localization/FitFactories/AstigGaussFitFR.py +175 -0
- PYME/localization/FitFactories/AstigGaussGPUFitFR.py +326 -0
- PYME/localization/FitFactories/BeadConvolvedAstigGaussFit.py +206 -0
- PYME/localization/FitFactories/ChecksumFR.py +82 -0
- PYME/localization/FitFactories/ChecksumGPUFR.py +213 -0
- PYME/localization/FitFactories/ChecksumMultifitSR.py +86 -0
- PYME/localization/FitFactories/ConfocCOIR.py +93 -0
- PYME/localization/FitFactories/Dumbell3DFitR.py +193 -0
- PYME/localization/FitFactories/DumbellFitR.py +163 -0
- PYME/localization/FitFactories/FFBase.py +403 -0
- PYME/localization/FitFactories/Gauss3DFitR.py +213 -0
- PYME/localization/FitFactories/GaussFitConstrR.py +177 -0
- PYME/localization/FitFactories/GaussMultifitLinesR.py +351 -0
- PYME/localization/FitFactories/GaussMultifitR.py +326 -0
- PYME/localization/FitFactories/GaussMultifitSR.py +373 -0
- PYME/localization/FitFactories/InterpFitR.py +236 -0
- PYME/localization/FitFactories/InterpFitWFR.py +111 -0
- PYME/localization/FitFactories/Interpolators/CSInterpolator.py +148 -0
- PYME/localization/FitFactories/Interpolators/CubeSInterpolatorP.py +121 -0
- PYME/localization/FitFactories/Interpolators/CubicSplineInterpolator.py +69 -0
- PYME/localization/FitFactories/Interpolators/LinearInterpolator.py +135 -0
- PYME/localization/FitFactories/Interpolators/LinearPInterpolatorP.py +95 -0
- PYME/localization/FitFactories/Interpolators/__init__.py +33 -0
- PYME/localization/FitFactories/Interpolators/baseInterpolator.py +202 -0
- PYME/localization/FitFactories/LatFitCOIR.py +82 -0
- PYME/localization/FitFactories/LatGaussFitFR.py +220 -0
- PYME/localization/FitFactories/LatObjFindFR.py +85 -0
- PYME/localization/FitFactories/LatPSFFitR.py +142 -0
- PYME/localization/FitFactories/MultiviewFitInterpNR.py +400 -0
- PYME/localization/FitFactories/PRInterpFitQR.py +192 -0
- PYME/localization/FitFactories/PRInterpFitR.py +223 -0
- PYME/localization/FitFactories/ROIExtractNR.py +93 -0
- PYME/localization/FitFactories/SpecGaussFitFR.py +174 -0
- PYME/localization/FitFactories/SplitterFitCOIR.py +142 -0
- PYME/localization/FitFactories/SplitterFitFNR.py +312 -0
- PYME/localization/FitFactories/SplitterFitFusionR.py +318 -0
- PYME/localization/FitFactories/SplitterFitInterpBNR.py +357 -0
- PYME/localization/FitFactories/SplitterFitInterpNR.py +343 -0
- PYME/localization/FitFactories/SplitterFitScavNR.py +352 -0
- PYME/localization/FitFactories/SplitterMultifitSR.py +410 -0
- PYME/localization/FitFactories/SplitterObjFindR.py +194 -0
- PYME/localization/FitFactories/SplitterROIExtractNR.py +91 -0
- PYME/localization/FitFactories/SplitterShiftEstFR.py +230 -0
- PYME/localization/FitFactories/__init__.py +103 -0
- PYME/localization/FitFactories/fitCommon.py +195 -0
- PYME/localization/FitFactories/zEstimators/__init__.py +29 -0
- PYME/localization/FitFactories/zEstimators/astigEstimator.py +173 -0
- PYME/localization/FitFactories/zEstimators/biplaneEstimator.py +209 -0
- PYME/localization/FitFactories/zEstimators/genericDualEstimator.py +232 -0
- PYME/localization/FitFactories/zEstimators/genericEstimator.py +224 -0
- PYME/localization/FitFactories/zEstimators/priEstimator.py +255 -0
- PYME/localization/MetaDataEdit.py +543 -0
- PYME/localization/Test/30_9_series_A_1.sf +0 -0
- PYME/localization/Test/PRI.md +45 -0
- PYME/localization/Test/PRI_qr.md +52 -0
- PYME/localization/Test/PRI_r.md +52 -0
- PYME/localization/Test/__init__.py +22 -0
- PYME/localization/Test/astig.md +50 -0
- PYME/localization/Test/astigCOI.md +50 -0
- PYME/localization/Test/astig_gauss.md +52 -0
- PYME/localization/Test/astig_pe.md +50 -0
- PYME/localization/Test/astig_splitter.md +66 -0
- PYME/localization/Test/astig_splitterCOI.md +67 -0
- PYME/localization/Test/astig_splitterQ.md +66 -0
- PYME/localization/Test/astig_theory.md +50 -0
- PYME/localization/Test/astig_theory.psf +0 -0
- PYME/localization/Test/astig_theory.tif +0 -0
- PYME/localization/Test/beads1.psf +0 -0
- PYME/localization/Test/beads1.tif +0 -0
- PYME/localization/Test/biplane_splitter.md +75 -0
- PYME/localization/Test/biplane_splitter2.md +103 -0
- PYME/localization/Test/biplane_splitter3.md +115 -0
- PYME/localization/Test/defoc.md +48 -0
- PYME/localization/Test/dumbell_3d.md +52 -0
- PYME/localization/Test/fitTestJig.py +172 -0
- PYME/localization/Test/fitTestJigSCMOS.py +487 -0
- PYME/localization/Test/fitTestJigWC.py +225 -0
- PYME/localization/Test/gauss.md +50 -0
- PYME/localization/Test/gaussCOI.md +50 -0
- PYME/localization/Test/pri_pe.md +50 -0
- PYME/localization/Test/pri_theory.md +51 -0
- PYME/localization/Test/pri_theory.psf +0 -0
- PYME/localization/Test/pri_theory.tif +0 -0
- PYME/localization/Test/psf_astig.psf +0 -0
- PYME/localization/Test/psf_astig.tif +0 -0
- PYME/localization/Test/psf_astig_bg_d.psf +0 -0
- PYME/localization/Test/psf_astig_bg_d.tif +0 -0
- PYME/localization/Test/psf_astig_pe.psf +0 -0
- PYME/localization/Test/psf_astig_pe.tif +0 -0
- PYME/localization/Test/psf_decon.psf +0 -0
- PYME/localization/Test/psf_decon.tif +0 -0
- PYME/localization/Test/psf_decon_pe.psf +0 -0
- PYME/localization/Test/psf_decon_pe.tif +0 -0
- PYME/localization/Test/psf_guess.psf +0 -0
- PYME/localization/Test/splitter.md +63 -0
- PYME/localization/Test/splitter_scav.md +63 -0
- PYME/localization/Test/test.psf +0 -0
- PYME/localization/Test/test.tif +0 -0
- PYME/localization/Test/test_decon.psf +0 -0
- PYME/localization/Test/test_decon.tif +0 -0
- PYME/localization/Test/test_pe.psf +0 -0
- PYME/localization/Test/test_pe.tif +0 -0
- PYME/localization/Test/wf_theory.psf +0 -0
- PYME/localization/Test/wf_theory2.psf +0 -0
- PYME/localization/__init__.py +17 -0
- PYME/localization/autopsf/__init__.py +1 -0
- PYME/localization/autopsf/autopsf.py +486 -0
- PYME/localization/autopsf/roifit.py +310 -0
- PYME/localization/cInterp/__init__.py +24 -0
- PYME/localization/cInterp/cInterp.cp39-win_amd64.dll.a +0 -0
- PYME/localization/cInterp/cInterp.cp39-win_amd64.pyd +0 -0
- PYME/localization/cInterp/setup.py +54 -0
- PYME/localization/cModels/__init__.py +24 -0
- PYME/localization/cModels/gauss_app.cp39-win_amd64.dll.a +0 -0
- PYME/localization/cModels/gauss_app.cp39-win_amd64.pyd +0 -0
- PYME/localization/ofind.py +444 -0
- PYME/localization/ofind3d.py +306 -0
- PYME/localization/ofind_nofilt.py +128 -0
- PYME/localization/ofind_pri.py +478 -0
- PYME/localization/ofind_split.py +399 -0
- PYME/localization/ofind_xcorr.py +351 -0
- PYME/localization/ofind_xcorr_fw.py +441 -0
- PYME/localization/remFitBuf.py +657 -0
- PYME/localization/setup.py +56 -0
- PYME/localization/splitting.py +240 -0
- PYME/localization/twist.py +60 -0
- PYME/misc/TiSettings.py +36 -0
- PYME/misc/TiffImagePlugin.py +838 -0
- PYME/misc/VolProj.py +144 -0
- PYME/misc/__init__.py +25 -0
- PYME/misc/aligned_array.py +10 -0
- PYME/misc/aniso.py +29 -0
- PYME/misc/big_sur_fix.py +20 -0
- PYME/misc/check_for_updates.py +139 -0
- PYME/misc/colormaps.py +199 -0
- PYME/misc/colorsys.py +201 -0
- PYME/misc/computerName.py +33 -0
- PYME/misc/coverslipGeometries.py +60 -0
- PYME/misc/cython_numpy_monkey.py +40 -0
- PYME/misc/depGraph.py +406 -0
- PYME/misc/dirSize.py +31 -0
- PYME/misc/diskCleanup.py +64 -0
- PYME/misc/djangoRecarray.py +57 -0
- PYME/misc/django_widgets.py +27 -0
- PYME/misc/editgrid.py +101 -0
- PYME/misc/eventSpy.py +122 -0
- PYME/misc/exceptions.py +30 -0
- PYME/misc/facsPlot.py +89 -0
- PYME/misc/fbpIcons.py +90 -0
- PYME/misc/fftw_compat.py +28 -0
- PYME/misc/findCycles.py +91 -0
- PYME/misc/fortran_interrupt_defeat.py +43 -0
- PYME/misc/glassModel.py +64 -0
- PYME/misc/hash32.py +41 -0
- PYME/misc/hillcurve.py +88 -0
- PYME/misc/hybrid_ns.py +84 -0
- PYME/misc/kdFit.py +72 -0
- PYME/misc/locate.py +10 -0
- PYME/misc/memUsage.py +65 -0
- PYME/misc/memmap.py +52 -0
- PYME/misc/mock_ctypes.py +28 -0
- PYME/misc/mock_traits.py +59 -0
- PYME/misc/mock_traitsui.py +29 -0
- PYME/misc/pyme_zeroconf.py +287 -0
- PYME/misc/pyro_tracebacks.py +32 -0
- PYME/misc/read_agf.py +605 -0
- PYME/misc/sffake.py +16 -0
- PYME/misc/snells.py +71 -0
- PYME/misc/sqlite_ns.py +132 -0
- PYME/misc/sqlitendarray.py +56 -0
- PYME/misc/taskDef.py +44 -0
- PYME/misc/tempDB.py +56 -0
- PYME/misc/wxThread.py +33 -0
- PYME/misc/zemax.py +259 -0
- PYME/misc/zernike.py +151 -0
- PYME/pyme_warnings.py +70 -0
- PYME/recipes/Recipes/STEDDeconvolution.yaml +14 -0
- PYME/recipes/Recipes/STEDMinistacks.yaml +15 -0
- PYME/recipes/Recipes/STED_COP3.yaml +25 -0
- PYME/recipes/Recipes/blobMeas.yaml +19 -0
- PYME/recipes/Recipes/blobMeas2.yaml +37 -0
- PYME/recipes/Recipes/blobMeasSR.yaml +66 -0
- PYME/recipes/Recipes/coloc.yaml +43 -0
- PYME/recipes/Recipes/coloc_roi_masked.yaml +46 -0
- PYME/recipes/Recipes/multiblob.yaml +38 -0
- PYME/recipes/Recipes/pre_particleTracking.yaml +30 -0
- PYME/recipes/Recipes/segmentAndMeasureLoci.yaml +41 -0
- PYME/recipes/__init__.py +6 -0
- PYME/recipes/acquisition.py +158 -0
- PYME/recipes/bakeshop.py +32 -0
- PYME/recipes/base.py +1393 -0
- PYME/recipes/batchProcess.py +147 -0
- PYME/recipes/filters.py +394 -0
- PYME/recipes/fudges.py +41 -0
- PYME/recipes/graphing.py +108 -0
- PYME/recipes/html_recipe_editor.py +90 -0
- PYME/recipes/localisations.py +1386 -0
- PYME/recipes/machine_learning.py +286 -0
- PYME/recipes/measurement.py +1062 -0
- PYME/recipes/modules.py +50 -0
- PYME/recipes/multiview.py +435 -0
- PYME/recipes/output.py +667 -0
- PYME/recipes/pointcloud.py +510 -0
- PYME/recipes/processing.py +2954 -0
- PYME/recipes/pyramid.py +206 -0
- PYME/recipes/recipe.py +965 -0
- PYME/recipes/recipeGui.py +1029 -0
- PYME/recipes/recipeLayout.py +499 -0
- PYME/recipes/runRecipe.py +140 -0
- PYME/recipes/setup.py +37 -0
- PYME/recipes/simulation.py +91 -0
- PYME/recipes/skfilters.py +143 -0
- PYME/recipes/surface_fitting.py +776 -0
- PYME/recipes/tablefilters.py +275 -0
- PYME/recipes/tracking.py +343 -0
- PYME/recipes/traits.py +157 -0
- PYME/recipes/vertical_recipe_display.py +373 -0
- PYME/resources/__init__.py +23 -0
- PYME/resources/config_template/distributor.yaml +14 -0
- PYME/resources/config_template/nodeserver.yaml +15 -0
- PYME/resources/icons/LUT.png +0 -0
- PYME/resources/icons/YSM_Shield.png +0 -0
- PYME/resources/icons/YSM_logo.png +0 -0
- PYME/resources/icons/arrow.png +0 -0
- PYME/resources/icons/crosshairs.png +0 -0
- PYME/resources/icons/line_select.png +0 -0
- PYME/resources/icons/logo_fmhs.png +0 -0
- PYME/resources/icons/media-playback-pause.png +0 -0
- PYME/resources/icons/media-playback-start.png +0 -0
- PYME/resources/icons/media-skip-backward.png +0 -0
- PYME/resources/icons/media-skip-forward.png +0 -0
- PYME/resources/icons/mesh.png +0 -0
- PYME/resources/icons/mesh.svg +28 -0
- PYME/resources/icons/pmacquire.icns +0 -0
- PYME/resources/icons/pmacquire.ico +0 -0
- PYME/resources/icons/pmanal.icns +0 -0
- PYME/resources/icons/pmanal.ico +0 -0
- PYME/resources/icons/pmvis.icns +0 -0
- PYME/resources/icons/pmvis.ico +0 -0
- PYME/resources/icons/points.png +0 -0
- PYME/resources/icons/points.svg +16 -0
- PYME/resources/icons/pymeLogo.png +0 -0
- PYME/resources/icons/rect_select.png +0 -0
- PYME/resources/icons/squiggle_select.png +0 -0
- PYME/resources/icons/text3800.icns +0 -0
- PYME/resources/icons/text3800.ico +0 -0
- PYME/resources/icons/text3800.png +0 -0
- PYME/resources/icons/text3800.tif +0 -0
- PYME/resources/icons/text5377.icns +0 -0
- PYME/resources/icons/text5377.ico +0 -0
- PYME/resources/icons/text5377.png +0 -0
- PYME/resources/icons/tracks.png +0 -0
- PYME/resources/icons/tracks.svg +19 -0
- PYME/resources/icons/view-front.png +0 -0
- PYME/resources/icons/view-home.png +0 -0
- PYME/resources/icons/view-right.png +0 -0
- PYME/resources/icons/view-top.png +0 -0
- PYME/resources/test_datasets/README.md +8 -0
- PYME/resources/test_datasets/t_im.tif +0 -0
- PYME/resources/test_datasets/test_csv.csv +4282 -0
- PYME/resources/test_datasets/test_hdf.hdf +0 -0
- PYME/resources/web/rule_inline.html +21 -0
- PYME/resources/web/ruleserver.html +75 -0
- PYME/resources/web/static/bootstrap.min.js +7 -0
- PYME/resources/web/static/cluster.css +137 -0
- PYME/resources/web/static/css/bootstrap-theme.css +587 -0
- PYME/resources/web/static/css/bootstrap-theme.min.css +6 -0
- PYME/resources/web/static/css/bootstrap.css +6757 -0
- PYME/resources/web/static/css/bootstrap.min.css +6 -0
- PYME/resources/web/static/css/flat_design_colors_full.css +306 -0
- PYME/resources/web/static/css/recipe_editor.css +106 -0
- PYME/resources/web/static/dashboard.css +106 -0
- PYME/resources/web/static/fileselector.js +77 -0
- PYME/resources/web/static/fonts/glyphicons-halflings-regular.eot +0 -0
- PYME/resources/web/static/fonts/glyphicons-halflings-regular.svg +288 -0
- PYME/resources/web/static/fonts/glyphicons-halflings-regular.ttf +0 -0
- PYME/resources/web/static/fonts/glyphicons-halflings-regular.woff +0 -0
- PYME/resources/web/static/fonts/glyphicons-halflings-regular.woff2 +0 -0
- PYME/resources/web/static/images/osd_flat/LICENSE.txt +116 -0
- PYME/resources/web/static/images/osd_flat/README.md +9 -0
- PYME/resources/web/static/images/osd_flat/fullpage_grouphover.png +0 -0
- PYME/resources/web/static/images/osd_flat/fullpage_hover.png +0 -0
- PYME/resources/web/static/images/osd_flat/fullpage_pressed.png +0 -0
- PYME/resources/web/static/images/osd_flat/fullpage_rest.png +0 -0
- PYME/resources/web/static/images/osd_flat/home_grouphover.png +0 -0
- PYME/resources/web/static/images/osd_flat/home_hover.png +0 -0
- PYME/resources/web/static/images/osd_flat/home_pressed.png +0 -0
- PYME/resources/web/static/images/osd_flat/home_rest.png +0 -0
- PYME/resources/web/static/images/osd_flat/next_grouphover.png +0 -0
- PYME/resources/web/static/images/osd_flat/next_hover.png +0 -0
- PYME/resources/web/static/images/osd_flat/next_pressed.png +0 -0
- PYME/resources/web/static/images/osd_flat/next_rest.png +0 -0
- PYME/resources/web/static/images/osd_flat/previous_grouphover.png +0 -0
- PYME/resources/web/static/images/osd_flat/previous_hover.png +0 -0
- PYME/resources/web/static/images/osd_flat/previous_pressed.png +0 -0
- PYME/resources/web/static/images/osd_flat/previous_rest.png +0 -0
- PYME/resources/web/static/images/osd_flat/rotateleft_grouphover.png +0 -0
- PYME/resources/web/static/images/osd_flat/rotateleft_hover.png +0 -0
- PYME/resources/web/static/images/osd_flat/rotateleft_pressed.png +0 -0
- PYME/resources/web/static/images/osd_flat/rotateleft_rest.png +0 -0
- PYME/resources/web/static/images/osd_flat/rotateright_grouphover.png +0 -0
- PYME/resources/web/static/images/osd_flat/rotateright_hover.png +0 -0
- PYME/resources/web/static/images/osd_flat/rotateright_pressed.png +0 -0
- PYME/resources/web/static/images/osd_flat/rotateright_rest.png +0 -0
- PYME/resources/web/static/images/osd_flat/zoomin_grouphover.png +0 -0
- PYME/resources/web/static/images/osd_flat/zoomin_hover.png +0 -0
- PYME/resources/web/static/images/osd_flat/zoomin_pressed.png +0 -0
- PYME/resources/web/static/images/osd_flat/zoomin_rest.png +0 -0
- PYME/resources/web/static/images/osd_flat/zoomout_grouphover.png +0 -0
- PYME/resources/web/static/images/osd_flat/zoomout_hover.png +0 -0
- PYME/resources/web/static/images/osd_flat/zoomout_pressed.png +0 -0
- PYME/resources/web/static/images/osd_flat/zoomout_rest.png +0 -0
- PYME/resources/web/static/jigna-vue-bare.js +1395 -0
- PYME/resources/web/static/jquery.min.js +2 -0
- PYME/resources/web/static/jstree.min.js +6 -0
- PYME/resources/web/static/openSeadragonGL.js +102 -0
- PYME/resources/web/static/openseadragon-svg-overlay.js +103 -0
- PYME/resources/web/static/openseadragon-viewerinputhook.min.js +6 -0
- PYME/resources/web/static/openseadragon.min.js +9 -0
- PYME/resources/web/static/recipe_editor.js +119 -0
- PYME/resources/web/static/shaders/fragment/lut.glsl +20 -0
- PYME/resources/web/static/shaders/fragment/none.glsl +17 -0
- PYME/resources/web/static/shaders/fragment/outline.glsl +43 -0
- PYME/resources/web/static/shaders/fragment/rect.glsl +7 -0
- PYME/resources/web/static/shaders/fragment/sobel3.glsl +71 -0
- PYME/resources/web/static/shaders/fragment/white.glsl +4 -0
- PYME/resources/web/static/shaders/vertex/rect.glsl +22 -0
- PYME/resources/web/static/shaders/vertex/square.glsl +9 -0
- PYME/resources/web/static/utils.js +3 -0
- PYME/resources/web/static/viaWebGL.js +217 -0
- PYME/setup.py +109 -0
- PYME/setup_exe.py +57 -0
- PYME/simulation/__init__.py +0 -0
- PYME/simulation/locify.py +319 -0
- PYME/simulation/pointsets.py +279 -0
- PYME/simulation/wormlike.py +99 -0
- PYME/simulation/wormlike2.py +198 -0
- PYME/simulation/wormlike3.py +197 -0
- PYME/simulation/wormlike_curled.py +104 -0
- PYME/tileviewer/__init__.py +0 -0
- PYME/tileviewer/templates/roi_list.html +12 -0
- PYME/tileviewer/templates/tileviewer.html +247 -0
- PYME/tileviewer/tileviewer.py +207 -0
- PYME/ui/AUIFrame.py +279 -0
- PYME/ui/MetadataTree.py +469 -0
- PYME/ui/__init__.py +4 -0
- PYME/ui/about_dlg.py +29 -0
- PYME/ui/autoFoldPanel.py +643 -0
- PYME/ui/cascading_layout.py +45 -0
- PYME/ui/crop_dialog.py +145 -0
- PYME/ui/custom_traits_editors.py +508 -0
- PYME/ui/editFilterDialog.py +80 -0
- PYME/ui/editList.py +40 -0
- PYME/ui/fastGraph.py +240 -0
- PYME/ui/filterPane.py +348 -0
- PYME/ui/histLimits.py +543 -0
- PYME/ui/layerFoldPanel.py +232 -0
- PYME/ui/manualFoldPanel.py +870 -0
- PYME/ui/mytimer.py +116 -0
- PYME/ui/patch_traitsui.py +151 -0
- PYME/ui/progress.py +160 -0
- PYME/ui/recArrayView.py +145 -0
- PYME/ui/selection.py +71 -0
- PYME/ui/shell.py +37 -0
- PYME/ui/traitsui_constants.py +91 -0
- PYME/ui/wx_compat.py +43 -0
- PYME/update_version.py +159 -0
- PYME/util/__init__.py +3 -0
- PYME/util/authenticate.py +164 -0
- PYME/util/execfile.py +21 -0
- PYME/util/fProfile/__init__.py +1 -0
- PYME/util/fProfile/convertProfile.py +117 -0
- PYME/util/fProfile/fProfile.py +162 -0
- PYME/util/fProfile/html/404.html +60 -0
- PYME/util/fProfile/html/css/main.css +258 -0
- PYME/util/fProfile/html/css/normalize.css +427 -0
- PYME/util/fProfile/html/css/normalize.min.css +1 -0
- PYME/util/fProfile/html/index.html +64 -0
- PYME/util/fProfile/html/js/main.js +492 -0
- PYME/util/fProfile/html/js/plugins.js +24 -0
- PYME/util/fProfile/html/js/vendor/crossfilter.min.js +1 -0
- PYME/util/fProfile/html/js/vendor/jquery-1.11.2.min.js +4 -0
- PYME/util/fProfile/html/js/vendor/modernizr-2.8.3.min.js +4 -0
- PYME/util/fProfile/html/js/vendor/tinycolor-min.js +4 -0
- PYME/util/fProfile/viewProfile.py +30 -0
- PYME/util/install_dependencies.py +95 -0
- PYME/util/log_verbosity.py +11 -0
- PYME/util/mProfile/__init__.py +1 -0
- PYME/util/mProfile/colorize_db_t.py +297 -0
- PYME/util/mProfile/mProfile.py +164 -0
- PYME/util/mProfile/p.py +101 -0
- PYME/util/mProfile/tProfile.py +165 -0
- PYME/util/pymelauncher.py +47 -0
- PYME/util/shmarray/__init__.py +0 -0
- PYME/util/shmarray/shmTest.py +74 -0
- PYME/util/shmarray/shmarray.py +165 -0
- PYME/util/webframework.py +365 -0
- PYME/version.py +65 -0
- python_microscopy-25.6.5.dist-info/METADATA +68 -0
- python_microscopy-25.6.5.dist-info/RECORD +1277 -0
- python_microscopy-25.6.5.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,2954 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Mon May 25 17:15:01 2015
|
|
4
|
+
|
|
5
|
+
@author: david
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .base import ModuleBase, register_module, Filter, da
|
|
9
|
+
from PYME.recipes.traits import Input, Output, Float, Enum, CStr, Bool, Int, List, FileOrURI, CInt
|
|
10
|
+
|
|
11
|
+
#try:
|
|
12
|
+
# from traitsui.api import View, Item, Group
|
|
13
|
+
#except SystemExit:
|
|
14
|
+
# print('Got stupid OSX SystemExit exception - using dummy traitsui')
|
|
15
|
+
# from PYME.misc.mock_traitsui import *
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from scipy import ndimage
|
|
19
|
+
from PYME.IO.image import ImageStack
|
|
20
|
+
from PYME.IO import tabular
|
|
21
|
+
from PYME.IO import MetaDataHandler
|
|
22
|
+
from PYME import config
|
|
23
|
+
|
|
24
|
+
import logging
|
|
25
|
+
logger=logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
@register_module('SimpleThreshold')
|
|
28
|
+
class SimpleThreshold(Filter):
|
|
29
|
+
threshold = Float(0.5)
|
|
30
|
+
|
|
31
|
+
def apply_filter(self, data, voxelsize):
|
|
32
|
+
mask = data > self.threshold
|
|
33
|
+
return mask
|
|
34
|
+
|
|
35
|
+
#def completeMetadata(self, im):
|
|
36
|
+
# im.mdh['Processing.SimpleThreshold'] = self.threshold
|
|
37
|
+
|
|
38
|
+
@register_module('FractionalThreshold')
|
|
39
|
+
class FractionalThreshold(Filter):
|
|
40
|
+
"""Chose a threshold such that the given fraction of the total labelling is
|
|
41
|
+
included in the mask.
|
|
42
|
+
"""
|
|
43
|
+
fractionThreshold = Float(0.5)
|
|
44
|
+
|
|
45
|
+
def apply_filter(self, data, voxelsize):
|
|
46
|
+
N, bins = np.histogram(data, bins=5000)
|
|
47
|
+
#calculate bin centres
|
|
48
|
+
bin_mids = (bins[:-1] )
|
|
49
|
+
cN = np.cumsum(N*bin_mids)
|
|
50
|
+
i = np.argmin(abs(cN - cN[-1]*(1-self.fractionThreshold)))
|
|
51
|
+
threshold = bins[i]
|
|
52
|
+
|
|
53
|
+
mask = data > threshold
|
|
54
|
+
return mask
|
|
55
|
+
|
|
56
|
+
#def completeMetadata(self, im):
|
|
57
|
+
# im.mdh['Processing.FractionalThreshold'] = self.fractionThreshold
|
|
58
|
+
|
|
59
|
+
@register_module('Threshold')
|
|
60
|
+
class Threshold(Filter):
|
|
61
|
+
""" Catch all class for automatic thresholding
|
|
62
|
+
|
|
63
|
+
Currently offers 'otsu' and 'isodata' methods, which are probably the most useful
|
|
64
|
+
for biological data, but could be expanded.
|
|
65
|
+
|
|
66
|
+
For preformance reasons, the algorithms work on a histogram of the data to determine the threshold.
|
|
67
|
+
If the data has a very large dynamic range (as in a lot of single molecule localisation reconstructions),
|
|
68
|
+
linear binning may not be optimal as the best threshold may not be resolved in the histogram. In this case
|
|
69
|
+
consider using 'log' or 'adaptive' bin spacing.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
method = Enum(['isodata', 'otsu'])
|
|
73
|
+
n_histogram_bins = Int(255)
|
|
74
|
+
bin_spacing = Enum(['linear', 'log', 'adaptive'])
|
|
75
|
+
|
|
76
|
+
def apply_filter(self, data, voxelsize):
|
|
77
|
+
from PYME.Analysis import thresholding
|
|
78
|
+
|
|
79
|
+
if self.method == 'isodata':
|
|
80
|
+
threshold = thresholding.isodata_f(data, nbins=self.n_histogram_bins, bin_spacing=self.bin_spacing)
|
|
81
|
+
elif self.method =='otsu':
|
|
82
|
+
threshold = thresholding.otsu(data, nbins=self.n_histogram_bins, bin_spacing=self.bin_spacing)
|
|
83
|
+
|
|
84
|
+
mask = data > threshold
|
|
85
|
+
return mask
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@register_module('Label')
|
|
89
|
+
class Label(Filter):
|
|
90
|
+
"""Asigns a unique integer label to each contiguous region in the input mask.
|
|
91
|
+
Optionally throws away all regions which are smaller than a cutoff size.
|
|
92
|
+
|
|
93
|
+
Labelling is generally reqired before operations such as measuring object properties.
|
|
94
|
+
|
|
95
|
+
NB - minRegionPixels is currently ignored when running chunked
|
|
96
|
+
"""
|
|
97
|
+
minRegionPixels = Int(10)
|
|
98
|
+
|
|
99
|
+
def apply_filter(self, data, voxelsize):
|
|
100
|
+
mask = data > 0.5
|
|
101
|
+
labs, nlabs = ndimage.label(mask)
|
|
102
|
+
|
|
103
|
+
rSize = self.minRegionPixels
|
|
104
|
+
|
|
105
|
+
if rSize > 1:
|
|
106
|
+
m2 = 0*mask
|
|
107
|
+
objs = ndimage.find_objects(labs)
|
|
108
|
+
for i, o in enumerate(objs):
|
|
109
|
+
r = labs[o] == i+1
|
|
110
|
+
#print r.shape
|
|
111
|
+
if r.sum() > rSize:
|
|
112
|
+
m2[o] += r
|
|
113
|
+
|
|
114
|
+
labs, nlabs = ndimage.label(m2 > 0)
|
|
115
|
+
|
|
116
|
+
return labs
|
|
117
|
+
|
|
118
|
+
def run(self, inputName):
|
|
119
|
+
if hasattr(inputName.data_xyztc, 'chunksize') and da and config.get('recipes-use_dask', False):
|
|
120
|
+
# vanilla label doesn't work well when running blocked as it needs to propagate labels across block edges
|
|
121
|
+
# need to use dask-image label instead (or revert back to )
|
|
122
|
+
try:
|
|
123
|
+
from dask_image import ndmeasure
|
|
124
|
+
from PYME.IO.DataSources import ArrayDataSource
|
|
125
|
+
|
|
126
|
+
chunksize = self._chunk_dims(inputName.data_xyztc)
|
|
127
|
+
c_image = da.from_array(inputName.data_xyztc, chunks = chunksize)
|
|
128
|
+
labs, _ = ndmeasure.label(c_image > 0.5)
|
|
129
|
+
|
|
130
|
+
im = ImageStack(ArrayDataSource.XYZTCArrayDataSource(labs), titleStub = self.outputName)
|
|
131
|
+
im.mdh.copyEntriesFrom(inputName.mdh)
|
|
132
|
+
im.mdh['Parent'] = inputName.filename
|
|
133
|
+
|
|
134
|
+
return im
|
|
135
|
+
except ImportError:
|
|
136
|
+
logger.warning('Blocked image detected but could not import dask_image, forcing entire image into memory')
|
|
137
|
+
return self.filter(inputName)
|
|
138
|
+
else:
|
|
139
|
+
return self.filter(inputName)
|
|
140
|
+
|
|
141
|
+
def completeMetadata(self, im):
|
|
142
|
+
im.mdh['Labelling.MinSize'] = self.minRegionPixels
|
|
143
|
+
|
|
144
|
+
@register_module('SelectLabel')
|
|
145
|
+
class SelectLabel(Filter):
|
|
146
|
+
"""Creates a mask corresponding to all pixels with the given label"""
|
|
147
|
+
label = Int(1)
|
|
148
|
+
|
|
149
|
+
def apply_filter(self, data, voxelsize):
|
|
150
|
+
mask = (data == self.label)
|
|
151
|
+
return mask
|
|
152
|
+
|
|
153
|
+
#def completeMetadata(self, im):
|
|
154
|
+
# im.mdh['Processing.SelectedLabel'] = self.label
|
|
155
|
+
|
|
156
|
+
@register_module('SelectLargestLabel')
|
|
157
|
+
class SelectLargestLabel(Filter):
|
|
158
|
+
"""Creates a mask corresponding to all pixels with the largest label
|
|
159
|
+
|
|
160
|
+
NOTE: the input image must be a labeled image (e.g. the output of `Processing.Label`) in which contiguous
|
|
161
|
+
areas have unique integer IDs
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def apply_filter(self, data, voxelsize):
|
|
165
|
+
uni, counts = np.unique(data[data > 0], return_counts=True)
|
|
166
|
+
self.label = uni[np.argmax(counts)]
|
|
167
|
+
mask = (data == self.label)
|
|
168
|
+
return mask
|
|
169
|
+
|
|
170
|
+
#def completeMetadata(self, im):
|
|
171
|
+
# im.mdh['Processing.SelectedLabel'] = self.label
|
|
172
|
+
|
|
173
|
+
@register_module('LocalMaxima')
|
|
174
|
+
class LocalMaxima(Filter):
|
|
175
|
+
"""Detect local maxima in an image.
|
|
176
|
+
|
|
177
|
+
Uses `skimage.feature.peak_local_max` to identify local maxima in an image.
|
|
178
|
+
|
|
179
|
+
"""
|
|
180
|
+
threshold = Float(.3)
|
|
181
|
+
minDistance = Int(10)
|
|
182
|
+
|
|
183
|
+
def apply_filter(self, data, voxelsize):
|
|
184
|
+
import skimage.feature
|
|
185
|
+
im = data.astype('f')/data.max()
|
|
186
|
+
return skimage.feature.peak_local_max(im, threshold_abs = self.threshold, min_distance = self.minDistance, indices=False)
|
|
187
|
+
|
|
188
|
+
# def completeMetadata(self, im):
|
|
189
|
+
# im.mdh['LocalMaxima.threshold'] = self.threshold
|
|
190
|
+
# im.mdh['LocalMaxima.minDistance'] = self.minDistance
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# from PYME.IO.DataSources import BaseDataSource
|
|
194
|
+
# class _OpticFlowDataSource(BaseDataSource.BaseDataSource):
|
|
195
|
+
# def __init__(self, data, filterRadius, supportRadius, regularizationLambda):
|
|
196
|
+
# self.data = data
|
|
197
|
+
# self.filterRadius = filterRadius
|
|
198
|
+
# self.supportRadius = supportRadius
|
|
199
|
+
# self.regularizationLambda = regularizationLambda
|
|
200
|
+
#
|
|
201
|
+
# self.additionalDims = data.additionalDims
|
|
202
|
+
# self.sizeC = data.sizeC
|
|
203
|
+
#
|
|
204
|
+
# def _calc_frame_flow(self, data, i, chanNum):
|
|
205
|
+
#
|
|
206
|
+
#
|
|
207
|
+
# def getSlice(self, ind):
|
|
208
|
+
# """Return the nth 2D slice of the DataSource where the higher dimensions
|
|
209
|
+
# have been flattened.
|
|
210
|
+
#
|
|
211
|
+
# equivalent to indexing contiguous 4D data with data[:,:,ind%data.shape[2], ind/data.shape[3]]
|
|
212
|
+
#
|
|
213
|
+
# e.g. for a 100x100x50x2 DataSource, getSlice(20) would return data[:,:,20,0].squeeze()
|
|
214
|
+
# whereas getSlice(75) would return data[:,:,25, 1].squeeze()
|
|
215
|
+
# """
|
|
216
|
+
#
|
|
217
|
+
# from PYME.Analysis import optic_flow
|
|
218
|
+
# dx, dy = 0, 0
|
|
219
|
+
#
|
|
220
|
+
# print('OF %d' % ind)
|
|
221
|
+
#
|
|
222
|
+
# if ind >= 1:
|
|
223
|
+
# dx, dy = optic_flow.reg_of(self.data.getSlice(ind-1).squeeze(), self.data.getSlice(ind).squeeze(),
|
|
224
|
+
# self.filterRadius, self.supportRadius, self.regularizationLambda)
|
|
225
|
+
# if (ind < (self.data.getNumSlices() - 1)):
|
|
226
|
+
# dx_, dy_ = optic_flow.reg_of(self.data.getSlice(ind).squeeze(), self.data.getSlice(ind+1).squeeze(),
|
|
227
|
+
# self.filterRadius, self.supportRadius, self.regularizationLambda)
|
|
228
|
+
# dx = dx + dx_
|
|
229
|
+
# dy = dy + dy_
|
|
230
|
+
#
|
|
231
|
+
# def getSliceShape(self):
|
|
232
|
+
# """Return the 2D shape of a slice"""
|
|
233
|
+
# return self.data.getSliceShape()
|
|
234
|
+
#
|
|
235
|
+
# def getNumSlices(self):
|
|
236
|
+
# """Return the number of 2D slices. This is the product of the
|
|
237
|
+
# dimensions > 2
|
|
238
|
+
# """
|
|
239
|
+
# raise self.data.getNumSlices
|
|
240
|
+
|
|
241
|
+
@register_module('OpticalFlow')
|
|
242
|
+
class OpticalFlow(ModuleBase):
|
|
243
|
+
filterRadius = Float(1)
|
|
244
|
+
supportRadius = Float(10)
|
|
245
|
+
regularizationLambda = Float(0)
|
|
246
|
+
inputName = Input('input')
|
|
247
|
+
outputNameX = Output('flow_x')
|
|
248
|
+
outputNameY = Output('flow_y')
|
|
249
|
+
|
|
250
|
+
def _calc_frame_flow(self, data, i, chanNum):
|
|
251
|
+
from PYME.Analysis import optic_flow
|
|
252
|
+
dx, dy = 0, 0
|
|
253
|
+
|
|
254
|
+
print('OF %d' % i)
|
|
255
|
+
|
|
256
|
+
if i >= 1:
|
|
257
|
+
dx, dy = optic_flow.reg_of(data[:, :, i - 1, chanNum].squeeze(), data[:, :, i, chanNum].squeeze(),
|
|
258
|
+
self.filterRadius, self.supportRadius, self.regularizationLambda)
|
|
259
|
+
if (i < (data.shape[2] - 1)):
|
|
260
|
+
dx_, dy_ = optic_flow.reg_of(data[:, :, i, chanNum].squeeze(), data[:, :, i + 1, chanNum].squeeze(),
|
|
261
|
+
self.filterRadius, self.supportRadius, self.regularizationLambda)
|
|
262
|
+
dx = dx + dx_
|
|
263
|
+
dy = dy + dy_
|
|
264
|
+
|
|
265
|
+
return dx, dy
|
|
266
|
+
|
|
267
|
+
def calc_flow(self, data, chanNum):
|
|
268
|
+
|
|
269
|
+
flow_x = []
|
|
270
|
+
flow_y = []
|
|
271
|
+
|
|
272
|
+
for i in range(0, data.shape[2]):
|
|
273
|
+
dx, dy = self._calc_frame_flow(data, i, chanNum)
|
|
274
|
+
|
|
275
|
+
flow_x.append(np.atleast_3d(dx))
|
|
276
|
+
flow_y.append(np.atleast_3d(dy))
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
return np.concatenate(flow_x, 2),np.concatenate(flow_y, 2)
|
|
280
|
+
|
|
281
|
+
def _mp_calc_frame_flow(self, data_s, flow_x, flow_y, frames):
|
|
282
|
+
for i in frames:
|
|
283
|
+
dx, dy = self._calc_frame_flow(data_s, i, 0)
|
|
284
|
+
|
|
285
|
+
flow_x[:, :, i] = dx
|
|
286
|
+
flow_y[:, :, i] = dy
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def calc_flow_mp(self, data, chanNum):
|
|
290
|
+
from PYME.util.shmarray import shmarray
|
|
291
|
+
import multiprocessing
|
|
292
|
+
|
|
293
|
+
data_s = shmarray.zeros(list(data.shape[:3] + [1,]))
|
|
294
|
+
#print data_s.shape, data.shape
|
|
295
|
+
data_s[:,:,:,0] = data[:,:,:,chanNum]
|
|
296
|
+
|
|
297
|
+
flow_x = shmarray.zeros(data.shape[:3])
|
|
298
|
+
flow_y = shmarray.zeros(data.shape[:3])
|
|
299
|
+
|
|
300
|
+
nCPUs = multiprocessing.cpu_count()
|
|
301
|
+
|
|
302
|
+
all_frames = range(0, data.shape[2])
|
|
303
|
+
tasks = [all_frames[i::nCPUs] for i in range(nCPUs)]
|
|
304
|
+
|
|
305
|
+
processes = [multiprocessing.Process(target=self._mp_calc_frame_flow, args=(data_s, flow_x, flow_y, frames))
|
|
306
|
+
for frames in tasks]
|
|
307
|
+
|
|
308
|
+
for p in processes:
|
|
309
|
+
p.start()
|
|
310
|
+
|
|
311
|
+
for p in processes:
|
|
312
|
+
p.join()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
return flow_x, flow_y
|
|
316
|
+
|
|
317
|
+
# def execute(self, namespace):
|
|
318
|
+
# import multiprocessing
|
|
319
|
+
# image = namespace[self.inputName]
|
|
320
|
+
# flow_x = []
|
|
321
|
+
# flow_y = []
|
|
322
|
+
# for chanNum in range(image.data.shape[3]):
|
|
323
|
+
# if False:#(image.data.shape[2] > 10) and not multiprocessing.current_process().daemon:
|
|
324
|
+
# #use multiple processes for computation
|
|
325
|
+
# fx, fy = self.calc_flow_mp(image.data, chanNum)
|
|
326
|
+
# else:
|
|
327
|
+
# fx, fy = self.calc_flow(image.data, chanNum)
|
|
328
|
+
|
|
329
|
+
# flow_x.append(fx)
|
|
330
|
+
# flow_y.append(fy)
|
|
331
|
+
|
|
332
|
+
# im = ImageStack(flow_x, titleStub = self.outputNameX)
|
|
333
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
334
|
+
# im.mdh['Parent'] = image.filename
|
|
335
|
+
|
|
336
|
+
# self.completeMetadata(im)
|
|
337
|
+
# namespace[self.outputNameX] = im
|
|
338
|
+
|
|
339
|
+
# im = ImageStack(flow_y, titleStub = self.outputNameY)
|
|
340
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
341
|
+
# im.mdh['Parent'] = image.filename
|
|
342
|
+
|
|
343
|
+
# self.completeMetadata(im)
|
|
344
|
+
# namespace[self.outputNameY] = im
|
|
345
|
+
|
|
346
|
+
def run(self, inputName):
|
|
347
|
+
import multiprocessing
|
|
348
|
+
image = inputName
|
|
349
|
+
flow_x = []
|
|
350
|
+
flow_y = []
|
|
351
|
+
for chanNum in range(image.data.shape[3]):
|
|
352
|
+
if False:#(image.data.shape[2] > 10) and not multiprocessing.current_process().daemon:
|
|
353
|
+
#use multiple processes for computation
|
|
354
|
+
fx, fy = self.calc_flow_mp(image.data, chanNum)
|
|
355
|
+
else:
|
|
356
|
+
fx, fy = self.calc_flow(image.data, chanNum)
|
|
357
|
+
|
|
358
|
+
flow_x.append(fx)
|
|
359
|
+
flow_y.append(fy)
|
|
360
|
+
|
|
361
|
+
imx = ImageStack(flow_x, titleStub = self.outputNameX)
|
|
362
|
+
#imx.mdh.copyEntriesFrom(image.mdh)
|
|
363
|
+
imx.mdh['Parent'] = image.filename
|
|
364
|
+
|
|
365
|
+
imy = ImageStack(flow_y, titleStub = self.outputNameY)
|
|
366
|
+
#imy.mdh.copyEntriesFrom(image.mdh)
|
|
367
|
+
imy.mdh['Parent'] = image.filename
|
|
368
|
+
|
|
369
|
+
return {'outputNameX' : imx, 'outputNameY' : imy}
|
|
370
|
+
|
|
371
|
+
# def completeMetadata(self, im):
|
|
372
|
+
# im.mdh['OpticalFlow.filterRadius'] = self.filterRadius
|
|
373
|
+
# im.mdh['OpticalFlow.supportRadius'] = self.supportRadius
|
|
374
|
+
|
|
375
|
+
@register_module('WavefrontDetection')
|
|
376
|
+
class WavefrontDetection(ModuleBase):
|
|
377
|
+
""" Detect Ca2+ wavefronts by looking at the difference images between two consecutive frames.
|
|
378
|
+
|
|
379
|
+
Wavefront position corresponds to the position of the transient maximum (i.e. the zero-crossing in the temporal
|
|
380
|
+
derivative), detected by finding all pixels where the magnitude of the temporal derivative is less than
|
|
381
|
+
`gradientThreshold`. An intensity threshold is also used to reject areas of the image where temporal changes are due
|
|
382
|
+
to noise alone.
|
|
383
|
+
|
|
384
|
+
Works best on low-pass filtered data.
|
|
385
|
+
"""
|
|
386
|
+
inputName = Input('input')
|
|
387
|
+
intensityThreshold = Float(50)
|
|
388
|
+
gradientThreshold = Float(0.5)
|
|
389
|
+
outputName = Output('wavefronts')
|
|
390
|
+
|
|
391
|
+
# def execute(self, namespace):
|
|
392
|
+
# from skimage.morphology import skeletonize
|
|
393
|
+
# img = namespace[self.inputName]
|
|
394
|
+
|
|
395
|
+
# data = img.data[:,:,:]
|
|
396
|
+
|
|
397
|
+
# out = np.zeros_like(data)
|
|
398
|
+
# for i in range(1, data.shape[2]):
|
|
399
|
+
# frnt_i = (np.abs(data[:,:,i] - data[:,:,(i-1)]) < self.gradientThreshold)*(data[:,:,i] > self.intensityThreshold)
|
|
400
|
+
# out[:,:,i] = skeletonize(frnt_i.squeeze())
|
|
401
|
+
|
|
402
|
+
# im = ImageStack(out, titleStub=self.outputName)
|
|
403
|
+
# im.mdh.copyEntriesFrom(img.mdh)
|
|
404
|
+
# im.mdh['Parent'] = img.filename
|
|
405
|
+
|
|
406
|
+
# namespace[self.outputName] = im
|
|
407
|
+
|
|
408
|
+
def run(self, inputName):
|
|
409
|
+
from skimage.morphology import skeletonize
|
|
410
|
+
#img = namespace[self.inputName]
|
|
411
|
+
|
|
412
|
+
data = inputName.data[:,:,:]
|
|
413
|
+
|
|
414
|
+
out = np.zeros_like(data)
|
|
415
|
+
for i in range(1, data.shape[2]):
|
|
416
|
+
frnt_i = (np.abs(data[:,:,i] - data[:,:,(i-1)]) < self.gradientThreshold)*(data[:,:,i] > self.intensityThreshold)
|
|
417
|
+
out[:,:,i] = skeletonize(frnt_i.squeeze())
|
|
418
|
+
|
|
419
|
+
im = ImageStack(out, titleStub=self.outputName)
|
|
420
|
+
#im.mdh.copyEntriesFrom(img.mdh)
|
|
421
|
+
im.mdh['Parent'] = iinputName.filename
|
|
422
|
+
return im
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@register_module('WavefrontVelocity')
|
|
427
|
+
class WavefrontVelocity(ModuleBase):
|
|
428
|
+
"""
|
|
429
|
+
Calculates wavefront velocity given a wavefront image and optic flow images
|
|
430
|
+
"""
|
|
431
|
+
inputWavefronts = Input('wavefronts')
|
|
432
|
+
inputFlowX = Input('flow_x')
|
|
433
|
+
inputFlowY = Input('flow_y')
|
|
434
|
+
timeWindow = Int(5)
|
|
435
|
+
outputName = Output('wavefront_velocities')
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
# def execute(self, namespace):
|
|
440
|
+
# from skimage.measure import profile_line
|
|
441
|
+
# print('Calculating wavefront velocities')
|
|
442
|
+
# wavefronts = namespace[self.inputWavefronts]
|
|
443
|
+
|
|
444
|
+
# waves = wavefronts.data
|
|
445
|
+
# flow_x = namespace[self.inputFlowX].data
|
|
446
|
+
# flow_y = namespace[self.inputFlowY].data
|
|
447
|
+
|
|
448
|
+
# velocities = np.zeros(waves.shape, 'f')
|
|
449
|
+
|
|
450
|
+
# wave_coords = []
|
|
451
|
+
# #precompute arrays of wavefront coordinates
|
|
452
|
+
# for i in range(waves.shape[2]):
|
|
453
|
+
# if waves[:, :, i].max() > 0:
|
|
454
|
+
# xp, yp = np.argwhere(waves[:, :, i].squeeze()).T
|
|
455
|
+
|
|
456
|
+
# xf = flow_x[:, :, i][xp, yp]
|
|
457
|
+
# yf = flow_y[:, :, i][xp, yp]
|
|
458
|
+
|
|
459
|
+
# flow_m = np.sqrt(xf * xf + yf * yf)
|
|
460
|
+
# xf = xf / flow_m
|
|
461
|
+
# yf = yf / flow_m
|
|
462
|
+
|
|
463
|
+
# wave_coords.append([xp, yp, xf, yf])
|
|
464
|
+
# else:
|
|
465
|
+
# wave_coords.append([np.empty(0), np.empty(0), np.empty(0), np.empty(0)])
|
|
466
|
+
|
|
467
|
+
# for i in range(waves.shape[2]):
|
|
468
|
+
# xp, yp, xf, yf = wave_coords[i]
|
|
469
|
+
# print('WaveV: %d' % i)
|
|
470
|
+
# #print(len(xp), xp, waves[:,:,i].max())
|
|
471
|
+
# if len(xp) >0:
|
|
472
|
+
# j_vals = range(max(i - self.timeWindow, 0), min(i + self.timeWindow + 1, waves.shape[2]))
|
|
473
|
+
# A = np.vstack([j_vals, np.ones_like(j_vals)]).T
|
|
474
|
+
# #A = np.ones_like(xp)[:,None,None]*A[None,:,:]
|
|
475
|
+
|
|
476
|
+
# ks = np.zeros([len(xp), len(j_vals)])
|
|
477
|
+
# j0 = j_vals[0]
|
|
478
|
+
# I = np.arange(len(xp))
|
|
479
|
+
# for j in j_vals:
|
|
480
|
+
# xp_j, yp_j, xf_j, yf_j = wave_coords[j]
|
|
481
|
+
# if len(xp_j) > 0:
|
|
482
|
+
# k = xf*(-xp[:,None] + xp_j[None,:]) + yf*(-yp[:,None] + yp_j[None,:])
|
|
483
|
+
# km = np.sqrt(k*k)
|
|
484
|
+
# #print k
|
|
485
|
+
# #print km.argmin(1)
|
|
486
|
+
# #print k[I,km.argmin(1)]
|
|
487
|
+
# ks[:, j-j0] = k[I,km.argmin(1)]
|
|
488
|
+
# else:
|
|
489
|
+
# ks[:, j - j0] = np.nan #mask out our matrix for the missing data
|
|
490
|
+
|
|
491
|
+
# #print ks
|
|
492
|
+
# vels = np.zeros_like(xp, 'f')
|
|
493
|
+
# for k in range(len(xp)):
|
|
494
|
+
# kk = ks[k,:]
|
|
495
|
+
# #print kk
|
|
496
|
+
# vels[k] = np.linalg.lstsq(A[~np.isnan(kk),:], kk[~np.isnan(kk)])[0][0]
|
|
497
|
+
|
|
498
|
+
# #print vels.shape, velocities[xp,yp,i].shape
|
|
499
|
+
# velocities[xp, yp, i] = vels[:,None]
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
# # for i in range(waves.shape[2]):
|
|
504
|
+
# # print(i)
|
|
505
|
+
# # if waves[:,:,i].max() > 0:
|
|
506
|
+
# # xp, yp = np.argwhere(waves[:,:,i].squeeze()).T
|
|
507
|
+
# #
|
|
508
|
+
# # xf = flow_x[:,:, i][xp,yp]
|
|
509
|
+
# # yf = flow_y[:,:, i][xp,yp]
|
|
510
|
+
# #
|
|
511
|
+
# # flow_m = np.sqrt(xf*xf + yf*yf)
|
|
512
|
+
# # xf = xf/flow_m
|
|
513
|
+
# # yf = yf/flow_m
|
|
514
|
+
# #
|
|
515
|
+
# # j_vals= range(max(i-self.timeWindow, 0), min(i+ self.timeWindow + 1, waves.shape[2]))
|
|
516
|
+
# # A = np.vstack([j_vals, np.ones_like(j_vals)]).T
|
|
517
|
+
# #
|
|
518
|
+
# # for x_k, y_k, xf_k, yf_k in zip(xp, yp, xf, yf):
|
|
519
|
+
# # prof = []
|
|
520
|
+
# # start, end = (x_k - 50 * xf_k, y_k - 50 * yf_k), (x_k + 50 * xf_k, y_k + 50 * yf_k)
|
|
521
|
+
# # for j in j_vals:
|
|
522
|
+
# # prof.append(np.argmax(profile_line(waves[:,:,j], start, end)))
|
|
523
|
+
# #
|
|
524
|
+
# # prof = np.array(prof, 'f')
|
|
525
|
+
# #
|
|
526
|
+
# # #print j_vals, prof
|
|
527
|
+
# # m, c = np.linalg.lstsq(A[prof>0, :], prof[prof>0])[0]
|
|
528
|
+
# #
|
|
529
|
+
# # velocities[x_k,y_k,i] = m
|
|
530
|
+
|
|
531
|
+
# im = ImageStack(velocities, titleStub=self.outputName)
|
|
532
|
+
# im.mdh.copyEntriesFrom(wavefronts.mdh)
|
|
533
|
+
# im.mdh['Parent'] = wavefronts.filename
|
|
534
|
+
|
|
535
|
+
# namespace[self.outputName] = im
|
|
536
|
+
|
|
537
|
+
def run(self, inputWavefronts, inputFlowX, inputFlowY):
|
|
538
|
+
from skimage.measure import profile_line
|
|
539
|
+
print('Calculating wavefront velocities')
|
|
540
|
+
|
|
541
|
+
waves = inputWavefronts.data
|
|
542
|
+
flow_x = inputFlowX.data
|
|
543
|
+
flow_y = inputFlowY.data
|
|
544
|
+
|
|
545
|
+
velocities = np.zeros(waves.shape, 'f')
|
|
546
|
+
|
|
547
|
+
wave_coords = []
|
|
548
|
+
#precompute arrays of wavefront coordinates
|
|
549
|
+
for i in range(waves.shape[2]):
|
|
550
|
+
if waves[:, :, i].max() > 0:
|
|
551
|
+
xp, yp = np.argwhere(waves[:, :, i].squeeze()).T
|
|
552
|
+
|
|
553
|
+
xf = flow_x[:, :, i][xp, yp]
|
|
554
|
+
yf = flow_y[:, :, i][xp, yp]
|
|
555
|
+
|
|
556
|
+
flow_m = np.sqrt(xf * xf + yf * yf)
|
|
557
|
+
xf = xf / flow_m
|
|
558
|
+
yf = yf / flow_m
|
|
559
|
+
|
|
560
|
+
wave_coords.append([xp, yp, xf, yf])
|
|
561
|
+
else:
|
|
562
|
+
wave_coords.append([np.empty(0), np.empty(0), np.empty(0), np.empty(0)])
|
|
563
|
+
|
|
564
|
+
for i in range(waves.shape[2]):
|
|
565
|
+
xp, yp, xf, yf = wave_coords[i]
|
|
566
|
+
print('WaveV: %d' % i)
|
|
567
|
+
#print(len(xp), xp, waves[:,:,i].max())
|
|
568
|
+
if len(xp) >0:
|
|
569
|
+
j_vals = range(max(i - self.timeWindow, 0), min(i + self.timeWindow + 1, waves.shape[2]))
|
|
570
|
+
A = np.vstack([j_vals, np.ones_like(j_vals)]).T
|
|
571
|
+
#A = np.ones_like(xp)[:,None,None]*A[None,:,:]
|
|
572
|
+
|
|
573
|
+
ks = np.zeros([len(xp), len(j_vals)])
|
|
574
|
+
j0 = j_vals[0]
|
|
575
|
+
I = np.arange(len(xp))
|
|
576
|
+
for j in j_vals:
|
|
577
|
+
xp_j, yp_j, xf_j, yf_j = wave_coords[j]
|
|
578
|
+
if len(xp_j) > 0:
|
|
579
|
+
k = xf*(-xp[:,None] + xp_j[None,:]) + yf*(-yp[:,None] + yp_j[None,:])
|
|
580
|
+
km = np.sqrt(k*k)
|
|
581
|
+
#print k
|
|
582
|
+
#print km.argmin(1)
|
|
583
|
+
#print k[I,km.argmin(1)]
|
|
584
|
+
ks[:, j-j0] = k[I,km.argmin(1)]
|
|
585
|
+
else:
|
|
586
|
+
ks[:, j - j0] = np.nan #mask out our matrix for the missing data
|
|
587
|
+
|
|
588
|
+
#print ks
|
|
589
|
+
vels = np.zeros_like(xp, 'f')
|
|
590
|
+
for k in range(len(xp)):
|
|
591
|
+
kk = ks[k,:]
|
|
592
|
+
#print kk
|
|
593
|
+
vels[k] = np.linalg.lstsq(A[~np.isnan(kk),:], kk[~np.isnan(kk)])[0][0]
|
|
594
|
+
|
|
595
|
+
#print vels.shape, velocities[xp,yp,i].shape
|
|
596
|
+
velocities[xp, yp, i] = vels[:,None]
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
# for i in range(waves.shape[2]):
|
|
601
|
+
# print(i)
|
|
602
|
+
# if waves[:,:,i].max() > 0:
|
|
603
|
+
# xp, yp = np.argwhere(waves[:,:,i].squeeze()).T
|
|
604
|
+
#
|
|
605
|
+
# xf = flow_x[:,:, i][xp,yp]
|
|
606
|
+
# yf = flow_y[:,:, i][xp,yp]
|
|
607
|
+
#
|
|
608
|
+
# flow_m = np.sqrt(xf*xf + yf*yf)
|
|
609
|
+
# xf = xf/flow_m
|
|
610
|
+
# yf = yf/flow_m
|
|
611
|
+
#
|
|
612
|
+
# j_vals= range(max(i-self.timeWindow, 0), min(i+ self.timeWindow + 1, waves.shape[2]))
|
|
613
|
+
# A = np.vstack([j_vals, np.ones_like(j_vals)]).T
|
|
614
|
+
#
|
|
615
|
+
# for x_k, y_k, xf_k, yf_k in zip(xp, yp, xf, yf):
|
|
616
|
+
# prof = []
|
|
617
|
+
# start, end = (x_k - 50 * xf_k, y_k - 50 * yf_k), (x_k + 50 * xf_k, y_k + 50 * yf_k)
|
|
618
|
+
# for j in j_vals:
|
|
619
|
+
# prof.append(np.argmax(profile_line(waves[:,:,j], start, end)))
|
|
620
|
+
#
|
|
621
|
+
# prof = np.array(prof, 'f')
|
|
622
|
+
#
|
|
623
|
+
# #print j_vals, prof
|
|
624
|
+
# m, c = np.linalg.lstsq(A[prof>0, :], prof[prof>0])[0]
|
|
625
|
+
#
|
|
626
|
+
# velocities[x_k,y_k,i] = m
|
|
627
|
+
|
|
628
|
+
im = ImageStack(velocities, titleStub=self.outputName)
|
|
629
|
+
im.mdh.copyEntriesFrom(inputWavefronts.mdh) # TODO - needed?
|
|
630
|
+
im.mdh['Parent'] = inputWavefronts.filename
|
|
631
|
+
|
|
632
|
+
return im
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
class CaWave(object):
|
|
636
|
+
default_recipe = '''
|
|
637
|
+
- processing.OpticalFlow:
|
|
638
|
+
filterRadius: 10.0
|
|
639
|
+
inputName: intensity
|
|
640
|
+
outputNameX: flow_x
|
|
641
|
+
outputNameY: flow_y
|
|
642
|
+
regularizationLambda: 0.1
|
|
643
|
+
supportRadius: 30.0
|
|
644
|
+
- filters.GaussianFilter:
|
|
645
|
+
inputName: flow_x
|
|
646
|
+
outputName: flow_xf
|
|
647
|
+
processFramesIndividually: false
|
|
648
|
+
sigmaX: 1.0
|
|
649
|
+
sigmaY: 1.0
|
|
650
|
+
sigmaZ: 5.0
|
|
651
|
+
- filters.GaussianFilter:
|
|
652
|
+
inputName: flow_y
|
|
653
|
+
outputName: flow_yf
|
|
654
|
+
processFramesIndividually: false
|
|
655
|
+
sigmaX: 1.0
|
|
656
|
+
sigmaY: 1.0
|
|
657
|
+
sigmaZ: 5.0
|
|
658
|
+
- processing.WavefrontVelocity:
|
|
659
|
+
inputFlowX: flow_xf
|
|
660
|
+
inputFlowY: flow_yf
|
|
661
|
+
inputWavefronts: wavefronts
|
|
662
|
+
outputName: wavefront_velocities
|
|
663
|
+
timeWindow: 5
|
|
664
|
+
- measurement.ImageHistogram:
|
|
665
|
+
inputImage: wavefront_velocities
|
|
666
|
+
inputMask: wavefronts
|
|
667
|
+
left: 0.0
|
|
668
|
+
nbins: 50
|
|
669
|
+
outputName: velocity_histogram
|
|
670
|
+
right: 16.0
|
|
671
|
+
normalize: True
|
|
672
|
+
- processing.VectorfieldAngle:
|
|
673
|
+
inputX: flow_xf
|
|
674
|
+
inputY: flow_yf
|
|
675
|
+
inputZ: ''
|
|
676
|
+
outputPhi: phi
|
|
677
|
+
outputTheta: theta
|
|
678
|
+
- measurement.ImageHistogram:
|
|
679
|
+
inputImage: theta
|
|
680
|
+
inputMask: wavefronts
|
|
681
|
+
left: -3.15
|
|
682
|
+
nbins: 120
|
|
683
|
+
outputName: angle_hist
|
|
684
|
+
right: 3.15
|
|
685
|
+
normalize: True
|
|
686
|
+
'''
|
|
687
|
+
def __init__(self, wavefronts, intensity, trange, recipe=''):
|
|
688
|
+
from PYME.recipes import Recipe
|
|
689
|
+
|
|
690
|
+
self.trange = trange
|
|
691
|
+
|
|
692
|
+
if recipe == '':
|
|
693
|
+
recipe = self.default_recipe
|
|
694
|
+
|
|
695
|
+
self._mc = Recipe.fromYAML(recipe)
|
|
696
|
+
|
|
697
|
+
print('Executing wave sub-recipe')
|
|
698
|
+
|
|
699
|
+
self._mc.execute(wavefronts=wavefronts, intensity=intensity)
|
|
700
|
+
|
|
701
|
+
print('wave sub-recipe done')
|
|
702
|
+
|
|
703
|
+
@property
|
|
704
|
+
def start_frame(self):
|
|
705
|
+
return int(self.trange[0])
|
|
706
|
+
|
|
707
|
+
@property
|
|
708
|
+
def end_frame(self):
|
|
709
|
+
return int(self.trange[1])
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
@property
|
|
713
|
+
def direction_plot(self):
|
|
714
|
+
import matplotlib.pyplot as plt
|
|
715
|
+
import mpld3
|
|
716
|
+
import warnings
|
|
717
|
+
if warnings.filters[0] == ('always', None, DeprecationWarning, None, 0):
|
|
718
|
+
#mpld3 has messed with warnings - undo
|
|
719
|
+
warnings.filters.pop(0)
|
|
720
|
+
|
|
721
|
+
plt.ioff()
|
|
722
|
+
f = plt.figure(figsize=(4, 3))
|
|
723
|
+
|
|
724
|
+
bins = self._mc.namespace['angle_hist']['bins']
|
|
725
|
+
counts = self._mc.namespace['angle_hist']['counts']
|
|
726
|
+
|
|
727
|
+
plt.polar(bins, counts)
|
|
728
|
+
|
|
729
|
+
plt.title('Propagation direction')
|
|
730
|
+
|
|
731
|
+
plt.tight_layout(pad=2)
|
|
732
|
+
|
|
733
|
+
plt.ion()
|
|
734
|
+
|
|
735
|
+
ret = mpld3.fig_to_html(f)
|
|
736
|
+
|
|
737
|
+
plt.close(f)
|
|
738
|
+
|
|
739
|
+
return ret
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
@property
|
|
743
|
+
def direction_data(self):
|
|
744
|
+
import json
|
|
745
|
+
|
|
746
|
+
return json.dumps(np.array([self._mc.namespace['angle_hist']['bins'],
|
|
747
|
+
self._mc.namespace['angle_hist']['counts']]).T.tolist())
|
|
748
|
+
|
|
749
|
+
@property
|
|
750
|
+
def velocity_plot(self):
|
|
751
|
+
import matplotlib.pyplot as plt
|
|
752
|
+
import mpld3
|
|
753
|
+
import warnings
|
|
754
|
+
if warnings.filters[0] == ('always', None, DeprecationWarning, None, 0):
|
|
755
|
+
#mpld3 has messed with warnings - undo
|
|
756
|
+
warnings.filters.pop(0)
|
|
757
|
+
|
|
758
|
+
plt.ioff()
|
|
759
|
+
f = plt.figure(figsize=(4, 3))
|
|
760
|
+
|
|
761
|
+
bins = self._mc.namespace['velocity_histogram']['bins']
|
|
762
|
+
counts = self._mc.namespace['velocity_histogram']['counts']
|
|
763
|
+
|
|
764
|
+
plt.bar(bins, counts, width=(bins[1] - bins[0]))
|
|
765
|
+
plt.xlabel('Velocity [pixels/frame]')
|
|
766
|
+
plt.ylabel('Frequency')
|
|
767
|
+
plt.title('Velocity distribution')
|
|
768
|
+
|
|
769
|
+
plt.tight_layout(pad=2)
|
|
770
|
+
|
|
771
|
+
plt.ion()
|
|
772
|
+
|
|
773
|
+
ret = mpld3.fig_to_html(f)
|
|
774
|
+
|
|
775
|
+
plt.close(f)
|
|
776
|
+
|
|
777
|
+
return ret
|
|
778
|
+
|
|
779
|
+
@property
|
|
780
|
+
def velocity_data(self):
|
|
781
|
+
import json
|
|
782
|
+
|
|
783
|
+
return json.dumps(np.array([self._mc.namespace['velocity_histogram']['bins'], self._mc.namespace['velocity_histogram']['counts']]).T.tolist())
|
|
784
|
+
|
|
785
|
+
@property
|
|
786
|
+
def wavefront_image(self):
|
|
787
|
+
import matplotlib.pyplot as plt
|
|
788
|
+
from io import BytesIO
|
|
789
|
+
|
|
790
|
+
try:
|
|
791
|
+
from PIL import Image
|
|
792
|
+
except ImportError:
|
|
793
|
+
import Image
|
|
794
|
+
|
|
795
|
+
wavefronts = self._mc.namespace['wavefronts'].data
|
|
796
|
+
|
|
797
|
+
nFrames = wavefronts.getNumSlices()
|
|
798
|
+
|
|
799
|
+
sx, sy = wavefronts.getSliceShape()
|
|
800
|
+
|
|
801
|
+
out = np.zeros([sx, sy, 3])
|
|
802
|
+
|
|
803
|
+
for i in range(nFrames):
|
|
804
|
+
c = np.array(plt.cm.jet(float(i)/float(nFrames))[:3])
|
|
805
|
+
#print c
|
|
806
|
+
#print out.shape, wavefronts.getSlice(i)[:,:,None].shape, c.shape
|
|
807
|
+
out += wavefronts.getSlice(i)[:,:,None]*c[None, None, :]
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
outf = BytesIO()
|
|
811
|
+
|
|
812
|
+
Image.fromarray((255*out).astype('uint8')).save(outf, 'PNG')
|
|
813
|
+
|
|
814
|
+
s = outf.getvalue()
|
|
815
|
+
|
|
816
|
+
outf.close()
|
|
817
|
+
return s
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
@property
|
|
821
|
+
def velocity_image(self):
|
|
822
|
+
import matplotlib.pyplot as plt
|
|
823
|
+
from io import BytesIO
|
|
824
|
+
|
|
825
|
+
try:
|
|
826
|
+
from PIL import Image
|
|
827
|
+
except ImportError:
|
|
828
|
+
import Image
|
|
829
|
+
|
|
830
|
+
wavefronts = self._mc.namespace['wavefronts'].data
|
|
831
|
+
velocities = self._mc.namespace['wavefront_velocities'].data
|
|
832
|
+
v_max = float(velocities[:,:,:].max())
|
|
833
|
+
|
|
834
|
+
nFrames = wavefronts.getNumSlices()
|
|
835
|
+
|
|
836
|
+
sx, sy = wavefronts.getSliceShape()
|
|
837
|
+
|
|
838
|
+
out = np.zeros([sx, sy, 3])
|
|
839
|
+
|
|
840
|
+
for i in range(nFrames):
|
|
841
|
+
out += wavefronts.getSlice(i)[:, :, None] * plt.cm.jet(velocities.getSlice(i)/v_max)[:,:,:3]
|
|
842
|
+
|
|
843
|
+
outf = BytesIO()
|
|
844
|
+
|
|
845
|
+
Image.fromarray((255 * out).astype('uint8')).save(outf, 'PNG')
|
|
846
|
+
|
|
847
|
+
s = outf.getvalue()
|
|
848
|
+
|
|
849
|
+
outf.close()
|
|
850
|
+
return s
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
@register_module('FindCaWaves')
|
|
854
|
+
class FindCaWaves(ModuleBase):
|
|
855
|
+
'''
|
|
856
|
+
Finds contiguous calcium wave events from detected wavefronts.
|
|
857
|
+
'''
|
|
858
|
+
inputWavefronts = Input('wavefronts')
|
|
859
|
+
inputIntensity = Input('intensity')
|
|
860
|
+
|
|
861
|
+
waveRecipeFileName = CStr('')
|
|
862
|
+
|
|
863
|
+
minWaveFrames = Int(5)
|
|
864
|
+
minActivePixels = Int(10)
|
|
865
|
+
|
|
866
|
+
outputName = Output('waves')
|
|
867
|
+
|
|
868
|
+
# def execute(self, namespace):
|
|
869
|
+
# from scipy import ndimage
|
|
870
|
+
# from PYME.IO.DataSources import CropDataSource
|
|
871
|
+
# print('Finding Ca Waves ...')
|
|
872
|
+
# wavefronts = namespace[self.inputWavefronts] #segmented wavefront mask
|
|
873
|
+
# intensity = namespace[self.inputIntensity]
|
|
874
|
+
# wavefront_I = np.array([wavefronts.data.getSlice(i).sum() for i in range(wavefronts.data.getNumSlices())]).squeeze()
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
# #a wave is a contiguous region of non-zero wavefronts
|
|
879
|
+
# wave_labels, nWaves = ndimage.label(wavefront_I > float(self.minActivePixels))
|
|
880
|
+
|
|
881
|
+
# print('Detected %d wave candidates' % nWaves)
|
|
882
|
+
|
|
883
|
+
# waves = []
|
|
884
|
+
|
|
885
|
+
# #print(wave_labels, nWaves)
|
|
886
|
+
|
|
887
|
+
# for i in range(nWaves):
|
|
888
|
+
# wv_idx = np.argwhere(wave_labels == (i+1))
|
|
889
|
+
|
|
890
|
+
# print('wave%d: wave at %d-%d' % (i, wv_idx[0], wv_idx[-1]))
|
|
891
|
+
|
|
892
|
+
# if len(wv_idx) >= self.minWaveFrames:
|
|
893
|
+
|
|
894
|
+
# trange = (wv_idx[0], wv_idx[-1])
|
|
895
|
+
# # TODO - Fix to use new and improved XYZTC CropDataSource.DataSource
|
|
896
|
+
# cropped_wavefronts = ImageStack(CropDataSource._DataSource(wavefronts.data, trange=trange),
|
|
897
|
+
# mdh=getattr(wavefronts, 'mdh', None))
|
|
898
|
+
# cropped_intensity = ImageStack(CropDataSource._DataSource(intensity.data, trange=trange),
|
|
899
|
+
# mdh=getattr(intensity, 'mdh', None))
|
|
900
|
+
# waves.append(CaWave(cropped_wavefronts, cropped_intensity, trange))
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
# namespace[self.outputName] = waves
|
|
904
|
+
|
|
905
|
+
def run(self, inputWavefronts, inputIntensity):
|
|
906
|
+
from scipy import ndimage
|
|
907
|
+
from PYME.IO.DataSources import CropDataSource
|
|
908
|
+
print('Finding Ca Waves ...')
|
|
909
|
+
|
|
910
|
+
wavefront_I = np.array([inputWavefronts.data.getSlice(i).sum() for i in range(inputWavefronts.data.getNumSlices())]).squeeze()
|
|
911
|
+
|
|
912
|
+
#a wave is a contiguous region of non-zero wavefronts
|
|
913
|
+
wave_labels, nWaves = ndimage.label(wavefront_I > float(self.minActivePixels))
|
|
914
|
+
|
|
915
|
+
print('Detected %d wave candidates' % nWaves)
|
|
916
|
+
|
|
917
|
+
waves = []
|
|
918
|
+
|
|
919
|
+
#print(wave_labels, nWaves)
|
|
920
|
+
|
|
921
|
+
for i in range(nWaves):
|
|
922
|
+
wv_idx = np.argwhere(wave_labels == (i+1))
|
|
923
|
+
|
|
924
|
+
print('wave%d: wave at %d-%d' % (i, wv_idx[0], wv_idx[-1]))
|
|
925
|
+
|
|
926
|
+
if len(wv_idx) >= self.minWaveFrames:
|
|
927
|
+
|
|
928
|
+
trange = (wv_idx[0], wv_idx[-1])
|
|
929
|
+
# TODO - Fix to use new and improved XYZTC CropDataSource.DataSource
|
|
930
|
+
cropped_wavefronts = ImageStack(CropDataSource._DataSource(inputWavefronts.data, trange=trange),
|
|
931
|
+
mdh=getattr(inputWavefronts, 'mdh', None))
|
|
932
|
+
cropped_intensity = ImageStack(CropDataSource._DataSource(inputIntensity.data, trange=trange),
|
|
933
|
+
mdh=getattr(inputIntensity, 'mdh', None))
|
|
934
|
+
waves.append(CaWave(cropped_wavefronts, cropped_intensity, trange))
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
return waves
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
@register_module('Gradient')
|
|
946
|
+
class Gradient2D(ModuleBase):
|
|
947
|
+
"""
|
|
948
|
+
Calculate the gradient along x and y for each channel of an ImageStack
|
|
949
|
+
|
|
950
|
+
Parameters
|
|
951
|
+
----------
|
|
952
|
+
inputName : PYME.IO.image.ImageStack
|
|
953
|
+
input image
|
|
954
|
+
units : Enum
|
|
955
|
+
specify whether to return gradient in units of intensity/pixel or
|
|
956
|
+
intensity/um. Note that intensity/um will account for anisotropic
|
|
957
|
+
voxels, while the per pixel in intensity/pixel can be direction
|
|
958
|
+
dependent.
|
|
959
|
+
"""
|
|
960
|
+
inputName = Input('input')
|
|
961
|
+
outputNameX = Output('grad_x')
|
|
962
|
+
outputNameY = Output('grad_y')
|
|
963
|
+
units = Enum(['intensity/pixel', 'intensity/um'])
|
|
964
|
+
|
|
965
|
+
def calc_grad(self, data, chanNum):
|
|
966
|
+
grad_x = []
|
|
967
|
+
grad_y = []
|
|
968
|
+
|
|
969
|
+
for i in range(0, data.shape[2]):
|
|
970
|
+
dx, dy = np.gradient(data[:,:,i, chanNum].squeeze())
|
|
971
|
+
grad_x.append(np.atleast_3d(dx))
|
|
972
|
+
grad_y.append(np.atleast_3d(dy))
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
return np.concatenate(grad_x, 2),np.concatenate(grad_y, 2)
|
|
976
|
+
|
|
977
|
+
# def execute(self, namespace):
|
|
978
|
+
# image = namespace[self.inputName]
|
|
979
|
+
# grad_x = []
|
|
980
|
+
# grad_y = []
|
|
981
|
+
# for chanNum in range(image.data.shape[3]):
|
|
982
|
+
# fx, fy = self.calc_grad(image.data, chanNum)
|
|
983
|
+
# if self.units == 'intensity/um':
|
|
984
|
+
# fx /= (image.voxelsize_nm.x / 1e3) # [data/pix] -> [data/um]
|
|
985
|
+
# fy /= (image.voxelsize_nm.y / 1e3)
|
|
986
|
+
# grad_x.append(fx)
|
|
987
|
+
# grad_y.append(fy)
|
|
988
|
+
|
|
989
|
+
# im = ImageStack(grad_x, titleStub = self.outputNameX)
|
|
990
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
991
|
+
# im.mdh['Parent'] = image.filename
|
|
992
|
+
|
|
993
|
+
# #self.completeMetadata(im)
|
|
994
|
+
# namespace[self.outputNameX] = im
|
|
995
|
+
|
|
996
|
+
# im = ImageStack(grad_y, titleStub = self.outputNameY)
|
|
997
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
998
|
+
# im.mdh['Parent'] = image.filename
|
|
999
|
+
|
|
1000
|
+
# #self.completeMetadata(im)
|
|
1001
|
+
# namespace[self.outputNameY] = im
|
|
1002
|
+
|
|
1003
|
+
def run(self, inputName):
|
|
1004
|
+
grad_x = []
|
|
1005
|
+
grad_y = []
|
|
1006
|
+
for chanNum in range(inputName.data.shape[3]):
|
|
1007
|
+
fx, fy = self.calc_grad(inputName.data, chanNum)
|
|
1008
|
+
if self.units == 'intensity/um':
|
|
1009
|
+
fx /= (inputName.voxelsize_nm.x / 1e3) # [data/pix] -> [data/um]
|
|
1010
|
+
fy /= (inputName.voxelsize_nm.y / 1e3)
|
|
1011
|
+
grad_x.append(fx)
|
|
1012
|
+
grad_y.append(fy)
|
|
1013
|
+
|
|
1014
|
+
imx = ImageStack(grad_x, titleStub = self.outputNameX)
|
|
1015
|
+
#im.mdh.copyEntriesFrom(inputName.mdh)
|
|
1016
|
+
imx.mdh['Parent'] = inputName.filename
|
|
1017
|
+
|
|
1018
|
+
imy = ImageStack(grad_y, titleStub = self.outputNameY)
|
|
1019
|
+
#im.mdh.copyEntriesFrom(inputName.mdh)
|
|
1020
|
+
imy.mdh['Parent'] = inputName.filename
|
|
1021
|
+
|
|
1022
|
+
return {'outputNameX' : imx, 'outputNameY' : imy}
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
@register_module('Gradient3D')
|
|
1026
|
+
class Gradient3D(ModuleBase):
|
|
1027
|
+
"""
|
|
1028
|
+
Calculate the gradient along x, y, and z for each channel of an ImageStack
|
|
1029
|
+
|
|
1030
|
+
Parameters
|
|
1031
|
+
----------
|
|
1032
|
+
inputName : PYME.IO.image.ImageStack
|
|
1033
|
+
input image
|
|
1034
|
+
units : Enum
|
|
1035
|
+
specify whether to return gradient in units of intensity/pixel or
|
|
1036
|
+
intensity/um. Note that intensity/um will account for anisotropic
|
|
1037
|
+
voxels, while the per pixel in intensity/pixel can be direction
|
|
1038
|
+
dependent.
|
|
1039
|
+
"""
|
|
1040
|
+
inputName = Input('input')
|
|
1041
|
+
outputNameX = Output('grad_x')
|
|
1042
|
+
outputNameY = Output('grad_y')
|
|
1043
|
+
outputNameZ = Output('grad_z')
|
|
1044
|
+
units = Enum(['intensity/pixel', 'intensity/um'])
|
|
1045
|
+
|
|
1046
|
+
def calc_grad(self, data, chanNum):
|
|
1047
|
+
dx, dy, dz = np.gradient(np.atleast_3d(data[:,:,:,chanNum].squeeze()))
|
|
1048
|
+
|
|
1049
|
+
return dx, dy, dz
|
|
1050
|
+
|
|
1051
|
+
# def execute(self, namespace):
|
|
1052
|
+
# image = namespace[self.inputName]
|
|
1053
|
+
# grad_x = []
|
|
1054
|
+
# grad_y = []
|
|
1055
|
+
# grad_z = []
|
|
1056
|
+
|
|
1057
|
+
# for chanNum in range(image.data.shape[3]):
|
|
1058
|
+
# fx, fy, fz = self.calc_grad(image.data, chanNum)
|
|
1059
|
+
# if self.units == 'intensity/um':
|
|
1060
|
+
# fx /= (image.voxelsize_nm.x / 1e3) # [data/pix] -> [data/um]
|
|
1061
|
+
# fy /= (image.voxelsize_nm.y / 1e3)
|
|
1062
|
+
# fz /= (image.voxelsize_nm.z / 1e3)
|
|
1063
|
+
# grad_x.append(fx)
|
|
1064
|
+
# grad_y.append(fy)
|
|
1065
|
+
# grad_z.append(fz)
|
|
1066
|
+
|
|
1067
|
+
# im = ImageStack(grad_x, titleStub=self.outputNameX)
|
|
1068
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
1069
|
+
# namespace[self.outputNameX] = im
|
|
1070
|
+
|
|
1071
|
+
# im = ImageStack(grad_y, titleStub=self.outputNameY)
|
|
1072
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
1073
|
+
# namespace[self.outputNameY] = im
|
|
1074
|
+
|
|
1075
|
+
# im = ImageStack(grad_z, titleStub=self.outputNameY)
|
|
1076
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
1077
|
+
# namespace[self.outputNameZ] = im
|
|
1078
|
+
|
|
1079
|
+
def run(self, inputName):
|
|
1080
|
+
grad_x = []
|
|
1081
|
+
grad_y = []
|
|
1082
|
+
grad_z = []
|
|
1083
|
+
|
|
1084
|
+
for chanNum in range(inputName.data.shape[3]):
|
|
1085
|
+
fx, fy, fz = self.calc_grad(inputName.data, chanNum)
|
|
1086
|
+
if self.units == 'intensity/um':
|
|
1087
|
+
fx /= (inputName.voxelsize_nm.x / 1e3) # [data/pix] -> [data/um]
|
|
1088
|
+
fy /= (inputName.voxelsize_nm.y / 1e3)
|
|
1089
|
+
fz /= (inputName.voxelsize_nm.z / 1e3)
|
|
1090
|
+
grad_x.append(fx)
|
|
1091
|
+
grad_y.append(fy)
|
|
1092
|
+
grad_z.append(fz)
|
|
1093
|
+
|
|
1094
|
+
return {'outputNameX' : ImageStack(grad_x, titleStub=self.outputNameX),
|
|
1095
|
+
'outputNameY' : ImageStack(grad_y, titleStub=self.outputNameY),
|
|
1096
|
+
'outputNameZ' : ImageStack(grad_z, titleStub=self.outputNameZ)}
|
|
1097
|
+
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
@register_module('DirectionToMask3D')
|
|
1101
|
+
class DirectionToMask3D(ModuleBase):
|
|
1102
|
+
"""
|
|
1103
|
+
Estimates the direction from a pixel to the edge of a mask.
|
|
1104
|
+
"""
|
|
1105
|
+
inputName = Input('input')
|
|
1106
|
+
outputNameX = Output('grad_x')
|
|
1107
|
+
outputNameY = Output('grad_y')
|
|
1108
|
+
outputNameZ = Output('grad_z')
|
|
1109
|
+
|
|
1110
|
+
kernelSize = Int(7)
|
|
1111
|
+
|
|
1112
|
+
def calc_grad(self, data, chanNum):
|
|
1113
|
+
from scipy import ndimage
|
|
1114
|
+
|
|
1115
|
+
data = np.atleast_3d(data[:,:,:,chanNum].squeeze())
|
|
1116
|
+
|
|
1117
|
+
ks = float(self.kernelSize)
|
|
1118
|
+
X,Y,Z = np.mgrid[-ks:(ks+1), -ks:(ks+1), -ks:(ks+1)]
|
|
1119
|
+
R = np.sqrt(X*X + Y*Y + Z*Z)
|
|
1120
|
+
|
|
1121
|
+
kernel_norm = 1.0/R
|
|
1122
|
+
kernel_norm[ks,ks,ks] = 0
|
|
1123
|
+
|
|
1124
|
+
kernel_x = X/(R*R)
|
|
1125
|
+
kernel_x[ks, ks, ks] = 0
|
|
1126
|
+
|
|
1127
|
+
kernel_y = Y / (R * R)
|
|
1128
|
+
kernel_y[ks, ks, ks] = 0
|
|
1129
|
+
|
|
1130
|
+
kernel_z = Z / (R * R)
|
|
1131
|
+
kernel_z[ks, ks, ks] = 0
|
|
1132
|
+
|
|
1133
|
+
norm = np.maximum(0.01, ndimage.convolve(data, kernel_norm))
|
|
1134
|
+
|
|
1135
|
+
dx = ndimage.convolve(data, kernel_x)/norm
|
|
1136
|
+
dy = ndimage.convolve(data, kernel_y) / norm
|
|
1137
|
+
dz = ndimage.convolve(data, kernel_z) / norm
|
|
1138
|
+
|
|
1139
|
+
norm2 = np.maximum(.01, np.sqrt(dx*dx + dy*dy + dz*dz))
|
|
1140
|
+
|
|
1141
|
+
return dx/norm2, dy/norm2, dz/norm2
|
|
1142
|
+
|
|
1143
|
+
# def execute(self, namespace):
|
|
1144
|
+
# image = namespace[self.inputName]
|
|
1145
|
+
# grad_x = []
|
|
1146
|
+
# grad_y = []
|
|
1147
|
+
# grad_z = []
|
|
1148
|
+
|
|
1149
|
+
# for chanNum in range(image.data.shape[3]):
|
|
1150
|
+
# fx, fy, fz = self.calc_grad(image.data, chanNum)
|
|
1151
|
+
# grad_x.append(fx)
|
|
1152
|
+
# grad_y.append(fy)
|
|
1153
|
+
# grad_z.append(fz)
|
|
1154
|
+
|
|
1155
|
+
# im = ImageStack(grad_x, titleStub=self.outputNameX)
|
|
1156
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
1157
|
+
# namespace[self.outputNameX] = im
|
|
1158
|
+
|
|
1159
|
+
# im = ImageStack(grad_y, titleStub=self.outputNameY)
|
|
1160
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
1161
|
+
# namespace[self.outputNameY] = im
|
|
1162
|
+
|
|
1163
|
+
# im = ImageStack(grad_z, titleStub=self.outputNameY)
|
|
1164
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
1165
|
+
# namespace[self.outputNameZ] = im
|
|
1166
|
+
|
|
1167
|
+
def run(self, inputName):
|
|
1168
|
+
grad_x = []
|
|
1169
|
+
grad_y = []
|
|
1170
|
+
grad_z = []
|
|
1171
|
+
|
|
1172
|
+
for chanNum in range(inputName.data.shape[3]):
|
|
1173
|
+
fx, fy, fz = self.calc_grad(inputName.data, chanNum)
|
|
1174
|
+
grad_x.append(fx)
|
|
1175
|
+
grad_y.append(fy)
|
|
1176
|
+
grad_z.append(fz)
|
|
1177
|
+
|
|
1178
|
+
return {'outputNameX' : ImageStack(grad_x, titleStub=self.outputNameX),
|
|
1179
|
+
'outputNameY' : ImageStack(grad_y, titleStub=self.outputNameY),
|
|
1180
|
+
'outputNameZ' : ImageStack(grad_z, titleStub=self.outputNameZ)}
|
|
1181
|
+
|
|
1182
|
+
@register_module('VectorfieldCurl')
|
|
1183
|
+
class VectorfieldCurl(ModuleBase):
|
|
1184
|
+
"""Calculates the curl of a vector field defined by three inputs.
|
|
1185
|
+
|
|
1186
|
+
|
|
1187
|
+
Notes
|
|
1188
|
+
-----
|
|
1189
|
+
|
|
1190
|
+
returns
|
|
1191
|
+
.. math::
|
|
1192
|
+
|
|
1193
|
+
(\frac{\del F_z}{\del y} - \frac{\del F_y}{\del z}, \frac{\del F_x}{\del z} - \frac{\del F_z}{\del x}, \frac{\del F_y}{\del x} - \frac{\del F_x}{\del y})$$
|
|
1194
|
+
|
|
1195
|
+
"""
|
|
1196
|
+
inputX = Input('inp_x')
|
|
1197
|
+
inputY = Input('inp_y')
|
|
1198
|
+
inputZ = Input('inp_z')
|
|
1199
|
+
|
|
1200
|
+
outputX = Output('out_x')
|
|
1201
|
+
outputY = Output('out_y')
|
|
1202
|
+
outputZ = Output('out_z')
|
|
1203
|
+
|
|
1204
|
+
|
|
1205
|
+
# def execute(self, namespace):
|
|
1206
|
+
# Fx = namespace[self.inputX].data[:,:,:,0].squeeze()
|
|
1207
|
+
# Fy = namespace[self.inputY].data[:, :, :, 0].squeeze()
|
|
1208
|
+
# Fz = namespace[self.inputZ].data[:, :, :, 0].squeeze()
|
|
1209
|
+
|
|
1210
|
+
# mdh = namespace[self.inputX].mdh
|
|
1211
|
+
|
|
1212
|
+
# dFzdx, dFzdy, dFzdz = np.gradient(Fz)
|
|
1213
|
+
# dFydx, dFydy, dFydz = np.gradient(Fy)
|
|
1214
|
+
# dFxdx, dFxdy, dFxdz = np.gradient(Fx)
|
|
1215
|
+
|
|
1216
|
+
# im = ImageStack(dFzdy - dFydz, titleStub=self.outputX)
|
|
1217
|
+
# im.mdh.copyEntriesFrom(mdh)
|
|
1218
|
+
# namespace[self.outputX] = im
|
|
1219
|
+
|
|
1220
|
+
# im = ImageStack(dFxdz - dFzdx, titleStub=self.outputY)
|
|
1221
|
+
# im.mdh.copyEntriesFrom(mdh)
|
|
1222
|
+
# namespace[self.outputY] = im
|
|
1223
|
+
|
|
1224
|
+
# im = ImageStack(dFydx - dFzdy, titleStub=self.outputZ)
|
|
1225
|
+
# im.mdh.copyEntriesFrom(mdh)
|
|
1226
|
+
# namespace[self.outputZ] = im
|
|
1227
|
+
|
|
1228
|
+
def run(self, inputX, inputY, inputZ):
|
|
1229
|
+
Fx = inputX.data[:,:,:,0].squeeze()
|
|
1230
|
+
Fy = inputY.data[:, :, :, 0].squeeze()
|
|
1231
|
+
Fz = inputZ.data[:, :, :, 0].squeeze()
|
|
1232
|
+
|
|
1233
|
+
dFzdx, dFzdy, dFzdz = np.gradient(Fz)
|
|
1234
|
+
dFydx, dFydy, dFydz = np.gradient(Fy)
|
|
1235
|
+
dFxdx, dFxdy, dFxdz = np.gradient(Fx)
|
|
1236
|
+
|
|
1237
|
+
return {'outputX' : ImageStack(dFzdy - dFydz, titleStub=self.outputX),
|
|
1238
|
+
'outputY': ImageStack(dFxdz - dFzdx, titleStub=self.outputY),
|
|
1239
|
+
'outputZ' : ImageStack(dFydx - dFzdy, titleStub=self.outputZ)}
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
@register_module('VectorfieldNorm')
|
|
1243
|
+
class VectorfieldNorm(ModuleBase):
|
|
1244
|
+
"""Calculates the norm of a vector field defined by three inputs.
|
|
1245
|
+
|
|
1246
|
+
|
|
1247
|
+
Notes
|
|
1248
|
+
-----
|
|
1249
|
+
|
|
1250
|
+
returns
|
|
1251
|
+
.. math::
|
|
1252
|
+
|
|
1253
|
+
sqrt(x*x + y*y + z*z)
|
|
1254
|
+
|
|
1255
|
+
Also works for 2D vector fields if inputZ is an empty string.
|
|
1256
|
+
"""
|
|
1257
|
+
inputX = Input('inp_x')
|
|
1258
|
+
inputY = Input('inp_y')
|
|
1259
|
+
inputZ = Input('inp_z')
|
|
1260
|
+
|
|
1261
|
+
outputName = Output('output')
|
|
1262
|
+
|
|
1263
|
+
# def execute(self, namespace):
|
|
1264
|
+
# x = namespace[self.inputX].data[:,:,:,0].squeeze()
|
|
1265
|
+
# y = namespace[self.inputY].data[:, :, :, 0].squeeze()
|
|
1266
|
+
# if self.inputZ == '':
|
|
1267
|
+
# z = 0
|
|
1268
|
+
# else:
|
|
1269
|
+
# z = namespace[self.inputZ].data[:, :, :, 0].squeeze()
|
|
1270
|
+
|
|
1271
|
+
# mdh = namespace[self.inputX].mdh
|
|
1272
|
+
|
|
1273
|
+
# norm = np.sqrt(x*x + y*y + z*z)
|
|
1274
|
+
|
|
1275
|
+
# im = ImageStack(norm, titleStub=self.outputName)
|
|
1276
|
+
# im.mdh.copyEntriesFrom(mdh)
|
|
1277
|
+
# namespace[self.outputName] = im
|
|
1278
|
+
|
|
1279
|
+
def run(self, inputX, inputY, inputZ):
|
|
1280
|
+
x = inputX.data[:,:,:,0].squeeze()
|
|
1281
|
+
y = inputY.data[:, :, :, 0].squeeze()
|
|
1282
|
+
if not inputZ:
|
|
1283
|
+
z = 0
|
|
1284
|
+
else:
|
|
1285
|
+
z = inputZ.data[:, :, :, 0].squeeze()
|
|
1286
|
+
|
|
1287
|
+
norm = np.sqrt(x*x + y*y + z*z)
|
|
1288
|
+
|
|
1289
|
+
return ImageStack(norm, titleStub=self.outputName)
|
|
1290
|
+
|
|
1291
|
+
@register_module('VectorfieldAngle')
|
|
1292
|
+
class VectorfieldAngle(ModuleBase):
|
|
1293
|
+
"""Calculates the angle of a vector field.
|
|
1294
|
+
|
|
1295
|
+
Theta is the angle in the x-y plane, and phi is the dip angle
|
|
1296
|
+
|
|
1297
|
+
|
|
1298
|
+
Notes
|
|
1299
|
+
-----
|
|
1300
|
+
|
|
1301
|
+
returns
|
|
1302
|
+
.. math::
|
|
1303
|
+
|
|
1304
|
+
sqrt(x*x + y*y + z*z)
|
|
1305
|
+
|
|
1306
|
+
Also works for 2D vector fields if inputZ is an empty string.
|
|
1307
|
+
"""
|
|
1308
|
+
inputX = Input('inp_x')
|
|
1309
|
+
inputY = Input('inp_y')
|
|
1310
|
+
inputZ = Input('inp_z')
|
|
1311
|
+
|
|
1312
|
+
outputTheta = Output('theta')
|
|
1313
|
+
outputPhi = Output('phi')
|
|
1314
|
+
|
|
1315
|
+
# def execute(self, namespace):
|
|
1316
|
+
# x = namespace[self.inputX].data[:,:,:,0].squeeze()
|
|
1317
|
+
# y = namespace[self.inputY].data[:, :, :, 0].squeeze()
|
|
1318
|
+
|
|
1319
|
+
# theta = np.angle(x + 1j*y)
|
|
1320
|
+
|
|
1321
|
+
# if self.inputZ == '':
|
|
1322
|
+
# z = 0
|
|
1323
|
+
# phi = 0*theta
|
|
1324
|
+
# else:
|
|
1325
|
+
# z = namespace[self.inputZ].data[:, :, :, 0].squeeze()
|
|
1326
|
+
|
|
1327
|
+
# r = np.sqrt(x*x + y*y)
|
|
1328
|
+
|
|
1329
|
+
# phi = np.angle(r + 1j*z)
|
|
1330
|
+
|
|
1331
|
+
# mdh = namespace[self.inputX].mdh
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
# im = ImageStack(theta, titleStub=self.outputTheta)
|
|
1336
|
+
# im.mdh.copyEntriesFrom(mdh)
|
|
1337
|
+
# namespace[self.outputTheta] = im
|
|
1338
|
+
|
|
1339
|
+
# im = ImageStack(phi, titleStub=self.outputPhi)
|
|
1340
|
+
# im.mdh.copyEntriesFrom(mdh)
|
|
1341
|
+
# namespace[self.outputPhi] = im
|
|
1342
|
+
|
|
1343
|
+
def run(self, inputX, inputY, inputZ):
|
|
1344
|
+
x = inputX.data[:,:,:,0].squeeze()
|
|
1345
|
+
y = inputY.data[:, :, :, 0].squeeze()
|
|
1346
|
+
|
|
1347
|
+
theta = np.angle(x + 1j*y)
|
|
1348
|
+
|
|
1349
|
+
if not inputZ:
|
|
1350
|
+
z = 0
|
|
1351
|
+
phi = 0*theta
|
|
1352
|
+
else:
|
|
1353
|
+
z = inputZ.data[:, :, :, 0].squeeze()
|
|
1354
|
+
|
|
1355
|
+
r = np.sqrt(x*x + y*y)
|
|
1356
|
+
|
|
1357
|
+
phi = np.angle(r + 1j*z)
|
|
1358
|
+
|
|
1359
|
+
|
|
1360
|
+
return {'outputTheta' : ImageStack(theta, titleStub=self.outputTheta),
|
|
1361
|
+
'outputPhi': ImageStack(phi, titleStub=self.outputPhi)}
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
|
|
1365
|
+
@register_module('ProjectOnVector')
|
|
1366
|
+
class ProjectOnVector(ModuleBase):
|
|
1367
|
+
"""Project onto a set of direction vectors, producing p and s components"""
|
|
1368
|
+
inputX = Input('inputX')
|
|
1369
|
+
inputY = Input('inputY')
|
|
1370
|
+
inputDirX = Input('dirX')
|
|
1371
|
+
inputDirY = Input('dirY')
|
|
1372
|
+
|
|
1373
|
+
outputNameP = Output('proj_p')
|
|
1374
|
+
outputNameS = Output('proj_s')
|
|
1375
|
+
|
|
1376
|
+
def do_proj(self, inpX, inpY, dirX, dirY):
|
|
1377
|
+
"""project onto basis vectors"""
|
|
1378
|
+
norm = np.sqrt(dirX*dirX + dirY*dirY)
|
|
1379
|
+
dx, dy = dirX/norm, dirY/norm
|
|
1380
|
+
|
|
1381
|
+
projX = inpX*dx + inpY*dy
|
|
1382
|
+
projY = -inpX*dy + inpY*dx
|
|
1383
|
+
|
|
1384
|
+
return projX, projY
|
|
1385
|
+
|
|
1386
|
+
def calc_proj(self, inpX, inpY, dirX, dirY, chanNum):
|
|
1387
|
+
proj_p = []
|
|
1388
|
+
proj_s = []
|
|
1389
|
+
|
|
1390
|
+
for i in range(0, inpX.shape[2]):
|
|
1391
|
+
pp, ps = self.do_proj(inpX[:,:,i, chanNum].squeeze(), inpY[:,:,i, chanNum].squeeze(),
|
|
1392
|
+
dirX[:,:,i, chanNum].squeeze(), dirY[:,:,i, chanNum].squeeze())
|
|
1393
|
+
proj_p.append(np.atleast_3d(pp))
|
|
1394
|
+
proj_s.append(np.atleast_3d(ps))
|
|
1395
|
+
|
|
1396
|
+
|
|
1397
|
+
return np.concatenate(proj_p, 2),np.concatenate(proj_s, 2)
|
|
1398
|
+
|
|
1399
|
+
# def execute(self, namespace):
|
|
1400
|
+
# inpX = namespace[self.inputX]
|
|
1401
|
+
# inpY = namespace[self.inputY]
|
|
1402
|
+
# dirX = namespace[self.inputDirX]
|
|
1403
|
+
# dirY = namespace[self.inputDirY]
|
|
1404
|
+
|
|
1405
|
+
# proj_p = []
|
|
1406
|
+
# proj_s = []
|
|
1407
|
+
# for chanNum in range(inpX.data.shape[3]):
|
|
1408
|
+
# fx, fy = self.calc_proj(inpX.data, inpY.data, dirX.data, dirY.data, chanNum)
|
|
1409
|
+
# proj_p.append(fx)
|
|
1410
|
+
# proj_s.append(fy)
|
|
1411
|
+
|
|
1412
|
+
# im = ImageStack(proj_p, titleStub = self.outputNameP)
|
|
1413
|
+
# im.mdh.copyEntriesFrom(inpX.mdh)
|
|
1414
|
+
# im.mdh['Parent'] = inpX.filename
|
|
1415
|
+
|
|
1416
|
+
# #self.completeMetadata(im)
|
|
1417
|
+
# namespace[self.outputNameP] = im
|
|
1418
|
+
|
|
1419
|
+
# im = ImageStack(proj_s, titleStub = self.outputNameS)
|
|
1420
|
+
# im.mdh.copyEntriesFrom(inpX.mdh)
|
|
1421
|
+
# im.mdh['Parent'] = inpX.filename
|
|
1422
|
+
|
|
1423
|
+
# #self.completeMetadata(im)
|
|
1424
|
+
# namespace[self.outputNameS] = im
|
|
1425
|
+
|
|
1426
|
+
def run(self, inputX, inputY, inputDirX, inputDirY):
|
|
1427
|
+
proj_p = []
|
|
1428
|
+
proj_s = []
|
|
1429
|
+
for chanNum in range(inputX.data.shape[3]):
|
|
1430
|
+
fx, fy = self.calc_proj(inputX.data, inputY.data, inputDirX.data, inputDirY.data, chanNum)
|
|
1431
|
+
proj_p.append(fx)
|
|
1432
|
+
proj_s.append(fy)
|
|
1433
|
+
|
|
1434
|
+
return {'outputNameP' : ImageStack(proj_p, titleStub = self.outputNameP),
|
|
1435
|
+
'outputNameS' : ImageStack(proj_s, titleStub = self.outputNameS)}
|
|
1436
|
+
|
|
1437
|
+
|
|
1438
|
+
|
|
1439
|
+
class PSFFile(FileOrURI):
|
|
1440
|
+
'''Custom trait that verifies that the file can be loaded as a PSF'''
|
|
1441
|
+
|
|
1442
|
+
info_text = 'a file name for a pyme PSF (.tif or .psf)'
|
|
1443
|
+
|
|
1444
|
+
def validate(self, object, name, value):
|
|
1445
|
+
value = FileOrURI.validate(self, object, name, value)
|
|
1446
|
+
|
|
1447
|
+
# Traitsui hangs up if a file doesn't validate correctly and doesn't allow selecting a replacement - disable validation for now :(
|
|
1448
|
+
# FIXME
|
|
1449
|
+
return value
|
|
1450
|
+
|
|
1451
|
+
if value == '':
|
|
1452
|
+
return value
|
|
1453
|
+
|
|
1454
|
+
try:
|
|
1455
|
+
assert(value.endswith('.tif') or value.endswith('.psf')) # is the file a valid psf format?
|
|
1456
|
+
|
|
1457
|
+
# try loading as a PSF
|
|
1458
|
+
object.GetPSF((70., 70., 200.), psfFilename=value)
|
|
1459
|
+
return value
|
|
1460
|
+
except Exception as e:
|
|
1461
|
+
import traceback
|
|
1462
|
+
traceback.print_exc()
|
|
1463
|
+
|
|
1464
|
+
self.error(object, name, value)
|
|
1465
|
+
|
|
1466
|
+
|
|
1467
|
+
|
|
1468
|
+
@register_module('Deconvolve')
|
|
1469
|
+
class Deconvolve(Filter):
|
|
1470
|
+
"""
|
|
1471
|
+
Deconvolves an image using either Richardson-Lucy or Iterative Constrained Tikhonov-Miller (ICTM) deconvolution.
|
|
1472
|
+
|
|
1473
|
+
In general, Richardson-Lucy is preferred for images which are photon limited (e.g. most fluorescence microscopy), while ICTM can be better when the
|
|
1474
|
+
noise is more Gaussian in nature. ICTM has a regularisation parameter which can be adjusted to trade off resolution and noise amplification, and is stable
|
|
1475
|
+
at high iteration numbers. Richardson-Lucy has no regularisation parameter, and is effectively regularised by stopping the iterations early. Our RL implementation
|
|
1476
|
+
starts with a uniform image (which makes for a strong reularisation prior at low iteration counts), and is pretty safe to ~20-50 iterations.
|
|
1477
|
+
|
|
1478
|
+
In addition to resolution enhancement / deblurring, deconvolution can also be a very effective denoising method as
|
|
1479
|
+
it icorporates knowledge of the PSF and noise model. If you are using deconvolution for denoising, 10-20 iterations of RL
|
|
1480
|
+
are usually about right.
|
|
1481
|
+
|
|
1482
|
+
The PSF can be specified in 4 ways:
|
|
1483
|
+
1) Load from a file - this should be a 3D PSF measured from sub-resolution beads, or a synthetically generated PSF. The PSF should be
|
|
1484
|
+
normalised such that it sums to 1. The pixel size of the PSF should ideally match that of the image being deconvolved, but code will
|
|
1485
|
+
resample the PSF to match the image voxel size if necessary.
|
|
1486
|
+
4) Bead - this is a bit special and is provided so that the deconvolution can be used to remove the effect of bead size on a PSF measurement.
|
|
1487
|
+
This PSF is a binary sphere of the specified diameter, sampled at the image voxel size.
|
|
1488
|
+
2) Lorentzian - a 2D Lorentzian PSF is generated with the specified FWHM this is a somewhat crude approximation to a 2D plane through STED PSF
|
|
1489
|
+
and is only supported for 2D deconvolution. Although crude, this can work quite well in practice, especially as propper STED PSFs can be hard to
|
|
1490
|
+
measure (PSF depends on fluorophore and local environment, so beads are not ideal).
|
|
1491
|
+
3) Gaussian - a 2D Gaussian PSF is generated with the specified FWHM - this is an *extremely* crude approximation to a confocal PSF and is only
|
|
1492
|
+
supported for 2D deconvolution (movies). It should only be used for noise removal, not resolution enhancement, but will kill noise with better
|
|
1493
|
+
structural preservation than simple fltering.
|
|
1494
|
+
|
|
1495
|
+
TODO - support synthetic 3D PSFs
|
|
1496
|
+
|
|
1497
|
+
|
|
1498
|
+
Images can be padded to reduce edge artefacts during deconvolution. Padding is performed by mirroring the image content at the edges, and the padding
|
|
1499
|
+
is cropped off after deconvolution. Padding is particularly important for widefield images where there is significant out of focus light, or when the
|
|
1500
|
+
background intensity varies from one side of the image to the other. A width of around 3-5 times the PSF extent is usually sufficient.
|
|
1501
|
+
"""
|
|
1502
|
+
offset = Float(0, desc='Offset to remove from image before deconvolution - this should be an estimate of the background level. Allows the positivity constraint to kick in properly. Use with some care, as if you overestimate the offset you will get artifacts')
|
|
1503
|
+
method = Enum('Richardson-Lucy', 'ICTM')
|
|
1504
|
+
iterations = Int(10, desc='Number of iterations to perform') #number of iterations to perform
|
|
1505
|
+
psfType = Enum('file', 'bead', 'Lorentzian', 'Gaussian')
|
|
1506
|
+
psfFilename = PSFFile('', exists=True, desc='Filename or cluster URI of PSF file. Only used for psfType == file') #only used for psfType == 'file'
|
|
1507
|
+
lorentzianFWHM = Float(50., desc='Lorentzian FWHM in nm, only used for psfType=Lorentzian') #only used for psfType == 'Lorentzian'
|
|
1508
|
+
gaussianFWHM = Float(50., desc='Gaussian FWHM in nm, only used for PSF tupe == Gaussian') #only used for psfType == 'Lorentzian'
|
|
1509
|
+
beadDiameter = Float(200., desc='Bead diameter [nm], only used for psf type bead') #only used for psfType == 'bead'
|
|
1510
|
+
regularisationLambda = Float(0.1, desc='Regularisation parameter [ICTM only]') #Regularisation - ICTM only
|
|
1511
|
+
padding = Int(0, desc='How much to pad the image by (iin pixels) to reduce edge effects') #how much to pad the image by (to reduce edge effects)
|
|
1512
|
+
zPadding = Int(0, desc='Axial padding in pixels (to reduce edge effects)') # padding along the z axis
|
|
1513
|
+
|
|
1514
|
+
overlap = Int(30, descr='Amount to overlap neighbouring blocks by (ignored when not using blocking)')
|
|
1515
|
+
|
|
1516
|
+
# Make deconvolution 3D by default
|
|
1517
|
+
#processFramesIndividually = False
|
|
1518
|
+
dimensionality = Enum('XYZ', 'XY', desc='Which image dimensions should the filter be applied to?')
|
|
1519
|
+
|
|
1520
|
+
_psfCache = {}
|
|
1521
|
+
_decCache = {}
|
|
1522
|
+
|
|
1523
|
+
def _block_overlap(self):
|
|
1524
|
+
return int(self.overlap)
|
|
1525
|
+
|
|
1526
|
+
def default_traits_view(self):
|
|
1527
|
+
from traitsui.api import View, Item, Group
|
|
1528
|
+
from PYME.ui.custom_traits_editors import CBEditor
|
|
1529
|
+
|
|
1530
|
+
return View(Item(name='inputName', editor=CBEditor(choices=self._namespace_keys)),
|
|
1531
|
+
Item(name='outputName'),
|
|
1532
|
+
Item(name='dimensionality'),
|
|
1533
|
+
Group(Item(name='method'),
|
|
1534
|
+
Item(name='iterations'),
|
|
1535
|
+
Item(name='offset'),
|
|
1536
|
+
Item(name='padding'),
|
|
1537
|
+
Item(name='zPadding'),
|
|
1538
|
+
Item(name='regularisationLambda', visible_when='method=="ICTM"'),
|
|
1539
|
+
label='Deconvolution Parameters'),
|
|
1540
|
+
Group(Item(name='psfType'),
|
|
1541
|
+
Item(name='psfFilename', visible_when='psfType=="file"'),
|
|
1542
|
+
Item(name='lorentzianFWHM', visible_when='psfType=="Lorentzian"'),
|
|
1543
|
+
Item(name='gaussianFWHM', visible_when='psfType=="Gaussian"'),
|
|
1544
|
+
Item(name='beadDiameter', visible_when='psfType=="bead"'),
|
|
1545
|
+
label='PSF Parameters'),
|
|
1546
|
+
resizable = True,
|
|
1547
|
+
buttons = [ 'OK' ])
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
def GetPSF(self, vshint, psfFilename=None):
|
|
1552
|
+
from PYME.IO.load_psf import load_psf
|
|
1553
|
+
|
|
1554
|
+
if psfFilename is None:
|
|
1555
|
+
psfFilename = self.psfFilename
|
|
1556
|
+
|
|
1557
|
+
psfKey = (self.psfType, psfFilename, self.lorentzianFWHM, self.gaussianFWHM, self.beadDiameter, vshint, self.processFramesIndividually)
|
|
1558
|
+
|
|
1559
|
+
if not psfKey in self._psfCache.keys():
|
|
1560
|
+
if self.psfType == 'file':
|
|
1561
|
+
psf, vs = load_psf(psfFilename)
|
|
1562
|
+
psf = np.atleast_3d(psf)
|
|
1563
|
+
|
|
1564
|
+
if self.processFramesIndividually and psf.shape[2] > 1:
|
|
1565
|
+
raise RuntimeError('Selected 2D deconvolution but PSF is 3D')
|
|
1566
|
+
elif (not self.processFramesIndividually) and (psf.shape[2] == 1):
|
|
1567
|
+
raise RuntimeError('Selected 3D deconvolution but PSF is 2D')
|
|
1568
|
+
|
|
1569
|
+
vsa = np.array([vs.x, vs.y, vs.z])
|
|
1570
|
+
|
|
1571
|
+
if not np.allclose(vshint, vsa, rtol=.03):
|
|
1572
|
+
psf = ndimage.zoom(psf, vshint/vsa)
|
|
1573
|
+
|
|
1574
|
+
self._psfCache[psfKey] = (psf, vs)
|
|
1575
|
+
elif (self.psfType == 'Lorentzian'):
|
|
1576
|
+
from scipy import stats
|
|
1577
|
+
|
|
1578
|
+
if not self.processFramesIndividually:
|
|
1579
|
+
raise RuntimeError('Lorentzian PSF only supported for 2D deconvolution')
|
|
1580
|
+
|
|
1581
|
+
sc = self.lorentzianFWHM/2.0
|
|
1582
|
+
X, Y = np.mgrid[-30.:31., -30.:31.]
|
|
1583
|
+
R = np.sqrt(X*X + Y*Y)
|
|
1584
|
+
|
|
1585
|
+
if not vshint is None:
|
|
1586
|
+
vx = vshint[0]
|
|
1587
|
+
else:
|
|
1588
|
+
vx = sc/2.
|
|
1589
|
+
|
|
1590
|
+
vs = type('vs', (object,), dict(x=vx, y=vx))
|
|
1591
|
+
|
|
1592
|
+
psf = np.atleast_3d(stats.cauchy.pdf(vx*R, scale=sc))
|
|
1593
|
+
|
|
1594
|
+
self._psfCache[psfKey] = (psf/psf.sum(), vs)
|
|
1595
|
+
|
|
1596
|
+
elif (self.psfType == 'Gaussian'):
|
|
1597
|
+
from scipy import stats
|
|
1598
|
+
|
|
1599
|
+
if not self.processFramesIndividually:
|
|
1600
|
+
raise RuntimeError('Gaussian PSF only supported for 2D deconvolution')
|
|
1601
|
+
|
|
1602
|
+
sc = self.gaussianFWHM/2.35
|
|
1603
|
+
X, Y = np.mgrid[-30.:31., -30.:31.]
|
|
1604
|
+
R = np.sqrt(X*X + Y*Y)
|
|
1605
|
+
|
|
1606
|
+
if not vshint is None:
|
|
1607
|
+
vx = vshint[0]
|
|
1608
|
+
else:
|
|
1609
|
+
vx = sc/2.
|
|
1610
|
+
|
|
1611
|
+
vs = type('vs', (object,), dict(x=vx, y=vx))
|
|
1612
|
+
|
|
1613
|
+
psf = np.atleast_3d(stats.norm.pdf(vx*R, scale=sc))
|
|
1614
|
+
|
|
1615
|
+
self._psfCache[psfKey] = (psf/psf.sum(), vs)
|
|
1616
|
+
elif (self.psfType == 'bead'):
|
|
1617
|
+
from PYME.Deconv import beadGen
|
|
1618
|
+
psf = beadGen.genBeadImage(self.beadDiameter/2, vshint)
|
|
1619
|
+
|
|
1620
|
+
if self.processFramesIndividually:
|
|
1621
|
+
# project our PSF if we are doing a 2D deconvolution.
|
|
1622
|
+
psf=np.atleast_3d(psf.sum(2))
|
|
1623
|
+
|
|
1624
|
+
vs = type('vs', (object,), dict(x=vshint[0], y=vshint[1]))
|
|
1625
|
+
|
|
1626
|
+
self._psfCache[psfKey] = (psf/psf.sum(), vs)
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
return self._psfCache[psfKey]
|
|
1630
|
+
|
|
1631
|
+
def GetDec(self, dp, vshint):
|
|
1632
|
+
"""Get a (potentially cached) deconvolution object"""
|
|
1633
|
+
from PYME.Deconv import dec, richardsonLucy
|
|
1634
|
+
decKey = (self.psfType, self.psfFilename, self.lorentzianFWHM, self.beadDiameter, vshint, dp.shape, self.method)
|
|
1635
|
+
|
|
1636
|
+
if not decKey in self._decCache.keys():
|
|
1637
|
+
psf = self.GetPSF(vshint)[0]
|
|
1638
|
+
|
|
1639
|
+
#create the right deconvolution object
|
|
1640
|
+
if self.method == 'ICTM':
|
|
1641
|
+
if self.psfType == 'bead':
|
|
1642
|
+
dc = dec.dec_bead()
|
|
1643
|
+
else:
|
|
1644
|
+
dc = dec.dec_conv()
|
|
1645
|
+
else:
|
|
1646
|
+
if self.psfType == 'bead':
|
|
1647
|
+
dc = richardsonLucy.rlbead()
|
|
1648
|
+
else:
|
|
1649
|
+
dc = richardsonLucy.dec_conv()
|
|
1650
|
+
|
|
1651
|
+
#resize the PSF to fit, and do any required FFT planning etc ...
|
|
1652
|
+
dc.psf_calc(psf, dp.shape)
|
|
1653
|
+
|
|
1654
|
+
self._decCache[decKey] = dc
|
|
1655
|
+
|
|
1656
|
+
return self._decCache[decKey]
|
|
1657
|
+
|
|
1658
|
+
|
|
1659
|
+
def apply_filter(self, data, voxelsize):
|
|
1660
|
+
d = np.atleast_3d(data.astype('f') - self.offset)
|
|
1661
|
+
#vx, vy, vz = np.array(im.voxelsize)*1e-3
|
|
1662
|
+
|
|
1663
|
+
#Pad the data (if desired)
|
|
1664
|
+
if self.padding > 0:
|
|
1665
|
+
padsize = np.array([self.padding, self.padding, self.zPadding])
|
|
1666
|
+
dp = np.ones(np.array(d.shape) + 2*padsize, 'f')*d.mean()
|
|
1667
|
+
weights = np.zeros_like(dp)
|
|
1668
|
+
px, py, pz = padsize
|
|
1669
|
+
|
|
1670
|
+
dp[px:-px, py:-py, pz:-pz] = d
|
|
1671
|
+
weights[px:-px, py:-py, pz:-pz] = 1.
|
|
1672
|
+
weights = weights.ravel()
|
|
1673
|
+
else: #no padding
|
|
1674
|
+
dp = d
|
|
1675
|
+
weights = 1
|
|
1676
|
+
|
|
1677
|
+
#psf, vs = self.GetPSF(im.voxelsize)
|
|
1678
|
+
|
|
1679
|
+
#Get appropriate deconvolution object
|
|
1680
|
+
dec = self.GetDec(dp, voxelsize)
|
|
1681
|
+
|
|
1682
|
+
#run deconvolution
|
|
1683
|
+
res = dec.deconv(dp, self.regularisationLambda, self.iterations, weights).reshape(dec.shape)
|
|
1684
|
+
|
|
1685
|
+
#crop away the padding
|
|
1686
|
+
if self.padding > 0:
|
|
1687
|
+
res = res[px:-px, py:-py, pz:-pz]
|
|
1688
|
+
|
|
1689
|
+
return res.squeeze()
|
|
1690
|
+
|
|
1691
|
+
# def completeMetadata(self, im):
|
|
1692
|
+
# im.mdh['Deconvolution.Offset'] = self.offset
|
|
1693
|
+
# im.mdh['Deconvolution.Method'] = self.method
|
|
1694
|
+
# im.mdh['Deconvolution.Iterations'] = self.iterations
|
|
1695
|
+
# im.mdh['Deconvolution.PsfType'] = self.psfType
|
|
1696
|
+
# im.mdh['Deconvolution.PSFFilename'] = self.psfFilename
|
|
1697
|
+
# im.mdh['Deconvolution.LorentzianFWHM'] = self.lorentzianFWHM
|
|
1698
|
+
# im.mdh['Deconvolution.BeadDiameter'] = self.beadDiameter
|
|
1699
|
+
# im.mdh['Deconvolution.RegularisationLambda'] = self.regularisationLambda
|
|
1700
|
+
# im.mdh['Deconvolution.Padding'] = self.padding
|
|
1701
|
+
# im.mdh['Deconvolution.ZPadding'] = self.zPadding
|
|
1702
|
+
|
|
1703
|
+
|
|
1704
|
+
@register_module('DeconvolveMotionCompensating')
|
|
1705
|
+
class DeconvolveMotionCompensating(Deconvolve):
|
|
1706
|
+
method = Enum('Richardson-Lucy')
|
|
1707
|
+
processFramesIndividually = Bool(True)
|
|
1708
|
+
flowScale = Float(10)
|
|
1709
|
+
inputFlowX = Input('flow_x')
|
|
1710
|
+
inputFlowY = Input('flow_y')
|
|
1711
|
+
|
|
1712
|
+
# def execute(self, namespace):
|
|
1713
|
+
# self._flow_x = namespace[self.inputFlowX]
|
|
1714
|
+
# self._flow_y = namespace[self.inputFlowY]
|
|
1715
|
+
# namespace[self.outputName] = self.filter(namespace[self.inputName])
|
|
1716
|
+
|
|
1717
|
+
def run(self, inputName, inputFlowX, inputFlowY):
|
|
1718
|
+
self._flow_x, self._flow_y = inputFlowX, inputFlowY
|
|
1719
|
+
return self.filter(inputName)
|
|
1720
|
+
|
|
1721
|
+
def GetDec(self, dp, vshint):
|
|
1722
|
+
"""Get a (potentially cached) deconvolution object"""
|
|
1723
|
+
from PYME.Deconv import richardsonLucyMVM
|
|
1724
|
+
decKey = (self.psfType, self.psfFilename, self.lorentzianFWHM, self.beadDiameter, vshint, dp.shape, self.method)
|
|
1725
|
+
|
|
1726
|
+
if not decKey in self._decCache.keys():
|
|
1727
|
+
psf = self.GetPSF(vshint)[0]
|
|
1728
|
+
|
|
1729
|
+
#create the right deconvolution object
|
|
1730
|
+
if self.psfType == 'bead':
|
|
1731
|
+
dc = richardsonLucyMVM.rlbead()
|
|
1732
|
+
else:
|
|
1733
|
+
dc = richardsonLucyMVM.dec_conv()
|
|
1734
|
+
|
|
1735
|
+
#resize the PSF to fit, and do any required FFT planning etc ...
|
|
1736
|
+
dc.psf_calc(np.atleast_3d(psf), np.atleast_3d(dp).shape)
|
|
1737
|
+
|
|
1738
|
+
self._decCache[decKey] = dc
|
|
1739
|
+
|
|
1740
|
+
return self._decCache[decKey]
|
|
1741
|
+
|
|
1742
|
+
def applyFilter(self, data, chanNum, frNum, im):
|
|
1743
|
+
from PYME.Analysis import optic_flow
|
|
1744
|
+
d = np.atleast_3d(data.astype('f') - self.offset)
|
|
1745
|
+
|
|
1746
|
+
#Pad the data (if desired)
|
|
1747
|
+
if False: #self.padding > 0:
|
|
1748
|
+
padsize = np.array([self.padding, self.padding, self.zPadding])
|
|
1749
|
+
dp = np.ones(np.array(d.shape) + 2 * padsize, 'f') * d.mean()
|
|
1750
|
+
weights = np.zeros_like(dp)
|
|
1751
|
+
px, py, pz = padsize
|
|
1752
|
+
|
|
1753
|
+
dp[px:-px, py:-py, pz:-pz] = d
|
|
1754
|
+
weights[px:-px, py:-py, pz:-pz] = 1.
|
|
1755
|
+
weights = weights.ravel()
|
|
1756
|
+
else: #no padding
|
|
1757
|
+
#dp = d
|
|
1758
|
+
weights = 1
|
|
1759
|
+
|
|
1760
|
+
#Get appropriate deconvolution object
|
|
1761
|
+
rmv = self.GetDec(d, im.voxelsize)
|
|
1762
|
+
|
|
1763
|
+
#mFr = min(frNum + 2, im.data.shape[2] -1)
|
|
1764
|
+
#if frNum < mFr:
|
|
1765
|
+
# dx, dy = optic_flow.reg_of(im.data[:,:,frNum,chanNum].squeeze().astype('f'), im.data[:,:,mFr, chanNum].squeeze().astype('f'),
|
|
1766
|
+
# self.flowFilterRadius, self.flowSupportRadius, self.flowRegularizationLambda)
|
|
1767
|
+
#else:
|
|
1768
|
+
# dx, dy = 0,0
|
|
1769
|
+
|
|
1770
|
+
dx = self._flow_x.data[:,:,frNum].squeeze()
|
|
1771
|
+
dy = self._flow_y.data[:, :, frNum].squeeze()
|
|
1772
|
+
|
|
1773
|
+
|
|
1774
|
+
#run deconvolution
|
|
1775
|
+
mFr = min(frNum + 5, im.data.shape[2])
|
|
1776
|
+
data = np.atleast_3d([im.data[:,:,i, chanNum].astype('f').squeeze() for i in range(frNum,mFr)])
|
|
1777
|
+
#print data.shape
|
|
1778
|
+
print('MC Deconvolution - frame # %d' % frNum)
|
|
1779
|
+
res = rmv.deconv(data,
|
|
1780
|
+
self.regularisationLambda, self.iterations, bg=0, vx = -dx*self.flowScale, vy = -dy*self.flowScale).squeeze().reshape(d.shape)
|
|
1781
|
+
|
|
1782
|
+
#crop away the padding
|
|
1783
|
+
if self.padding > 0:
|
|
1784
|
+
res = res[px:-px, py:-py, pz:-pz]
|
|
1785
|
+
|
|
1786
|
+
return res
|
|
1787
|
+
|
|
1788
|
+
def default_traits_view(self):
|
|
1789
|
+
from traitsui.api import View, Item, Group
|
|
1790
|
+
from PYME.ui.custom_traits_editors import CBEditor
|
|
1791
|
+
|
|
1792
|
+
return View(Item(name='inputName', editor=CBEditor(choices=self._namespace_keys)),
|
|
1793
|
+
Item(name='outputName'),
|
|
1794
|
+
Group(Item(name='method'),
|
|
1795
|
+
Item(name='iterations'),
|
|
1796
|
+
Item(name='offset'),
|
|
1797
|
+
Item(name='padding'),
|
|
1798
|
+
Item(name='zPadding'),
|
|
1799
|
+
Item(name='regularisationLambda', visible_when='method=="ICTM"'),
|
|
1800
|
+
label='Deconvolution Parameters'),
|
|
1801
|
+
Group(Item(name='psfType'),
|
|
1802
|
+
Item(name='psfFilename', visible_when='psfType=="file"'),
|
|
1803
|
+
Item(name='lorentzianFWHM', visible_when='psfType=="Lorentzian"'),
|
|
1804
|
+
Item(name='gaussianFWHM', visible_when='psfType=="Gaussian"'),
|
|
1805
|
+
Item(name='beadDiameter', visible_when='psfType=="bead"'),
|
|
1806
|
+
label='PSF Parameters'),
|
|
1807
|
+
Group(
|
|
1808
|
+
Item(name='flowScale'),
|
|
1809
|
+
label='Flow estimation'),
|
|
1810
|
+
resizable = True,
|
|
1811
|
+
buttons = [ 'OK' ])
|
|
1812
|
+
|
|
1813
|
+
|
|
1814
|
+
|
|
1815
|
+
|
|
1816
|
+
@register_module('DistanceTransform')
|
|
1817
|
+
class DistanceTransform(Filter):
|
|
1818
|
+
def apply_filter(self, data, voxelsize):
|
|
1819
|
+
mask = 1.0*(data > 0.5)
|
|
1820
|
+
voxelsize = np.array(im.voxelsize)[:mask.ndim]
|
|
1821
|
+
dt = -ndimage.distance_transform_edt(data, sampling=voxelsize)
|
|
1822
|
+
dt = dt + ndimage.distance_transform_edt(1 - ndimage.binary_dilation(mask), sampling=voxelsize)
|
|
1823
|
+
return dt
|
|
1824
|
+
|
|
1825
|
+
@register_module('BinaryDilation')
|
|
1826
|
+
class BinaryDilation(Filter):
|
|
1827
|
+
iterations = Int(1)
|
|
1828
|
+
radius = Float(1)
|
|
1829
|
+
|
|
1830
|
+
def apply_filter(self, data, voxelsize):
|
|
1831
|
+
import skimage.morphology
|
|
1832
|
+
|
|
1833
|
+
if len(data.shape) == 3: #3D
|
|
1834
|
+
selem = skimage.morphology.ball(self.radius)
|
|
1835
|
+
else:
|
|
1836
|
+
selem = skimage.morphology.disk(self.radius)
|
|
1837
|
+
return ndimage.binary_dilation(data, selem, iterations=self.iterations)
|
|
1838
|
+
|
|
1839
|
+
@register_module('BinaryErosion')
|
|
1840
|
+
class BinaryErosion(Filter):
|
|
1841
|
+
iterations = Int(1)
|
|
1842
|
+
radius = Float(1)
|
|
1843
|
+
|
|
1844
|
+
def apply_filter(self, data, voxelsize):
|
|
1845
|
+
import skimage.morphology
|
|
1846
|
+
|
|
1847
|
+
if len(data.shape) == 3: #3D
|
|
1848
|
+
selem = skimage.morphology.ball(self.radius)
|
|
1849
|
+
else:
|
|
1850
|
+
selem = skimage.morphology.disk(self.radius)
|
|
1851
|
+
return ndimage.binary_erosion(data, selem, iterations=self.iterations)
|
|
1852
|
+
|
|
1853
|
+
@register_module('BinaryFillHoles')
|
|
1854
|
+
class BinaryFillHoles(Filter):
|
|
1855
|
+
radius = Float(1)
|
|
1856
|
+
|
|
1857
|
+
def apply_filter(self, data, voxelsize):
|
|
1858
|
+
import skimage.morphology
|
|
1859
|
+
|
|
1860
|
+
if len(data.shape) == 3: #3D
|
|
1861
|
+
selem = skimage.morphology.ball(self.radius)
|
|
1862
|
+
else:
|
|
1863
|
+
selem = skimage.morphology.disk(self.radius)
|
|
1864
|
+
return ndimage.binary_fill_holes(data, selem)
|
|
1865
|
+
|
|
1866
|
+
@register_module('GreyDilation')
|
|
1867
|
+
class GreyDilation(Filter):
|
|
1868
|
+
radius = Float(1)
|
|
1869
|
+
|
|
1870
|
+
def apply_filter(self, data, voxelsize):
|
|
1871
|
+
import skimage.morphology
|
|
1872
|
+
|
|
1873
|
+
if len(data.shape) == 3: #3D
|
|
1874
|
+
selem = skimage.morphology.ball(self.radius)
|
|
1875
|
+
else:
|
|
1876
|
+
selem = skimage.morphology.disk(self.radius)
|
|
1877
|
+
return ndimage.grey_dilation(data, structure=selem)
|
|
1878
|
+
|
|
1879
|
+
@register_module('GreyErosion')
|
|
1880
|
+
class GreyErosion(Filter):
|
|
1881
|
+
radius = Float(1)
|
|
1882
|
+
|
|
1883
|
+
def apply_filter(self, data, voxelsize):
|
|
1884
|
+
import skimage.morphology
|
|
1885
|
+
|
|
1886
|
+
if len(data.shape) == 3: #3D
|
|
1887
|
+
selem = skimage.morphology.ball(self.radius)
|
|
1888
|
+
else:
|
|
1889
|
+
selem = skimage.morphology.disk(self.radius)
|
|
1890
|
+
return ndimage.grey_erosion(data, structure=selem)
|
|
1891
|
+
|
|
1892
|
+
@register_module('WhiteTophat')
|
|
1893
|
+
class WhiteTophat(Filter):
|
|
1894
|
+
radius = Float(1)
|
|
1895
|
+
|
|
1896
|
+
def apply_filter(self, data, voxelsize):
|
|
1897
|
+
import skimage.morphology
|
|
1898
|
+
|
|
1899
|
+
if len(data.shape) == 3: #3D
|
|
1900
|
+
selem = skimage.morphology.ball(self.radius)
|
|
1901
|
+
else:
|
|
1902
|
+
selem = skimage.morphology.disk(self.radius)
|
|
1903
|
+
return ndimage.white_tophat(data, structure=selem)
|
|
1904
|
+
|
|
1905
|
+
|
|
1906
|
+
@register_module('Watershed')
|
|
1907
|
+
class Watershed(ModuleBase):
|
|
1908
|
+
"""Module with one image input and one image output"""
|
|
1909
|
+
inputImage = Input('input')
|
|
1910
|
+
inputMarkers = Input('markers')
|
|
1911
|
+
inputMask = Input('')
|
|
1912
|
+
outputName = Output('watershed')
|
|
1913
|
+
|
|
1914
|
+
processFramesIndividually = Bool(False)
|
|
1915
|
+
|
|
1916
|
+
def filter(self, image, markers, mask=None):
|
|
1917
|
+
if self.processFramesIndividually:
|
|
1918
|
+
filt_ims = []
|
|
1919
|
+
for chanNum in range(image.data.shape[3]):
|
|
1920
|
+
if not mask is None:
|
|
1921
|
+
filt_ims.append(np.concatenate([np.atleast_3d(self._apply_ws(image.data[:,:,i,chanNum].squeeze(), markers.data[:,:,i,chanNum].squeeze(), mask.data[:,:,i,chanNum].squeeze())) for i in range(image.data.shape[2])], 2))
|
|
1922
|
+
else:
|
|
1923
|
+
filt_ims.append(np.concatenate([np.atleast_3d(self._apply_ws(image.data[:,:,i,chanNum].squeeze(), markers.data[:,:,i,chanNum].squeeze())) for i in range(image.data.shape[2])], 2))
|
|
1924
|
+
else:
|
|
1925
|
+
if not mask is None:
|
|
1926
|
+
filt_ims = [np.atleast_3d(self._apply_ws(image.data[:,:,:,chanNum].squeeze(), markers.data[:,:,:,chanNum].squeeze(), mask.data[:,:,:,chanNum].squeeze())) for chanNum in range(image.data.shape[3])]
|
|
1927
|
+
else:
|
|
1928
|
+
filt_ims = [np.atleast_3d(self._apply_ws(image.data[:,:,:,chanNum].squeeze(), mask.data[:,:,:,chanNum].squeeze())) for chanNum in range(image.data.shape[3])]
|
|
1929
|
+
|
|
1930
|
+
im = ImageStack(filt_ims, titleStub = self.outputName)
|
|
1931
|
+
im.mdh.copyEntriesFrom(image.mdh)
|
|
1932
|
+
im.mdh['Parent'] = image.filename
|
|
1933
|
+
|
|
1934
|
+
#self.completeMetadata(im)
|
|
1935
|
+
|
|
1936
|
+
return im
|
|
1937
|
+
|
|
1938
|
+
def _apply_ws(self, image,markers, mask=None):
|
|
1939
|
+
import skimage.morphology
|
|
1940
|
+
|
|
1941
|
+
img = ((image/image.max())*2**15).astype('int16')
|
|
1942
|
+
|
|
1943
|
+
if not mask is None:
|
|
1944
|
+
return skimage.morphology.watershed(img, markers.astype('int16'), mask = mask.astype('int16'))
|
|
1945
|
+
else:
|
|
1946
|
+
return skimage.morphology.watershed(img, markers.astype('int16'))
|
|
1947
|
+
|
|
1948
|
+
# def execute(self, namespace):
|
|
1949
|
+
# image = namespace[self.inputImage]
|
|
1950
|
+
# markers = namespace[self.inputMarkers]
|
|
1951
|
+
# if self.inputMask in ['', 'none', 'None']:
|
|
1952
|
+
# namespace[self.outputName] = self.filter(image, markers)
|
|
1953
|
+
# else:
|
|
1954
|
+
# mask = namespace[self.inputMask]
|
|
1955
|
+
# namespace[self.outputName] = self.filter(image, markers, mask)
|
|
1956
|
+
|
|
1957
|
+
def run(self, inputImage, inputMarkers, inputMask=None):
|
|
1958
|
+
if inputMask:
|
|
1959
|
+
return self.filter(inputImage, inputMarkers)
|
|
1960
|
+
else:
|
|
1961
|
+
return self.filter(inputImage, inputMarkers, inputMask)
|
|
1962
|
+
|
|
1963
|
+
|
|
1964
|
+
@register_module('FlatfieldAndDarkCorrect')
|
|
1965
|
+
class FlatfiledAndDarkCorrect(ModuleBase):
|
|
1966
|
+
inputImage = Input('input')
|
|
1967
|
+
flatfieldFilename = CStr('')
|
|
1968
|
+
darkFilename = CStr('')
|
|
1969
|
+
outputName = Output('corrected')
|
|
1970
|
+
|
|
1971
|
+
# def execute(self, namespace):
|
|
1972
|
+
# from PYME.IO.DataSources import FlatFieldDataSource
|
|
1973
|
+
# #from PYME.IO import unifiedIO
|
|
1974
|
+
# from PYME.IO.image import ImageStack
|
|
1975
|
+
# image = namespace[self.inputImage]
|
|
1976
|
+
|
|
1977
|
+
# if self.flatfieldFilename != '':
|
|
1978
|
+
# flat = ImageStack(filename=self.flatfieldFilename).data_xyztc[:,:,0,0,0].squeeze()
|
|
1979
|
+
# else:
|
|
1980
|
+
# flat = None
|
|
1981
|
+
|
|
1982
|
+
# if not self.darkFilename == '':
|
|
1983
|
+
# dark = ImageStack(filename=self.darkFilename).data_xyztc[:,:,0, 0, 0].squeeze()
|
|
1984
|
+
# else:
|
|
1985
|
+
# dark = None
|
|
1986
|
+
|
|
1987
|
+
# ffd = FlatFieldDataSource.DataSource(image.data, image.mdh, flatfield=flat, dark=dark)
|
|
1988
|
+
|
|
1989
|
+
# im = ImageStack(ffd, titleStub=self.outputName)
|
|
1990
|
+
# im.mdh.copyEntriesFrom(image.mdh)
|
|
1991
|
+
# im.mdh['Parent'] = image.filename
|
|
1992
|
+
# namespace[self.outputName] = im
|
|
1993
|
+
|
|
1994
|
+
def run(self, inputImage):
|
|
1995
|
+
from PYME.IO.DataSources import FlatFieldDataSource
|
|
1996
|
+
#from PYME.IO import unifiedIO
|
|
1997
|
+
from PYME.IO.image import ImageStack
|
|
1998
|
+
|
|
1999
|
+
if self.flatfieldFilename != '':
|
|
2000
|
+
flat = ImageStack(filename=self.flatfieldFilename).data_xyztc[:,:,0,0,0].squeeze()
|
|
2001
|
+
else:
|
|
2002
|
+
flat = None
|
|
2003
|
+
|
|
2004
|
+
if not self.darkFilename == '':
|
|
2005
|
+
dark = ImageStack(filename=self.darkFilename).data_xyztc[:,:,0, 0, 0].squeeze()
|
|
2006
|
+
else:
|
|
2007
|
+
dark = None
|
|
2008
|
+
|
|
2009
|
+
ffd = FlatFieldDataSource.DataSource(inputImage.data, inputImage.mdh, flatfield=flat, dark=dark)
|
|
2010
|
+
|
|
2011
|
+
im = ImageStack(ffd, titleStub=self.outputName)
|
|
2012
|
+
#im.mdh.copyEntriesFrom(inputImage.mdh)
|
|
2013
|
+
im.mdh['Parent'] = inputImage.filename
|
|
2014
|
+
|
|
2015
|
+
return im
|
|
2016
|
+
|
|
2017
|
+
@register_module('Colocalisation')
|
|
2018
|
+
class Colocalisation(ModuleBase):
|
|
2019
|
+
"""
|
|
2020
|
+
Calculate thresholded manders and Pearsons coefficients
|
|
2021
|
+
|
|
2022
|
+
|
|
2023
|
+
"""
|
|
2024
|
+
inputImageA = Input('chan0')
|
|
2025
|
+
inputMaskA = Input('mask0')
|
|
2026
|
+
inputImageB = Input('chan1')
|
|
2027
|
+
inputMaskB = Input('mask1')
|
|
2028
|
+
inputRoiMask = Input('')
|
|
2029
|
+
outputTable = Output('coloc')
|
|
2030
|
+
|
|
2031
|
+
# def execute(self, namespace):
|
|
2032
|
+
# from PYME.Analysis.Colocalisation import correlationCoeffs
|
|
2033
|
+
# from PYME.IO import tabular
|
|
2034
|
+
|
|
2035
|
+
# imA = namespace[self.inputImageA].data[:,:,:,0].squeeze()
|
|
2036
|
+
# imB = namespace[self.inputImageB].data[:,:,:,0].squeeze()
|
|
2037
|
+
# if not np.all(imB.shape == imA.shape):
|
|
2038
|
+
# raise RuntimeError('imageB (shape=%s) not the same size as image data (shape=%s)' % (imB.shape, imA.shape))
|
|
2039
|
+
|
|
2040
|
+
# mA = namespace[self.inputMaskA].data[:,:,:,0].squeeze()
|
|
2041
|
+
# if not np.all(mA.shape == imA.shape):
|
|
2042
|
+
# raise RuntimeError('maskA (shape=%s) not the same size as image data (shape=%s)' % (mA.shape, imA.shape))
|
|
2043
|
+
|
|
2044
|
+
# mB = namespace[self.inputMaskB].data[:,:,:,0].squeeze()
|
|
2045
|
+
# if not np.all(mB.shape == imA.shape):
|
|
2046
|
+
# raise RuntimeError('maskB (shape=%s) not the same size as image data (shape=%s)' % (mB.shape, imA.shape))
|
|
2047
|
+
|
|
2048
|
+
# if not self.inputRoiMask == '':
|
|
2049
|
+
# roi_mask = namespace[self.inputRoiMask].data[:,:,:,0].squeeze() > 0.5
|
|
2050
|
+
# if not np.all(roi_mask.shape == imA.shape):
|
|
2051
|
+
# raise RuntimeError('ROI mask (shape=%s) not the same size as image data (shape=%s)' % (roi_mask.shape, imA.shape))
|
|
2052
|
+
|
|
2053
|
+
# else:
|
|
2054
|
+
# roi_mask = None
|
|
2055
|
+
|
|
2056
|
+
# print('Calculating Pearson and Manders coefficients ...')
|
|
2057
|
+
# pearson = correlationCoeffs.pearson(imA, imB, roi_mask=roi_mask)
|
|
2058
|
+
# MA, MB = correlationCoeffs.maskManders(imA, imB, mA, mB, roi_mask=roi_mask)
|
|
2059
|
+
|
|
2060
|
+
# out = tabular.DictSource({'pearson' : pearson, 'manders_A' : MA, 'manders_B' : MB})
|
|
2061
|
+
|
|
2062
|
+
# namespace[self.outputTable] = out
|
|
2063
|
+
|
|
2064
|
+
def run(self, inputImageA, inputMaskA, inputImageB, inputMaskB, inputRoiMask=None):
|
|
2065
|
+
from PYME.Analysis.Colocalisation import correlationCoeffs
|
|
2066
|
+
from PYME.IO import tabular
|
|
2067
|
+
|
|
2068
|
+
imA = inputImageA.data[:,:,:,0].squeeze()
|
|
2069
|
+
imB = inputImageB.data[:,:,:,0].squeeze()
|
|
2070
|
+
if not np.all(imB.shape == imA.shape):
|
|
2071
|
+
raise RuntimeError('imageB (shape=%s) not the same size as image data (shape=%s)' % (imB.shape, imA.shape))
|
|
2072
|
+
|
|
2073
|
+
mA = inputMaskA.data[:,:,:,0].squeeze()
|
|
2074
|
+
if not np.all(mA.shape == imA.shape):
|
|
2075
|
+
raise RuntimeError('maskA (shape=%s) not the same size as image data (shape=%s)' % (mA.shape, imA.shape))
|
|
2076
|
+
|
|
2077
|
+
mB = inputMaskB.data[:,:,:,0].squeeze()
|
|
2078
|
+
if not np.all(mB.shape == imA.shape):
|
|
2079
|
+
raise RuntimeError('maskB (shape=%s) not the same size as image data (shape=%s)' % (mB.shape, imA.shape))
|
|
2080
|
+
|
|
2081
|
+
if inputRoiMask:
|
|
2082
|
+
roi_mask = inputRoiMask.data[:,:,:,0].squeeze() > 0.5
|
|
2083
|
+
if not np.all(roi_mask.shape == imA.shape):
|
|
2084
|
+
raise RuntimeError('ROI mask (shape=%s) not the same size as image data (shape=%s)' % (roi_mask.shape, imA.shape))
|
|
2085
|
+
|
|
2086
|
+
else:
|
|
2087
|
+
roi_mask = None
|
|
2088
|
+
|
|
2089
|
+
print('Calculating Pearson and Manders coefficients ...')
|
|
2090
|
+
pearson = correlationCoeffs.pearson(imA, imB, roi_mask=roi_mask)
|
|
2091
|
+
MA, MB = correlationCoeffs.maskManders(imA, imB, mA, mB, roi_mask=roi_mask)
|
|
2092
|
+
|
|
2093
|
+
return tabular.DictSource({'pearson' : pearson, 'manders_A' : MA, 'manders_B' : MB})
|
|
2094
|
+
|
|
2095
|
+
|
|
2096
|
+
|
|
2097
|
+
@register_module('ColocalisationEDT')
|
|
2098
|
+
class ColocalisationEDT(ModuleBase):
|
|
2099
|
+
"""
|
|
2100
|
+
Perform distance-transform based colocalisation of an image with a mask. Returns the relative
|
|
2101
|
+
enrichment, and the total signal contained within a given distance from a mask.
|
|
2102
|
+
|
|
2103
|
+
Parameters
|
|
2104
|
+
==========
|
|
2105
|
+
|
|
2106
|
+
inputImage : an intensity image
|
|
2107
|
+
mask : a mask (usually derived from a different channel)
|
|
2108
|
+
inputImageB : [optional] the intensity image for the channel used to create the mask. If present, this is used to
|
|
2109
|
+
assess the colocalisation of the mask channel with itself as a control.
|
|
2110
|
+
outputTable : table into which to save results
|
|
2111
|
+
|
|
2112
|
+
minimumDistance, maximumDistance, binSize : float, nm the range of distances to calculate the histogram over
|
|
2113
|
+
|
|
2114
|
+
|
|
2115
|
+
Returns
|
|
2116
|
+
=======
|
|
2117
|
+
|
|
2118
|
+
outputTable : a table containing the following columns:
|
|
2119
|
+
'bins' : the right hand edges of the histogram bins (as suitable for the cdf plots)
|
|
2120
|
+
'enrichment' : the enrichment of the label at a given distance from the mask (when compared to a uniform spatial distribution)
|
|
2121
|
+
'enclosed' : the fraction of the signal enclosed within a given distance from the mask
|
|
2122
|
+
'enclosed_area' : the fraction of the total area enclosed within a given radius. This gives you the curve you
|
|
2123
|
+
would see if the label was randomly distributed.
|
|
2124
|
+
'enrichment_m' : enrichment of the mask source channel (if provided) at a given distance from the mask. This is a control for the thresholding
|
|
2125
|
+
'enclosed_m' : fraction of mask source channel signal (if provided) within a given distance from the mask. This is a control for the thresholding.
|
|
2126
|
+
|
|
2127
|
+
|
|
2128
|
+
Notes
|
|
2129
|
+
=====
|
|
2130
|
+
|
|
2131
|
+
- If the input image has multiple colour channels, the 0th channel will be taken (i.e. split channels first)
|
|
2132
|
+
- To do colocalisation both ways between two images, you will need two copies of this module
|
|
2133
|
+
|
|
2134
|
+
TODO: handle channel names appropriately, support for ROI masks
|
|
2135
|
+
"""
|
|
2136
|
+
inputImage = Input('input')
|
|
2137
|
+
inputMask = Input('mask')
|
|
2138
|
+
inputImageB = Input('')
|
|
2139
|
+
inputRoiMask = Input('')
|
|
2140
|
+
outputTable = Output('edt_coloc')
|
|
2141
|
+
outputPlot = Output('coloc_plot')
|
|
2142
|
+
|
|
2143
|
+
minimumDistanceNM = Float(-500.)
|
|
2144
|
+
maximumDistanceNM = Float(2000.)
|
|
2145
|
+
binSizeNM = Float(100.)
|
|
2146
|
+
|
|
2147
|
+
|
|
2148
|
+
# def execute(self, namespace):
|
|
2149
|
+
# from PYME.IO import tabular
|
|
2150
|
+
# from PYME.Analysis.Colocalisation import edtColoc
|
|
2151
|
+
# from PYME.recipes.graphing import Plot
|
|
2152
|
+
|
|
2153
|
+
# bins = np.arange(float(self.minimumDistanceNM), float(self.maximumDistanceNM), float(self.binSizeNM))
|
|
2154
|
+
|
|
2155
|
+
# im = namespace[self.inputImage]
|
|
2156
|
+
# imA = im.data[:, :, :, 0].squeeze()
|
|
2157
|
+
# voxelsize = im.voxelsize[:imA.ndim]
|
|
2158
|
+
|
|
2159
|
+
# m_im = namespace[self.inputMask]
|
|
2160
|
+
# mask = m_im.data[:,:,:,0].squeeze() > 0.5
|
|
2161
|
+
|
|
2162
|
+
# if not np.all(mask.shape == imA.shape):
|
|
2163
|
+
# raise RuntimeError('Mask (shape=%s) not the same size as image data (shape=%s)' % (mask.shape, imA.shape))
|
|
2164
|
+
|
|
2165
|
+
# if not self.inputRoiMask == '':
|
|
2166
|
+
# roi_mask = namespace[self.inputRoiMask].data[:,:,:,0].squeeze() > 0.5
|
|
2167
|
+
# if not np.all(roi_mask.shape == imA.shape):
|
|
2168
|
+
# raise RuntimeError('ROI mask (shape=%s) not the same size as image data (shape=%s)' % (roi_mask.shape, imA.shape))
|
|
2169
|
+
# else:
|
|
2170
|
+
# roi_mask = None
|
|
2171
|
+
|
|
2172
|
+
|
|
2173
|
+
# bins_, enrichment, enclosed, enclosed_area = edtColoc.image_enrichment_and_fraction_at_distance(imA, mask, voxelsize,
|
|
2174
|
+
# bins, roi_mask=roi_mask)
|
|
2175
|
+
|
|
2176
|
+
# out = tabular.MappingFilter(tabular.DictSource({'bins' : bins[1:], 'enrichment' : enrichment, 'enclosed' : enclosed, 'enclosed_area' : enclosed_area}))
|
|
2177
|
+
# out.mdh = getattr(im, 'mdh', None)
|
|
2178
|
+
|
|
2179
|
+
# if not self.inputImageB == '':
|
|
2180
|
+
# imB = namespace[self.inputImageB].data[:,:, :,0].squeeze()
|
|
2181
|
+
# if not np.all(imB.shape == imA.shape):
|
|
2182
|
+
# raise RuntimeError(
|
|
2183
|
+
# 'ImageB (shape=%s) not the same size as image data (shape=%s)' % (imB.shape, imA.shape))
|
|
2184
|
+
|
|
2185
|
+
# bins_, enrichment_m, enclosed_m, _ = edtColoc.image_enrichment_and_fraction_at_distance(imB, mask, voxelsize,
|
|
2186
|
+
# bins, roi_mask=roi_mask)
|
|
2187
|
+
# out.addColumn('enrichment_m', enrichment_m)
|
|
2188
|
+
# out.addColumn('enclosed_m', enclosed_m)
|
|
2189
|
+
|
|
2190
|
+
# else:
|
|
2191
|
+
# enrichment_m = None
|
|
2192
|
+
# enclosed_m = None
|
|
2193
|
+
|
|
2194
|
+
# namespace[self.outputTable] = out
|
|
2195
|
+
# namespace[self.outputPlot] = Plot(lambda: edtColoc.plot_image_dist_coloc_figure(bins, enrichment, enrichment_m,
|
|
2196
|
+
# enclosed, enclosed_m,
|
|
2197
|
+
# enclosed_area,
|
|
2198
|
+
# nameA=m_im.names[0],
|
|
2199
|
+
# nameB=im.names[0]))
|
|
2200
|
+
|
|
2201
|
+
def run(self, inputImage, inputMask, inputImageB=None, inputRoiMask=None):
|
|
2202
|
+
from PYME.IO import tabular
|
|
2203
|
+
from PYME.Analysis.Colocalisation import edtColoc
|
|
2204
|
+
from PYME.recipes.graphing import Plot
|
|
2205
|
+
|
|
2206
|
+
bins = np.arange(float(self.minimumDistanceNM), float(self.maximumDistanceNM), float(self.binSizeNM))
|
|
2207
|
+
|
|
2208
|
+
imA = inputImage.data[:, :, :, 0].squeeze()
|
|
2209
|
+
voxelsize = inputImage.voxelsize[:imA.ndim]
|
|
2210
|
+
|
|
2211
|
+
mask = inputMask.data[:,:,:,0].squeeze() > 0.5
|
|
2212
|
+
|
|
2213
|
+
if not np.all(mask.shape == imA.shape):
|
|
2214
|
+
raise RuntimeError('Mask (shape=%s) not the same size as image data (shape=%s)' % (mask.shape, imA.shape))
|
|
2215
|
+
|
|
2216
|
+
if inputRoiMask :
|
|
2217
|
+
roi_mask = inputRoiMask.data[:,:,:,0].squeeze() > 0.5
|
|
2218
|
+
if not np.all(roi_mask.shape == imA.shape):
|
|
2219
|
+
raise RuntimeError('ROI mask (shape=%s) not the same size as image data (shape=%s)' % (roi_mask.shape, imA.shape))
|
|
2220
|
+
else:
|
|
2221
|
+
roi_mask = None
|
|
2222
|
+
|
|
2223
|
+
|
|
2224
|
+
bins_, enrichment, enclosed, enclosed_area = edtColoc.image_enrichment_and_fraction_at_distance(imA, mask, voxelsize,
|
|
2225
|
+
bins, roi_mask=roi_mask)
|
|
2226
|
+
|
|
2227
|
+
out = tabular.MappingFilter(tabular.DictSource({'bins' : bins[1:], 'enrichment' : enrichment, 'enclosed' : enclosed, 'enclosed_area' : enclosed_area}))
|
|
2228
|
+
out.mdh = getattr(inputImage, 'mdh', None)
|
|
2229
|
+
|
|
2230
|
+
if inputImageB:
|
|
2231
|
+
imB = inputImageB.data[:,:, :,0].squeeze()
|
|
2232
|
+
if not np.all(imB.shape == imA.shape):
|
|
2233
|
+
raise RuntimeError(
|
|
2234
|
+
'ImageB (shape=%s) not the same size as image data (shape=%s)' % (imB.shape, imA.shape))
|
|
2235
|
+
|
|
2236
|
+
bins_, enrichment_m, enclosed_m, _ = edtColoc.image_enrichment_and_fraction_at_distance(imB, mask, voxelsize,
|
|
2237
|
+
bins, roi_mask=roi_mask)
|
|
2238
|
+
out.addColumn('enrichment_m', enrichment_m)
|
|
2239
|
+
out.addColumn('enclosed_m', enclosed_m)
|
|
2240
|
+
|
|
2241
|
+
else:
|
|
2242
|
+
enrichment_m = None
|
|
2243
|
+
enclosed_m = None
|
|
2244
|
+
|
|
2245
|
+
return {'outputTable' : out,
|
|
2246
|
+
'outputPlot' : Plot(lambda: edtColoc.plot_image_dist_coloc_figure(bins, enrichment, enrichment_m,
|
|
2247
|
+
enclosed, enclosed_m,
|
|
2248
|
+
enclosed_area,
|
|
2249
|
+
nameA=inputMask.names[0],
|
|
2250
|
+
nameB=inputImage.names[0]))}
|
|
2251
|
+
|
|
2252
|
+
|
|
2253
|
+
|
|
2254
|
+
@register_module('AverageFramesByZStep')
|
|
2255
|
+
class AverageFramesByZStep(ModuleBase):
|
|
2256
|
+
"""
|
|
2257
|
+
Averages frames acquired at the same z-position, as determined by the associated events, or (fall-back) metadata.
|
|
2258
|
+
|
|
2259
|
+
Parameters
|
|
2260
|
+
----------
|
|
2261
|
+
input_image : string
|
|
2262
|
+
name of an ImageStack instance, with metadata / events describing which frames were taken at which z-position.
|
|
2263
|
+
input_zvals : string
|
|
2264
|
+
[optional] name of a table mapping frames to z values. If empty, the image events are used.
|
|
2265
|
+
|
|
2266
|
+
Returns
|
|
2267
|
+
-------
|
|
2268
|
+
output : traits.Output
|
|
2269
|
+
ImageStack instance, where frames taken at the same z-position have been averaged together.
|
|
2270
|
+
"""
|
|
2271
|
+
|
|
2272
|
+
input_image = Input('input')
|
|
2273
|
+
input_zvals = Input('') #if undefined, use events
|
|
2274
|
+
z_column_name = CStr('z')
|
|
2275
|
+
output = Output('averaged_by_frame')
|
|
2276
|
+
|
|
2277
|
+
# def execute(self, namespace):
|
|
2278
|
+
# from PYME.Analysis import piezo_movement_correction
|
|
2279
|
+
# from scipy.stats import mode
|
|
2280
|
+
# import time
|
|
2281
|
+
|
|
2282
|
+
# image_stack = namespace[self.input_image]
|
|
2283
|
+
|
|
2284
|
+
# if self.input_zvals == '':
|
|
2285
|
+
# # z from events
|
|
2286
|
+
# frames = np.arange(image_stack.data.shape[2], dtype=int)
|
|
2287
|
+
|
|
2288
|
+
# z_vals = piezo_movement_correction.correct_target_positions(frames, image_stack.events, image_stack.mdh)
|
|
2289
|
+
# else:
|
|
2290
|
+
# #z values are provided as input
|
|
2291
|
+
# z_vals = namespace[self.input_zvals][self.z_column_name]
|
|
2292
|
+
|
|
2293
|
+
# # later we will mash z with %3.3f, round here so we don't duplicate steps
|
|
2294
|
+
# # TODO - make rounding precision a parameter?
|
|
2295
|
+
# z_vals = np.round(z_vals, decimals=3)
|
|
2296
|
+
|
|
2297
|
+
# # make sure everything is sorted. We'll carry the args to sort, rather than creating another full array
|
|
2298
|
+
# frames_z_sorted = np.argsort(z_vals)
|
|
2299
|
+
# z = z_vals[frames_z_sorted]
|
|
2300
|
+
# z_steps, count = np.unique(z, return_counts=True)
|
|
2301
|
+
|
|
2302
|
+
# n_steps = len(z_steps)
|
|
2303
|
+
# logger.debug('Averaged stack size: %d' % n_steps)
|
|
2304
|
+
|
|
2305
|
+
# new_stack = []
|
|
2306
|
+
# # TODO - should we default to zero or abort?
|
|
2307
|
+
# t = image_stack.mdh.getOrDefault('StartTime', 0)
|
|
2308
|
+
# fudged_events = []
|
|
2309
|
+
# cycle_time = image_stack.mdh.getOrDefault('Camera.CycleTime', 1.0)
|
|
2310
|
+
# for ci in range(image_stack.data.shape[3]):
|
|
2311
|
+
# data_avg = np.zeros((image_stack.data.shape[0], image_stack.data.shape[1], n_steps))
|
|
2312
|
+
# start = 0
|
|
2313
|
+
# for si in range(n_steps):
|
|
2314
|
+
# for fi in range(count[si]):
|
|
2315
|
+
# # sum frames from this step directly into the output array
|
|
2316
|
+
# data_avg[:, :, si] += image_stack.data[:, :, frames_z_sorted[start + fi], ci].squeeze()
|
|
2317
|
+
# start += count[si]
|
|
2318
|
+
# fudged_events.append(('ProtocolFocus', t, '%d, %3.3f' % (si, z_steps[si])))
|
|
2319
|
+
# fudged_events.append((('StartAq', t, '%d' % si)))
|
|
2320
|
+
# t += cycle_time
|
|
2321
|
+
# # complete the average for this color channel and append to output
|
|
2322
|
+
# new_stack.append(data_avg / count[None, None, :])
|
|
2323
|
+
|
|
2324
|
+
# # FIXME - make this follow the correct event dtype
|
|
2325
|
+
# fudged_events = np.array(fudged_events, dtype=[('EventName', 'S32'), ('Time', '<f8'), ('EventDescr', 'S256')])
|
|
2326
|
+
# averaged = ImageStack(new_stack, mdh=MetaDataHandler.NestedClassMDHandler(image_stack.mdh), events=fudged_events)
|
|
2327
|
+
|
|
2328
|
+
# # fudge metadata, leaving breadcrumbs
|
|
2329
|
+
# averaged.mdh['Camera.CycleTime'] = cycle_time
|
|
2330
|
+
# averaged.mdh['StackSettings.NumSlices'] = n_steps
|
|
2331
|
+
# averaged.mdh['StackSettings.StepSize'] = abs(mode(np.diff(z))[0][0])
|
|
2332
|
+
|
|
2333
|
+
# namespace[self.output] = averaged
|
|
2334
|
+
|
|
2335
|
+
def run(self, input_image, input_zvals=None):
|
|
2336
|
+
from PYME.Analysis import piezo_movement_correction
|
|
2337
|
+
from scipy.stats import mode
|
|
2338
|
+
import time
|
|
2339
|
+
|
|
2340
|
+
if not input_zvals:
|
|
2341
|
+
# z from events
|
|
2342
|
+
frames = np.arange(input_image.data.shape[2], dtype=int)
|
|
2343
|
+
|
|
2344
|
+
z_vals = piezo_movement_correction.correct_target_positions(frames, input_image.events, input_image.mdh)
|
|
2345
|
+
else:
|
|
2346
|
+
#z values are provided as input
|
|
2347
|
+
z_vals = input_zvals[self.z_column_name]
|
|
2348
|
+
|
|
2349
|
+
# later we will mash z with %3.3f, round here so we don't duplicate steps
|
|
2350
|
+
# TODO - make rounding precision a parameter?
|
|
2351
|
+
z_vals = np.round(z_vals, decimals=3)
|
|
2352
|
+
|
|
2353
|
+
# make sure everything is sorted. We'll carry the args to sort, rather than creating another full array
|
|
2354
|
+
frames_z_sorted = np.argsort(z_vals)
|
|
2355
|
+
z = z_vals[frames_z_sorted]
|
|
2356
|
+
z_steps, count = np.unique(z, return_counts=True)
|
|
2357
|
+
|
|
2358
|
+
n_steps = len(z_steps)
|
|
2359
|
+
logger.debug('Averaged stack size: %d' % n_steps)
|
|
2360
|
+
|
|
2361
|
+
new_stack = []
|
|
2362
|
+
# TODO - should we default to zero or abort?
|
|
2363
|
+
t = input_image.mdh.getOrDefault('StartTime', 0)
|
|
2364
|
+
fudged_events = []
|
|
2365
|
+
cycle_time = input_image.mdh.getOrDefault('Camera.CycleTime', 1.0)
|
|
2366
|
+
for ci in range(input_image.data.shape[3]):
|
|
2367
|
+
data_avg = np.zeros((input_image.data.shape[0], input_image.data.shape[1], n_steps))
|
|
2368
|
+
start = 0
|
|
2369
|
+
for si in range(n_steps):
|
|
2370
|
+
for fi in range(count[si]):
|
|
2371
|
+
# sum frames from this step directly into the output array
|
|
2372
|
+
data_avg[:, :, si] += input_image.data[:, :, frames_z_sorted[start + fi], ci].squeeze()
|
|
2373
|
+
start += count[si]
|
|
2374
|
+
fudged_events.append(('ProtocolFocus', t, '%d, %3.3f' % (si, z_steps[si])))
|
|
2375
|
+
fudged_events.append((('StartAq', t, '%d' % si)))
|
|
2376
|
+
t += cycle_time
|
|
2377
|
+
# complete the average for this color channel and append to output
|
|
2378
|
+
new_stack.append(data_avg / count[None, None, :])
|
|
2379
|
+
|
|
2380
|
+
# FIXME - make this follow the correct event dtype
|
|
2381
|
+
fudged_events = np.array(fudged_events, dtype=[('EventName', 'S32'), ('Time', '<f8'), ('EventDescr', 'S256')])
|
|
2382
|
+
averaged = ImageStack(new_stack, mdh=MetaDataHandler.NestedClassMDHandler(input_image.mdh), events=fudged_events)
|
|
2383
|
+
|
|
2384
|
+
# fudge metadata, leaving breadcrumbs
|
|
2385
|
+
averaged.mdh['Camera.CycleTime'] = cycle_time
|
|
2386
|
+
averaged.mdh['StackSettings.NumSlices'] = n_steps
|
|
2387
|
+
averaged.mdh['StackSettings.StepSize'] = abs(mode(np.diff(z))[0][0])
|
|
2388
|
+
|
|
2389
|
+
return averaged
|
|
2390
|
+
|
|
2391
|
+
|
|
2392
|
+
@register_module('ResampleZ')
|
|
2393
|
+
class ResampleZ(ModuleBase):
|
|
2394
|
+
"""
|
|
2395
|
+
Resamples input stack at even intervals along z using a linear interpolation. If the input stack contains multiple
|
|
2396
|
+
frames taken at the same z position, the stack should first be run through AverageFramesByZStep.
|
|
2397
|
+
|
|
2398
|
+
Parameters
|
|
2399
|
+
----------
|
|
2400
|
+
input: Input
|
|
2401
|
+
ImageStack instance
|
|
2402
|
+
z_sampling: Float
|
|
2403
|
+
Spacing to resample the stack axially, units of micrometers
|
|
2404
|
+
|
|
2405
|
+
Returns
|
|
2406
|
+
-------
|
|
2407
|
+
output: Output
|
|
2408
|
+
ImageStack instance
|
|
2409
|
+
|
|
2410
|
+
"""
|
|
2411
|
+
|
|
2412
|
+
input = Input('input')
|
|
2413
|
+
z_sampling = Float(0.05)
|
|
2414
|
+
output = Output('regular_stack')
|
|
2415
|
+
|
|
2416
|
+
# def execute(self, namespace):
|
|
2417
|
+
# from PYME.Analysis import piezo_movement_correction
|
|
2418
|
+
# from scipy.interpolate import RegularGridInterpolator
|
|
2419
|
+
|
|
2420
|
+
# stack = namespace[self.input]
|
|
2421
|
+
|
|
2422
|
+
# # grab z from events if we can
|
|
2423
|
+
# frames = np.arange(stack.data.shape[2], dtype=int)
|
|
2424
|
+
|
|
2425
|
+
# z_vals = piezo_movement_correction.correct_target_positions(frames, stack.events, stack.mdh)
|
|
2426
|
+
|
|
2427
|
+
# x = np.arange(0, stack.mdh['voxelsize.x'] * stack.data.shape[0], stack.mdh['voxelsize.x'])
|
|
2428
|
+
# y = np.arange(0, stack.mdh['voxelsize.y'] * stack.data.shape[1], stack.mdh['voxelsize.y'])
|
|
2429
|
+
|
|
2430
|
+
# # generate grid for sampling
|
|
2431
|
+
# new_z = np.arange(np.min(z_vals), np.max(z_vals), self.z_sampling)
|
|
2432
|
+
# xx, yy, zz = np.meshgrid(x, y, new_z, indexing='ij')
|
|
2433
|
+
# # RegularGridInterpolator needs z to be strictly ascending need to average frames from the same step first
|
|
2434
|
+
# uni, counts = np.unique(z_vals, return_counts=True)
|
|
2435
|
+
# if np.any(counts > 1):
|
|
2436
|
+
# raise RuntimeError('Resampling requires one frame per z-step. Please run AverageFramesByZStep first')
|
|
2437
|
+
# I = np.argsort(z_vals)
|
|
2438
|
+
# sorted_z_vals = z_vals[I]
|
|
2439
|
+
# regular = []
|
|
2440
|
+
# for ci in range(stack.data.shape[3]):
|
|
2441
|
+
# interp = RegularGridInterpolator((x, y, sorted_z_vals), stack.data[:, :, :, ci][:,:,I], method='linear')
|
|
2442
|
+
# regular.append(interp((xx, yy, zz)))
|
|
2443
|
+
|
|
2444
|
+
# mdh = MetaDataHandler.DictMDHandler({
|
|
2445
|
+
# 'RegularizedStack': True,
|
|
2446
|
+
# 'StackSettings.StepSize': self.z_sampling,
|
|
2447
|
+
# 'StackSettings.StartPos': new_z[0],
|
|
2448
|
+
# 'StackSettings.EndPos': new_z[-1],
|
|
2449
|
+
# 'StackSettings.NumSlices': len(new_z),
|
|
2450
|
+
# 'voxelsize.z': self.z_sampling
|
|
2451
|
+
# })
|
|
2452
|
+
# mdh.mergeEntriesFrom(stack.mdh)
|
|
2453
|
+
|
|
2454
|
+
# regular_stack = ImageStack(regular, mdh=mdh)
|
|
2455
|
+
|
|
2456
|
+
# namespace[self.output] = regular_stack
|
|
2457
|
+
|
|
2458
|
+
def run(self, input):
|
|
2459
|
+
from PYME.Analysis import piezo_movement_correction
|
|
2460
|
+
from scipy.interpolate import RegularGridInterpolator
|
|
2461
|
+
|
|
2462
|
+
# grab z from events if we can
|
|
2463
|
+
frames = np.arange(input.data.shape[2], dtype=int)
|
|
2464
|
+
|
|
2465
|
+
z_vals = piezo_movement_correction.correct_target_positions(frames, input.events, input.mdh)
|
|
2466
|
+
|
|
2467
|
+
x = np.arange(0, input.mdh['voxelsize.x'] * input.data.shape[0], input.mdh['voxelsize.x'])
|
|
2468
|
+
y = np.arange(0, input.mdh['voxelsize.y'] * input.data.shape[1], input.mdh['voxelsize.y'])
|
|
2469
|
+
|
|
2470
|
+
# generate grid for sampling
|
|
2471
|
+
new_z = np.arange(np.min(z_vals), np.max(z_vals), self.z_sampling)
|
|
2472
|
+
xx, yy, zz = np.meshgrid(x, y, new_z, indexing='ij')
|
|
2473
|
+
# RegularGridInterpolator needs z to be strictly ascending need to average frames from the same step first
|
|
2474
|
+
uni, counts = np.unique(z_vals, return_counts=True)
|
|
2475
|
+
if np.any(counts > 1):
|
|
2476
|
+
raise RuntimeError('Resampling requires one frame per z-step. Please run AverageFramesByZStep first')
|
|
2477
|
+
I = np.argsort(z_vals)
|
|
2478
|
+
sorted_z_vals = z_vals[I]
|
|
2479
|
+
regular = []
|
|
2480
|
+
for ci in range(input.data.shape[3]):
|
|
2481
|
+
interp = RegularGridInterpolator((x, y, sorted_z_vals), input.data[:, :, :, ci][:,:,I], method='linear')
|
|
2482
|
+
regular.append(interp((xx, yy, zz)))
|
|
2483
|
+
|
|
2484
|
+
mdh = MetaDataHandler.DictMDHandler({
|
|
2485
|
+
'RegularizedStack': True,
|
|
2486
|
+
'StackSettings.StepSize': self.z_sampling,
|
|
2487
|
+
'StackSettings.StartPos': new_z[0],
|
|
2488
|
+
'StackSettings.EndPos': new_z[-1],
|
|
2489
|
+
'StackSettings.NumSlices': len(new_z),
|
|
2490
|
+
'voxelsize.z': self.z_sampling
|
|
2491
|
+
})
|
|
2492
|
+
mdh.mergeEntriesFrom(input.mdh)
|
|
2493
|
+
|
|
2494
|
+
return ImageStack(regular, mdh=mdh)
|
|
2495
|
+
|
|
2496
|
+
@register_module('BackgroundSubtractionMovingAverage')
|
|
2497
|
+
class BackgroundSubtractionMovingAverage(ModuleBase):
|
|
2498
|
+
"""
|
|
2499
|
+
Estimates and subtracts the background of a series using a sliding window average on a per-pixel basis.
|
|
2500
|
+
|
|
2501
|
+
Parameters
|
|
2502
|
+
----------
|
|
2503
|
+
input_name : Input
|
|
2504
|
+
PYME.IO.ImageStack
|
|
2505
|
+
window = List
|
|
2506
|
+
Describes the window, much like range or numpy.arrange, format is [start, finish, stride]
|
|
2507
|
+
|
|
2508
|
+
Returns
|
|
2509
|
+
-------
|
|
2510
|
+
output_name = Output
|
|
2511
|
+
PYME.IO.ImageStack of the background-subtracted 'input_name' series
|
|
2512
|
+
|
|
2513
|
+
Notes
|
|
2514
|
+
-----
|
|
2515
|
+
|
|
2516
|
+
input and output images are the same size.
|
|
2517
|
+
|
|
2518
|
+
"""
|
|
2519
|
+
|
|
2520
|
+
input_name = Input('input')
|
|
2521
|
+
window = List(type=CInt, value=[-32, 0, 1])
|
|
2522
|
+
output_name = Output('background_subtracted')
|
|
2523
|
+
|
|
2524
|
+
percentile = 0
|
|
2525
|
+
|
|
2526
|
+
# def execute(self, namespace):
|
|
2527
|
+
# from PYME.IO.DataSources import BGSDataSource
|
|
2528
|
+
# from PYME.IO.image import ImageStack
|
|
2529
|
+
# series = namespace[self.input_name]
|
|
2530
|
+
|
|
2531
|
+
# bgs = BGSDataSource.DataSource(series.data, bgRange=self.window)
|
|
2532
|
+
# bgs.setBackgroundBufferPCT(self.percentile)
|
|
2533
|
+
|
|
2534
|
+
# background = ImageStack(data=bgs, mdh=MetaDataHandler.NestedClassMDHandler(series.mdh))
|
|
2535
|
+
|
|
2536
|
+
# background.mdh['Parent'] = series.filename
|
|
2537
|
+
# background.mdh['Processing.SlidingWindowBackground.Percentile'] = self.percentile
|
|
2538
|
+
# background.mdh['Processing.SlidingWindowBackground.Window'] = self.window
|
|
2539
|
+
|
|
2540
|
+
# namespace[self.output_name] = background
|
|
2541
|
+
|
|
2542
|
+
def run(self, input_name):
|
|
2543
|
+
from PYME.IO.DataSources import BGSDataSource
|
|
2544
|
+
from PYME.IO.image import ImageStack
|
|
2545
|
+
|
|
2546
|
+
bgs = BGSDataSource.DataSource(input_name.data, bgRange=self.window)
|
|
2547
|
+
bgs.setBackgroundBufferPCT(self.percentile)
|
|
2548
|
+
|
|
2549
|
+
background = ImageStack(data=bgs, mdh=MetaDataHandler.NestedClassMDHandler(input_name.mdh))
|
|
2550
|
+
|
|
2551
|
+
background.mdh['Parent'] = input_name.filename
|
|
2552
|
+
#background.mdh['Processing.SlidingWindowBackground.Percentile'] = self.percentile
|
|
2553
|
+
#background.mdh['Processing.SlidingWindowBackground.Window'] = self.window
|
|
2554
|
+
|
|
2555
|
+
return background
|
|
2556
|
+
|
|
2557
|
+
@register_module('BackgroundSubtractionMovingPercentile')
|
|
2558
|
+
class BackgroundSubtractionMovingPercentile(BackgroundSubtractionMovingAverage):
|
|
2559
|
+
"""
|
|
2560
|
+
Estimates the background of a series using a sliding window and taking an (adjusted) percentile
|
|
2561
|
+
(e.g. median at percentile = 0.5) over that window on a per-pixel basis.
|
|
2562
|
+
|
|
2563
|
+
Parameters
|
|
2564
|
+
----------
|
|
2565
|
+
input_name : Input
|
|
2566
|
+
PYME.IO.ImageStack
|
|
2567
|
+
percentile : Float
|
|
2568
|
+
Percentile to take as the background after sorting within the time window along each pixel
|
|
2569
|
+
window = List
|
|
2570
|
+
Describes the window, much like range or numpy.arrange, format is [start, finish, stride]
|
|
2571
|
+
|
|
2572
|
+
Returns
|
|
2573
|
+
-------
|
|
2574
|
+
output_name = Output
|
|
2575
|
+
PYME.IO.ImageStack of the background-subtracted 'input_name' series
|
|
2576
|
+
|
|
2577
|
+
Notes
|
|
2578
|
+
-----
|
|
2579
|
+
The percentile background isn't a simple percentile, but is adjusted slightly - see PYME.IO.DataSource.BGSDataSource
|
|
2580
|
+
|
|
2581
|
+
input and output images are the same size.
|
|
2582
|
+
"""
|
|
2583
|
+
percentile = Float(0.25)
|
|
2584
|
+
|
|
2585
|
+
|
|
2586
|
+
@register_module('Projection')
|
|
2587
|
+
class Projection(Filter):
|
|
2588
|
+
""" Project image along an axis
|
|
2589
|
+
|
|
2590
|
+
TODO - make this more efficient - we currently force the whole stack into memory
|
|
2591
|
+
|
|
2592
|
+
NOTE: coloured max returns results encoded as a complex number out = np.max(data, axis) + 1j*np.argmax(data, axis)
|
|
2593
|
+
|
|
2594
|
+
"""
|
|
2595
|
+
|
|
2596
|
+
kind = Enum(['Mean', 'Sum', 'Max', 'Median', 'Std', 'Min', 'Coloured Max'])
|
|
2597
|
+
axis = Int(2)
|
|
2598
|
+
|
|
2599
|
+
dimensionality = Enum('XYZ', 'XY')
|
|
2600
|
+
|
|
2601
|
+
_block_safe = False
|
|
2602
|
+
|
|
2603
|
+
def apply_filter(self, data, voxelsize):
|
|
2604
|
+
if self.kind == 'Mean':
|
|
2605
|
+
return np.mean(data, axis=int(self.axis))
|
|
2606
|
+
if self.kind == 'Sum':
|
|
2607
|
+
return np.sum(data, axis=int(self.axis))
|
|
2608
|
+
if self.kind == 'Max':
|
|
2609
|
+
return np.max(data, axis=int(self.axis))
|
|
2610
|
+
if self.kind == 'Median':
|
|
2611
|
+
return np.median(data, axis=int(self.axis))
|
|
2612
|
+
if self.kind == 'Std':
|
|
2613
|
+
return np.std(data, axis=int(self.axis))
|
|
2614
|
+
if self.kind == 'Min':
|
|
2615
|
+
return np.min(data, axis=int(self.axis))
|
|
2616
|
+
if self.kind == 'Coloured Max':
|
|
2617
|
+
m = np.min(data, axis=int(self.axis))
|
|
2618
|
+
im = np.argmin(data, axis=int(self.axis))
|
|
2619
|
+
|
|
2620
|
+
return m + 1j*im
|
|
2621
|
+
|
|
2622
|
+
|
|
2623
|
+
@register_module('StatisticsByFrame')
|
|
2624
|
+
class StatisticsByFrame(ModuleBase):
|
|
2625
|
+
"""
|
|
2626
|
+
Iterates through the time/z-position dimension of an ImageStack, calculating basic statistics for each frame,
|
|
2627
|
+
optionally using a 2D or 3D mask in the process.
|
|
2628
|
+
|
|
2629
|
+
NOTE: only operates on first colour channel of stack.
|
|
2630
|
+
|
|
2631
|
+
Parameters
|
|
2632
|
+
----------
|
|
2633
|
+
input_name : Input
|
|
2634
|
+
PYME.IO.ImageStack
|
|
2635
|
+
mask : Input
|
|
2636
|
+
PYME.IO.ImageStack. Optional mask to only calculate metrics
|
|
2637
|
+
|
|
2638
|
+
Returns
|
|
2639
|
+
-------
|
|
2640
|
+
output_name = Output
|
|
2641
|
+
|
|
2642
|
+
|
|
2643
|
+
Notes
|
|
2644
|
+
-----
|
|
2645
|
+
|
|
2646
|
+
"""
|
|
2647
|
+
|
|
2648
|
+
input_name = Input('input')
|
|
2649
|
+
|
|
2650
|
+
mask = Input('')
|
|
2651
|
+
|
|
2652
|
+
output_name = Output('cluster_metrics')
|
|
2653
|
+
|
|
2654
|
+
# def execute(self, namespace):
|
|
2655
|
+
# from scipy import stats
|
|
2656
|
+
|
|
2657
|
+
# series = namespace[self.input_name]
|
|
2658
|
+
# data = series.data
|
|
2659
|
+
|
|
2660
|
+
# if self.mask == '':
|
|
2661
|
+
# mask = None
|
|
2662
|
+
# else:
|
|
2663
|
+
# # again, handle our mask being either 2D, 3D, or 4D. NB - no color (4D) handling implemented at this point
|
|
2664
|
+
# mask = namespace[self.mask].data
|
|
2665
|
+
|
|
2666
|
+
# var = np.empty(data.shape[2], dtype=float)
|
|
2667
|
+
# mean = np.empty_like(var)
|
|
2668
|
+
# median = np.empty_like(var)
|
|
2669
|
+
# mode = np.empty_like(var)
|
|
2670
|
+
|
|
2671
|
+
# for si in range(data.shape[2]):
|
|
2672
|
+
# slice_data = data[:,:,si, 0]
|
|
2673
|
+
|
|
2674
|
+
# if mask is not None:
|
|
2675
|
+
# if mask.shape[2] == 1:
|
|
2676
|
+
# slice_data = slice_data[mask[:,:,0,0].astype('bool')]
|
|
2677
|
+
# elif mask.shape[2] == data.shape[2]:
|
|
2678
|
+
# slice_data = slice_data[mask[:,:,si,0].astype('bool')]
|
|
2679
|
+
# else:
|
|
2680
|
+
# raise RuntimeError('Mask dimensions do not match data dimensions')
|
|
2681
|
+
|
|
2682
|
+
# var[si] = np.var(slice_data)
|
|
2683
|
+
# mean[si] = np.mean(slice_data)
|
|
2684
|
+
# median[si] = np.median(slice_data)
|
|
2685
|
+
# mode[si] = stats.mode(slice_data, axis=None)[0][0]
|
|
2686
|
+
|
|
2687
|
+
# # package up and ship-out results
|
|
2688
|
+
# res = tabular.DictSource({'variance': var, 'mean': mean, 'median': median, 'mode': mode})
|
|
2689
|
+
# try:
|
|
2690
|
+
# res.mdh = series.mdh
|
|
2691
|
+
# except:
|
|
2692
|
+
# pass
|
|
2693
|
+
|
|
2694
|
+
# namespace[self.output_name] = res
|
|
2695
|
+
|
|
2696
|
+
def run(self, input_name, mask=None):
|
|
2697
|
+
from scipy import stats
|
|
2698
|
+
|
|
2699
|
+
data = input_name.data
|
|
2700
|
+
if mask:
|
|
2701
|
+
# again, handle our mask being either 2D, 3D, or 4D. NB - no color (4D) handling implemented at this point
|
|
2702
|
+
mask = mask.data
|
|
2703
|
+
|
|
2704
|
+
var = np.empty(data.shape[2], dtype=float)
|
|
2705
|
+
mean = np.empty_like(var)
|
|
2706
|
+
median = np.empty_like(var)
|
|
2707
|
+
mode = np.empty_like(var)
|
|
2708
|
+
|
|
2709
|
+
for si in range(data.shape[2]):
|
|
2710
|
+
slice_data = data[:,:,si, 0]
|
|
2711
|
+
|
|
2712
|
+
if mask is not None:
|
|
2713
|
+
if mask.shape[2] == 1:
|
|
2714
|
+
slice_data = slice_data[mask[:,:,0,0].astype('bool')]
|
|
2715
|
+
elif mask.shape[2] == data.shape[2]:
|
|
2716
|
+
slice_data = slice_data[mask[:,:,si,0].astype('bool')]
|
|
2717
|
+
else:
|
|
2718
|
+
raise RuntimeError('Mask dimensions do not match data dimensions')
|
|
2719
|
+
|
|
2720
|
+
var[si] = np.var(slice_data)
|
|
2721
|
+
mean[si] = np.mean(slice_data)
|
|
2722
|
+
median[si] = np.median(slice_data)
|
|
2723
|
+
mode[si] = stats.mode(slice_data, axis=None)[0].squeeze()
|
|
2724
|
+
|
|
2725
|
+
# package up and ship-out results
|
|
2726
|
+
return tabular.DictSource({'variance': var, 'mean': mean, 'median': median, 'mode': mode})
|
|
2727
|
+
|
|
2728
|
+
|
|
2729
|
+
@register_module('DarkAndVarianceMap')
|
|
2730
|
+
class DarkAndVarianceMap(ModuleBase):
|
|
2731
|
+
input = Input('input')
|
|
2732
|
+
output_variance = Output('variance')
|
|
2733
|
+
output_dark = Output('dark')
|
|
2734
|
+
dark_threshold = Float(1e4) # this really should depend on the gain mode (12bit vs 16 bit etc)
|
|
2735
|
+
variance_threshold = Float(300**2) # again this is currently picked fairly arbitrarily
|
|
2736
|
+
blemish_variance = Float(1e8) #set broken pixels to super high variance)
|
|
2737
|
+
start = Int(0)
|
|
2738
|
+
end = Int(-1)
|
|
2739
|
+
|
|
2740
|
+
# def execute(self, namespace):
|
|
2741
|
+
# from PYME.Analysis import gen_sCMOS_maps
|
|
2742
|
+
|
|
2743
|
+
# image = namespace[self.input]
|
|
2744
|
+
|
|
2745
|
+
# dark_map, variance_map = gen_sCMOS_maps.generate_maps(image, self.start, self.end,
|
|
2746
|
+
# darkthreshold=self.dark_threshold,
|
|
2747
|
+
# variancethreshold=self.variance_threshold,
|
|
2748
|
+
# blemishvariance=self.blemish_variance)
|
|
2749
|
+
|
|
2750
|
+
# namespace[self.output_dark] = dark_map
|
|
2751
|
+
# namespace[self.output_variance] = variance_map
|
|
2752
|
+
|
|
2753
|
+
def run(self, input):
|
|
2754
|
+
from PYME.Analysis import gen_sCMOS_maps
|
|
2755
|
+
|
|
2756
|
+
dark_map, variance_map = gen_sCMOS_maps.generate_maps(input, self.start, self.end,
|
|
2757
|
+
darkthreshold=self.dark_threshold,
|
|
2758
|
+
variancethreshold=self.variance_threshold,
|
|
2759
|
+
blemishvariance=self.blemish_variance)
|
|
2760
|
+
|
|
2761
|
+
return {'output_dark' : dark_map, 'output_variance' : variance_map}
|
|
2762
|
+
|
|
2763
|
+
|
|
2764
|
+
@register_module('Composite')
|
|
2765
|
+
class Composite(ModuleBase):
|
|
2766
|
+
"""
|
|
2767
|
+
Combine multiple single-channel images into a multi-channel image. Goes further than base.JoinChannels
|
|
2768
|
+
in that it supports re-scaling to match voxel sizes and optionally shift correction.
|
|
2769
|
+
|
|
2770
|
+
input0 is used to set the voxel size, with other images being re-scaled to match
|
|
2771
|
+
|
|
2772
|
+
"""
|
|
2773
|
+
input0 = Input('input')
|
|
2774
|
+
input1 = Input('')
|
|
2775
|
+
input2 = Input('')
|
|
2776
|
+
input3 = Input('')
|
|
2777
|
+
|
|
2778
|
+
shiftmap0 = FileOrURI('')
|
|
2779
|
+
shiftmap1 = FileOrURI('')
|
|
2780
|
+
shiftmap2 = FileOrURI('')
|
|
2781
|
+
shiftmap3 = FileOrURI('')
|
|
2782
|
+
|
|
2783
|
+
ignoreZOrigin = Bool(True)
|
|
2784
|
+
interpolate = Bool(True)
|
|
2785
|
+
|
|
2786
|
+
output = Output('composite')
|
|
2787
|
+
|
|
2788
|
+
|
|
2789
|
+
def execute(self, namespace):
|
|
2790
|
+
# NB - this needs to define .execute() as we allow channel speficiation in the inputs
|
|
2791
|
+
from PYME.Analysis import composite
|
|
2792
|
+
|
|
2793
|
+
imgs = []
|
|
2794
|
+
|
|
2795
|
+
for i in range(4):
|
|
2796
|
+
imn = getattr(self, 'input%d' % i)
|
|
2797
|
+
|
|
2798
|
+
if not (imn == ''):
|
|
2799
|
+
if '$' in imn:
|
|
2800
|
+
imn, chan = imn.split('$')
|
|
2801
|
+
chan=int(chan)
|
|
2802
|
+
else:
|
|
2803
|
+
chan=0
|
|
2804
|
+
|
|
2805
|
+
imgs.append((namespace[imn], chan, getattr(self, 'shiftmap%d' % i)))
|
|
2806
|
+
|
|
2807
|
+
namespace[self.output] = composite.make_composite(imgs, ignoreZ=self.ignoreZOrigin, interp=self.interpolate)
|
|
2808
|
+
|
|
2809
|
+
@property
|
|
2810
|
+
def inputs(self):
|
|
2811
|
+
"""
|
|
2812
|
+
Redefine inputs to remove channel suffixes
|
|
2813
|
+
|
|
2814
|
+
"""
|
|
2815
|
+
return {v.split('$')[0] for k, v in self.trait_get().items() if
|
|
2816
|
+
(k.startswith('input') or isinstance(k, Input)) and not v == ''}
|
|
2817
|
+
|
|
2818
|
+
|
|
2819
|
+
@register_module('RawADUToElectronsPerSecond')
|
|
2820
|
+
class RawADUToElectronsPerSecond(ModuleBase):
|
|
2821
|
+
"""
|
|
2822
|
+
Converts and image series from raw analog-digital units [ADU] to
|
|
2823
|
+
photoelectrons per second [e/s]
|
|
2824
|
+
|
|
2825
|
+
Parameters
|
|
2826
|
+
----------
|
|
2827
|
+
input_name : Input
|
|
2828
|
+
PYME.IO.ImageStack in units of ADU
|
|
2829
|
+
|
|
2830
|
+
Returns
|
|
2831
|
+
-------
|
|
2832
|
+
output_name : Output
|
|
2833
|
+
PYME.IO.ImageStack in units of photoelectrons/second
|
|
2834
|
+
|
|
2835
|
+
Warnings
|
|
2836
|
+
--------
|
|
2837
|
+
|
|
2838
|
+
A lot of PYME assumes that image units are in ADUs. This means that it would be easy to end up doing this correction
|
|
2839
|
+
in downstream modules as well as here and ending up with erroneous data as a result. Safe usage of this module in
|
|
2840
|
+
complex workflows will require building enhanced unit awareness into other parts of PYME which is not currently present.
|
|
2841
|
+
|
|
2842
|
+
At this point there is not much certainty about how unit awareness should be done, and it is likely to change signficantly from
|
|
2843
|
+
the first attempt here. In practice this means:
|
|
2844
|
+
|
|
2845
|
+
- this should be viewed as experimental
|
|
2846
|
+
- before building extensive pipelines that depend on Units metadata, or saving large numbers of files calibrated in e/s please
|
|
2847
|
+
force the issue (through, e.g an issue on github and a discussion) so we can finalise what unit support is going to look like
|
|
2848
|
+
and what metadata should be used).
|
|
2849
|
+
|
|
2850
|
+
"""
|
|
2851
|
+
input_name = Input('raw_adu')
|
|
2852
|
+
output_name = Output('electrons_per_s')
|
|
2853
|
+
|
|
2854
|
+
# def execute(self, namespace):
|
|
2855
|
+
# from PYME.IO.image import ImageStack
|
|
2856
|
+
# from PYME.IO.MetaDataHandler import DictMDHandler
|
|
2857
|
+
# from PYME.IO.DataSources import ElectronsPerSecondDataSource
|
|
2858
|
+
|
|
2859
|
+
# series_adu = namespace[self.input_name]
|
|
2860
|
+
# epers_ds = ElectronsPerSecondDataSource.DataSource(series_adu.data, series_adu.mdh)
|
|
2861
|
+
# series_epers = ImageStack(data=epers_ds, events=series_adu.events, mdh=DictMDHandler(series_adu.mdh))
|
|
2862
|
+
# series_epers.mdh['Parent'] = series_adu.filename
|
|
2863
|
+
|
|
2864
|
+
# series_epers.mdh['Units.Intensity'] = 'e/s'
|
|
2865
|
+
|
|
2866
|
+
# # Fudge metadata
|
|
2867
|
+
# # This should make metadata calibration more or less work where needed (at least until we have more comprehensive units support)
|
|
2868
|
+
# # note that in order for us to be able to get to electrons (not e/s) for noise models, we need to fudge either the gain or electrons
|
|
2869
|
+
# # per count with the integration time.
|
|
2870
|
+
# im.mdh['Camera.ElectronsPerCount'] = 1.0*series_adu.mdh['Camera.IntegrationTime']
|
|
2871
|
+
# im.mdh['Camera.TrueEMGain'] = 1.0
|
|
2872
|
+
# im.mdh['Camera.ADOffset'] = 0
|
|
2873
|
+
|
|
2874
|
+
# namespace[self.output_name] = series_epers
|
|
2875
|
+
|
|
2876
|
+
def run(self, input_name):
|
|
2877
|
+
from PYME.IO.image import ImageStack
|
|
2878
|
+
from PYME.IO.MetaDataHandler import DictMDHandler
|
|
2879
|
+
from PYME.IO.DataSources import ElectronsPerSecondDataSource
|
|
2880
|
+
|
|
2881
|
+
series_adu = input_name
|
|
2882
|
+
epers_ds = ElectronsPerSecondDataSource.DataSource(series_adu.data, series_adu.mdh)
|
|
2883
|
+
series_epers = ImageStack(data=epers_ds, events=series_adu.events, mdh=DictMDHandler(series_adu.mdh))
|
|
2884
|
+
series_epers.mdh['Parent'] = series_adu.filename
|
|
2885
|
+
|
|
2886
|
+
series_epers.mdh['Units.Intensity'] = 'e/s'
|
|
2887
|
+
|
|
2888
|
+
# Fudge metadata
|
|
2889
|
+
# This should make metadata calibration more or less work where needed (at least until we have more comprehensive units support)
|
|
2890
|
+
# note that in order for us to be able to get to electrons (not e/s) for noise models, we need to fudge either the gain or electrons
|
|
2891
|
+
# per count with the integration time.
|
|
2892
|
+
series_epers.mdh['Camera.ElectronsPerCount'] = 1.0*series_adu.mdh['Camera.IntegrationTime']
|
|
2893
|
+
series_epers.mdh['Camera.TrueEMGain'] = 1.0
|
|
2894
|
+
series_epers.mdh['Camera.ADOffset'] = 0
|
|
2895
|
+
|
|
2896
|
+
return series_epers
|
|
2897
|
+
|
|
2898
|
+
|
|
2899
|
+
@register_module('Rotate180')
|
|
2900
|
+
class Rotate180(Filter):
|
|
2901
|
+
"""
|
|
2902
|
+
Rotate the image 180 degrees in x-y
|
|
2903
|
+
|
|
2904
|
+
Parameters
|
|
2905
|
+
----------
|
|
2906
|
+
input_name : PYME.IO.ImageStack
|
|
2907
|
+
output_name : PYME.IO.ImageStack
|
|
2908
|
+
"""
|
|
2909
|
+
|
|
2910
|
+
dimensionality = Enum('XY', desc='Which image dimensions should the filter be applied to?') # our code only works in 2D, so restrict dimensionality choices
|
|
2911
|
+
|
|
2912
|
+
def apply_filter(self, data, voxelsize):
|
|
2913
|
+
return data[::-1,::-1]
|
|
2914
|
+
|
|
2915
|
+
|
|
2916
|
+
@register_module('SplineFitBackgroundEstimate')
|
|
2917
|
+
class SplineFitBackgroundEstimate(Filter):
|
|
2918
|
+
"""
|
|
2919
|
+
Background subtraction using a 2D smoothing spline fit
|
|
2920
|
+
|
|
2921
|
+
Parameters
|
|
2922
|
+
----------
|
|
2923
|
+
smooth : Positive smoothing factor defined for estimation
|
|
2924
|
+
"""
|
|
2925
|
+
smooth = Float(1e7)
|
|
2926
|
+
dimensionality = Enum('XY', desc='Which image dimensions should the filter be applied to?')
|
|
2927
|
+
|
|
2928
|
+
def apply_filter(self, data, voxelsize):
|
|
2929
|
+
from scipy.interpolate import RectBivariateSpline
|
|
2930
|
+
x = np.arange(data.shape[0])
|
|
2931
|
+
y = np.arange(data.shape[1])
|
|
2932
|
+
fit = RectBivariateSpline(x, y, data, s=self.smooth)
|
|
2933
|
+
return fit(x, y)
|
|
2934
|
+
|
|
2935
|
+
|
|
2936
|
+
@register_module('Offset')
|
|
2937
|
+
class Offset(Filter):
|
|
2938
|
+
"""
|
|
2939
|
+
Offset the grayscale value by a certain number
|
|
2940
|
+
|
|
2941
|
+
Parameters
|
|
2942
|
+
----------
|
|
2943
|
+
offset_constant : a constant that the image will subtract, float
|
|
2944
|
+
offset_selection : which kind of image offset is set to process
|
|
2945
|
+
"""
|
|
2946
|
+
offset_constant = Float(0.0)
|
|
2947
|
+
offset_selection = Enum('offset by a constant', 'offset by minimum')
|
|
2948
|
+
dimensionality = Enum('XY', desc='Which image dimensions should the filter be applied to?')
|
|
2949
|
+
|
|
2950
|
+
def applyFilter(self, data, chanNum, i, image0):
|
|
2951
|
+
if self.offset_selection == 'offset by a constant':
|
|
2952
|
+
return data - self.offset_constant
|
|
2953
|
+
elif self.offset_selection == 'offset by minimum':
|
|
2954
|
+
return data - np.min(data)
|