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.
Files changed (1277) hide show
  1. PYME/Acquire/ActionManager.py +450 -0
  2. PYME/Acquire/ExecTools.py +235 -0
  3. PYME/Acquire/Hardware/AAOptoelectronics/MDS.py +175 -0
  4. PYME/Acquire/Hardware/AAOptoelectronics/__init__.py +0 -0
  5. PYME/Acquire/Hardware/ARCoptix/__init__.py +0 -0
  6. PYME/Acquire/Hardware/ARCoptix/lcdriver.py +149 -0
  7. PYME/Acquire/Hardware/ARCoptix/lcserver32.py +123 -0
  8. PYME/Acquire/Hardware/AndorIXon/AndorCam.py +1095 -0
  9. PYME/Acquire/Hardware/AndorIXon/AndorControlFrame.py +312 -0
  10. PYME/Acquire/Hardware/AndorIXon/AndorIXon.py +737 -0
  11. PYME/Acquire/Hardware/AndorIXon/__init__.py +27 -0
  12. PYME/Acquire/Hardware/AndorNeo/AndorNeo.py +644 -0
  13. PYME/Acquire/Hardware/AndorNeo/AndorNeoControlFrame.py +161 -0
  14. PYME/Acquire/Hardware/AndorNeo/AndorZyla.py +761 -0
  15. PYME/Acquire/Hardware/AndorNeo/SDK3.py +307 -0
  16. PYME/Acquire/Hardware/AndorNeo/SDK3Cam.py +171 -0
  17. PYME/Acquire/Hardware/AndorNeo/ZylaControlPanel.py +105 -0
  18. PYME/Acquire/Hardware/AndorNeo/__init__.py +22 -0
  19. PYME/Acquire/Hardware/AndorNeo/parseAtdebug.py +46 -0
  20. PYME/Acquire/Hardware/AndorNeo/plotTimings.py +73 -0
  21. PYME/Acquire/Hardware/AndorNeo/testNeo.py +62 -0
  22. PYME/Acquire/Hardware/Camera.py +1199 -0
  23. PYME/Acquire/Hardware/CameraSkeleton.py +642 -0
  24. PYME/Acquire/Hardware/Coherent/OBIS.py +147 -0
  25. PYME/Acquire/Hardware/Coherent/Sapphire.py +162 -0
  26. PYME/Acquire/Hardware/Coherent/__init__.py +0 -0
  27. PYME/Acquire/Hardware/DMDGui.py +198 -0
  28. PYME/Acquire/Hardware/DigiData/DigiData.py +95 -0
  29. PYME/Acquire/Hardware/DigiData/DigiDataClient.py +30 -0
  30. PYME/Acquire/Hardware/DigiData/RemoteDigiData.py +51 -0
  31. PYME/Acquire/Hardware/DigiData/__init__.py +24 -0
  32. PYME/Acquire/Hardware/DigiData/axDD132x.py +348 -0
  33. PYME/Acquire/Hardware/EMCCDTheory.py +41 -0
  34. PYME/Acquire/Hardware/ExciterWheel.py +167 -0
  35. PYME/Acquire/Hardware/FilterWheel.py +184 -0
  36. PYME/Acquire/Hardware/FocCorr.py +106 -0
  37. PYME/Acquire/Hardware/FocCorrR.py +196 -0
  38. PYME/Acquire/Hardware/FrFilter.py +51 -0
  39. PYME/Acquire/Hardware/GCS/GCS_DLL.py +370 -0
  40. PYME/Acquire/Hardware/GCS/__init__.py +0 -0
  41. PYME/Acquire/Hardware/GCS/gcs.py +210 -0
  42. PYME/Acquire/Hardware/HamamatsuDCAM/HamamatsuDCAM.py +662 -0
  43. PYME/Acquire/Hardware/HamamatsuDCAM/HamamatsuORCA.py +602 -0
  44. PYME/Acquire/Hardware/HamamatsuDCAM/Hamamatsu_control_panel.py +54 -0
  45. PYME/Acquire/Hardware/HamamatsuDCAM/__init__.py +23 -0
  46. PYME/Acquire/Hardware/LaserControlFrame.py +160 -0
  47. PYME/Acquire/Hardware/MPBCommunications/MPBCW.py +132 -0
  48. PYME/Acquire/Hardware/MPBCommunications/__init__.py +0 -0
  49. PYME/Acquire/Hardware/Mercury/Mercury.py +269 -0
  50. PYME/Acquire/Hardware/Mercury/PI_Mercury_GCS_DLL.py +857 -0
  51. PYME/Acquire/Hardware/Mercury/__init__.py +22 -0
  52. PYME/Acquire/Hardware/Mercury/mercuryStepper.py +203 -0
  53. PYME/Acquire/Hardware/Mercury/mercuryStepperGCS.py +281 -0
  54. PYME/Acquire/Hardware/NikonTE2000.py +68 -0
  55. PYME/Acquire/Hardware/NikonTi.py +214 -0
  56. PYME/Acquire/Hardware/NikonTiGUI.py +81 -0
  57. PYME/Acquire/Hardware/OrielCornerstone.py +125 -0
  58. PYME/Acquire/Hardware/PM100USB.py +51 -0
  59. PYME/Acquire/Hardware/Piezos/__init__.py +22 -0
  60. PYME/Acquire/Hardware/Piezos/base_piezo.py +108 -0
  61. PYME/Acquire/Hardware/Piezos/offsetPiezo.py +162 -0
  62. PYME/Acquire/Hardware/Piezos/offsetPiezoREST.py +308 -0
  63. PYME/Acquire/Hardware/Piezos/piezo_c867.py +486 -0
  64. PYME/Acquire/Hardware/Piezos/piezo_e255.py +68 -0
  65. PYME/Acquire/Hardware/Piezos/piezo_e662.py +61 -0
  66. PYME/Acquire/Hardware/Piezos/piezo_e709.py +434 -0
  67. PYME/Acquire/Hardware/Piezos/piezo_e816.py +452 -0
  68. PYME/Acquire/Hardware/Piezos/piezo_e816_corr.py +70 -0
  69. PYME/Acquire/Hardware/Piezos/piezo_e816_dll.py +518 -0
  70. PYME/Acquire/Hardware/Piezos/piezo_e816b.py +70 -0
  71. PYME/Acquire/Hardware/Piezos/piezo_pipython_gcs.py +560 -0
  72. PYME/Acquire/Hardware/Piezos/piezo_test.py +55 -0
  73. PYME/Acquire/Hardware/Simulator/EmpiricalHist.py +273 -0
  74. PYME/Acquire/Hardware/Simulator/__init__.py +22 -0
  75. PYME/Acquire/Hardware/Simulator/dSimControl.py +961 -0
  76. PYME/Acquire/Hardware/Simulator/fakeCam.py +651 -0
  77. PYME/Acquire/Hardware/Simulator/fakePiezo.py +95 -0
  78. PYME/Acquire/Hardware/Simulator/fluor.py +336 -0
  79. PYME/Acquire/Hardware/Simulator/illuminate.cp39-win_amd64.dll.a +0 -0
  80. PYME/Acquire/Hardware/Simulator/illuminate.cp39-win_amd64.pyd +0 -0
  81. PYME/Acquire/Hardware/Simulator/illuminate.pyx +93 -0
  82. PYME/Acquire/Hardware/Simulator/lasersliders.py +90 -0
  83. PYME/Acquire/Hardware/Simulator/rend_im.py +446 -0
  84. PYME/Acquire/Hardware/Simulator/setup.py +69 -0
  85. PYME/Acquire/Hardware/Simulator/simcontrol.py +348 -0
  86. PYME/Acquire/Hardware/Simulator/simui_wx.py +794 -0
  87. PYME/Acquire/Hardware/Tango/__init__.py +0 -0
  88. PYME/Acquire/Hardware/Tango/marzhauser_tango.py +583 -0
  89. PYME/Acquire/Hardware/TiLightCrafter.py +388 -0
  90. PYME/Acquire/Hardware/__init__.py +22 -0
  91. PYME/Acquire/Hardware/aotf.py +271 -0
  92. PYME/Acquire/Hardware/arclampshutterpanel.py +71 -0
  93. PYME/Acquire/Hardware/cameraSoftwareBuffer.py +215 -0
  94. PYME/Acquire/Hardware/camera_noise.py +270 -0
  95. PYME/Acquire/Hardware/ccdAdjPanel.py +188 -0
  96. PYME/Acquire/Hardware/ccdCalibrator.py +161 -0
  97. PYME/Acquire/Hardware/cobaltLaser.py +108 -0
  98. PYME/Acquire/Hardware/cobaltLaser561.py +94 -0
  99. PYME/Acquire/Hardware/comports.py +26 -0
  100. PYME/Acquire/Hardware/driftTrackGUI.py +488 -0
  101. PYME/Acquire/Hardware/driftTracking.py +595 -0
  102. PYME/Acquire/Hardware/fakeShutters.py +52 -0
  103. PYME/Acquire/Hardware/focusKeys.py +204 -0
  104. PYME/Acquire/Hardware/focus_locks/__init__.py +0 -0
  105. PYME/Acquire/Hardware/focus_locks/reflection_focus_lock.py +684 -0
  106. PYME/Acquire/Hardware/frZStage.py +117 -0
  107. PYME/Acquire/Hardware/fw102.py +38 -0
  108. PYME/Acquire/Hardware/ids_peak_cam.py +561 -0
  109. PYME/Acquire/Hardware/ioslave.py +305 -0
  110. PYME/Acquire/Hardware/lasers.py +292 -0
  111. PYME/Acquire/Hardware/matchboxLaser.py +58 -0
  112. PYME/Acquire/Hardware/microscope_adapter.py +104 -0
  113. PYME/Acquire/Hardware/mpd_picosecond_delayer.py +395 -0
  114. PYME/Acquire/Hardware/multiview.py +287 -0
  115. PYME/Acquire/Hardware/olympusix81.py +59 -0
  116. PYME/Acquire/Hardware/pco/__init__.py +0 -0
  117. PYME/Acquire/Hardware/pco/pco_cam.py +348 -0
  118. PYME/Acquire/Hardware/pco/pco_edge_42_lt.py +34 -0
  119. PYME/Acquire/Hardware/pco/pco_sdk.py +1645 -0
  120. PYME/Acquire/Hardware/pco/pco_sdk_cam.py +518 -0
  121. PYME/Acquire/Hardware/pco/pco_sdk_cam_control_panel.py +54 -0
  122. PYME/Acquire/Hardware/phoxxLaser.py +199 -0
  123. PYME/Acquire/Hardware/phoxxLaserOLD.py +242 -0
  124. PYME/Acquire/Hardware/priorLumen.py +102 -0
  125. PYME/Acquire/Hardware/priorarclampshutter.py +52 -0
  126. PYME/Acquire/Hardware/setup.py +50 -0
  127. PYME/Acquire/Hardware/spacenav.py +148 -0
  128. PYME/Acquire/Hardware/splitter.py +394 -0
  129. PYME/Acquire/Hardware/thorlabsPiezo.py +98 -0
  130. PYME/Acquire/Hardware/thorlabs_cam.py +136 -0
  131. PYME/Acquire/Hardware/thorlabs_mff_flipper.py +400 -0
  132. PYME/Acquire/Hardware/toptica_ibeam.py +51 -0
  133. PYME/Acquire/Hardware/uc480/AndorControlFrame.py +308 -0
  134. PYME/Acquire/Hardware/uc480/__init__.py +27 -0
  135. PYME/Acquire/Hardware/uc480/uCam480.py +695 -0
  136. PYME/Acquire/Hardware/uc480/uc480.py +189 -0
  137. PYME/Acquire/Hardware/uc480/uc480Deprecated.py +87 -0
  138. PYME/Acquire/Hardware/uc480/uc480_h.py +767 -0
  139. PYME/Acquire/Hardware/uc480/uc480_h_gen.py +62 -0
  140. PYME/Acquire/Hardware/uc480/ucCamControlFrame.py +83 -0
  141. PYME/Acquire/Hardware/ueye.py +702 -0
  142. PYME/Acquire/PYMEAcquire.py +186 -0
  143. PYME/Acquire/Protocols/DMDMFM.py +74 -0
  144. PYME/Acquire/Protocols/__init__.py +22 -0
  145. PYME/Acquire/Protocols/darkCalibrate.py +56 -0
  146. PYME/Acquire/Protocols/dual671_470.py +45 -0
  147. PYME/Acquire/Protocols/dual671_488.py +46 -0
  148. PYME/Acquire/Protocols/gainCal.py +53 -0
  149. PYME/Acquire/Protocols/htsms-cal-psf.py +32 -0
  150. PYME/Acquire/Protocols/htsms-cal-registration.py +52 -0
  151. PYME/Acquire/Protocols/htsms-flow.py +29 -0
  152. PYME/Acquire/Protocols/htsms-staggered.py +31 -0
  153. PYME/Acquire/Protocols/htsms-tile.py +68 -0
  154. PYME/Acquire/Protocols/htsms-two-color.py +29 -0
  155. PYME/Acquire/Protocols/htsms-widefield.py +32 -0
  156. PYME/Acquire/Protocols/paint671.py +61 -0
  157. PYME/Acquire/Protocols/photoconversion.py +52 -0
  158. PYME/Acquire/Protocols/photoconversion2.py +55 -0
  159. PYME/Acquire/Protocols/prebleach488.py +70 -0
  160. PYME/Acquire/Protocols/prebleach490.py +69 -0
  161. PYME/Acquire/Protocols/prebleach561.py +72 -0
  162. PYME/Acquire/Protocols/prebleach561NeomEos2.py +70 -0
  163. PYME/Acquire/Protocols/prebleach561NeomEos2ND1.py +70 -0
  164. PYME/Acquire/Protocols/prebleach642.py +70 -0
  165. PYME/Acquire/Protocols/prebleach671.py +69 -0
  166. PYME/Acquire/Protocols/prebleach671ND1.py +58 -0
  167. PYME/Acquire/Protocols/prebleach671ND2.py +57 -0
  168. PYME/Acquire/Protocols/prebleach671Neo.py +69 -0
  169. PYME/Acquire/Protocols/recover671.py +62 -0
  170. PYME/Acquire/Protocols/sequence642_488.py +85 -0
  171. PYME/Acquire/Protocols/sequence642_561.py +87 -0
  172. PYME/Acquire/Protocols/shiftfield.py +198 -0
  173. PYME/Acquire/Protocols/simul488.py +59 -0
  174. PYME/Acquire/Protocols/simul642.py +63 -0
  175. PYME/Acquire/Protocols/simul642HTSMS.py +63 -0
  176. PYME/Acquire/Protocols/simulPA.py +37 -0
  177. PYME/Acquire/Protocols/simulSIM.py +76 -0
  178. PYME/Acquire/Protocols/simulStep.py +62 -0
  179. PYME/Acquire/Protocols/spdscan.py +178 -0
  180. PYME/Acquire/Protocols/standard470.py +45 -0
  181. PYME/Acquire/Protocols/standard488.py +47 -0
  182. PYME/Acquire/Protocols/standard671.py +48 -0
  183. PYME/Acquire/Protocols/standard671_no532.py +47 -0
  184. PYME/Acquire/Protocols/standardArclamp.py +46 -0
  185. PYME/Acquire/Protocols/strip_tile.py +204 -0
  186. PYME/Acquire/Protocols/tile.py +204 -0
  187. PYME/Acquire/Protocols/tile3D.py +192 -0
  188. PYME/Acquire/Protocols/tile671.py +190 -0
  189. PYME/Acquire/Protocols/tile_triggered.py +189 -0
  190. PYME/Acquire/Scripts/init.py +232 -0
  191. PYME/Acquire/Scripts/init_N1_Ti_Exeter_ZIx.py +374 -0
  192. PYME/Acquire/Scripts/init_N2_Ti_Zyla_lasers.py +290 -0
  193. PYME/Acquire/Scripts/init_Neo.py +266 -0
  194. PYME/Acquire/Scripts/init_NeoSim.py +159 -0
  195. PYME/Acquire/Scripts/init_TIRF.py +232 -0
  196. PYME/Acquire/Scripts/init_TIRF_Neo.py +242 -0
  197. PYME/Acquire/Scripts/init_TIRF_NeoO.py +241 -0
  198. PYME/Acquire/Scripts/init_TIRF_onecam.py +233 -0
  199. PYME/Acquire/Scripts/init_Ti.py +257 -0
  200. PYME/Acquire/Scripts/init_UOA_n.py +256 -0
  201. PYME/Acquire/Scripts/init_UOA_n2.py +246 -0
  202. PYME/Acquire/Scripts/init_Y1.py +249 -0
  203. PYME/Acquire/Scripts/init_Zyla.py +260 -0
  204. PYME/Acquire/Scripts/init_drift_tracking.py +80 -0
  205. PYME/Acquire/Scripts/init_emccd_basic.py +277 -0
  206. PYME/Acquire/Scripts/init_htsms.py +277 -0
  207. PYME/Acquire/Scripts/init_htsms_focus_lock.py +132 -0
  208. PYME/Acquire/Scripts/init_micrpi.py +45 -0
  209. PYME/Acquire/Scripts/init_orca.py +70 -0
  210. PYME/Acquire/Scripts/init_pco.py +49 -0
  211. PYME/Acquire/Scripts/init_rev.py +74 -0
  212. PYME/Acquire/Scripts/init_sim100.py +169 -0
  213. PYME/Acquire/Scripts/init_sim103.py +169 -0
  214. PYME/Acquire/Scripts/init_sim2.py +211 -0
  215. PYME/Acquire/Scripts/init_sim50.py +169 -0
  216. PYME/Acquire/Scripts/init_sim75.py +169 -0
  217. PYME/Acquire/Scripts/init_sim_drift_tracking.py +166 -0
  218. PYME/Acquire/Scripts/init_sim_htsms.py +224 -0
  219. PYME/Acquire/Scripts/init_sim_htsms_n.py +233 -0
  220. PYME/Acquire/Scripts/init_sim_main.py +229 -0
  221. PYME/Acquire/Scripts/init_sim_min.py +197 -0
  222. PYME/Acquire/Scripts/init_sim_rem.py +176 -0
  223. PYME/Acquire/Scripts/init_smi1.py +110 -0
  224. PYME/Acquire/Scripts/init_spectro.py +78 -0
  225. PYME/Acquire/Scripts/init_spim.py +116 -0
  226. PYME/Acquire/Scripts/init_thorcam.py +62 -0
  227. PYME/Acquire/Scripts/init_twophoton.py +112 -0
  228. PYME/Acquire/Scripts/init_uc480.py +262 -0
  229. PYME/Acquire/Scripts/init_ueye.py +261 -0
  230. PYME/Acquire/SpoolController.py +958 -0
  231. PYME/Acquire/Utils/MultiPointScanner.py +80 -0
  232. PYME/Acquire/Utils/__init__.py +13 -0
  233. PYME/Acquire/Utils/failsafe.py +157 -0
  234. PYME/Acquire/Utils/fastTiler.py +153 -0
  235. PYME/Acquire/Utils/pointScanner.py +496 -0
  236. PYME/Acquire/Utils/sarcSpacing.py +86 -0
  237. PYME/Acquire/Utils/strip_tiler.py +93 -0
  238. PYME/Acquire/Utils/tiler.py +464 -0
  239. PYME/Acquire/Utils/vibrationAnalysis.py +65 -0
  240. PYME/Acquire/__init__.py +44 -0
  241. PYME/Acquire/acquire_client.py +90 -0
  242. PYME/Acquire/acquire_server.py +316 -0
  243. PYME/Acquire/acquirebase.py +139 -0
  244. PYME/Acquire/acquiremainframe.py +716 -0
  245. PYME/Acquire/acquirewx.py +61 -0
  246. PYME/Acquire/acquisition_base.py +148 -0
  247. PYME/Acquire/actions.py +209 -0
  248. PYME/Acquire/autofocus.py +54 -0
  249. PYME/Acquire/eventLog.py +95 -0
  250. PYME/Acquire/event_loop.py +119 -0
  251. PYME/Acquire/frameWrangler.py +530 -0
  252. PYME/Acquire/htsms/__init__.py +8 -0
  253. PYME/Acquire/htsms/rule_ui.py +970 -0
  254. PYME/Acquire/htsms/rule_ui_v2.py +573 -0
  255. PYME/Acquire/htsms/tweeter.py +222 -0
  256. PYME/Acquire/microscope.py +1050 -0
  257. PYME/Acquire/positionTracker.py +181 -0
  258. PYME/Acquire/protocol.py +299 -0
  259. PYME/Acquire/protocol_acquisition.py +404 -0
  260. PYME/Acquire/sampleInformation.py +591 -0
  261. PYME/Acquire/sampleInformationDjangoDirect.py +446 -0
  262. PYME/Acquire/setup.py +52 -0
  263. PYME/Acquire/stackSettings.py +295 -0
  264. PYME/Acquire/stage_leveling.py +285 -0
  265. PYME/Acquire/ui/AnalysisSettingsUI.py +184 -0
  266. PYME/Acquire/ui/HDFSpoolFrame.py +584 -0
  267. PYME/Acquire/ui/__init__.py +0 -0
  268. PYME/Acquire/ui/actionUI.py +892 -0
  269. PYME/Acquire/ui/focus_lock_gui.py +149 -0
  270. PYME/Acquire/ui/intensity_trace.py +143 -0
  271. PYME/Acquire/ui/intsliders.py +222 -0
  272. PYME/Acquire/ui/lasersliders.py +329 -0
  273. PYME/Acquire/ui/mpd_picosecond_delay_panel.py +69 -0
  274. PYME/Acquire/ui/multiview_select.py +77 -0
  275. PYME/Acquire/ui/positionUI.py +519 -0
  276. PYME/Acquire/ui/preflight.py +58 -0
  277. PYME/Acquire/ui/scanner_panel.py +41 -0
  278. PYME/Acquire/ui/selectCameraPanel.py +46 -0
  279. PYME/Acquire/ui/seqdialog.py +501 -0
  280. PYME/Acquire/ui/splashScreen.py +175 -0
  281. PYME/Acquire/ui/spool_panel.py +830 -0
  282. PYME/Acquire/ui/tile_panel.py +556 -0
  283. PYME/Acquire/ui/tilesettingsui.py +91 -0
  284. PYME/Acquire/ui/voxelSizeDialog.py +149 -0
  285. PYME/Acquire/webui/__init__.py +36 -0
  286. PYME/Acquire/webui/ipy.py +361 -0
  287. PYME/Acquire/webui/static/css/pyme-bootstrap.css +9908 -0
  288. PYME/Acquire/webui/static/css/pymeacquire.css +85 -0
  289. PYME/Acquire/webui/static/js/pymeacquire.js +375 -0
  290. PYME/Acquire/webui/static/js/pzf.js +95 -0
  291. PYME/Acquire/webui/templates/PYMEAcquire.html +232 -0
  292. PYME/Acquire/webui/templates/login.html +84 -0
  293. PYME/Acquire/webui/templates/xtermpage.html +106 -0
  294. PYME/Acquire/xyztc.py +418 -0
  295. PYME/Acquire/zScanner.py +287 -0
  296. PYME/Analysis/BleachProfile/__init__.py +26 -0
  297. PYME/Analysis/BleachProfile/deMod.py +91 -0
  298. PYME/Analysis/BleachProfile/fitRecover.py +90 -0
  299. PYME/Analysis/BleachProfile/intensProf.py +519 -0
  300. PYME/Analysis/BleachProfile/kinModels.py +507 -0
  301. PYME/Analysis/BleachProfile/rawIntensity.py +227 -0
  302. PYME/Analysis/BleachProfile/rollins.py +555 -0
  303. PYME/Analysis/Colocalisation/__init__.py +27 -0
  304. PYME/Analysis/Colocalisation/colocScaleDep.py +126 -0
  305. PYME/Analysis/Colocalisation/correlationCoeffs.py +198 -0
  306. PYME/Analysis/Colocalisation/distColoc.py +110 -0
  307. PYME/Analysis/Colocalisation/edtColoc.py +191 -0
  308. PYME/Analysis/MetaData.py +278 -0
  309. PYME/Analysis/PSFEst/__init__.py +27 -0
  310. PYME/Analysis/PSFEst/dec.py +283 -0
  311. PYME/Analysis/PSFEst/extractImages.py +316 -0
  312. PYME/Analysis/PSFEst/fit_psf_zernikes.py +194 -0
  313. PYME/Analysis/PSFEst/psfQuality.py +225 -0
  314. PYME/Analysis/PSFGen/__init__.py +40 -0
  315. PYME/Analysis/PSFGen/fourier.py +138 -0
  316. PYME/Analysis/PSFGen/fourierHNA.py +1108 -0
  317. PYME/Analysis/PSFGen/fourierHNA2.py +979 -0
  318. PYME/Analysis/PSFGen/genCRBcurves.py +185 -0
  319. PYME/Analysis/PSFGen/nijboer_zernike.py +79 -0
  320. PYME/Analysis/PSFGen/ps_app.cp39-win_amd64.dll.a +0 -0
  321. PYME/Analysis/PSFGen/ps_app.cp39-win_amd64.pyd +0 -0
  322. PYME/Analysis/PSFGen/test_ps_app.py +24 -0
  323. PYME/Analysis/Tracking/__init__.py +3 -0
  324. PYME/Analysis/Tracking/msd.py +57 -0
  325. PYME/Analysis/Tracking/trackUtils.py +528 -0
  326. PYME/Analysis/Tracking/tracking.py +152 -0
  327. PYME/Analysis/__init__.py +30 -0
  328. PYME/Analysis/_fithelpers.py +161 -0
  329. PYME/Analysis/angleFilter.py +287 -0
  330. PYME/Analysis/annealThresh.py +157 -0
  331. PYME/Analysis/autoencoder_classifier.py +45 -0
  332. PYME/Analysis/binAvg.py +144 -0
  333. PYME/Analysis/composite.py +183 -0
  334. PYME/Analysis/cramerRao.py +259 -0
  335. PYME/Analysis/deTile.py +441 -0
  336. PYME/Analysis/density_filter.py +45 -0
  337. PYME/Analysis/distributed_pyramid.py +426 -0
  338. PYME/Analysis/fiducial_matching.py +220 -0
  339. PYME/Analysis/gen_sCMOS_maps.py +355 -0
  340. PYME/Analysis/graphing_filters.py +311 -0
  341. PYME/Analysis/implicitFilter.py +359 -0
  342. PYME/Analysis/keep_pairs.py +69 -0
  343. PYME/Analysis/optic_flow.py +108 -0
  344. PYME/Analysis/piecewise.py +36 -0
  345. PYME/Analysis/piecewiseMapping.py +304 -0
  346. PYME/Analysis/piezo_movement_correction.py +153 -0
  347. PYME/Analysis/points/DeClump/__init__.py +78 -0
  348. PYME/Analysis/points/DeClump/deClump.cp39-win_amd64.dll.a +0 -0
  349. PYME/Analysis/points/DeClump/deClump.cp39-win_amd64.pyd +0 -0
  350. PYME/Analysis/points/DeClump/deClumpGUI.py +87 -0
  351. PYME/Analysis/points/DeClump/pyDeClump.py +284 -0
  352. PYME/Analysis/points/DeClump/testClump.py +40 -0
  353. PYME/Analysis/points/DistHist/__init__.py +25 -0
  354. PYME/Analysis/points/DistHist/distHist.cp39-win_amd64.dll.a +0 -0
  355. PYME/Analysis/points/DistHist/distHist.cp39-win_amd64.pyd +0 -0
  356. PYME/Analysis/points/DistHist/distHistThreaded.py +71 -0
  357. PYME/Analysis/points/DistHist/setup.py +54 -0
  358. PYME/Analysis/points/EdgeDB/__init__.py +24 -0
  359. PYME/Analysis/points/EdgeDB/edgeDB.cp39-win_amd64.dll.a +0 -0
  360. PYME/Analysis/points/EdgeDB/edgeDB.cp39-win_amd64.pyd +0 -0
  361. PYME/Analysis/points/EdgeDB/edgeTest.py +66 -0
  362. PYME/Analysis/points/EdgeDB/edges.py +102 -0
  363. PYME/Analysis/points/EdgeDB/segment.py +75 -0
  364. PYME/Analysis/points/EdgeDB/setup.py +54 -0
  365. PYME/Analysis/points/QuadTree/QTrend.py +232 -0
  366. PYME/Analysis/points/QuadTree/__init__.py +25 -0
  367. PYME/Analysis/points/QuadTree/pointQT.py +227 -0
  368. PYME/Analysis/points/QuadTree/pointQTF.py +254 -0
  369. PYME/Analysis/points/QuadTree/qtUtils.py +36 -0
  370. PYME/Analysis/points/QuadTree/rendQT.py +118 -0
  371. PYME/Analysis/points/SoftRend/__init__.py +24 -0
  372. PYME/Analysis/points/SoftRend/setup.py +61 -0
  373. PYME/Analysis/points/SoftRend/testTetrahedra.py +41 -0
  374. PYME/Analysis/points/SoftRend/triRend.cp39-win_amd64.dll.a +0 -0
  375. PYME/Analysis/points/SoftRend/triRend.cp39-win_amd64.pyd +0 -0
  376. PYME/Analysis/points/__init__.py +3 -0
  377. PYME/Analysis/points/arcfit/__init__.py +1 -0
  378. PYME/Analysis/points/arcfit/arcmf.cp39-win_amd64.dll.a +0 -0
  379. PYME/Analysis/points/arcfit/arcmf.cp39-win_amd64.pyd +0 -0
  380. PYME/Analysis/points/arcfit/setup.py +51 -0
  381. PYME/Analysis/points/astigmatism/__init__.py +0 -0
  382. PYME/Analysis/points/astigmatism/astigTools.py +338 -0
  383. PYME/Analysis/points/astigmatism/astiglookup.cp39-win_amd64.dll.a +0 -0
  384. PYME/Analysis/points/astigmatism/astiglookup.cp39-win_amd64.pyd +0 -0
  385. PYME/Analysis/points/astigmatism/setup.py +54 -0
  386. PYME/Analysis/points/cluster_morphology.py +199 -0
  387. PYME/Analysis/points/coordinate_tools.py +278 -0
  388. PYME/Analysis/points/features/__init__.py +0 -0
  389. PYME/Analysis/points/features/metal.py +46 -0
  390. PYME/Analysis/points/features/shaders/vect_feat_3d.metal +49 -0
  391. PYME/Analysis/points/fiducials.py +142 -0
  392. PYME/Analysis/points/gen3DTriangs.py +796 -0
  393. PYME/Analysis/points/moments.py +143 -0
  394. PYME/Analysis/points/multiview.py +458 -0
  395. PYME/Analysis/points/objectMeasure.py +206 -0
  396. PYME/Analysis/points/qpaint.py +97 -0
  397. PYME/Analysis/points/rings.py +372 -0
  398. PYME/Analysis/points/ripleys.py +427 -0
  399. PYME/Analysis/points/segmentLL.py +134 -0
  400. PYME/Analysis/points/setup.py +56 -0
  401. PYME/Analysis/points/spherical_harmonics.py +909 -0
  402. PYME/Analysis/points/surfit.py +829 -0
  403. PYME/Analysis/points/tCluster.py +75 -0
  404. PYME/Analysis/points/traveling_salesperson/__init__.py +0 -0
  405. PYME/Analysis/points/traveling_salesperson/queue_opt.py +117 -0
  406. PYME/Analysis/points/traveling_salesperson/sectioned_two_opt.py +327 -0
  407. PYME/Analysis/points/traveling_salesperson/setup.py +40 -0
  408. PYME/Analysis/points/traveling_salesperson/sort.py +78 -0
  409. PYME/Analysis/points/traveling_salesperson/two_opt.py +189 -0
  410. PYME/Analysis/points/traveling_salesperson/two_opt_utils.cp39-win_amd64.dll.a +0 -0
  411. PYME/Analysis/points/traveling_salesperson/two_opt_utils.cp39-win_amd64.pyd +0 -0
  412. PYME/Analysis/points/traveling_salesperson/two_opt_utils.pyx +41 -0
  413. PYME/Analysis/points/twoColour.py +572 -0
  414. PYME/Analysis/points/twoColourPlot.py +107 -0
  415. PYME/Analysis/processLogger.py +129 -0
  416. PYME/Analysis/profile_extraction.py +72 -0
  417. PYME/Analysis/setup.py +60 -0
  418. PYME/Analysis/splitting.py +372 -0
  419. PYME/Analysis/svmSegment.py +214 -0
  420. PYME/Analysis/thresholding.py +192 -0
  421. PYME/Analysis/tile_pyramid.py +938 -0
  422. PYME/Analysis/widefieldify.py +54 -0
  423. PYME/DSView/DisplayOptionsPanel.py +503 -0
  424. PYME/DSView/LUT/__init__.py +33 -0
  425. PYME/DSView/LUT/lut.cp39-win_amd64.dll.a +0 -0
  426. PYME/DSView/LUT/lut.cp39-win_amd64.pyd +0 -0
  427. PYME/DSView/LUT/setup.py +54 -0
  428. PYME/DSView/OpenSequenceDialog.py +83 -0
  429. PYME/DSView/OverlaysPanel.py +111 -0
  430. PYME/DSView/__init__.py +32 -0
  431. PYME/DSView/arrayViewPanel.py +1035 -0
  432. PYME/DSView/displayOptions.py +453 -0
  433. PYME/DSView/displaySettingsPanel.py +271 -0
  434. PYME/DSView/dsviewer.py +542 -0
  435. PYME/DSView/eventLogViewer.py +805 -0
  436. PYME/DSView/fitInfo.py +486 -0
  437. PYME/DSView/htmlServe.py +72 -0
  438. PYME/DSView/logparser.py +100 -0
  439. PYME/DSView/modules/LMAnalysis.py +1053 -0
  440. PYME/DSView/modules/LMDisplay.py +194 -0
  441. PYME/DSView/modules/__init__.py +162 -0
  442. PYME/DSView/modules/_base.py +39 -0
  443. PYME/DSView/modules/annotation.py +725 -0
  444. PYME/DSView/modules/arrayView.py +71 -0
  445. PYME/DSView/modules/blobFinding.py +455 -0
  446. PYME/DSView/modules/blobMeasure.py +1093 -0
  447. PYME/DSView/modules/camera_maps.py +38 -0
  448. PYME/DSView/modules/coloc.py +414 -0
  449. PYME/DSView/modules/composite.py +715 -0
  450. PYME/DSView/modules/convnet.py +24 -0
  451. PYME/DSView/modules/cropping.py +133 -0
  452. PYME/DSView/modules/dark_and_flatfield.py +31 -0
  453. PYME/DSView/modules/deconvolution.py +499 -0
  454. PYME/DSView/modules/eventView.py +80 -0
  455. PYME/DSView/modules/extractBlobs.py +46 -0
  456. PYME/DSView/modules/fastGraphPanel.py +85 -0
  457. PYME/DSView/modules/filtering.py +335 -0
  458. PYME/DSView/modules/flowView.py +268 -0
  459. PYME/DSView/modules/fourierRingCorrelation.py +172 -0
  460. PYME/DSView/modules/gl_image_view.py +12 -0
  461. PYME/DSView/modules/graphViewPanel.py +272 -0
  462. PYME/DSView/modules/measure/__init__.py +1 -0
  463. PYME/DSView/modules/measure/filterPan.py +239 -0
  464. PYME/DSView/modules/metadataView.py +28 -0
  465. PYME/DSView/modules/multithreshold.py +98 -0
  466. PYME/DSView/modules/neuroglancer.py +129 -0
  467. PYME/DSView/modules/object_tagging.py +157 -0
  468. PYME/DSView/modules/particleTracking.py +463 -0
  469. PYME/DSView/modules/playback.py +256 -0
  470. PYME/DSView/modules/prebleachExtraction.py +75 -0
  471. PYME/DSView/modules/profileFitting.py +137 -0
  472. PYME/DSView/modules/profilePlotting.py +408 -0
  473. PYME/DSView/modules/projections.py +48 -0
  474. PYME/DSView/modules/psfExtraction.py +570 -0
  475. PYME/DSView/modules/psfGeneration.py +138 -0
  476. PYME/DSView/modules/psfTools.py +661 -0
  477. PYME/DSView/modules/pupilTools.py +204 -0
  478. PYME/DSView/modules/pupilView.py +139 -0
  479. PYME/DSView/modules/recipes.py +266 -0
  480. PYME/DSView/modules/redimension.py +75 -0
  481. PYME/DSView/modules/segmentation.py +192 -0
  482. PYME/DSView/modules/sheetView.py +24 -0
  483. PYME/DSView/modules/shell.py +39 -0
  484. PYME/DSView/modules/shiftfield_estimation.py +433 -0
  485. PYME/DSView/modules/sparkprepare.py +94 -0
  486. PYME/DSView/modules/splitter.py +148 -0
  487. PYME/DSView/modules/synchronise.py +64 -0
  488. PYME/DSView/modules/templates/astigCal.html +42 -0
  489. PYME/DSView/modules/templates/measureView.html +69 -0
  490. PYME/DSView/modules/templates/measureView2.html +48 -0
  491. PYME/DSView/modules/templates/measureView3.html +98 -0
  492. PYME/DSView/modules/templates/trackView.html +139 -0
  493. PYME/DSView/modules/templates/tracksOverview.html +21 -0
  494. PYME/DSView/modules/tiling.py +147 -0
  495. PYME/DSView/modules/unmixing.py +148 -0
  496. PYME/DSView/modules/vis3D.py +320 -0
  497. PYME/DSView/modules/visgui.py +163 -0
  498. PYME/DSView/modules/webview.py +14 -0
  499. PYME/DSView/overlays.py +295 -0
  500. PYME/DSView/scrolledImagePanel.py +188 -0
  501. PYME/DSView/setup.py +53 -0
  502. PYME/DSView/splashScreen.py +158 -0
  503. PYME/DSView/voxSizeDialog.py +84 -0
  504. PYME/Deconv/__init__.py +35 -0
  505. PYME/Deconv/beadGen.py +55 -0
  506. PYME/Deconv/dec.py +659 -0
  507. PYME/Deconv/decTask.py +65 -0
  508. PYME/Deconv/decThread.py +113 -0
  509. PYME/Deconv/dec_test.py +47 -0
  510. PYME/Deconv/deconvDialogs.py +417 -0
  511. PYME/Deconv/fftwWisdom.py +62 -0
  512. PYME/Deconv/richardsonLucy.py +141 -0
  513. PYME/Deconv/richardsonLucyMV.py +151 -0
  514. PYME/Deconv/richardsonLucyMVM.py +218 -0
  515. PYME/Deconv/test_block_dec.py +46 -0
  516. PYME/Deconv/tq_block_dec.py +331 -0
  517. PYME/Deconv/wiener.py +163 -0
  518. PYME/IO/DataSources/AlignDataSource.py +91 -0
  519. PYME/IO/DataSources/ArrayDataSource.py +260 -0
  520. PYME/IO/DataSources/BGSDataSource.py +353 -0
  521. PYME/IO/DataSources/BaseDataSource.py +457 -0
  522. PYME/IO/DataSources/BioformatsDataSource.py +178 -0
  523. PYME/IO/DataSources/BufferedDataSource.py +99 -0
  524. PYME/IO/DataSources/ClusterPZFDataSource.py +124 -0
  525. PYME/IO/DataSources/ConcatenatedDataSource.py +56 -0
  526. PYME/IO/DataSources/CropDataSource.py +243 -0
  527. PYME/IO/DataSources/DcimgDataSource.py +54 -0
  528. PYME/IO/DataSources/ElectronsPerSecondDataSource.py +61 -0
  529. PYME/IO/DataSources/FlatFieldDataSource.py +77 -0
  530. PYME/IO/DataSources/HDFDataSource.py +160 -0
  531. PYME/IO/DataSources/HTTPDataSource.py +80 -0
  532. PYME/IO/DataSources/ImageSeriesDataSource.py +123 -0
  533. PYME/IO/DataSources/MultiviewDataSource.py +99 -0
  534. PYME/IO/DataSources/OBFDataSource.py +67 -0
  535. PYME/IO/DataSources/RandomDataSource.py +23 -0
  536. PYME/IO/DataSources/SupertileDatasource.py +148 -0
  537. PYME/IO/DataSources/TQDataSource.py +41 -0
  538. PYME/IO/DataSources/TiffDataSource.py +193 -0
  539. PYME/IO/DataSources/TileDataSource.py +91 -0
  540. PYME/IO/DataSources/UnsplitDataSource.py +92 -0
  541. PYME/IO/DataSources/__init__.py +59 -0
  542. PYME/IO/FileUtils/IJLut.py +43 -0
  543. PYME/IO/FileUtils/RawToHdf5.py +105 -0
  544. PYME/IO/FileUtils/SPAD.py +54 -0
  545. PYME/IO/FileUtils/TiffSeqToHdf5.py +87 -0
  546. PYME/IO/FileUtils/__init__.py +24 -0
  547. PYME/IO/FileUtils/compHdf.py +39 -0
  548. PYME/IO/FileUtils/fileID.py +246 -0
  549. PYME/IO/FileUtils/fnlGen.py +40 -0
  550. PYME/IO/FileUtils/freeSpace.py +59 -0
  551. PYME/IO/FileUtils/h5-thumbnailer.py +85 -0
  552. PYME/IO/FileUtils/h5ExFrames.py +98 -0
  553. PYME/IO/FileUtils/h5ToTiffSeries.py +50 -0
  554. PYME/IO/FileUtils/h5r-thumbnailer.py +118 -0
  555. PYME/IO/FileUtils/h5rFormatDef.py +99 -0
  556. PYME/IO/FileUtils/h5rToTxt.py +77 -0
  557. PYME/IO/FileUtils/nameUtils.py +300 -0
  558. PYME/IO/FileUtils/obf_support.py +538 -0
  559. PYME/IO/FileUtils/ply.py +147 -0
  560. PYME/IO/FileUtils/readSpeckle.py +78 -0
  561. PYME/IO/FileUtils/readTiff.py +64 -0
  562. PYME/IO/FileUtils/saveTiffStack.py +137 -0
  563. PYME/IO/FileUtils/setup.py +36 -0
  564. PYME/IO/FileUtils/sf-thumbnailer.py +76 -0
  565. PYME/IO/FileUtils/stl.py +44 -0
  566. PYME/IO/FileUtils/thumbnailDatabase.py +104 -0
  567. PYME/IO/HTTPSpooler_v2.py +141 -0
  568. PYME/IO/MetaDataHandler.py +1072 -0
  569. PYME/IO/PZFFormat.py +375 -0
  570. PYME/IO/Spooler.py +279 -0
  571. PYME/IO/__init__.py +12 -0
  572. PYME/IO/acquisition_backends.py +353 -0
  573. PYME/IO/buffer_helpers.cp39-win_amd64.dll.a +0 -0
  574. PYME/IO/buffer_helpers.cp39-win_amd64.pyd +0 -0
  575. PYME/IO/buffer_helpers.pyx +190 -0
  576. PYME/IO/buffers.py +291 -0
  577. PYME/IO/clusterDuplication.py +204 -0
  578. PYME/IO/clusterExport.py +152 -0
  579. PYME/IO/clusterGlob.py +108 -0
  580. PYME/IO/clusterIO.py +1076 -0
  581. PYME/IO/clusterListing.py +232 -0
  582. PYME/IO/clusterResults.py +206 -0
  583. PYME/IO/clusterUpload.py +49 -0
  584. PYME/IO/cluster_directory.py +419 -0
  585. PYME/IO/cluster_streaming.py +216 -0
  586. PYME/IO/compatibility.py +12 -0
  587. PYME/IO/countdir/__init__.py +5 -0
  588. PYME/IO/countdir/setup.py +58 -0
  589. PYME/IO/csv_flavours.py +251 -0
  590. PYME/IO/dataExporter.py +470 -0
  591. PYME/IO/dataWrap.py +108 -0
  592. PYME/IO/dcimg.py +167 -0
  593. PYME/IO/events.py +161 -0
  594. PYME/IO/h5File.py +148 -0
  595. PYME/IO/h5rFile.py +323 -0
  596. PYME/IO/image.py +1447 -0
  597. PYME/IO/load_psf.py +26 -0
  598. PYME/IO/pyme_h5ls.py +58 -0
  599. PYME/IO/ragged.py +138 -0
  600. PYME/IO/rgb_image.py +94 -0
  601. PYME/IO/tabular.py +1338 -0
  602. PYME/IO/testClusterSpooling.py +199 -0
  603. PYME/IO/unifiedIO.py +301 -0
  604. PYME/LMVis/Extras/QPobjectSegment.py +220 -0
  605. PYME/LMVis/Extras/__init__.py +55 -0
  606. PYME/LMVis/Extras/animation.py +359 -0
  607. PYME/LMVis/Extras/annotation.py +78 -0
  608. PYME/LMVis/Extras/clusterAnalysis.py +460 -0
  609. PYME/LMVis/Extras/dockedPanel.py +69 -0
  610. PYME/LMVis/Extras/extra_layers.py +373 -0
  611. PYME/LMVis/Extras/fiducial.py +230 -0
  612. PYME/LMVis/Extras/flagZMotionArtifacts.py +60 -0
  613. PYME/LMVis/Extras/fusionUtils.py +9 -0
  614. PYME/LMVis/Extras/multiColourAnalysis.py +103 -0
  615. PYME/LMVis/Extras/multiviewMapping.py +489 -0
  616. PYME/LMVis/Extras/objectMeasurements.py +246 -0
  617. PYME/LMVis/Extras/particleTracking.py +272 -0
  618. PYME/LMVis/Extras/perFrameVariable.py +77 -0
  619. PYME/LMVis/Extras/photophysics.py +72 -0
  620. PYME/LMVis/Extras/pointSetGeneration.py +232 -0
  621. PYME/LMVis/Extras/pointwiseColoc.py +54 -0
  622. PYME/LMVis/Extras/saved_views.py +125 -0
  623. PYME/LMVis/Extras/scaleBox.py +115 -0
  624. PYME/LMVis/Extras/sequentialSpecies.py +125 -0
  625. PYME/LMVis/Extras/setBGImage.py +74 -0
  626. PYME/LMVis/Extras/shiftmapGenerator.py +138 -0
  627. PYME/LMVis/Extras/showShortcuts.py +69 -0
  628. PYME/LMVis/Extras/snapshot.py +135 -0
  629. PYME/LMVis/Extras/spherical_harmonics.py +98 -0
  630. PYME/LMVis/Extras/subsets.py +15 -0
  631. PYME/LMVis/Extras/surface_fitting.py +52 -0
  632. PYME/LMVis/Extras/temperaturePlot.py +53 -0
  633. PYME/LMVis/Extras/vibration.py +112 -0
  634. PYME/LMVis/VisGUI.py +506 -0
  635. PYME/LMVis/__init__.py +39 -0
  636. PYME/LMVis/colourFilterGUI.py +77 -0
  637. PYME/LMVis/colourPanel.py +491 -0
  638. PYME/LMVis/displayPane.py +462 -0
  639. PYME/LMVis/dyeRatios.py +116 -0
  640. PYME/LMVis/genImageDialog.py +298 -0
  641. PYME/LMVis/gl_offScreenHandler.py +134 -0
  642. PYME/LMVis/gl_render.py +1302 -0
  643. PYME/LMVis/gl_render3D.py +1453 -0
  644. PYME/LMVis/gl_render3D_shaders.py +1213 -0
  645. PYME/LMVis/glcanvas_core.py +1157 -0
  646. PYME/LMVis/imageView.py +752 -0
  647. PYME/LMVis/imageView2.py +372 -0
  648. PYME/LMVis/importTextDialog.py +342 -0
  649. PYME/LMVis/layer_panel.py +231 -0
  650. PYME/LMVis/layers/AxesOverlayLayer.py +126 -0
  651. PYME/LMVis/layers/LUTOverlayLayer.py +346 -0
  652. PYME/LMVis/layers/OverlayLayer.py +46 -0
  653. PYME/LMVis/layers/ScaleBarOverlayLayer.py +97 -0
  654. PYME/LMVis/layers/ScaleBoxOverlayLayer.py +181 -0
  655. PYME/LMVis/layers/SelectionOverlayLayer.py +124 -0
  656. PYME/LMVis/layers/__init__.py +53 -0
  657. PYME/LMVis/layers/base.py +319 -0
  658. PYME/LMVis/layers/image_layer.py +363 -0
  659. PYME/LMVis/layers/labels.py +186 -0
  660. PYME/LMVis/layers/layer_defaults.py +97 -0
  661. PYME/LMVis/layers/mesh.py +378 -0
  662. PYME/LMVis/layers/octree.py +194 -0
  663. PYME/LMVis/layers/pointcloud.py +372 -0
  664. PYME/LMVis/layers/quiver.py +271 -0
  665. PYME/LMVis/layers/text.py +250 -0
  666. PYME/LMVis/layers/tracks.py +309 -0
  667. PYME/LMVis/mv_math.py +78 -0
  668. PYME/LMVis/objectDarkMeasure.py +120 -0
  669. PYME/LMVis/pipeline.py +1227 -0
  670. PYME/LMVis/pipelineView.py +115 -0
  671. PYME/LMVis/pointSettingsPanel.py +135 -0
  672. PYME/LMVis/progGraph.py +59 -0
  673. PYME/LMVis/quadTreeSettings.py +90 -0
  674. PYME/LMVis/rendGauss.py +158 -0
  675. PYME/LMVis/renderers.py +576 -0
  676. PYME/LMVis/sessionpaths.py +136 -0
  677. PYME/LMVis/setup.py +39 -0
  678. PYME/LMVis/setup_exe.py +71 -0
  679. PYME/LMVis/shader_programs/DefaultShaderProgram.py +149 -0
  680. PYME/LMVis/shader_programs/GLProgram.py +119 -0
  681. PYME/LMVis/shader_programs/GouraudShaderProgram.py +157 -0
  682. PYME/LMVis/shader_programs/PointSpriteShaderProgram.py +163 -0
  683. PYME/LMVis/shader_programs/ShaderProgramFactory.py +54 -0
  684. PYME/LMVis/shader_programs/TesselShaderProgram.py +47 -0
  685. PYME/LMVis/shader_programs/WireFrameShaderProgram.py +50 -0
  686. PYME/LMVis/shader_programs/__init__.py +20 -0
  687. PYME/LMVis/shader_programs/oit_compositor.py +23 -0
  688. PYME/LMVis/shader_programs/shader_loader.py +84 -0
  689. PYME/LMVis/shader_programs/shader_program.py +168 -0
  690. PYME/LMVis/shader_programs/shaders/bigflatpoints_fs.glsl +16 -0
  691. PYME/LMVis/shader_programs/shaders/bigpoints_gs.glsl +40 -0
  692. PYME/LMVis/shader_programs/shaders/bigpointsprites_fs.glsl +20 -0
  693. PYME/LMVis/shader_programs/shaders/bigspheres_fs.glsl +53 -0
  694. PYME/LMVis/shader_programs/shaders/compose_fs.glsl +41 -0
  695. PYME/LMVis/shader_programs/shaders/compose_vs.glsl +25 -0
  696. PYME/LMVis/shader_programs/shaders/default_fs.glsl +27 -0
  697. PYME/LMVis/shader_programs/shaders/default_vs.glsl +88 -0
  698. PYME/LMVis/shader_programs/shaders/flatpoints_fs.glsl +27 -0
  699. PYME/LMVis/shader_programs/shaders/gouraud_flatpoints_fs.glsl +30 -0
  700. PYME/LMVis/shader_programs/shaders/gouraud_fs.glsl +26 -0
  701. PYME/LMVis/shader_programs/shaders/gouraud_oit_fs.glsl +68 -0
  702. PYME/LMVis/shader_programs/shaders/gouraud_vs.glsl +135 -0
  703. PYME/LMVis/shader_programs/shaders/image_fs.glsl +60 -0
  704. PYME/LMVis/shader_programs/shaders/image_vs.glsl +79 -0
  705. PYME/LMVis/shader_programs/shaders/phong_fs.glsl +43 -0
  706. PYME/LMVis/shader_programs/shaders/phong_vs.glsl +45 -0
  707. PYME/LMVis/shader_programs/shaders/pointsprites_fs.glsl +33 -0
  708. PYME/LMVis/shader_programs/shaders/pointsprites_vs.glsl +77 -0
  709. PYME/LMVis/shader_programs/shaders/spheres_fs.glsl +102 -0
  710. PYME/LMVis/shader_programs/shaders/tessel_fs.glsl +23 -0
  711. PYME/LMVis/shader_programs/shaders/tessel_vs.glsl +37 -0
  712. PYME/LMVis/shader_programs/shaders/text_fs.glsl +42 -0
  713. PYME/LMVis/shader_programs/shaders/text_vs.glsl +84 -0
  714. PYME/LMVis/shader_programs/shaders/widelines_fs.glsl +19 -0
  715. PYME/LMVis/shader_programs/shaders/widelines_gs.glsl +56 -0
  716. PYME/LMVis/shader_programs/shaders/widelines_vs.glsl +51 -0
  717. PYME/LMVis/statusLog.py +57 -0
  718. PYME/LMVis/triBlobs.py +142 -0
  719. PYME/LMVis/view_clipping_pane.py +429 -0
  720. PYME/LMVis/views.py +275 -0
  721. PYME/LMVis/visCore.py +1005 -0
  722. PYME/LMVis/visHelpers.py +880 -0
  723. PYME/LMVis/visHelpersMin.py +230 -0
  724. PYME/LMVis/workspaceTree.py +238 -0
  725. PYME/__init__.py +22 -0
  726. PYME/cluster/HTTPDataServer.py +1063 -0
  727. PYME/cluster/HTTPRulePusher.py +382 -0
  728. PYME/cluster/HTTPTaskPusher.py +377 -0
  729. PYME/cluster/PYMERuleNodeServer.py +192 -0
  730. PYME/cluster/PYMERuleServer.py +109 -0
  731. PYME/cluster/__init__.py +0 -0
  732. PYME/cluster/clusterUI/accounts/__init__.py +0 -0
  733. PYME/cluster/clusterUI/accounts/admin.py +85 -0
  734. PYME/cluster/clusterUI/accounts/forms.py +8 -0
  735. PYME/cluster/clusterUI/accounts/migrations/0001_initial.py +31 -0
  736. PYME/cluster/clusterUI/accounts/migrations/__init__.py +0 -0
  737. PYME/cluster/clusterUI/accounts/models.py +44 -0
  738. PYME/cluster/clusterUI/accounts/templates/accounts/user_form.html +13 -0
  739. PYME/cluster/clusterUI/accounts/templates/registration/initial_complete.html +6 -0
  740. PYME/cluster/clusterUI/accounts/templates/registration/initial_confirm.html +13 -0
  741. PYME/cluster/clusterUI/accounts/templates/registration/initial_done.html +7 -0
  742. PYME/cluster/clusterUI/accounts/templates/registration/verification.html +2 -0
  743. PYME/cluster/clusterUI/accounts/templates/registration/verification_subject.txt +1 -0
  744. PYME/cluster/clusterUI/accounts/urls.py +19 -0
  745. PYME/cluster/clusterUI/accounts/views.py +36 -0
  746. PYME/cluster/clusterUI/clusterUI/__init__.py +0 -0
  747. PYME/cluster/clusterUI/clusterUI/settings.py +147 -0
  748. PYME/cluster/clusterUI/clusterUI/urls.py +31 -0
  749. PYME/cluster/clusterUI/clusterUI/wsgi.py +16 -0
  750. PYME/cluster/clusterUI/clusterbrowser/__init__.py +0 -0
  751. PYME/cluster/clusterUI/clusterbrowser/admin.py +3 -0
  752. PYME/cluster/clusterUI/clusterbrowser/apps.py +7 -0
  753. PYME/cluster/clusterUI/clusterbrowser/migrations/__init__.py +0 -0
  754. PYME/cluster/clusterUI/clusterbrowser/models.py +5 -0
  755. PYME/cluster/clusterUI/clusterbrowser/templates/base.html +129 -0
  756. PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/dirlisting.html +231 -0
  757. PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/form_new_folder.html +18 -0
  758. PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/form_upload_file.html +17 -0
  759. PYME/cluster/clusterUI/clusterbrowser/templates/clusterbrowser/lightlisting.html +25 -0
  760. PYME/cluster/clusterUI/clusterbrowser/templates/registration/login.html +38 -0
  761. PYME/cluster/clusterUI/clusterbrowser/urls.py +10 -0
  762. PYME/cluster/clusterUI/clusterbrowser/views.py +148 -0
  763. PYME/cluster/clusterUI/clusterstatus/__init__.py +0 -0
  764. PYME/cluster/clusterUI/clusterstatus/admin.py +3 -0
  765. PYME/cluster/clusterUI/clusterstatus/apps.py +7 -0
  766. PYME/cluster/clusterUI/clusterstatus/migrations/__init__.py +0 -0
  767. PYME/cluster/clusterUI/clusterstatus/models.py +5 -0
  768. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/load_content.html +29 -0
  769. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/queue_info.html +28 -0
  770. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/queue_info_content.html +50 -0
  771. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/status_dash.html +29 -0
  772. PYME/cluster/clusterUI/clusterstatus/templates/clusterstatus/status_dash_content.html +90 -0
  773. PYME/cluster/clusterUI/clusterstatus/urls.py +9 -0
  774. PYME/cluster/clusterUI/clusterstatus/views.py +69 -0
  775. PYME/cluster/clusterUI/clusterui_tornado.py +117 -0
  776. PYME/cluster/clusterUI/deploy.py +62 -0
  777. PYME/cluster/clusterUI/localization/__init__.py +0 -0
  778. PYME/cluster/clusterUI/localization/admin.py +3 -0
  779. PYME/cluster/clusterUI/localization/apps.py +7 -0
  780. PYME/cluster/clusterUI/localization/migrations/__init__.py +0 -0
  781. PYME/cluster/clusterUI/localization/models.py +5 -0
  782. PYME/cluster/clusterUI/localization/templates/localization/settings_form.html +67 -0
  783. PYME/cluster/clusterUI/localization/urls.py +10 -0
  784. PYME/cluster/clusterUI/localization/views.py +170 -0
  785. PYME/cluster/clusterUI/manage.py +17 -0
  786. PYME/cluster/clusterUI/recipes/__init__.py +0 -0
  787. PYME/cluster/clusterUI/recipes/admin.py +3 -0
  788. PYME/cluster/clusterUI/recipes/migrations/__init__.py +0 -0
  789. PYME/cluster/clusterUI/recipes/models.py +3 -0
  790. PYME/cluster/clusterUI/recipes/templates/recipes/extra_inputs.html +35 -0
  791. PYME/cluster/clusterUI/recipes/templates/recipes/form_recipe.html +82 -0
  792. PYME/cluster/clusterUI/recipes/templates/recipes/input_list.html +7 -0
  793. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_editor.html +28 -0
  794. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_editor_common.html +46 -0
  795. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_editrun.html +93 -0
  796. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_standalone.html +100 -0
  797. PYME/cluster/clusterUI/recipes/templates/recipes/recipe_template.html +109 -0
  798. PYME/cluster/clusterUI/recipes/urls.py +17 -0
  799. PYME/cluster/clusterUI/recipes/views.py +109 -0
  800. PYME/cluster/cluster_of_one.py +200 -0
  801. PYME/cluster/dataserver_wsgi.py +615 -0
  802. PYME/cluster/distribution.py +71 -0
  803. PYME/cluster/rulenodeserver.py +487 -0
  804. PYME/cluster/rules.py +979 -0
  805. PYME/cluster/ruleserver.py +1106 -0
  806. PYME/cluster/status.py +101 -0
  807. PYME/cluster/taskWorkerHTTP.py +522 -0
  808. PYME/cluster/webdav.py +1102 -0
  809. PYME/config.py +659 -0
  810. PYME/contrib/TextCtrlAutoComplete.py +582 -0
  811. PYME/contrib/__init__.py +7 -0
  812. PYME/contrib/aniso/__init__.py +2 -0
  813. PYME/contrib/aniso/aniso.py +268 -0
  814. PYME/contrib/auiFloatBook.py +94 -0
  815. PYME/contrib/congrid.py +127 -0
  816. PYME/contrib/dispatch/__init__.py +19 -0
  817. PYME/contrib/dispatch/dispatcher.py +289 -0
  818. PYME/contrib/gohlke/__init__.py +1 -0
  819. PYME/contrib/gohlke/_tifffile.cp39-win_amd64.dll.a +0 -0
  820. PYME/contrib/gohlke/_tifffile.cp39-win_amd64.pyd +0 -0
  821. PYME/contrib/gohlke/czifile.py +1289 -0
  822. PYME/contrib/gohlke/fcsfiles.py +514 -0
  823. PYME/contrib/gohlke/setup.py +54 -0
  824. PYME/contrib/gohlke/tifffile.py +3095 -0
  825. PYME/contrib/listctrlMixins.py +896 -0
  826. PYME/contrib/lzw.py +838 -0
  827. PYME/contrib/pad/__init__.py +13 -0
  828. PYME/contrib/pad/info_pad.py +18 -0
  829. PYME/contrib/pad/pad.py +670 -0
  830. PYME/contrib/setup.py +38 -0
  831. PYME/contrib/wxPlotPanel.py +137 -0
  832. PYME/experimental/SimReconstruction.py +49 -0
  833. PYME/experimental/__init__.pxd +0 -0
  834. PYME/experimental/__init__.py +4 -0
  835. PYME/experimental/_octree.cp39-win_amd64.dll.a +0 -0
  836. PYME/experimental/_octree.cp39-win_amd64.pyd +0 -0
  837. PYME/experimental/_octree.pyx +477 -0
  838. PYME/experimental/_treap.cp39-win_amd64.dll.a +0 -0
  839. PYME/experimental/_treap.cp39-win_amd64.pyd +0 -0
  840. PYME/experimental/_treap.pyx +267 -0
  841. PYME/experimental/_triangle_mesh.cp39-win_amd64.dll.a +0 -0
  842. PYME/experimental/_triangle_mesh.cp39-win_amd64.pyd +0 -0
  843. PYME/experimental/_triangle_mesh.pxd +149 -0
  844. PYME/experimental/_triangle_mesh.pyx +2787 -0
  845. PYME/experimental/aqavg.py +40 -0
  846. PYME/experimental/dcimgFileChucker.py +375 -0
  847. PYME/experimental/dcimgSpoolShim.py +174 -0
  848. PYME/experimental/dcimg_chuck_test.py +78 -0
  849. PYME/experimental/dual_marching_cubes.py +801 -0
  850. PYME/experimental/dual_marching_cubes_v2.py +645 -0
  851. PYME/experimental/expectationMaximisation.py +298 -0
  852. PYME/experimental/func_octree.cp39-win_amd64.dll.a +0 -0
  853. PYME/experimental/func_octree.cp39-win_amd64.pyd +0 -0
  854. PYME/experimental/func_octree.pyx +381 -0
  855. PYME/experimental/fusionRadial.py +520 -0
  856. PYME/experimental/fusionUtils.py +522 -0
  857. PYME/experimental/isosurface.py +147 -0
  858. PYME/experimental/labview_spooling_hacks.py +48 -0
  859. PYME/experimental/marching_cubes.py +811 -0
  860. PYME/experimental/marching_tetrahedra.py +343 -0
  861. PYME/experimental/modified_marching_cubes.py +1245 -0
  862. PYME/experimental/music.py +25 -0
  863. PYME/experimental/octree.py +188 -0
  864. PYME/experimental/pSpaceMap.py +34 -0
  865. PYME/experimental/pointFitConfoc.py +112 -0
  866. PYME/experimental/scanner_control.py +107 -0
  867. PYME/experimental/setup.py +109 -0
  868. PYME/experimental/spark.py +31 -0
  869. PYME/experimental/tcHist.py +71 -0
  870. PYME/experimental/treap.py +262 -0
  871. PYME/experimental/triangle_mesh.py +2287 -0
  872. PYME/experimental/triangle_mesh_utils.cp39-win_amd64.dll.a +0 -0
  873. PYME/experimental/triangle_mesh_utils.cp39-win_amd64.pyd +0 -0
  874. PYME/experimental/triangle_utils.py +87 -0
  875. PYME/experimental/triangular_mesh.py +2255 -0
  876. PYME/experimental/zMotionArtifactUtils.py +55 -0
  877. PYME/localization/FitFactories/AstigGaussFitFR.py +175 -0
  878. PYME/localization/FitFactories/AstigGaussGPUFitFR.py +326 -0
  879. PYME/localization/FitFactories/BeadConvolvedAstigGaussFit.py +206 -0
  880. PYME/localization/FitFactories/ChecksumFR.py +82 -0
  881. PYME/localization/FitFactories/ChecksumGPUFR.py +213 -0
  882. PYME/localization/FitFactories/ChecksumMultifitSR.py +86 -0
  883. PYME/localization/FitFactories/ConfocCOIR.py +93 -0
  884. PYME/localization/FitFactories/Dumbell3DFitR.py +193 -0
  885. PYME/localization/FitFactories/DumbellFitR.py +163 -0
  886. PYME/localization/FitFactories/FFBase.py +403 -0
  887. PYME/localization/FitFactories/Gauss3DFitR.py +213 -0
  888. PYME/localization/FitFactories/GaussFitConstrR.py +177 -0
  889. PYME/localization/FitFactories/GaussMultifitLinesR.py +351 -0
  890. PYME/localization/FitFactories/GaussMultifitR.py +326 -0
  891. PYME/localization/FitFactories/GaussMultifitSR.py +373 -0
  892. PYME/localization/FitFactories/InterpFitR.py +236 -0
  893. PYME/localization/FitFactories/InterpFitWFR.py +111 -0
  894. PYME/localization/FitFactories/Interpolators/CSInterpolator.py +148 -0
  895. PYME/localization/FitFactories/Interpolators/CubeSInterpolatorP.py +121 -0
  896. PYME/localization/FitFactories/Interpolators/CubicSplineInterpolator.py +69 -0
  897. PYME/localization/FitFactories/Interpolators/LinearInterpolator.py +135 -0
  898. PYME/localization/FitFactories/Interpolators/LinearPInterpolatorP.py +95 -0
  899. PYME/localization/FitFactories/Interpolators/__init__.py +33 -0
  900. PYME/localization/FitFactories/Interpolators/baseInterpolator.py +202 -0
  901. PYME/localization/FitFactories/LatFitCOIR.py +82 -0
  902. PYME/localization/FitFactories/LatGaussFitFR.py +220 -0
  903. PYME/localization/FitFactories/LatObjFindFR.py +85 -0
  904. PYME/localization/FitFactories/LatPSFFitR.py +142 -0
  905. PYME/localization/FitFactories/MultiviewFitInterpNR.py +400 -0
  906. PYME/localization/FitFactories/PRInterpFitQR.py +192 -0
  907. PYME/localization/FitFactories/PRInterpFitR.py +223 -0
  908. PYME/localization/FitFactories/ROIExtractNR.py +93 -0
  909. PYME/localization/FitFactories/SpecGaussFitFR.py +174 -0
  910. PYME/localization/FitFactories/SplitterFitCOIR.py +142 -0
  911. PYME/localization/FitFactories/SplitterFitFNR.py +312 -0
  912. PYME/localization/FitFactories/SplitterFitFusionR.py +318 -0
  913. PYME/localization/FitFactories/SplitterFitInterpBNR.py +357 -0
  914. PYME/localization/FitFactories/SplitterFitInterpNR.py +343 -0
  915. PYME/localization/FitFactories/SplitterFitScavNR.py +352 -0
  916. PYME/localization/FitFactories/SplitterMultifitSR.py +410 -0
  917. PYME/localization/FitFactories/SplitterObjFindR.py +194 -0
  918. PYME/localization/FitFactories/SplitterROIExtractNR.py +91 -0
  919. PYME/localization/FitFactories/SplitterShiftEstFR.py +230 -0
  920. PYME/localization/FitFactories/__init__.py +103 -0
  921. PYME/localization/FitFactories/fitCommon.py +195 -0
  922. PYME/localization/FitFactories/zEstimators/__init__.py +29 -0
  923. PYME/localization/FitFactories/zEstimators/astigEstimator.py +173 -0
  924. PYME/localization/FitFactories/zEstimators/biplaneEstimator.py +209 -0
  925. PYME/localization/FitFactories/zEstimators/genericDualEstimator.py +232 -0
  926. PYME/localization/FitFactories/zEstimators/genericEstimator.py +224 -0
  927. PYME/localization/FitFactories/zEstimators/priEstimator.py +255 -0
  928. PYME/localization/MetaDataEdit.py +543 -0
  929. PYME/localization/Test/30_9_series_A_1.sf +0 -0
  930. PYME/localization/Test/PRI.md +45 -0
  931. PYME/localization/Test/PRI_qr.md +52 -0
  932. PYME/localization/Test/PRI_r.md +52 -0
  933. PYME/localization/Test/__init__.py +22 -0
  934. PYME/localization/Test/astig.md +50 -0
  935. PYME/localization/Test/astigCOI.md +50 -0
  936. PYME/localization/Test/astig_gauss.md +52 -0
  937. PYME/localization/Test/astig_pe.md +50 -0
  938. PYME/localization/Test/astig_splitter.md +66 -0
  939. PYME/localization/Test/astig_splitterCOI.md +67 -0
  940. PYME/localization/Test/astig_splitterQ.md +66 -0
  941. PYME/localization/Test/astig_theory.md +50 -0
  942. PYME/localization/Test/astig_theory.psf +0 -0
  943. PYME/localization/Test/astig_theory.tif +0 -0
  944. PYME/localization/Test/beads1.psf +0 -0
  945. PYME/localization/Test/beads1.tif +0 -0
  946. PYME/localization/Test/biplane_splitter.md +75 -0
  947. PYME/localization/Test/biplane_splitter2.md +103 -0
  948. PYME/localization/Test/biplane_splitter3.md +115 -0
  949. PYME/localization/Test/defoc.md +48 -0
  950. PYME/localization/Test/dumbell_3d.md +52 -0
  951. PYME/localization/Test/fitTestJig.py +172 -0
  952. PYME/localization/Test/fitTestJigSCMOS.py +487 -0
  953. PYME/localization/Test/fitTestJigWC.py +225 -0
  954. PYME/localization/Test/gauss.md +50 -0
  955. PYME/localization/Test/gaussCOI.md +50 -0
  956. PYME/localization/Test/pri_pe.md +50 -0
  957. PYME/localization/Test/pri_theory.md +51 -0
  958. PYME/localization/Test/pri_theory.psf +0 -0
  959. PYME/localization/Test/pri_theory.tif +0 -0
  960. PYME/localization/Test/psf_astig.psf +0 -0
  961. PYME/localization/Test/psf_astig.tif +0 -0
  962. PYME/localization/Test/psf_astig_bg_d.psf +0 -0
  963. PYME/localization/Test/psf_astig_bg_d.tif +0 -0
  964. PYME/localization/Test/psf_astig_pe.psf +0 -0
  965. PYME/localization/Test/psf_astig_pe.tif +0 -0
  966. PYME/localization/Test/psf_decon.psf +0 -0
  967. PYME/localization/Test/psf_decon.tif +0 -0
  968. PYME/localization/Test/psf_decon_pe.psf +0 -0
  969. PYME/localization/Test/psf_decon_pe.tif +0 -0
  970. PYME/localization/Test/psf_guess.psf +0 -0
  971. PYME/localization/Test/splitter.md +63 -0
  972. PYME/localization/Test/splitter_scav.md +63 -0
  973. PYME/localization/Test/test.psf +0 -0
  974. PYME/localization/Test/test.tif +0 -0
  975. PYME/localization/Test/test_decon.psf +0 -0
  976. PYME/localization/Test/test_decon.tif +0 -0
  977. PYME/localization/Test/test_pe.psf +0 -0
  978. PYME/localization/Test/test_pe.tif +0 -0
  979. PYME/localization/Test/wf_theory.psf +0 -0
  980. PYME/localization/Test/wf_theory2.psf +0 -0
  981. PYME/localization/__init__.py +17 -0
  982. PYME/localization/autopsf/__init__.py +1 -0
  983. PYME/localization/autopsf/autopsf.py +486 -0
  984. PYME/localization/autopsf/roifit.py +310 -0
  985. PYME/localization/cInterp/__init__.py +24 -0
  986. PYME/localization/cInterp/cInterp.cp39-win_amd64.dll.a +0 -0
  987. PYME/localization/cInterp/cInterp.cp39-win_amd64.pyd +0 -0
  988. PYME/localization/cInterp/setup.py +54 -0
  989. PYME/localization/cModels/__init__.py +24 -0
  990. PYME/localization/cModels/gauss_app.cp39-win_amd64.dll.a +0 -0
  991. PYME/localization/cModels/gauss_app.cp39-win_amd64.pyd +0 -0
  992. PYME/localization/ofind.py +444 -0
  993. PYME/localization/ofind3d.py +306 -0
  994. PYME/localization/ofind_nofilt.py +128 -0
  995. PYME/localization/ofind_pri.py +478 -0
  996. PYME/localization/ofind_split.py +399 -0
  997. PYME/localization/ofind_xcorr.py +351 -0
  998. PYME/localization/ofind_xcorr_fw.py +441 -0
  999. PYME/localization/remFitBuf.py +657 -0
  1000. PYME/localization/setup.py +56 -0
  1001. PYME/localization/splitting.py +240 -0
  1002. PYME/localization/twist.py +60 -0
  1003. PYME/misc/TiSettings.py +36 -0
  1004. PYME/misc/TiffImagePlugin.py +838 -0
  1005. PYME/misc/VolProj.py +144 -0
  1006. PYME/misc/__init__.py +25 -0
  1007. PYME/misc/aligned_array.py +10 -0
  1008. PYME/misc/aniso.py +29 -0
  1009. PYME/misc/big_sur_fix.py +20 -0
  1010. PYME/misc/check_for_updates.py +139 -0
  1011. PYME/misc/colormaps.py +199 -0
  1012. PYME/misc/colorsys.py +201 -0
  1013. PYME/misc/computerName.py +33 -0
  1014. PYME/misc/coverslipGeometries.py +60 -0
  1015. PYME/misc/cython_numpy_monkey.py +40 -0
  1016. PYME/misc/depGraph.py +406 -0
  1017. PYME/misc/dirSize.py +31 -0
  1018. PYME/misc/diskCleanup.py +64 -0
  1019. PYME/misc/djangoRecarray.py +57 -0
  1020. PYME/misc/django_widgets.py +27 -0
  1021. PYME/misc/editgrid.py +101 -0
  1022. PYME/misc/eventSpy.py +122 -0
  1023. PYME/misc/exceptions.py +30 -0
  1024. PYME/misc/facsPlot.py +89 -0
  1025. PYME/misc/fbpIcons.py +90 -0
  1026. PYME/misc/fftw_compat.py +28 -0
  1027. PYME/misc/findCycles.py +91 -0
  1028. PYME/misc/fortran_interrupt_defeat.py +43 -0
  1029. PYME/misc/glassModel.py +64 -0
  1030. PYME/misc/hash32.py +41 -0
  1031. PYME/misc/hillcurve.py +88 -0
  1032. PYME/misc/hybrid_ns.py +84 -0
  1033. PYME/misc/kdFit.py +72 -0
  1034. PYME/misc/locate.py +10 -0
  1035. PYME/misc/memUsage.py +65 -0
  1036. PYME/misc/memmap.py +52 -0
  1037. PYME/misc/mock_ctypes.py +28 -0
  1038. PYME/misc/mock_traits.py +59 -0
  1039. PYME/misc/mock_traitsui.py +29 -0
  1040. PYME/misc/pyme_zeroconf.py +287 -0
  1041. PYME/misc/pyro_tracebacks.py +32 -0
  1042. PYME/misc/read_agf.py +605 -0
  1043. PYME/misc/sffake.py +16 -0
  1044. PYME/misc/snells.py +71 -0
  1045. PYME/misc/sqlite_ns.py +132 -0
  1046. PYME/misc/sqlitendarray.py +56 -0
  1047. PYME/misc/taskDef.py +44 -0
  1048. PYME/misc/tempDB.py +56 -0
  1049. PYME/misc/wxThread.py +33 -0
  1050. PYME/misc/zemax.py +259 -0
  1051. PYME/misc/zernike.py +151 -0
  1052. PYME/pyme_warnings.py +70 -0
  1053. PYME/recipes/Recipes/STEDDeconvolution.yaml +14 -0
  1054. PYME/recipes/Recipes/STEDMinistacks.yaml +15 -0
  1055. PYME/recipes/Recipes/STED_COP3.yaml +25 -0
  1056. PYME/recipes/Recipes/blobMeas.yaml +19 -0
  1057. PYME/recipes/Recipes/blobMeas2.yaml +37 -0
  1058. PYME/recipes/Recipes/blobMeasSR.yaml +66 -0
  1059. PYME/recipes/Recipes/coloc.yaml +43 -0
  1060. PYME/recipes/Recipes/coloc_roi_masked.yaml +46 -0
  1061. PYME/recipes/Recipes/multiblob.yaml +38 -0
  1062. PYME/recipes/Recipes/pre_particleTracking.yaml +30 -0
  1063. PYME/recipes/Recipes/segmentAndMeasureLoci.yaml +41 -0
  1064. PYME/recipes/__init__.py +6 -0
  1065. PYME/recipes/acquisition.py +158 -0
  1066. PYME/recipes/bakeshop.py +32 -0
  1067. PYME/recipes/base.py +1393 -0
  1068. PYME/recipes/batchProcess.py +147 -0
  1069. PYME/recipes/filters.py +394 -0
  1070. PYME/recipes/fudges.py +41 -0
  1071. PYME/recipes/graphing.py +108 -0
  1072. PYME/recipes/html_recipe_editor.py +90 -0
  1073. PYME/recipes/localisations.py +1386 -0
  1074. PYME/recipes/machine_learning.py +286 -0
  1075. PYME/recipes/measurement.py +1062 -0
  1076. PYME/recipes/modules.py +50 -0
  1077. PYME/recipes/multiview.py +435 -0
  1078. PYME/recipes/output.py +667 -0
  1079. PYME/recipes/pointcloud.py +510 -0
  1080. PYME/recipes/processing.py +2954 -0
  1081. PYME/recipes/pyramid.py +206 -0
  1082. PYME/recipes/recipe.py +965 -0
  1083. PYME/recipes/recipeGui.py +1029 -0
  1084. PYME/recipes/recipeLayout.py +499 -0
  1085. PYME/recipes/runRecipe.py +140 -0
  1086. PYME/recipes/setup.py +37 -0
  1087. PYME/recipes/simulation.py +91 -0
  1088. PYME/recipes/skfilters.py +143 -0
  1089. PYME/recipes/surface_fitting.py +776 -0
  1090. PYME/recipes/tablefilters.py +275 -0
  1091. PYME/recipes/tracking.py +343 -0
  1092. PYME/recipes/traits.py +157 -0
  1093. PYME/recipes/vertical_recipe_display.py +373 -0
  1094. PYME/resources/__init__.py +23 -0
  1095. PYME/resources/config_template/distributor.yaml +14 -0
  1096. PYME/resources/config_template/nodeserver.yaml +15 -0
  1097. PYME/resources/icons/LUT.png +0 -0
  1098. PYME/resources/icons/YSM_Shield.png +0 -0
  1099. PYME/resources/icons/YSM_logo.png +0 -0
  1100. PYME/resources/icons/arrow.png +0 -0
  1101. PYME/resources/icons/crosshairs.png +0 -0
  1102. PYME/resources/icons/line_select.png +0 -0
  1103. PYME/resources/icons/logo_fmhs.png +0 -0
  1104. PYME/resources/icons/media-playback-pause.png +0 -0
  1105. PYME/resources/icons/media-playback-start.png +0 -0
  1106. PYME/resources/icons/media-skip-backward.png +0 -0
  1107. PYME/resources/icons/media-skip-forward.png +0 -0
  1108. PYME/resources/icons/mesh.png +0 -0
  1109. PYME/resources/icons/mesh.svg +28 -0
  1110. PYME/resources/icons/pmacquire.icns +0 -0
  1111. PYME/resources/icons/pmacquire.ico +0 -0
  1112. PYME/resources/icons/pmanal.icns +0 -0
  1113. PYME/resources/icons/pmanal.ico +0 -0
  1114. PYME/resources/icons/pmvis.icns +0 -0
  1115. PYME/resources/icons/pmvis.ico +0 -0
  1116. PYME/resources/icons/points.png +0 -0
  1117. PYME/resources/icons/points.svg +16 -0
  1118. PYME/resources/icons/pymeLogo.png +0 -0
  1119. PYME/resources/icons/rect_select.png +0 -0
  1120. PYME/resources/icons/squiggle_select.png +0 -0
  1121. PYME/resources/icons/text3800.icns +0 -0
  1122. PYME/resources/icons/text3800.ico +0 -0
  1123. PYME/resources/icons/text3800.png +0 -0
  1124. PYME/resources/icons/text3800.tif +0 -0
  1125. PYME/resources/icons/text5377.icns +0 -0
  1126. PYME/resources/icons/text5377.ico +0 -0
  1127. PYME/resources/icons/text5377.png +0 -0
  1128. PYME/resources/icons/tracks.png +0 -0
  1129. PYME/resources/icons/tracks.svg +19 -0
  1130. PYME/resources/icons/view-front.png +0 -0
  1131. PYME/resources/icons/view-home.png +0 -0
  1132. PYME/resources/icons/view-right.png +0 -0
  1133. PYME/resources/icons/view-top.png +0 -0
  1134. PYME/resources/test_datasets/README.md +8 -0
  1135. PYME/resources/test_datasets/t_im.tif +0 -0
  1136. PYME/resources/test_datasets/test_csv.csv +4282 -0
  1137. PYME/resources/test_datasets/test_hdf.hdf +0 -0
  1138. PYME/resources/web/rule_inline.html +21 -0
  1139. PYME/resources/web/ruleserver.html +75 -0
  1140. PYME/resources/web/static/bootstrap.min.js +7 -0
  1141. PYME/resources/web/static/cluster.css +137 -0
  1142. PYME/resources/web/static/css/bootstrap-theme.css +587 -0
  1143. PYME/resources/web/static/css/bootstrap-theme.min.css +6 -0
  1144. PYME/resources/web/static/css/bootstrap.css +6757 -0
  1145. PYME/resources/web/static/css/bootstrap.min.css +6 -0
  1146. PYME/resources/web/static/css/flat_design_colors_full.css +306 -0
  1147. PYME/resources/web/static/css/recipe_editor.css +106 -0
  1148. PYME/resources/web/static/dashboard.css +106 -0
  1149. PYME/resources/web/static/fileselector.js +77 -0
  1150. PYME/resources/web/static/fonts/glyphicons-halflings-regular.eot +0 -0
  1151. PYME/resources/web/static/fonts/glyphicons-halflings-regular.svg +288 -0
  1152. PYME/resources/web/static/fonts/glyphicons-halflings-regular.ttf +0 -0
  1153. PYME/resources/web/static/fonts/glyphicons-halflings-regular.woff +0 -0
  1154. PYME/resources/web/static/fonts/glyphicons-halflings-regular.woff2 +0 -0
  1155. PYME/resources/web/static/images/osd_flat/LICENSE.txt +116 -0
  1156. PYME/resources/web/static/images/osd_flat/README.md +9 -0
  1157. PYME/resources/web/static/images/osd_flat/fullpage_grouphover.png +0 -0
  1158. PYME/resources/web/static/images/osd_flat/fullpage_hover.png +0 -0
  1159. PYME/resources/web/static/images/osd_flat/fullpage_pressed.png +0 -0
  1160. PYME/resources/web/static/images/osd_flat/fullpage_rest.png +0 -0
  1161. PYME/resources/web/static/images/osd_flat/home_grouphover.png +0 -0
  1162. PYME/resources/web/static/images/osd_flat/home_hover.png +0 -0
  1163. PYME/resources/web/static/images/osd_flat/home_pressed.png +0 -0
  1164. PYME/resources/web/static/images/osd_flat/home_rest.png +0 -0
  1165. PYME/resources/web/static/images/osd_flat/next_grouphover.png +0 -0
  1166. PYME/resources/web/static/images/osd_flat/next_hover.png +0 -0
  1167. PYME/resources/web/static/images/osd_flat/next_pressed.png +0 -0
  1168. PYME/resources/web/static/images/osd_flat/next_rest.png +0 -0
  1169. PYME/resources/web/static/images/osd_flat/previous_grouphover.png +0 -0
  1170. PYME/resources/web/static/images/osd_flat/previous_hover.png +0 -0
  1171. PYME/resources/web/static/images/osd_flat/previous_pressed.png +0 -0
  1172. PYME/resources/web/static/images/osd_flat/previous_rest.png +0 -0
  1173. PYME/resources/web/static/images/osd_flat/rotateleft_grouphover.png +0 -0
  1174. PYME/resources/web/static/images/osd_flat/rotateleft_hover.png +0 -0
  1175. PYME/resources/web/static/images/osd_flat/rotateleft_pressed.png +0 -0
  1176. PYME/resources/web/static/images/osd_flat/rotateleft_rest.png +0 -0
  1177. PYME/resources/web/static/images/osd_flat/rotateright_grouphover.png +0 -0
  1178. PYME/resources/web/static/images/osd_flat/rotateright_hover.png +0 -0
  1179. PYME/resources/web/static/images/osd_flat/rotateright_pressed.png +0 -0
  1180. PYME/resources/web/static/images/osd_flat/rotateright_rest.png +0 -0
  1181. PYME/resources/web/static/images/osd_flat/zoomin_grouphover.png +0 -0
  1182. PYME/resources/web/static/images/osd_flat/zoomin_hover.png +0 -0
  1183. PYME/resources/web/static/images/osd_flat/zoomin_pressed.png +0 -0
  1184. PYME/resources/web/static/images/osd_flat/zoomin_rest.png +0 -0
  1185. PYME/resources/web/static/images/osd_flat/zoomout_grouphover.png +0 -0
  1186. PYME/resources/web/static/images/osd_flat/zoomout_hover.png +0 -0
  1187. PYME/resources/web/static/images/osd_flat/zoomout_pressed.png +0 -0
  1188. PYME/resources/web/static/images/osd_flat/zoomout_rest.png +0 -0
  1189. PYME/resources/web/static/jigna-vue-bare.js +1395 -0
  1190. PYME/resources/web/static/jquery.min.js +2 -0
  1191. PYME/resources/web/static/jstree.min.js +6 -0
  1192. PYME/resources/web/static/openSeadragonGL.js +102 -0
  1193. PYME/resources/web/static/openseadragon-svg-overlay.js +103 -0
  1194. PYME/resources/web/static/openseadragon-viewerinputhook.min.js +6 -0
  1195. PYME/resources/web/static/openseadragon.min.js +9 -0
  1196. PYME/resources/web/static/recipe_editor.js +119 -0
  1197. PYME/resources/web/static/shaders/fragment/lut.glsl +20 -0
  1198. PYME/resources/web/static/shaders/fragment/none.glsl +17 -0
  1199. PYME/resources/web/static/shaders/fragment/outline.glsl +43 -0
  1200. PYME/resources/web/static/shaders/fragment/rect.glsl +7 -0
  1201. PYME/resources/web/static/shaders/fragment/sobel3.glsl +71 -0
  1202. PYME/resources/web/static/shaders/fragment/white.glsl +4 -0
  1203. PYME/resources/web/static/shaders/vertex/rect.glsl +22 -0
  1204. PYME/resources/web/static/shaders/vertex/square.glsl +9 -0
  1205. PYME/resources/web/static/utils.js +3 -0
  1206. PYME/resources/web/static/viaWebGL.js +217 -0
  1207. PYME/setup.py +109 -0
  1208. PYME/setup_exe.py +57 -0
  1209. PYME/simulation/__init__.py +0 -0
  1210. PYME/simulation/locify.py +319 -0
  1211. PYME/simulation/pointsets.py +279 -0
  1212. PYME/simulation/wormlike.py +99 -0
  1213. PYME/simulation/wormlike2.py +198 -0
  1214. PYME/simulation/wormlike3.py +197 -0
  1215. PYME/simulation/wormlike_curled.py +104 -0
  1216. PYME/tileviewer/__init__.py +0 -0
  1217. PYME/tileviewer/templates/roi_list.html +12 -0
  1218. PYME/tileviewer/templates/tileviewer.html +247 -0
  1219. PYME/tileviewer/tileviewer.py +207 -0
  1220. PYME/ui/AUIFrame.py +279 -0
  1221. PYME/ui/MetadataTree.py +469 -0
  1222. PYME/ui/__init__.py +4 -0
  1223. PYME/ui/about_dlg.py +29 -0
  1224. PYME/ui/autoFoldPanel.py +643 -0
  1225. PYME/ui/cascading_layout.py +45 -0
  1226. PYME/ui/crop_dialog.py +145 -0
  1227. PYME/ui/custom_traits_editors.py +508 -0
  1228. PYME/ui/editFilterDialog.py +80 -0
  1229. PYME/ui/editList.py +40 -0
  1230. PYME/ui/fastGraph.py +240 -0
  1231. PYME/ui/filterPane.py +348 -0
  1232. PYME/ui/histLimits.py +543 -0
  1233. PYME/ui/layerFoldPanel.py +232 -0
  1234. PYME/ui/manualFoldPanel.py +870 -0
  1235. PYME/ui/mytimer.py +116 -0
  1236. PYME/ui/patch_traitsui.py +151 -0
  1237. PYME/ui/progress.py +160 -0
  1238. PYME/ui/recArrayView.py +145 -0
  1239. PYME/ui/selection.py +71 -0
  1240. PYME/ui/shell.py +37 -0
  1241. PYME/ui/traitsui_constants.py +91 -0
  1242. PYME/ui/wx_compat.py +43 -0
  1243. PYME/update_version.py +159 -0
  1244. PYME/util/__init__.py +3 -0
  1245. PYME/util/authenticate.py +164 -0
  1246. PYME/util/execfile.py +21 -0
  1247. PYME/util/fProfile/__init__.py +1 -0
  1248. PYME/util/fProfile/convertProfile.py +117 -0
  1249. PYME/util/fProfile/fProfile.py +162 -0
  1250. PYME/util/fProfile/html/404.html +60 -0
  1251. PYME/util/fProfile/html/css/main.css +258 -0
  1252. PYME/util/fProfile/html/css/normalize.css +427 -0
  1253. PYME/util/fProfile/html/css/normalize.min.css +1 -0
  1254. PYME/util/fProfile/html/index.html +64 -0
  1255. PYME/util/fProfile/html/js/main.js +492 -0
  1256. PYME/util/fProfile/html/js/plugins.js +24 -0
  1257. PYME/util/fProfile/html/js/vendor/crossfilter.min.js +1 -0
  1258. PYME/util/fProfile/html/js/vendor/jquery-1.11.2.min.js +4 -0
  1259. PYME/util/fProfile/html/js/vendor/modernizr-2.8.3.min.js +4 -0
  1260. PYME/util/fProfile/html/js/vendor/tinycolor-min.js +4 -0
  1261. PYME/util/fProfile/viewProfile.py +30 -0
  1262. PYME/util/install_dependencies.py +95 -0
  1263. PYME/util/log_verbosity.py +11 -0
  1264. PYME/util/mProfile/__init__.py +1 -0
  1265. PYME/util/mProfile/colorize_db_t.py +297 -0
  1266. PYME/util/mProfile/mProfile.py +164 -0
  1267. PYME/util/mProfile/p.py +101 -0
  1268. PYME/util/mProfile/tProfile.py +165 -0
  1269. PYME/util/pymelauncher.py +47 -0
  1270. PYME/util/shmarray/__init__.py +0 -0
  1271. PYME/util/shmarray/shmTest.py +74 -0
  1272. PYME/util/shmarray/shmarray.py +165 -0
  1273. PYME/util/webframework.py +365 -0
  1274. PYME/version.py +65 -0
  1275. python_microscopy-25.6.5.dist-info/METADATA +68 -0
  1276. python_microscopy-25.6.5.dist-info/RECORD +1277 -0
  1277. 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)