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.
- PYMEcs/Acquire/Actions/__init__.py +0 -0
- PYMEcs/Acquire/Actions/custom.py +167 -0
- PYMEcs/Acquire/Hardware/LPthreadedSimple.py +248 -0
- PYMEcs/Acquire/Hardware/LPthreadedSimpleSim.py +246 -0
- PYMEcs/Acquire/Hardware/NikonTiFlaskServer.py +45 -0
- PYMEcs/Acquire/Hardware/NikonTiFlaskServerT.py +59 -0
- PYMEcs/Acquire/Hardware/NikonTiRESTClient.py +73 -0
- PYMEcs/Acquire/Hardware/NikonTiSim.py +35 -0
- PYMEcs/Acquire/Hardware/__init__.py +0 -0
- PYMEcs/Acquire/Hardware/driftTrackGUI.py +329 -0
- PYMEcs/Acquire/Hardware/driftTrackGUI_n.py +472 -0
- PYMEcs/Acquire/Hardware/driftTracking.py +424 -0
- PYMEcs/Acquire/Hardware/driftTracking_n.py +433 -0
- PYMEcs/Acquire/Hardware/fakeCamX.py +15 -0
- PYMEcs/Acquire/Hardware/offsetPiezoRESTCorrelLog.py +38 -0
- PYMEcs/Acquire/__init__.py +0 -0
- PYMEcs/Analysis/MBMcollection.py +552 -0
- PYMEcs/Analysis/MINFLUX.py +280 -0
- PYMEcs/Analysis/MapUtils.py +77 -0
- PYMEcs/Analysis/NPC.py +1176 -0
- PYMEcs/Analysis/Paraflux.py +218 -0
- PYMEcs/Analysis/Simpler.py +81 -0
- PYMEcs/Analysis/Sofi.py +140 -0
- PYMEcs/Analysis/__init__.py +0 -0
- PYMEcs/Analysis/decSofi.py +211 -0
- PYMEcs/Analysis/eventProperties.py +50 -0
- PYMEcs/Analysis/fitDarkTimes.py +569 -0
- PYMEcs/Analysis/objectVolumes.py +20 -0
- PYMEcs/Analysis/offlineTracker.py +130 -0
- PYMEcs/Analysis/stackTracker.py +180 -0
- PYMEcs/Analysis/timeSeries.py +63 -0
- PYMEcs/Analysis/trackFiducials.py +186 -0
- PYMEcs/Analysis/zerocross.py +91 -0
- PYMEcs/IO/MINFLUX.py +851 -0
- PYMEcs/IO/NPC.py +117 -0
- PYMEcs/IO/__init__.py +0 -0
- PYMEcs/IO/darkTimes.py +19 -0
- PYMEcs/IO/picasso.py +219 -0
- PYMEcs/IO/tabular.py +11 -0
- PYMEcs/__init__.py +0 -0
- PYMEcs/experimental/CalcZfactor.py +51 -0
- PYMEcs/experimental/FRC.py +338 -0
- PYMEcs/experimental/ImageJROItools.py +49 -0
- PYMEcs/experimental/MINFLUX.py +1537 -0
- PYMEcs/experimental/NPCcalcLM.py +560 -0
- PYMEcs/experimental/Simpler.py +369 -0
- PYMEcs/experimental/Sofi.py +78 -0
- PYMEcs/experimental/__init__.py +0 -0
- PYMEcs/experimental/binEventProperty.py +187 -0
- PYMEcs/experimental/chaining.py +23 -0
- PYMEcs/experimental/clusterTrack.py +179 -0
- PYMEcs/experimental/combine_maps.py +104 -0
- PYMEcs/experimental/eventProcessing.py +93 -0
- PYMEcs/experimental/fiducials.py +323 -0
- PYMEcs/experimental/fiducialsNew.py +402 -0
- PYMEcs/experimental/mapTools.py +271 -0
- PYMEcs/experimental/meas2DplotDh5view.py +107 -0
- PYMEcs/experimental/mortensen.py +131 -0
- PYMEcs/experimental/ncsDenoise.py +158 -0
- PYMEcs/experimental/onTimes.py +295 -0
- PYMEcs/experimental/procPoints.py +77 -0
- PYMEcs/experimental/pyme2caml.py +73 -0
- PYMEcs/experimental/qPAINT.py +965 -0
- PYMEcs/experimental/randMap.py +188 -0
- PYMEcs/experimental/regExtraCmaps.py +11 -0
- PYMEcs/experimental/selectROIfilterTable.py +72 -0
- PYMEcs/experimental/showErrs.py +51 -0
- PYMEcs/experimental/showErrsDh5view.py +58 -0
- PYMEcs/experimental/showShiftMap.py +56 -0
- PYMEcs/experimental/snrEvents.py +188 -0
- PYMEcs/experimental/specLabeling.py +51 -0
- PYMEcs/experimental/splitRender.py +246 -0
- PYMEcs/experimental/testChannelByName.py +36 -0
- PYMEcs/experimental/timedSpecies.py +28 -0
- PYMEcs/experimental/utils.py +31 -0
- PYMEcs/misc/ExtraCmaps.py +177 -0
- PYMEcs/misc/__init__.py +0 -0
- PYMEcs/misc/configUtils.py +169 -0
- PYMEcs/misc/guiMsgBoxes.py +27 -0
- PYMEcs/misc/mapUtils.py +230 -0
- PYMEcs/misc/matplotlib.py +136 -0
- PYMEcs/misc/rectsFromSVG.py +182 -0
- PYMEcs/misc/shellutils.py +1110 -0
- PYMEcs/misc/utils.py +205 -0
- PYMEcs/misc/versionCheck.py +20 -0
- PYMEcs/misc/zcInfo.py +90 -0
- PYMEcs/pyme_warnings.py +4 -0
- PYMEcs/recipes/__init__.py +0 -0
- PYMEcs/recipes/base.py +75 -0
- PYMEcs/recipes/localisations.py +2380 -0
- PYMEcs/recipes/manipulate_yaml.py +83 -0
- PYMEcs/recipes/output.py +177 -0
- PYMEcs/recipes/processing.py +247 -0
- PYMEcs/recipes/simpler.py +290 -0
- PYMEcs/version.py +2 -0
- pyme_extra-1.0.4.post0.dist-info/METADATA +114 -0
- pyme_extra-1.0.4.post0.dist-info/RECORD +101 -0
- pyme_extra-1.0.4.post0.dist-info/WHEEL +5 -0
- pyme_extra-1.0.4.post0.dist-info/entry_points.txt +3 -0
- pyme_extra-1.0.4.post0.dist-info/licenses/LICENSE +674 -0
- 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)
|