PYME-extra 1.0.4.post0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. PYMEcs/Acquire/Actions/__init__.py +0 -0
  2. PYMEcs/Acquire/Actions/custom.py +167 -0
  3. PYMEcs/Acquire/Hardware/LPthreadedSimple.py +248 -0
  4. PYMEcs/Acquire/Hardware/LPthreadedSimpleSim.py +246 -0
  5. PYMEcs/Acquire/Hardware/NikonTiFlaskServer.py +45 -0
  6. PYMEcs/Acquire/Hardware/NikonTiFlaskServerT.py +59 -0
  7. PYMEcs/Acquire/Hardware/NikonTiRESTClient.py +73 -0
  8. PYMEcs/Acquire/Hardware/NikonTiSim.py +35 -0
  9. PYMEcs/Acquire/Hardware/__init__.py +0 -0
  10. PYMEcs/Acquire/Hardware/driftTrackGUI.py +329 -0
  11. PYMEcs/Acquire/Hardware/driftTrackGUI_n.py +472 -0
  12. PYMEcs/Acquire/Hardware/driftTracking.py +424 -0
  13. PYMEcs/Acquire/Hardware/driftTracking_n.py +433 -0
  14. PYMEcs/Acquire/Hardware/fakeCamX.py +15 -0
  15. PYMEcs/Acquire/Hardware/offsetPiezoRESTCorrelLog.py +38 -0
  16. PYMEcs/Acquire/__init__.py +0 -0
  17. PYMEcs/Analysis/MBMcollection.py +552 -0
  18. PYMEcs/Analysis/MINFLUX.py +280 -0
  19. PYMEcs/Analysis/MapUtils.py +77 -0
  20. PYMEcs/Analysis/NPC.py +1176 -0
  21. PYMEcs/Analysis/Paraflux.py +218 -0
  22. PYMEcs/Analysis/Simpler.py +81 -0
  23. PYMEcs/Analysis/Sofi.py +140 -0
  24. PYMEcs/Analysis/__init__.py +0 -0
  25. PYMEcs/Analysis/decSofi.py +211 -0
  26. PYMEcs/Analysis/eventProperties.py +50 -0
  27. PYMEcs/Analysis/fitDarkTimes.py +569 -0
  28. PYMEcs/Analysis/objectVolumes.py +20 -0
  29. PYMEcs/Analysis/offlineTracker.py +130 -0
  30. PYMEcs/Analysis/stackTracker.py +180 -0
  31. PYMEcs/Analysis/timeSeries.py +63 -0
  32. PYMEcs/Analysis/trackFiducials.py +186 -0
  33. PYMEcs/Analysis/zerocross.py +91 -0
  34. PYMEcs/IO/MINFLUX.py +851 -0
  35. PYMEcs/IO/NPC.py +117 -0
  36. PYMEcs/IO/__init__.py +0 -0
  37. PYMEcs/IO/darkTimes.py +19 -0
  38. PYMEcs/IO/picasso.py +219 -0
  39. PYMEcs/IO/tabular.py +11 -0
  40. PYMEcs/__init__.py +0 -0
  41. PYMEcs/experimental/CalcZfactor.py +51 -0
  42. PYMEcs/experimental/FRC.py +338 -0
  43. PYMEcs/experimental/ImageJROItools.py +49 -0
  44. PYMEcs/experimental/MINFLUX.py +1537 -0
  45. PYMEcs/experimental/NPCcalcLM.py +560 -0
  46. PYMEcs/experimental/Simpler.py +369 -0
  47. PYMEcs/experimental/Sofi.py +78 -0
  48. PYMEcs/experimental/__init__.py +0 -0
  49. PYMEcs/experimental/binEventProperty.py +187 -0
  50. PYMEcs/experimental/chaining.py +23 -0
  51. PYMEcs/experimental/clusterTrack.py +179 -0
  52. PYMEcs/experimental/combine_maps.py +104 -0
  53. PYMEcs/experimental/eventProcessing.py +93 -0
  54. PYMEcs/experimental/fiducials.py +323 -0
  55. PYMEcs/experimental/fiducialsNew.py +402 -0
  56. PYMEcs/experimental/mapTools.py +271 -0
  57. PYMEcs/experimental/meas2DplotDh5view.py +107 -0
  58. PYMEcs/experimental/mortensen.py +131 -0
  59. PYMEcs/experimental/ncsDenoise.py +158 -0
  60. PYMEcs/experimental/onTimes.py +295 -0
  61. PYMEcs/experimental/procPoints.py +77 -0
  62. PYMEcs/experimental/pyme2caml.py +73 -0
  63. PYMEcs/experimental/qPAINT.py +965 -0
  64. PYMEcs/experimental/randMap.py +188 -0
  65. PYMEcs/experimental/regExtraCmaps.py +11 -0
  66. PYMEcs/experimental/selectROIfilterTable.py +72 -0
  67. PYMEcs/experimental/showErrs.py +51 -0
  68. PYMEcs/experimental/showErrsDh5view.py +58 -0
  69. PYMEcs/experimental/showShiftMap.py +56 -0
  70. PYMEcs/experimental/snrEvents.py +188 -0
  71. PYMEcs/experimental/specLabeling.py +51 -0
  72. PYMEcs/experimental/splitRender.py +246 -0
  73. PYMEcs/experimental/testChannelByName.py +36 -0
  74. PYMEcs/experimental/timedSpecies.py +28 -0
  75. PYMEcs/experimental/utils.py +31 -0
  76. PYMEcs/misc/ExtraCmaps.py +177 -0
  77. PYMEcs/misc/__init__.py +0 -0
  78. PYMEcs/misc/configUtils.py +169 -0
  79. PYMEcs/misc/guiMsgBoxes.py +27 -0
  80. PYMEcs/misc/mapUtils.py +230 -0
  81. PYMEcs/misc/matplotlib.py +136 -0
  82. PYMEcs/misc/rectsFromSVG.py +182 -0
  83. PYMEcs/misc/shellutils.py +1110 -0
  84. PYMEcs/misc/utils.py +205 -0
  85. PYMEcs/misc/versionCheck.py +20 -0
  86. PYMEcs/misc/zcInfo.py +90 -0
  87. PYMEcs/pyme_warnings.py +4 -0
  88. PYMEcs/recipes/__init__.py +0 -0
  89. PYMEcs/recipes/base.py +75 -0
  90. PYMEcs/recipes/localisations.py +2380 -0
  91. PYMEcs/recipes/manipulate_yaml.py +83 -0
  92. PYMEcs/recipes/output.py +177 -0
  93. PYMEcs/recipes/processing.py +247 -0
  94. PYMEcs/recipes/simpler.py +290 -0
  95. PYMEcs/version.py +2 -0
  96. pyme_extra-1.0.4.post0.dist-info/METADATA +114 -0
  97. pyme_extra-1.0.4.post0.dist-info/RECORD +101 -0
  98. pyme_extra-1.0.4.post0.dist-info/WHEEL +5 -0
  99. pyme_extra-1.0.4.post0.dist-info/entry_points.txt +3 -0
  100. pyme_extra-1.0.4.post0.dist-info/licenses/LICENSE +674 -0
  101. pyme_extra-1.0.4.post0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,560 @@
1
+ from PYMEcs.pyme_warnings import warn
2
+ from PYMEcs.Analysis.NPC import estimate_nlabeled, npclabel_fit, plotcdf_npc3d
3
+ from PYME.recipes import tablefilters
4
+ import wx
5
+ from traits.api import HasTraits, Str, Int, CStr, List, Enum, Float, Bool
6
+ import numpy as np
7
+ import matplotlib.pyplot as plt
8
+ from PYMEcs.misc.utils import unique_name
9
+ from PYMEcs.IO.NPC import findNPCset
10
+ import logging
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ def selectWithDialog(choices, message='select image from list', caption='Selection'):
15
+ dlg = wx.SingleChoiceDialog(None, message, caption, list(choices), wx.CHOICEDLG_STYLE)
16
+ if dlg.ShowModal() == wx.ID_OK:
17
+ item = dlg.GetStringSelection()
18
+ else:
19
+ item = None
20
+ dlg.Destroy()
21
+ return item
22
+
23
+ def setNPCsfromImg(pipeline,img):
24
+ with_ids = unique_name('with_ids',pipeline.dataSources.keys())
25
+ valid_ids = unique_name('valid_ids',pipeline.dataSources.keys())
26
+ recipe = pipeline.recipe
27
+ mapp = tablefilters.Mapping(recipe,inputName=pipeline.selectedDataSourceKey,
28
+ outputName=with_ids)
29
+ recipe.add_module(mapp)
30
+ recipe.execute()
31
+
32
+ withIDs = recipe.namespace[with_ids]
33
+
34
+ pixX = np.round((pipeline['x'] - img.imgBounds.x0 )/img.pixelSize).astype('i')
35
+ pixY = np.round((pipeline['y'] - img.imgBounds.y0 )/img.pixelSize).astype('i')
36
+
37
+ ind = (pixX < img.data_xyztc.shape[0])*(pixY < img.data_xyztc.shape[1])*(pixX >= 0)*(pixY >= 0)
38
+
39
+ ids = np.zeros_like(pixX)
40
+ #assume there is only one channel
41
+ ids[ind] = img.data_xyztc[:,:,0,0,0].squeeze()[pixX[ind], pixY[ind]].astype('i')
42
+
43
+ numPerObject, b = np.histogram(ids, np.arange(ids.max() + 1.5) + .5)
44
+
45
+ withIDs.addColumn('objectID', ids)
46
+ withIDs.addColumn('NEvents', numPerObject[ids-1])
47
+
48
+ recipe.add_module(tablefilters.FilterTable(recipe,inputName=with_ids, outputName=valid_ids,
49
+ filters={'objectID' : [.5, 1e5]}))
50
+ recipe.execute()
51
+
52
+ pipeline.selectDataSource(valid_ids)
53
+
54
+ class NPCsettings(HasTraits):
55
+ SegmentThreshold_2D = Int(10,label='Threshold localisation count (2D)',
56
+ desc="a minimum number of localisations in a segment in 2D fitting to be counted as labeled; "+
57
+ "NOTE: definition changed from original code where 11 localisations where required with a threshold of 10 etc!")
58
+ SegmentThreshold_3D = Int(1,label='Threshold localisation count (3D)',
59
+ desc="a minimum number of localisations in a segment in 3D fitting to be counted as labeled")
60
+ SecondPass_2D = Bool(False,label='Second pass for NPC fitting (2D)',
61
+ desc="a second pass for 2D fitting should be run; we have experimented with a second pass rotation "+
62
+ "estimate and fitting hoping to improve on the first estimate; still experimental")
63
+ StartHeight_3D = Float(50.0,label='Starting ring spacing for 3D fitting',
64
+ desc="starting ring spacing value for the 3D fit; note only considered when doing the initial full fit; "+
65
+ "not considered when re-evaluating existing fit")
66
+ StartDiam_3D = Float(107.0,label='Starting ring diameter for 3D fitting',
67
+ desc="starting ring diameter value for the 3D fit; note only considered when doing the initial full fit; "+
68
+ "not considered when re-evaluating existing fit")
69
+ TemplateMode_3D = Enum(['standard','detailed','twostage'],desc="standard or detailed NPC template")
70
+ TemplateSigma_3D = Float(7.0,label='Sigma value for 3D template smoothing',desc="smoothing SD (sigma) to smooth the 3D fitting template, default is 7 nm")
71
+ FitMode = Enum(['abs','square'],label='Fit mode for NPC rotation',
72
+ desc="fit mode for NPC rotation; in 2D and 3D estimation of the NPC lateral rotation a simple algorithm is used to find the start of the 'pizza pieces'; "+
73
+ "this mode refers to use of absolute or squared differences in the calculation; default should be ok")
74
+ RotationLocked_3D = Bool(True,label='NPC rotation estimate locked (3D)',
75
+ desc="when estimating the NPC rotation (pizza slice boundaries), the top and bottom rings in 3D should be locked, "+
76
+ "i.e. have the same rotation from the underlying structure")
77
+ RadiusUncertainty_3D = Float(20.0,label="Radius scatter in nm",
78
+ desc="a radius scatter that determines how much localisations can deviate from the mean ring radius "+
79
+ "and still be accepted as part of the NPC; allows for distortions of NPCs and localisation errors")
80
+ # the two next things seem to set the same thing, so unify
81
+ Zclip_3D = Float(75.0,label='Z-clip value from center of NPC',
82
+ desc='the used zrange from the (estimated) center of the NPC, from (-zclip..+zclip) in 3D fitting')
83
+ OffsetMode_3D = Enum(['median','mean'],label='Method to estimate NPC center',
84
+ desc="Method to estimate the likely 3D center of an NPC; median seems more robust against outliners (say in z)")
85
+ SkipEmptyTopOrBottom_3D = Bool(False,desc="if to skip NPCs with empty top or bottom ring")
86
+ KnownNumber_3D = Int(-1, desc="if a known number of NPCs are present but some have 0 events use this to set; only considered if value >0")
87
+ NPCRotationAngle = Enum(['positive','negative','zero'],desc="way to treat rotation for NPC gallery")
88
+ NPCGalleryArrangement = Enum(['SingleAverageSBS','TopOverBottom','TopBesideBottom','SingleAverage'],desc="how to arrange 3D NPC parts in NPC gallery; SBS = SideBySide top and bottom")
89
+ NoRotationForSingleNPCFit = Bool(False,desc="if rotation is disabled for single NPC fit")
90
+ IncludeSegmentLinesWithGallery = Bool(True,desc="if 3D segement lines are generated when NPC 3D gallery is created")
91
+
92
+
93
+ # this should be a backwards compatible way to access the main filename associated with the pipeline/datasource
94
+ def pipeline_filename(pipeline):
95
+ try:
96
+ filename = pipeline.filename
97
+ except (KeyError,AttributeError):
98
+ # latest versions with session saving associated filenames with the actual datasource read in
99
+ filename = pipeline.dataSources['FitResults'].filename
100
+ return filename
101
+
102
+ class NPCcalc():
103
+ def __init__(self, visFr):
104
+ self.visFr = visFr
105
+
106
+ visFr.AddMenuItem('Experimental>NPC2D', "Select NPCs by Mask", self.OnSelectNPCsByMask)
107
+ visFr.AddMenuItem('Experimental>NPC3D', "Select NPCs by Mask", self.OnSelectNPCsByMask)
108
+ visFr.AddMenuItem('Experimental>NPC2D', "Analyse single 2D NPC\tCtrl+N", self.OnAnalyseSingleNPC)
109
+ visFr.AddMenuItem('Experimental>NPC2D', "Analyse 2D NPCs by ID", self.OnAnalyseNPCsByID)
110
+ visFr.AddMenuItem('Experimental>NPC2D', "Show 2D NPC labeling Statistics", self.OnNPCstats)
111
+ visFr.AddMenuItem('Experimental>NPC2D', "Select by mask, analyse and show stats", self.OnNPCcombinedFuncs)
112
+ visFr.AddMenuItem('Experimental>NPC3D', "Analyse 3D NPCs by ID", self.OnAnalyse3DNPCsByID)
113
+ visFr.AddMenuItem('Experimental>NPC3D', "Add 3D NPC templates", self.On3DNPCaddTemplates)
114
+ visFr.AddMenuItem('Experimental>NPC3D', "Save NPC Set with full fit analysis", self.OnNPC3DSaveNPCSet)
115
+ visFr.AddMenuItem('Experimental>NPC3D', "Load stored NPC Set with full fit analysis", self.OnNPC3DLoadNPCSet)
116
+ visFr.AddMenuItem('Experimental>NPC3D', "Save Measurements Only (csv, no fit info saved)",self.OnNPC3DSaveMeasurements)
117
+ visFr.AddMenuItem('Experimental>NPC3D', "Load and display saved Measurements (from csv)",self.OnNPC3DLoadMeasurements)
118
+ visFr.AddMenuItem('Experimental>NPC3D', "Show NPC geometry statistics",self.OnNPC3DGeometryStats)
119
+ visFr.AddMenuItem('Experimental>NPC3D', "Save NPC geometry statistics as CSV",self.OnNPC3DSaveGeometryStats)
120
+ visFr.AddMenuItem('Experimental>NPC3D', "Show NPC template fit statistics",self.OnNPC3DTemplateFitStats)
121
+ visFr.AddMenuItem('Experimental>NPC2D', 'NPC Analysis settings', self.OnNPCsettings)
122
+ visFr.AddMenuItem('Experimental>NPC3D', 'NPC Analysis settings', self.OnNPCsettings)
123
+ visFr.AddMenuItem('Experimental>NPC3D', 'Add NPC Gallery', self.On3DNPCaddGallery)
124
+ visFr.AddMenuItem('Experimental>NPC3D', 'Plot NPC by-segment data', self.OnNPC3DPlotBySegments)
125
+ visFr.AddMenuItem('Experimental>NPC3D', 'Save NPC by-segment data', self.OnNPC3DSaveBySegments)
126
+
127
+ self._npcsettings = None
128
+ self.gallery_layer = None
129
+ self.segment_layer = None
130
+
131
+ @property
132
+ def NPCsettings(self):
133
+ if self._npcsettings is None:
134
+ foreshortening=self.visFr.pipeline.mdh.get('MINFLUX.Foreshortening',1.0)
135
+ if foreshortening < 1.0:
136
+ self._npcsettings = NPCsettings(StartHeight_3D=70.0*foreshortening,Zclip_3D=75.0*foreshortening)
137
+ else:
138
+ self._npcsettings = NPCsettings(StartHeight_3D=50.0,Zclip_3D=55.0)
139
+ return self._npcsettings
140
+
141
+ def OnNPCsettings(self, event=None):
142
+ if self.NPCsettings.configure_traits(kind='modal'):
143
+ pass
144
+
145
+ def OnAnalyseSingleNPC(self, event):
146
+ pipeline = self.visFr.pipeline
147
+ Nevents = pipeline['x'].size
148
+ if Nevents > 500:
149
+ warn('More than 500 events in pipeline - check if really a single NPC')
150
+ return
151
+
152
+ xExtent = pipeline['x'].max() - pipeline['x'].min()
153
+ yExtent = pipeline['y'].max() - pipeline['y'].min()
154
+
155
+ maxextent_nm = 300
156
+ if (xExtent > maxextent_nm) or (yExtent > maxextent_nm):
157
+ warn('x or y bounding box > %d nm (%d,%d) - check if single NPC' % (maxextent_nm,xExtent,yExtent))
158
+ return
159
+
160
+ if self.NPCsettings.NoRotationForSingleNPCFit:
161
+ rot = 0
162
+ else:
163
+ rot = None
164
+ estimate_nlabeled(pipeline['x'],pipeline['y'],nthresh=self.NPCsettings.SegmentThreshold_2D,rotation=rot,
165
+ do_plot=True,secondpass=self.NPCsettings.SecondPass_2D,fitmode=self.NPCsettings.FitMode)
166
+
167
+
168
+ def OnAnalyseNPCsByID(self, event=None):
169
+ from PYMEcs.recipes.localisations import NPCAnalysisByID
170
+ pipeline = self.visFr.pipeline
171
+ recipe = pipeline.recipe
172
+ with_npclinfo = unique_name('with_npcinfo',pipeline.dataSources.keys())
173
+ # for the NPCAnalysisByID module use the current NPCsettings
174
+ npcanalysis = NPCAnalysisByID(inputName=pipeline.selectedDataSourceKey,outputName=with_npclinfo,
175
+ SegmentThreshold=self.NPCsettings.SegmentThreshold_2D,
176
+ SecondPass=self.NPCsettings.SecondPass_2D,FitMode=self.NPCsettings.FitMode)
177
+ if not npcanalysis.configure_traits(kind='modal'):
178
+ return
179
+
180
+ recipe.add_module(npcanalysis)
181
+ recipe.execute()
182
+
183
+ pipeline.selectDataSource(with_npclinfo)
184
+
185
+ def OnAnalyse3DNPCsByID(self, event=None):
186
+ from PYMEcs.Analysis.NPC import NPC3DSet
187
+ pipeline = self.visFr.pipeline
188
+
189
+ if findNPCset(pipeline,warnings=False) is not None:
190
+ npcs = findNPCset(pipeline)
191
+ do_plot = False
192
+ else:
193
+ from PYMEcs.IO.MINFLUX import foreshortening
194
+ npcs = NPC3DSet(filename=pipeline_filename(pipeline),
195
+ zclip=self.NPCsettings.Zclip_3D,
196
+ offset_mode=self.NPCsettings.OffsetMode_3D,
197
+ NPCdiam=self.NPCsettings.StartDiam_3D,
198
+ NPCheight=self.NPCsettings.StartHeight_3D,
199
+ foreshortening=pipeline.mdh.get('MINFLUX.Foreshortening',1.0),
200
+ known_number=self.NPCsettings.KnownNumber_3D,
201
+ templatemode=self.NPCsettings.TemplateMode_3D,
202
+ sigma=self.NPCsettings.TemplateSigma_3D)
203
+ do_plot = True
204
+ for oid in np.unique(pipeline['objectID']):
205
+ npcs.addNPCfromPipeline(pipeline,oid)
206
+
207
+ # for example use of ProgressDialog see also
208
+ # https://github.com/Metallicow/wxPython-Sample-Apps-and-Demos/blob/master/101_Common_Dialogs/ProgressDialog/ProgressDialog_extended.py
209
+ progress = wx.ProgressDialog("NPC analysis in progress", "please wait", maximum=len(npcs.npcs),
210
+ parent=self.visFr,
211
+ style=wx.PD_SMOOTH
212
+ | wx.PD_AUTO_HIDE
213
+ | wx.PD_CAN_ABORT
214
+ | wx.PD_ESTIMATED_TIME
215
+ | wx.PD_REMAINING_TIME)
216
+ if do_plot:
217
+ fig, axes=plt.subplots(2,3)
218
+ if 'templatemode' in dir(npcs) and npcs.templatemode == 'twostage':
219
+ figpre, axespre=plt.subplots(2,3,label='pre-llm')
220
+ cancelled = False
221
+ npcs.measurements = []
222
+ if 'templatemode' in dir(npcs) and npcs.templatemode == 'detailed':
223
+ rotation = 22.5 # this value may need adjustment
224
+ else:
225
+ rotation = None
226
+
227
+ # keep track if any fits were performed
228
+ anyfits = False
229
+ for i,npc in enumerate(npcs.npcs):
230
+ if not npc.fitted:
231
+ if 'templatemode' in dir(npcs) and npcs.templatemode == 'twostage':
232
+ npc.fitbymll(npcs.llm,plot=True,printpars=False,axes=axes,preminimizer=npcs.llmpre,axespre=axespre)
233
+ else:
234
+ npc.fitbymll(npcs.llm,plot=True,printpars=False,axes=axes)
235
+ anyfits = True
236
+ nt,nb = npc.nlabeled(nthresh=self.NPCsettings.SegmentThreshold_3D,
237
+ dr=self.NPCsettings.RadiusUncertainty_3D,
238
+ rotlocked=self.NPCsettings.RotationLocked_3D,
239
+ zrange=self.NPCsettings.Zclip_3D,
240
+ rotation=rotation)
241
+ if self.NPCsettings.SkipEmptyTopOrBottom_3D and (nt == 0 or nb == 0):
242
+ pass # we skip NPCs with empty rings in this case
243
+ else:
244
+ npcs.measurements.append([nt,nb])
245
+ (keepGoing, skip) = progress.Update(i+1)
246
+ if not keepGoing:
247
+ logger.info('OnAnalyse3DNPCsByID: progress cancelled, aborting NPC analysis')
248
+ cancelled = True
249
+ progress.Destroy()
250
+ wx.Yield()
251
+ # Cancelled by user.
252
+ break
253
+ wx.Yield()
254
+ else:
255
+ if anyfits:
256
+ pipeline.npcs = npcs # we update the pipeline npcs attribute only if the for loop completed normally and we fitted
257
+
258
+ if cancelled:
259
+ return
260
+
261
+ npcs.plot_labeleff(thresh=self.NPCsettings.SegmentThreshold_3D)
262
+
263
+
264
+ def OnNPC3DSaveBySegments(self, event=None):
265
+ pipeline = self.visFr.pipeline
266
+ if findNPCset(pipeline) is not None:
267
+ nbs = findNPCset(pipeline).n_bysegments()
268
+ if nbs is None:
269
+ warn("could not find npcs with by-segment fitting info, have you carried out fitting with recent code?")
270
+ return
271
+ with wx.FileDialog(self.visFr, 'Save NPC by-segment data as ...',
272
+ wildcard='CSV (*.csv)|*.csv',
273
+ style=wx.FD_SAVE) as fdialog:
274
+ if fdialog.ShowModal() != wx.ID_OK:
275
+ return
276
+ else:
277
+ fpath = fdialog.GetPath()
278
+
279
+ import pandas as pd
280
+ df = pd.DataFrame.from_dict(dict(top=nbs['top'].flatten(),bottom=nbs['bottom'].flatten()))
281
+ df.to_csv(fpath,index=False)
282
+ else:
283
+ warn("could not find valid NPC set, have you carried out fitting?")
284
+
285
+ def OnNPC3DPlotBySegments(self, event=None):
286
+ pipeline = self.visFr.pipeline
287
+ if findNPCset(pipeline) is not None:
288
+ nbs = findNPCset(pipeline).n_bysegments()
289
+ if nbs is None:
290
+ warn("could not find npcs with by-segment fitting info, have you carried out fitting with recent code?")
291
+ return
292
+ fig = plt.figure()
293
+ plt.hist(nbs['bottom'].flatten(),bins=np.arange(nbs['bottom'].max()+2)-0.5,alpha=0.5,label='bottom',density=True,histtype='step')
294
+ plt.plot([nbs['bottom'].mean(),nbs['bottom'].mean()],[0,0.2],'b--')
295
+ plt.hist(nbs['top'].flatten(),bins=np.arange(nbs['top'].max()+2)-0.5,alpha=0.5,label='top',density=True,histtype='step')
296
+ plt.plot([nbs['top'].mean(),nbs['top'].mean()],[0,0.2],'r--')
297
+ plt.legend()
298
+ nbsflat = nbs['bottom'].flatten()
299
+ b_meanall = 0.5*nbsflat.mean() # 0.5 to get per site because we have two sites per segment
300
+ b_meannz = 0.5*nbsflat[nbsflat>0].mean() # 0.5 to get per site because we have two sites per segment
301
+ plt.text(0.65,0.5,'cytop per site: %.1f (%.1f per labeled)' % (b_meanall,b_meannz), horizontalalignment='center',
302
+ verticalalignment='center', color='b', transform=fig.transFigure)
303
+ nbsflat = nbs['top'].flatten()
304
+ t_meanall = 0.5*nbsflat.mean() # 0.5 to get per site because we have two sites per segment
305
+ t_meannz = 0.5*nbsflat[nbsflat>0].mean() # 0.5 to get per site because we have two sites per segment
306
+ plt.text(0.65,0.44,'nucleop per site: %.1f (%.1f per labeled)' % (t_meanall,t_meannz), horizontalalignment='center',
307
+ verticalalignment='center', color='r', transform=fig.transFigure)
308
+ else:
309
+ warn("could not find valid NPC set, have you carried out fitting?")
310
+
311
+ def On3DNPCaddGallery(self, event=None):
312
+ pipeline = self.visFr.pipeline
313
+ npcs = findNPCset(pipeline)
314
+ if npcs is None or 'measurements' not in dir(npcs):
315
+ warn('no valid NPC measurements found, therefore cannot add gallery...')
316
+ return
317
+
318
+ gallery_ds = None
319
+ segment_ds = None
320
+ npcmod = findNPCset(pipeline,return_mod=True)
321
+ if npcmod is not None:
322
+ gallery_ds = npcmod.outputGallery
323
+ segment_ds = npcmod.outputSegments
324
+ else:
325
+ if 'npc_gallery' in pipeline.dataSources.keys():
326
+ # this should not happen if we use a module, so only if somebody already manually made this ds
327
+ warn("dataSource 'npc_gallery' already exists, currently we do not support recalculating a new gallery")
328
+ return
329
+ from PYMEcs.Analysis.NPC import mk_NPC_gallery
330
+ gallery, segments = mk_NPC_gallery(npcs,self.NPCsettings.NPCGalleryArrangement,self.NPCsettings.Zclip_3D,self.NPCsettings.NPCRotationAngle)
331
+ pipeline.addDataSource('npc_gallery',gallery,False) # should check if already exists!
332
+ gallery_ds = 'npc_gallery'
333
+ if self.NPCsettings.IncludeSegmentLinesWithGallery:
334
+ pipeline.addDataSource('npc_segments',segments,False)
335
+ segment_ds = 'npc_segments'
336
+ pipeline.Rebuild() # check, is this the right way when add a new non-module based dataSource?
337
+
338
+ if gallery_ds is not None and self.gallery_layer is None:
339
+ from PYME.LMVis.layers.pointcloud import PointCloudRenderLayer
340
+ self.gallery_layer = PointCloudRenderLayer(pipeline, dsname=gallery_ds, method='pointsprites', cmap='plasma', point_size=4.0, alpha=0.5, vertexColour='z')
341
+ self.visFr.add_layer( self.gallery_layer)
342
+
343
+ if segment_ds is not None and self.segment_layer is None:
344
+ from PYME.LMVis.layers.tracks import TrackRenderLayer
345
+ self.segment_layer = TrackRenderLayer(pipeline, dsname=segment_ds, method='tracks', clump_key='polyIndex', line_width=2.0,
346
+ alpha=0.5,cmap='SolidWhite')
347
+ self.visFr.add_layer(self.segment_layer)
348
+
349
+ def On3DNPCaddTemplates(self, event=None):
350
+ pipeline = self.visFr.pipeline
351
+
352
+ npcs = findNPCset(pipeline)
353
+ if npcs is None or 'measurements' not in dir(npcs):
354
+ warn('no valid NPC measurements found, therefore cannot add templates...')
355
+ return
356
+
357
+ ds_template_name = None
358
+ npcmod = findNPCset(pipeline,return_mod=True)
359
+ if npcmod is not None:
360
+ ds_template_name = npcmod.outputTemplates
361
+ else:
362
+ warn("You are not using the NPCAnalysisInput module!\n\nWill attempt to create NPC templates, but session saving will not work")
363
+ ds_template_name = 'NPCtemplates'
364
+ if ds_template_name in pipeline.dataSources.keys():
365
+ warn("dataSource '%s' already exists, we do not support recalculating a new template set, use the NPCAnalysisInput module instead" % ds_template_name)
366
+ return
367
+ from PYMEcs.Analysis.NPC import mk_npctemplates
368
+ ds_template = mk_npctemplates(npcs)
369
+ pipeline.addDataSource(ds_template_name,ds_template,False) # should check if already exists!
370
+ pipeline.Rebuild() # check, is this the right way when add a new non-module based dataSource?
371
+
372
+ # now we add a track layer to render our template polygons
373
+ # TODO - we may need to check if this happened before or not!
374
+ from PYME.LMVis.layers.tracks import TrackRenderLayer # NOTE: we may rename the clumpIndex variable in this layer to polyIndex or similar
375
+ layer = TrackRenderLayer(pipeline, dsname=ds_template_name, method='tracks', clump_key='polyIndex', line_width=2.0, alpha=0.5)
376
+ self.visFr.add_layer(layer)
377
+
378
+ def OnNPC3DSaveMeasurements(self, event=None):
379
+ pipeline = self.visFr.pipeline
380
+ npcs = findNPCset(pipeline)
381
+ if npcs is None or 'measurements' not in dir(npcs):
382
+ warn('no valid NPC measurements found, therefore cannot save...')
383
+ return
384
+
385
+ fdialog = wx.FileDialog(self.visFr, 'Save NPC measurements as ...',
386
+ wildcard='CSV (*.csv)|*.csv',
387
+ style=wx.FD_SAVE)
388
+ if fdialog.ShowModal() != wx.ID_OK:
389
+ return
390
+
391
+ fpath = fdialog.GetPath()
392
+ meas = np.array(npcs.measurements, dtype='i')
393
+ import pandas as pd
394
+ df = pd.DataFrame({'Ntop_NPC3D': meas[:, 0], 'Nbot_NPC3D': meas[:, 1]})
395
+
396
+ from pathlib import Path
397
+ with open(fpath, 'w') as f:
398
+ f.write('# threshold %d, source data file %s\n' %
399
+ (self.NPCsettings.SegmentThreshold_3D,Path(pipeline_filename(pipeline)).name))
400
+
401
+ df.to_csv(fpath,index=False, mode='a')
402
+
403
+
404
+ def OnNPC3DLoadMeasurements(self, event=None):
405
+ from PYMEcs.misc.utils import get_timestamp_from_filename
406
+ fdialog = wx.FileDialog(self.visFr, 'Load NPC measurements from ...',
407
+ wildcard='CSV (*.csv)|*.csv',
408
+ style=wx.FD_OPEN)
409
+ if fdialog.ShowModal() != wx.ID_OK:
410
+ return
411
+ import pandas as pd
412
+ fname = fdialog.GetPath()
413
+ meas = pd.read_csv(fname,comment='#')
414
+ # here we need some plotting code
415
+ nlab = meas['Ntop_NPC3D'] + meas['Nbot_NPC3D']
416
+ plt.figure()
417
+ plotcdf_npc3d(nlab,timestamp=get_timestamp_from_filename(fname))
418
+
419
+ def OnNPC3DLoadNPCSet(self, event=None):
420
+ from PYMEcs.IO.NPC import load_NPC_set
421
+
422
+ pipeline = self.visFr.pipeline
423
+ fdialog = wx.FileDialog(self.visFr, 'Load NPC measurements from ...',
424
+ wildcard='Pickle (*.pickle)|*.pickle',
425
+ style=wx.FD_OPEN)
426
+ if fdialog.ShowModal() != wx.ID_OK:
427
+ return
428
+
429
+ pipeline.npcs = load_NPC_set(fdialog.GetPath(),
430
+ ts=pipeline.mdh.get('MINFLUX.TimeStamp'),
431
+ foreshortening=pipeline.mdh.get('MINFLUX.Foreshortening',1.0))
432
+
433
+ def OnNPC3DSaveNPCSet(self, event=None):
434
+ from PYMEcs.IO.NPC import save_NPC_set
435
+ pipeline = self.visFr.pipeline
436
+ defaultFile = ''
437
+ MINFLUXts = pipeline.mdh.get('MINFLUX.TimeStamp')
438
+ if MINFLUXts is not None:
439
+ defaultFile = "%s-NPCset.pickle" % MINFLUXts
440
+ npcs = findNPCset(pipeline)
441
+ if npcs is None:
442
+ warn('no valid NPC Set found, therefore cannot save...')
443
+ return
444
+ fdialog = wx.FileDialog(self.visFr, 'Save NPC Set as ...',
445
+ wildcard='Pickle (*.pickle)|*.pickle',
446
+ defaultFile=defaultFile,
447
+ style=wx.FD_SAVE)
448
+ if fdialog.ShowModal() != wx.ID_OK:
449
+ return
450
+
451
+ save_NPC_set(npcs,fdialog.GetPath())
452
+
453
+ def OnNPC3DSaveGeometryStats(self,event=None):
454
+ pipeline = self.visFr.pipeline
455
+ npcs = findNPCset(pipeline)
456
+ if npcs is None:
457
+ warn('no valid NPC measurements found, thus no geometry info available...')
458
+ return
459
+ diams = np.asarray(npcs.diam())
460
+ heights = np.asarray(npcs.height())
461
+ import pandas as pd
462
+ geo_df = pd.DataFrame.from_dict(dict(diameter=diams,height=heights))
463
+ with wx.FileDialog(self.visFr, 'Save NPC measurements as ...',
464
+ wildcard='CSV (*.csv)|*.csv',
465
+ style=wx.FD_SAVE) as fdialog:
466
+ if fdialog.ShowModal() != wx.ID_OK:
467
+ return
468
+ fpath = fdialog.GetPath()
469
+
470
+ geo_df.to_csv(fpath,index=False)
471
+
472
+ def OnNPC3DGeometryStats(self,event=None):
473
+ pipeline = self.visFr.pipeline
474
+ npcs = findNPCset(pipeline)
475
+ if npcs is None:
476
+ warn('no valid NPC measurements found, thus no geometry info available...')
477
+ return
478
+ import pandas as pd
479
+ from PYMEcs.misc.matplotlib import boxswarmplot, figuredefaults
480
+ diams = np.asarray(npcs.diam())
481
+ heights = np.asarray(npcs.height())
482
+ geo_df = pd.DataFrame.from_dict(dict(diameter=diams,height=heights))
483
+ figuredefaults(fontsize=12)
484
+ plt.figure()
485
+ from scipy.stats import iqr
486
+ iqrh = iqr(heights)
487
+ sdh = np.std(heights)
488
+ iqrd = iqr(diams)
489
+ sdd = np.std(diams)
490
+ bp = boxswarmplot(geo_df,width=0.35,annotate_medians=True,annotate_means=True,showmeans=True,swarmalpha=0.4,swarmsize=4)
491
+ plt.text(0.0,50,"IQR %.1f\nSD %.1f" % (iqrd,sdd), horizontalalignment='center')
492
+ plt.text(1.0,120,"IQR %.1f\nSD %.1f" % (iqrh,sdh), horizontalalignment='center')
493
+ # res = plt.boxplot([diams,heights],showmeans=True,labels=['diameter','height'])
494
+ plt.title("%d NPCs, mean diam %.0f nm, mean ring spacing %.0f nm" % (heights.size,diams.mean(),heights.mean()), fontsize=11)
495
+ plt.ylim(0,150)
496
+
497
+ def OnNPC3DTemplateFitStats(self,event=None):
498
+ pipeline = self.visFr.pipeline
499
+ npcs = findNPCset(pipeline)
500
+ if npcs is None:
501
+ warn('no valid NPC measurements found, thus no geometry info available...')
502
+ return
503
+ import pandas as pd
504
+ from PYMEcs.misc.matplotlib import boxswarmplot, figuredefaults
505
+ id = [npc.objectID for npc in npcs.npcs] # not used right now
506
+ llperloc = [npc.opt_result.fun/npc.npts.shape[0] for npc in npcs.npcs]
507
+ ll_df = pd.DataFrame.from_dict(dict(llperloc=llperloc))
508
+ figuredefaults(fontsize=12)
509
+ plt.figure()
510
+ bp = boxswarmplot(ll_df,width=0.35,annotate_medians=True,annotate_means=True,showmeans=True,swarmalpha=0.6,swarmsize=5)
511
+ plt.title("NPC neg-log-likelihood per localization")
512
+
513
+ def OnSelectNPCsByMask(self,event=None):
514
+ from PYME.DSView import dsviewer
515
+
516
+ pipeline = self.visFr.pipeline
517
+
518
+ selection = selectWithDialog(dsviewer.openViewers.keys())
519
+ if selection is not None:
520
+ img = dsviewer.openViewers[selection].image
521
+
522
+ if img is None:
523
+ return
524
+
525
+ setNPCsfromImg(pipeline,img)
526
+
527
+ def OnNPCstats(self,event=None):
528
+ pipeline = self.visFr.pipeline
529
+ for prop in ['NPCnlabeled','objectID']:
530
+ if prop not in pipeline.keys():
531
+ warn("property '%s' not found in datasource" % prop)
532
+ return
533
+
534
+ uids,indices,idcounts = np.unique(pipeline['objectID'],return_index=True,return_counts=True)
535
+ uidnz = uids > 0
536
+
537
+ nlabel_uq = pipeline['NPCnlabeled'][indices[uidnz]]
538
+
539
+ ds_mdh = pipeline.selectedDataSource.mdh
540
+ plt.figure()
541
+ nlab, bions, patches = plt.hist(nlabel_uq,bins=-0.5+np.arange(10,dtype='int'))
542
+ plabel, nlabels, perr = npclabel_fit(nlab)
543
+ plt.plot(np.arange(9),nlabels,'-o')
544
+ plt.xlabel('Number of segments labeled')
545
+ plt.ylabel('Frequency')
546
+ title = 'NPC labeling statistics, pl_fit = %.2f+-%.3f' % (plabel,perr)
547
+ if 'NPCAnalysis.EventThreshold' in ds_mdh:
548
+ title += "\nEvent threshold = %d, mode = %s" % (int(ds_mdh['NPCAnalysis.EventThreshold']),
549
+ ds_mdh['NPCAnalysis.RotationAlgorithm'])
550
+ plt.title(title)
551
+ plt.tight_layout()
552
+
553
+ def OnNPCcombinedFuncs(self,event):
554
+ self.OnSelectNPCsByMask()
555
+ self.OnAnalyseNPCsByID()
556
+ self.OnNPCstats()
557
+
558
+ def Plug(visFr):
559
+ """Plugs this module into the gui"""
560
+ NPCcalc(visFr)