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,965 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
|
|
3
|
+
import wx
|
|
4
|
+
import numpy as np
|
|
5
|
+
import sys
|
|
6
|
+
from scipy import ndimage
|
|
7
|
+
from PYMEcs.misc.guiMsgBoxes import Warn
|
|
8
|
+
from PYME.recipes import tablefilters
|
|
9
|
+
from PYMEcs.Analysis import fitDarkTimes
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
logger = logging.getLogger(__file__)
|
|
13
|
+
|
|
14
|
+
from traits.api import HasTraits, Str, Int, CStr, List, Enum, Float, Bool
|
|
15
|
+
from traitsui.api import View, Item, Group
|
|
16
|
+
from traitsui.menu import OKButton, CancelButton, OKCancelButtons
|
|
17
|
+
|
|
18
|
+
class KeyChoice(HasTraits):
|
|
19
|
+
clist = List([])
|
|
20
|
+
Key = Enum(values='clist')
|
|
21
|
+
|
|
22
|
+
traits_view = View(
|
|
23
|
+
'Key',
|
|
24
|
+
title = 'Select Measure',
|
|
25
|
+
resizable = True,
|
|
26
|
+
buttons = OKCancelButtons
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def add_keys(self,chans):
|
|
30
|
+
for chan in chans:
|
|
31
|
+
if chan not in self.clist:
|
|
32
|
+
self.clist.append(chan)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class myCChoice(HasTraits):
|
|
36
|
+
clist = List([])
|
|
37
|
+
RatioChannel1 = Enum(values='clist')
|
|
38
|
+
RatioChannel2 = Enum(values='clist')
|
|
39
|
+
Channel1Calibration = Float(1.0)
|
|
40
|
+
Channel2Calibration = Float(1.0)
|
|
41
|
+
|
|
42
|
+
traits_view = View(Group(Item(name = 'RatioChannel1'),
|
|
43
|
+
Item(name = 'Channel1Calibration'),
|
|
44
|
+
Item('_'),
|
|
45
|
+
Item(name = 'RatioChannel2'),
|
|
46
|
+
Item(name = 'Channel2Calibration'),
|
|
47
|
+
label = 'Select Channels and Calibration',
|
|
48
|
+
show_border = True),
|
|
49
|
+
buttons = OKCancelButtons)
|
|
50
|
+
|
|
51
|
+
cal_view = View(Group(Item(name = 'Channel1Calibration'),
|
|
52
|
+
Item('_'),
|
|
53
|
+
Item(name = 'Channel2Calibration'),
|
|
54
|
+
label = 'Select Calibration',
|
|
55
|
+
show_border = True),
|
|
56
|
+
buttons = OKCancelButtons)
|
|
57
|
+
|
|
58
|
+
one_view = View(Group(Item(name = 'RatioChannel1'),
|
|
59
|
+
Item(name = 'Channel1Calibration'),
|
|
60
|
+
label = 'Select Channel and Calibration',
|
|
61
|
+
show_border = True),
|
|
62
|
+
buttons = OKCancelButtons)
|
|
63
|
+
|
|
64
|
+
def add_channels(self,chans):
|
|
65
|
+
for chan in chans:
|
|
66
|
+
if chan not in self.clist:
|
|
67
|
+
self.clist.append(chan)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TimedSpecies(HasTraits):
|
|
71
|
+
Species1 = CStr()
|
|
72
|
+
Species1FromTime = Float()
|
|
73
|
+
Species1ToTime = Float()
|
|
74
|
+
|
|
75
|
+
Species2 = CStr()
|
|
76
|
+
Species2FromTime = Float()
|
|
77
|
+
Species2ToTime = Float()
|
|
78
|
+
|
|
79
|
+
Species3 = CStr()
|
|
80
|
+
Species3FromTime = Float()
|
|
81
|
+
Species3ToTime = Float()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
traits_view = View(Group(Item(name = 'Species1'),
|
|
85
|
+
Item(name = 'Species1FromTime'),
|
|
86
|
+
Item(name = 'Species1ToTime'),
|
|
87
|
+
Item('_'),
|
|
88
|
+
Item(name = 'Species2'),
|
|
89
|
+
Item(name = 'Species2FromTime'),
|
|
90
|
+
Item(name = 'Species2ToTime'),
|
|
91
|
+
Item('_'),
|
|
92
|
+
Item(name = 'Species3'),
|
|
93
|
+
Item(name = 'Species3FromTime'),
|
|
94
|
+
Item(name = 'Species3ToTime'),
|
|
95
|
+
label = 'Specify Timed Species',
|
|
96
|
+
show_border = True),
|
|
97
|
+
buttons = OKCancelButtons)
|
|
98
|
+
|
|
99
|
+
def getSpeciesDescriptor(self):
|
|
100
|
+
speclist = {}
|
|
101
|
+
if self.Species1: # empty strings will be ignored
|
|
102
|
+
speclist[self.Species1] = (self.Species1FromTime,
|
|
103
|
+
self.Species1ToTime)
|
|
104
|
+
if self.Species2: # empty strings will be ignored
|
|
105
|
+
speclist[self.Species2] = (self.Species2FromTime,
|
|
106
|
+
self.Species2ToTime)
|
|
107
|
+
if self.Species3: # empty strings will be ignored
|
|
108
|
+
speclist[self.Species3] = (self.Species3FromTime,
|
|
109
|
+
self.Species3ToTime)
|
|
110
|
+
|
|
111
|
+
logger.info('speclist is ' + repr(speclist))
|
|
112
|
+
return speclist
|
|
113
|
+
|
|
114
|
+
def uniqueByID(ids,column):
|
|
115
|
+
uids, idx = np.unique(ids, return_index=True)
|
|
116
|
+
ucol = column[idx]
|
|
117
|
+
valid = uids > 0
|
|
118
|
+
return uids[valid], ucol[valid]
|
|
119
|
+
|
|
120
|
+
def selectWithDialog(choices, message='select image from list', caption='Selection'):
|
|
121
|
+
dlg = wx.SingleChoiceDialog(None, message, caption, list(choices), wx.CHOICEDLG_STYLE)
|
|
122
|
+
if dlg.ShowModal() == wx.ID_OK:
|
|
123
|
+
item = dlg.GetStringSelection()
|
|
124
|
+
else:
|
|
125
|
+
item = None
|
|
126
|
+
dlg.Destroy()
|
|
127
|
+
return item
|
|
128
|
+
|
|
129
|
+
def unique_name(stem,names):
|
|
130
|
+
if stem not in names:
|
|
131
|
+
return stem
|
|
132
|
+
for i in range(1,11):
|
|
133
|
+
stem2 = "%s%d" % (stem,i)
|
|
134
|
+
if stem2 not in names:
|
|
135
|
+
return stem2
|
|
136
|
+
|
|
137
|
+
return stem2 # here we just give up and accept a duplicate name
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class QPCalc:
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
def __init__(self, visFr):
|
|
145
|
+
self.visFr = visFr
|
|
146
|
+
self.pipeline = visFr.pipeline
|
|
147
|
+
self.qpMeasurements = {}
|
|
148
|
+
self.fitSettings = fitDarkTimes.FitSettings()
|
|
149
|
+
self.useTau = 2 # we change to using the proper histogram for fitting
|
|
150
|
+
if self.useTau == 1:
|
|
151
|
+
self.tausrc = {
|
|
152
|
+
'tau' : 'tau1',
|
|
153
|
+
'tauerr' : 'tau1err',
|
|
154
|
+
'chisq' : 'chisqr1'}
|
|
155
|
+
elif self.useTau == 2:
|
|
156
|
+
self.tausrc = {
|
|
157
|
+
'tau' : 'tau2',
|
|
158
|
+
'tauerr' : 'tau2err',
|
|
159
|
+
'chisq' : 'chisqr2'}
|
|
160
|
+
else:
|
|
161
|
+
raise RuntimeError("Invalid useTau mode %d (must be 1 or 2)" % self.useTau)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
visFr.AddMenuItem('Experimental>qPAINT', "From Image - Set driftpars",self.OnSetDriftPars)
|
|
165
|
+
visFr.AddMenuItem('Experimental>qPAINT', "From Image - Set ROI clipping",self.OnClipFromImage)
|
|
166
|
+
visFr.AddMenuItem('Experimental>qPAINT', "From Image - Set objectIDs",self.OnGetIDsfromImage)
|
|
167
|
+
visFr.AddMenuItem('Experimental>qPAINT', "From Image - Get Areas by ID",self.OnAreaFromLabels)
|
|
168
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Qindex - Measure object ID dark times",self.OnMeasureTau2)
|
|
169
|
+
visFr.AddMenuItem('Experimental>qPAINT', "All in 1 go: select Image, set drift, IDs, measure qindex, areas",
|
|
170
|
+
self.OnSelectImgAndProcess)
|
|
171
|
+
|
|
172
|
+
visFr.AddMenuItem('Experimental>qPAINT', itemType='separator') #--------------------------
|
|
173
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Multicolour - set timed species by Dialog",self.OnTimedSpecies)
|
|
174
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Multicolour - set timed species from image",self.OnTimedSpeciesFromImage)
|
|
175
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Multicolour - qIndex by channel",self.OnChannelMeasureTau)
|
|
176
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Multicolour - Merge Channel Measures", self.OnMergeChannelMeasures)
|
|
177
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Multicolour - Calculate Channel Ratios", self.OnChannelMeasurementRatios)
|
|
178
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Multicolour - calibrate channel qIndices (Pseudo test function)",self.OnChannelCalibrate)
|
|
179
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Multicolour - in 1 go: select Image, set drift, IDs, areas, species, qIndex & merge & ratio",
|
|
180
|
+
self.OnSelectImgAndProcessMulticol)
|
|
181
|
+
|
|
182
|
+
visFr.AddMenuItem('Experimental>qPAINT', itemType='separator') #--------------------------
|
|
183
|
+
visFr.AddMenuItem('Experimental>qPAINT', 'Points based: Set objectIDs by DBSCAN clumping', self.OnClumpObjects,
|
|
184
|
+
helpText='Calculate objectID using DBSCAN algorithm')
|
|
185
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Points based: Measure object ID volumes (area if 2D) by convex hull",self.OnMeasureVol)
|
|
186
|
+
|
|
187
|
+
visFr.AddMenuItem('Experimental>qPAINT', itemType='separator') #--------------------------
|
|
188
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Darktime distribution of selected events\tCtrl+T",self.OnDarkT)
|
|
189
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Select Fit Settings",self.OnFitSettings)
|
|
190
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Calibrate a qIndex (or any column)",self.OnQindexCalibrate)
|
|
191
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Ratio two qIndices (or any two columns)",self.OnQindexRatio)
|
|
192
|
+
|
|
193
|
+
visFr.AddMenuItem('Experimental>qPAINT', itemType='separator') #--------------------------
|
|
194
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Plot histogram of one data column",self.OnGeneralHist)
|
|
195
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Scatter plot by ID",self.OnScatterByID)
|
|
196
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Image of qPAINT measure from label image",self.OnLabelLookupByID)
|
|
197
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Load Measurements",self.OnLoadMeasurements)
|
|
198
|
+
visFr.AddMenuItem('Experimental>qPAINT', "Save Measurements",self.OnSaveMeasurements)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def OnClumpObjects(self, event=None):
|
|
202
|
+
"""
|
|
203
|
+
Runs sklearn DBSCAN clustering algorithm on pipeline filtered results using the GUI defined in the DBSCAN
|
|
204
|
+
recipe module.
|
|
205
|
+
|
|
206
|
+
Args are user defined through GUI
|
|
207
|
+
searchRadius: search radius for clustering
|
|
208
|
+
minClumpSize: number of points within eps required for a given point to be considered a core point
|
|
209
|
+
|
|
210
|
+
This version is generally used to identify clumps identifying fiduciaries and therefore the
|
|
211
|
+
default searchRadius is set fairly generous.
|
|
212
|
+
"""
|
|
213
|
+
from PYMEcs.recipes import localisations
|
|
214
|
+
|
|
215
|
+
clumper = localisations.DBSCANClustering(minClumpSize = 20, searchRadius = 20.0)
|
|
216
|
+
if clumper.configure_traits(kind='modal'):
|
|
217
|
+
namespace = {clumper.inputName: self.pipeline}
|
|
218
|
+
clumper.execute(namespace)
|
|
219
|
+
|
|
220
|
+
self.pipeline.addColumn('objectID', namespace[clumper.outputName]['dbscanClumpID'])
|
|
221
|
+
|
|
222
|
+
def OnGetIDsfromImage(self, event, img=None):
|
|
223
|
+
from PYME.DSView import dsviewer
|
|
224
|
+
|
|
225
|
+
pipeline = self.pipeline
|
|
226
|
+
|
|
227
|
+
if img is None:
|
|
228
|
+
selection = selectWithDialog(dsviewer.openViewers.keys())
|
|
229
|
+
if selection is not None:
|
|
230
|
+
img = dsviewer.openViewers[selection].image
|
|
231
|
+
|
|
232
|
+
if img is not None:
|
|
233
|
+
with_ids = unique_name('with_ids',pipeline.dataSources.keys())
|
|
234
|
+
valid_ids = unique_name('valid_ids',pipeline.dataSources.keys())
|
|
235
|
+
recipe = pipeline.recipe
|
|
236
|
+
mapp = tablefilters.Mapping(recipe,inputName=pipeline.selectedDataSourceKey,
|
|
237
|
+
outputName=with_ids)
|
|
238
|
+
recipe.add_module(mapp)
|
|
239
|
+
recipe.execute()
|
|
240
|
+
|
|
241
|
+
withIDs = recipe.namespace[with_ids]
|
|
242
|
+
|
|
243
|
+
pixX = np.round((pipeline['x'] - img.imgBounds.x0 )/img.pixelSize).astype('i')
|
|
244
|
+
pixY = np.round((pipeline['y'] - img.imgBounds.y0 )/img.pixelSize).astype('i')
|
|
245
|
+
|
|
246
|
+
ind = (pixX < img.data.shape[0])*(pixY < img.data.shape[1])*(pixX >= 0)*(pixY >= 0)
|
|
247
|
+
|
|
248
|
+
ids = np.zeros_like(pixX)
|
|
249
|
+
#assume there is only one channel
|
|
250
|
+
ids[ind] = img.data[:,:,:,0].squeeze()[pixX[ind], pixY[ind]].astype('i')
|
|
251
|
+
|
|
252
|
+
numPerObject, b = np.histogram(ids, np.arange(ids.max() + 1.5) + .5)
|
|
253
|
+
|
|
254
|
+
withIDs.addColumn('objectID', ids)
|
|
255
|
+
withIDs.addColumn('NEvents', numPerObject[ids-1])
|
|
256
|
+
|
|
257
|
+
recipe.add_module(tablefilters.FilterTable(recipe,inputName=with_ids, outputName=valid_ids,
|
|
258
|
+
filters={'objectID' : [.5, sys.maxsize]}))
|
|
259
|
+
recipe.execute()
|
|
260
|
+
|
|
261
|
+
pipeline.selectDataSource(valid_ids)
|
|
262
|
+
pipeline.Rebuild()
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def OnCopyDS(self, event=None):
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
"""
|
|
269
|
+
from PYMEcs.recipes.localisations import CopyMapped
|
|
270
|
+
recipe = self.pipeline.recipe
|
|
271
|
+
CM = CopyMapped(recipe, inputName=self.pipeline.selectedDataSourceKey,
|
|
272
|
+
outputName='%s-copy' % self.pipeline.selectedDataSourceKey)
|
|
273
|
+
if CM.configure_traits(kind='modal'):
|
|
274
|
+
recipe.add_module(CM)
|
|
275
|
+
recipe.execute()
|
|
276
|
+
self.pipeline.selectDataSource(CM.outputName)
|
|
277
|
+
|
|
278
|
+
self.visFr.RefreshView()
|
|
279
|
+
self.visFr.CreateFoldPanel()
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def OnSetDriftPars(self, event, img=None):
|
|
283
|
+
from PYME.DSView import dsviewer
|
|
284
|
+
|
|
285
|
+
if img is None:
|
|
286
|
+
selection = selectWithDialog(dsviewer.openViewers.keys())
|
|
287
|
+
if selection is not None:
|
|
288
|
+
img = dsviewer.openViewers[selection].image
|
|
289
|
+
|
|
290
|
+
if img is not None:
|
|
291
|
+
dpn = self.visFr.driftPane
|
|
292
|
+
dpn.tXExpr.SetValue(img.mdh['DriftCorrection.ExprX'])
|
|
293
|
+
dpn.tYExpr.SetValue(img.mdh['DriftCorrection.ExprY'])
|
|
294
|
+
dpn.tZExpr.SetValue(img.mdh['DriftCorrection.ExprZ'])
|
|
295
|
+
dpn.OnDriftExprChange(None)
|
|
296
|
+
destp = dpn.dp.driftCorrParams
|
|
297
|
+
srcp = img.mdh['DriftCorrection.Parameters']
|
|
298
|
+
for key in destp.keys():
|
|
299
|
+
if key.startswith(('a','b')):
|
|
300
|
+
destp[key] = srcp[key]
|
|
301
|
+
dpn.OnDriftApply(None)
|
|
302
|
+
dpn.OnDriftExprChange(None)
|
|
303
|
+
|
|
304
|
+
def OnTimedSpecies(self, event):
|
|
305
|
+
# a reworked version that uses a simpler TraitsUI interface
|
|
306
|
+
timedSpecies = TimedSpecies()
|
|
307
|
+
if timedSpecies.configure_traits(kind='modal'):
|
|
308
|
+
from PYME.LMVis import renderers
|
|
309
|
+
speclist = timedSpecies.getSpeciesDescriptor()
|
|
310
|
+
if len(speclist.keys())>0:
|
|
311
|
+
# not sure if this should be Source.TimedSpecies or just TimedSpecies
|
|
312
|
+
#renderers.renderMetadataProviders.append(lambda mdh:
|
|
313
|
+
# mdh.setEntry('Source.TimedSpecies', speclist))
|
|
314
|
+
|
|
315
|
+
pipeline = self.visFr.pipeline
|
|
316
|
+
pipeline.mdh.setEntry('TimedSpecies', speclist)
|
|
317
|
+
if pipeline.selectedDataSource is not None:
|
|
318
|
+
pipeline.selectedDataSource.setMapping('ColourNorm', '1.0 + 0*t')
|
|
319
|
+
for species in speclist.keys():
|
|
320
|
+
pipeline.selectedDataSource.setMapping('p_%s' % species,
|
|
321
|
+
'(t>= %d)*(t<%d)' % speclist[species])
|
|
322
|
+
pipeline.Rebuild()
|
|
323
|
+
# self.visFr.RegenFilter()
|
|
324
|
+
self.visFr.CreateFoldPanel()
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def OnClipFromImage(self, event, img=None):
|
|
328
|
+
from PYME.DSView import dsviewer
|
|
329
|
+
|
|
330
|
+
if img is None:
|
|
331
|
+
selection = selectWithDialog(list(dsviewer.openViewers.keys()))
|
|
332
|
+
if selection is not None:
|
|
333
|
+
img = dsviewer.openViewers[selection].image
|
|
334
|
+
|
|
335
|
+
if img is not None:
|
|
336
|
+
if img.mdh.getOrDefault('Filter.Keys',None) is None:
|
|
337
|
+
logger.debug('no Filter.Keys in image metadata')
|
|
338
|
+
return
|
|
339
|
+
try:
|
|
340
|
+
self.pipeline.filterKeys['x'] = img.mdh['Filter.Keys']['x']
|
|
341
|
+
except:
|
|
342
|
+
logger.debug('cannot read or set filterKeys x-component')
|
|
343
|
+
return
|
|
344
|
+
try:
|
|
345
|
+
self.pipeline.filterKeys['y'] = img.mdh['Filter.Keys']['y']
|
|
346
|
+
except:
|
|
347
|
+
logger.debug('cannot read or set filterKeys y-component')
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
self.pipeline.Rebuild()
|
|
351
|
+
#self.visFr.RegenFilter()
|
|
352
|
+
self.visFr.CreateFoldPanel()
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def OnTimedSpeciesFromImage(self, event, img=None):
|
|
356
|
+
from PYME.DSView import dsviewer
|
|
357
|
+
|
|
358
|
+
if img is None:
|
|
359
|
+
selection = selectWithDialog(dsviewer.openViewers.keys())
|
|
360
|
+
if selection is not None:
|
|
361
|
+
img = dsviewer.openViewers[selection].image
|
|
362
|
+
|
|
363
|
+
if img is not None:
|
|
364
|
+
# somewhat weired condition as sometimes TimeSpecies becomes None in the root namespce
|
|
365
|
+
if img.mdh.getOrDefault('TimedSpecies',None) is not None:
|
|
366
|
+
logger.debug('setting timed species from root level')
|
|
367
|
+
timedSpecies = img.mdh['TimedSpecies']
|
|
368
|
+
elif 'Source.TimedSpecies' in img.mdh.keys():
|
|
369
|
+
logger.debug('setting timed species from source level')
|
|
370
|
+
timedSpecies = img.mdh['Source.TimedSpecies']
|
|
371
|
+
logger.debug('time species is %s' % repr(timedSpecies))
|
|
372
|
+
else:
|
|
373
|
+
return
|
|
374
|
+
pipeline = self.visFr.pipeline
|
|
375
|
+
if pipeline.selectedDataSource is not None:
|
|
376
|
+
pipeline.selectedDataSource.setMapping('ColourNorm', '1.0 + 0*t')
|
|
377
|
+
# there are a couple different formats for timedSpecies, so check which one it is
|
|
378
|
+
try:
|
|
379
|
+
specs = timedSpecies.keys()
|
|
380
|
+
isdict = True
|
|
381
|
+
except AttributeError:
|
|
382
|
+
isdict = False
|
|
383
|
+
if isdict:
|
|
384
|
+
for species in timedSpecies.keys():
|
|
385
|
+
pipeline.selectedDataSource.setMapping('p_%s' % species,
|
|
386
|
+
'(t>= %d)*(t<%d)' % timedSpecies[species])
|
|
387
|
+
else:
|
|
388
|
+
for entry in timedSpecies:
|
|
389
|
+
pipeline.selectedDataSource.setMapping('p_%s' % entry['name'],
|
|
390
|
+
'(t>= %d)*(t<%d)' % (entry['t_start'],entry['t_end']))
|
|
391
|
+
# FIXME: (1) append if exists, (2) assigning to mdh ok?
|
|
392
|
+
pipeline.selectedDataSource.mdh['TimedSpecies'] = timedSpecies
|
|
393
|
+
self.visFr.pipeline.Rebuild()
|
|
394
|
+
self.visFr.CreateFoldPanel()
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def OnAreaFromLabels(self, event, img=None):
|
|
398
|
+
from PYME.DSView import dsviewer
|
|
399
|
+
|
|
400
|
+
if img is None:
|
|
401
|
+
selection = selectWithDialog(dsviewer.openViewers.keys())
|
|
402
|
+
if selection is not None:
|
|
403
|
+
img = dsviewer.openViewers[selection].image
|
|
404
|
+
|
|
405
|
+
if img is not None:
|
|
406
|
+
from PYME.recipes.measurement import Measure2D
|
|
407
|
+
|
|
408
|
+
# execute the measure2D module as a mini-recipe
|
|
409
|
+
MeasureIt = Measure2D(measureContour=False)
|
|
410
|
+
namespace = {MeasureIt.inputLabels: img, MeasureIt.inputIntensity: img}
|
|
411
|
+
MeasureIt.execute(namespace)
|
|
412
|
+
# save the measurements for further use
|
|
413
|
+
self.qpMeasurements['area'] = namespace[MeasureIt.outputName]
|
|
414
|
+
|
|
415
|
+
meas = self.qpMeasurements['area']
|
|
416
|
+
# currently the scale of areas is in 1000 nm^2
|
|
417
|
+
areas = meas['area'] * float(img.mdh['voxelsize.x'])*float(img.mdh['voxelsize.y'])*1e3
|
|
418
|
+
|
|
419
|
+
# add the area info to the qPAINT measures already available
|
|
420
|
+
for chan in (self.pipeline.colourFilter.getColourChans() + ['Everything']):
|
|
421
|
+
if chan in self.qpMeasurements.keys():
|
|
422
|
+
# here we copy the area over, other 2D measures could be similarly copied
|
|
423
|
+
self.qpMeasurements[chan].addNewColumnByID(meas['label'], 'area', areas)
|
|
424
|
+
|
|
425
|
+
# make a new objAreas column for the pipeline
|
|
426
|
+
# 1st we are making a lookup table
|
|
427
|
+
# for areas by label index in abyl
|
|
428
|
+
labels = meas['label'].astype('int')
|
|
429
|
+
abyl = np.zeros(labels.max()+1) # make it long enough to hold all labels present
|
|
430
|
+
abyl[labels] = areas
|
|
431
|
+
objIDs = self.pipeline['objectID'].astype('int')
|
|
432
|
+
objAreas = np.zeros_like(objIDs,dtype='float')
|
|
433
|
+
# now look up the area values by addressing into our lookup table abyl
|
|
434
|
+
objAreas[objIDs>0] = abyl[objIDs[objIDs>0]]
|
|
435
|
+
# enter the new column into the pipeline
|
|
436
|
+
self.pipeline.addColumn('objArea',objAreas)
|
|
437
|
+
|
|
438
|
+
def OnMeasureTau2(self, event):
|
|
439
|
+
|
|
440
|
+
pipeline = self.pipeline
|
|
441
|
+
#idCol = 'objectID'
|
|
442
|
+
idCol = self.fitSettings.IDcolumn
|
|
443
|
+
measure = fitDarkTimes.measureObjectsByID2(pipeline, idname=idCol, settings=self.fitSettings)
|
|
444
|
+
self.qpMeasurements[pipeline.selectedDataSourceKey] = {'Everything' : measure}
|
|
445
|
+
|
|
446
|
+
newcols = fitDarkTimes.retrieveMeasuresForIDs2(measure,pipeline[idCol])
|
|
447
|
+
|
|
448
|
+
qp_measured = unique_name('qp_measured',pipeline.dataSources.keys())
|
|
449
|
+
recipe = pipeline.recipe
|
|
450
|
+
mapp = tablefilters.Mapping(recipe,inputName=pipeline.selectedDataSourceKey,
|
|
451
|
+
outputName=qp_measured)
|
|
452
|
+
recipe.add_module(mapp)
|
|
453
|
+
recipe.execute()
|
|
454
|
+
|
|
455
|
+
qpm = recipe.namespace[qp_measured]
|
|
456
|
+
|
|
457
|
+
for sourceCol in newcols.keys():
|
|
458
|
+
qpm.addColumn("qp_%s" % sourceCol,newcols[sourceCol])
|
|
459
|
+
|
|
460
|
+
def OnMeasureTau(self, event):
|
|
461
|
+
|
|
462
|
+
# chans = self.pipeline.colourFilter.getColourChans()
|
|
463
|
+
|
|
464
|
+
ids = np.unique(self.pipeline['objectID'].astype('int'))
|
|
465
|
+
idsnz = ids[ids > 0]
|
|
466
|
+
pipeline = self.pipeline
|
|
467
|
+
|
|
468
|
+
measure = fitDarkTimes.measureObjectsByID(self.pipeline, set(idsnz))
|
|
469
|
+
self.qpMeasurements['Everything'] = measure
|
|
470
|
+
|
|
471
|
+
# mapping from measurements column name to new pipeline column name to add
|
|
472
|
+
colmapNames = {
|
|
473
|
+
'tau1':'taudark',
|
|
474
|
+
'NDarktimes':'NDarktimes',
|
|
475
|
+
'qindex1':'qIndex',
|
|
476
|
+
'tau1err':'taudark_error',
|
|
477
|
+
'chisqr1':'taudark_chisq2'}
|
|
478
|
+
|
|
479
|
+
newcols = fitDarkTimes.retrieveMeasuresForIDs(measure,pipeline['objectID'],
|
|
480
|
+
columns=colmapNames.keys())
|
|
481
|
+
for sourceCol in colmapNames.keys():
|
|
482
|
+
pipeline.addColumn(colmapNames[sourceCol],newcols[sourceCol])
|
|
483
|
+
|
|
484
|
+
# pipeline.Rebuild()
|
|
485
|
+
|
|
486
|
+
def OnChannelMeasureTau(self, event, chan=None, mapMeasuresToEvents=True):
|
|
487
|
+
|
|
488
|
+
if chan is None:
|
|
489
|
+
chan = selectWithDialog(self.pipeline.colourFilter.getColourChans(), message='select channel')
|
|
490
|
+
if chan is None:
|
|
491
|
+
return
|
|
492
|
+
|
|
493
|
+
pipeline = self.pipeline
|
|
494
|
+
dispColor = self.pipeline.colourFilter.currentColour
|
|
495
|
+
|
|
496
|
+
# make sure we get our IDs when everything is present -
|
|
497
|
+
# otherwise colourfiltering can create an issue with some IDs not present
|
|
498
|
+
# in some channels
|
|
499
|
+
pipeline.colourFilter.setColour('Everything')
|
|
500
|
+
ids = np.unique(pipeline['objectID'].astype('int'))
|
|
501
|
+
idsnz = ids[ids > 0]
|
|
502
|
+
|
|
503
|
+
pipeline.colourFilter.setColour(chan)
|
|
504
|
+
|
|
505
|
+
tausrc = self.tausrc
|
|
506
|
+
self.qpMeasurements[chan] = fitDarkTimes.measureObjectsByID(pipeline, set(idsnz), sigDefocused=165.0)
|
|
507
|
+
qmc = self.qpMeasurements[chan]
|
|
508
|
+
fitDarkTimes.makeRatio(qmc, 'qIndex', 100.0, qmc[tausrc['tau']])
|
|
509
|
+
fitDarkTimes.makeRatio(qmc, 'NTauDiff', np.abs(qmc['tau1']-qmc['tau2']), qmc['tau2'])
|
|
510
|
+
|
|
511
|
+
if mapMeasuresToEvents:
|
|
512
|
+
# switch back to all channels
|
|
513
|
+
pipeline.colourFilter.setColour('Everything')
|
|
514
|
+
colmapNames = {
|
|
515
|
+
tausrc['tau'] : 'taudark_%s' % chan,
|
|
516
|
+
tausrc['tauerr'] : 'taudark_error_%s' % chan,
|
|
517
|
+
tausrc['chisq'] : 'taudark_chisq2_%s' % chan,
|
|
518
|
+
'NDarktimes' : 'NDarktimes_%s' % chan,
|
|
519
|
+
'qIndex' : 'qIndex_%s' % chan,
|
|
520
|
+
'NEvents' : 'NEvents_%s' % chan,
|
|
521
|
+
'NDefocusedFrac' : 'NDefocusedFrac_%s' % chan,
|
|
522
|
+
'NTauDiff' : 'NTauDiff_%s' % chan
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
newcols = fitDarkTimes.retrieveMeasuresForIDs(self.qpMeasurements[chan],pipeline['objectID'],
|
|
526
|
+
columns=colmapNames.keys())
|
|
527
|
+
for sourceCol in colmapNames.keys():
|
|
528
|
+
pipeline.addColumn(colmapNames[sourceCol],newcols[sourceCol])
|
|
529
|
+
|
|
530
|
+
# restore original display settings
|
|
531
|
+
pipeline.colourFilter.setColour(dispColor)
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def OnMergeChannelMeasures(self, event):
|
|
535
|
+
if len(self.pipeline.colourFilter.getColourChans()) > 0:
|
|
536
|
+
channels = self.pipeline.colourFilter.getColourChans()
|
|
537
|
+
if np.all([chan in self.qpMeasurements.keys() for chan in channels]):
|
|
538
|
+
mergedMeas = fitDarkTimes.mergeChannelMeasurements(channels,[self.qpMeasurements[chan] for chan in channels])
|
|
539
|
+
self.qpMeasurements['Everything'] = mergedMeas
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def OnChannelMeasurementRatios(self, event=None, channels=None, cals=None):
|
|
543
|
+
from PYME.recipes.traits import HasTraits, Enum, Float
|
|
544
|
+
|
|
545
|
+
if not len(self.pipeline.colourFilter.getColourChans()) > 0:
|
|
546
|
+
return
|
|
547
|
+
|
|
548
|
+
if channels is None:
|
|
549
|
+
chans = self.pipeline.colourFilter.getColourChans()
|
|
550
|
+
cChoice = myCChoice()
|
|
551
|
+
cChoice.add_channels(chans)
|
|
552
|
+
if cChoice.configure_traits(kind='modal'):
|
|
553
|
+
channels = (cChoice.Channel1, cChoice.Channel2)
|
|
554
|
+
cals = (cChoice.Channel1Calibration,cChoice.Channel2Calibration)
|
|
555
|
+
|
|
556
|
+
# here if cancel from configure_traits
|
|
557
|
+
if channels is None:
|
|
558
|
+
return
|
|
559
|
+
|
|
560
|
+
if ('Everything' in self.qpMeasurements):
|
|
561
|
+
fitDarkTimes.mergedMeasurementsRatios(self.qpMeasurements['Everything'],
|
|
562
|
+
channels[0], channels[1], cals[0], cals[1])
|
|
563
|
+
|
|
564
|
+
def chanName(key,chan):
|
|
565
|
+
return '%s_%s' % (key,chan)
|
|
566
|
+
|
|
567
|
+
pipeline = self.pipeline
|
|
568
|
+
dispColor = pipeline.colourFilter.currentColour
|
|
569
|
+
pipeline.colourFilter.setColour('Everything')
|
|
570
|
+
|
|
571
|
+
tausrc = self.tausrc
|
|
572
|
+
colmapNames = {
|
|
573
|
+
tausrc['tau']: 'taudark',
|
|
574
|
+
'NDarktimes': 'NDarktimes',
|
|
575
|
+
'qIndex': 'qIndex',
|
|
576
|
+
'qIndexC': 'qIndexC',
|
|
577
|
+
tausrc['tauerr']: 'taudark',
|
|
578
|
+
tausrc['chisq']: 'taudark_chisq2',
|
|
579
|
+
'qDensity': 'qDensity',
|
|
580
|
+
'qDensityC': 'qDensityC'}
|
|
581
|
+
|
|
582
|
+
for chan in channels:
|
|
583
|
+
cmapChan = { chanName(key,chan) : chanName(value,chan) for key, value in colmapNames.items()}
|
|
584
|
+
newcols = fitDarkTimes.retrieveMeasuresForIDs(self.qpMeasurements['Everything'],pipeline['objectID'],
|
|
585
|
+
columns=cmapChan.keys())
|
|
586
|
+
for sourceCol in cmapChan.keys():
|
|
587
|
+
if cmapChan[sourceCol] not in pipeline.keys():
|
|
588
|
+
pipeline.addColumn(cmapChan[sourceCol],newcols[sourceCol])
|
|
589
|
+
|
|
590
|
+
for ratioName in ['qRatio','qRatioC']:
|
|
591
|
+
ratiokey = '%s_%svs%s' % (ratioName,channels[0],channels[1])
|
|
592
|
+
newcol = fitDarkTimes.retrieveMeasuresForIDs(self.qpMeasurements['Everything'],pipeline['objectID'],
|
|
593
|
+
columns=[ratiokey])
|
|
594
|
+
pipeline.addColumn(ratiokey, newcol[ratiokey])
|
|
595
|
+
|
|
596
|
+
meas = self.qpMeasurements['Everything']
|
|
597
|
+
chisq_chan1 = meas[chanName(self.tausrc['chisq'],channels[0])]
|
|
598
|
+
chisq_chan2 = meas[chanName(self.tausrc['chisq'],channels[0])]
|
|
599
|
+
ratioChisq = 'qRatio_chisq2'
|
|
600
|
+
fitDarkTimes.makeSum(meas,ratioChisq,chisq_chan1,chisq_chan2)
|
|
601
|
+
newcol = fitDarkTimes.retrieveMeasuresForIDs(self.qpMeasurements['Everything'],pipeline['objectID'],
|
|
602
|
+
columns=[ratioChisq])
|
|
603
|
+
pipeline.addColumn(ratioChisq, newcol[ratioChisq])
|
|
604
|
+
|
|
605
|
+
# restore original display settings
|
|
606
|
+
pipeline.colourFilter.setColour(dispColor)
|
|
607
|
+
|
|
608
|
+
def OnChannelCalibrate(self, event, channels=None):
|
|
609
|
+
# currently just an interface test function
|
|
610
|
+
|
|
611
|
+
chans = ['ryrtest','jphtest']
|
|
612
|
+
if channels is None:
|
|
613
|
+
cChoice = myCChoice()
|
|
614
|
+
cChoice.add_channels(chans)
|
|
615
|
+
if cChoice.configure_traits(view='one_view', kind='modal'):
|
|
616
|
+
channels = (cChoice.RatioChannel1, cChoice.RatioChannel2)
|
|
617
|
+
cals = (cChoice.Channel1Calibration,cChoice.Channel2Calibration)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def OnSelectImgAndProcess(self, event):
|
|
621
|
+
from PYME.DSView import dsviewer
|
|
622
|
+
selection = selectWithDialog(dsviewer.openViewers.keys())
|
|
623
|
+
if selection is not None:
|
|
624
|
+
img = dsviewer.openViewers[selection].image
|
|
625
|
+
self.OnSetDriftPars(None,img=img)
|
|
626
|
+
self.OnGetIDsfromImage(None,img=img)
|
|
627
|
+
self.OnMeasureTau(None)
|
|
628
|
+
# areas last so that qpMeasures get the area info
|
|
629
|
+
self.OnAreaFromLabels(None,img=img)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def OnLabelLookupByID(self, event):
|
|
633
|
+
from PYME.DSView import dsviewer, ViewIm3D
|
|
634
|
+
import PYME.IO.image as im
|
|
635
|
+
|
|
636
|
+
if ('Everything' in self.qpMeasurements):
|
|
637
|
+
meas = self.qpMeasurements['Everything']
|
|
638
|
+
selection = selectWithDialog(dsviewer.openViewers.keys())
|
|
639
|
+
if selection is None:
|
|
640
|
+
return
|
|
641
|
+
keyChoice = KeyChoice()
|
|
642
|
+
keyChoice.add_keys(sorted(meas.keys()))
|
|
643
|
+
if not keyChoice.configure_traits(kind='modal'):
|
|
644
|
+
return
|
|
645
|
+
|
|
646
|
+
labelimg = dsviewer.openViewers[selection].image
|
|
647
|
+
labels = labelimg.data[:,:,:].squeeze()
|
|
648
|
+
measures = meas.lookupByID(labels,keyChoice.Key)
|
|
649
|
+
newimg = im.ImageStack(measures, titleStub = 'Measure %s' % (keyChoice.Key))
|
|
650
|
+
newimg.mdh.copyEntriesFrom(labelimg.mdh)
|
|
651
|
+
newimg.mdh['Parent'] = labelimg.filename
|
|
652
|
+
newimg.mdh['Processing.qpMeasure'] = keyChoice.Key
|
|
653
|
+
|
|
654
|
+
ViewIm3D(newimg, mode='visGUI', title='Measure %s' % (keyChoice.Key),
|
|
655
|
+
glCanvas=self.visFr.glCanvas, parent=self.visFr)
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def OnSelectImgAndProcessMulticol(self, event):
|
|
659
|
+
from PYME.DSView import dsviewer
|
|
660
|
+
|
|
661
|
+
selection = selectWithDialog(dsviewer.openViewers.keys())
|
|
662
|
+
if selection is None:
|
|
663
|
+
return
|
|
664
|
+
|
|
665
|
+
img = dsviewer.openViewers[selection].image
|
|
666
|
+
self.OnTimedSpeciesFromImage(None,img=img)
|
|
667
|
+
|
|
668
|
+
# get channel ratio info
|
|
669
|
+
if len(self.pipeline.colourFilter.getColourChans()) > 0:
|
|
670
|
+
chans = self.pipeline.colourFilter.getColourChans()
|
|
671
|
+
cChoice = myCChoice()
|
|
672
|
+
cChoice.add_channels(chans)
|
|
673
|
+
if cChoice.configure_traits(kind='modal'):
|
|
674
|
+
channels = (cChoice.RatioChannel1, cChoice.RatioChannel2)
|
|
675
|
+
cals = (cChoice.Channel1Calibration,cChoice.Channel2Calibration)
|
|
676
|
+
else:
|
|
677
|
+
channels = None
|
|
678
|
+
cals = None
|
|
679
|
+
|
|
680
|
+
prog = wx.ProgressDialog("Process Multicolour Data", "Setting Drift...", 100,
|
|
681
|
+
style=wx.PD_ELAPSED_TIME | wx.PD_AUTO_HIDE)
|
|
682
|
+
prog.Update(15)
|
|
683
|
+
self.OnSetDriftPars(None,img=img)
|
|
684
|
+
|
|
685
|
+
prog.Update(40,"Setting IDs...")
|
|
686
|
+
self.OnGetIDsfromImage(None,img=img)
|
|
687
|
+
|
|
688
|
+
prog.Update(50,"Calculating qIndices...")
|
|
689
|
+
for chan in self.pipeline.colourFilter.getColourChans():
|
|
690
|
+
self.OnChannelMeasureTau(None,chan=chan)
|
|
691
|
+
|
|
692
|
+
prog.Update(85,"Calculating Areas and Ratios...")
|
|
693
|
+
# areas last so that qpMeasures get the area info
|
|
694
|
+
self.OnAreaFromLabels(None,img=img)
|
|
695
|
+
self.OnMergeChannelMeasures(None)
|
|
696
|
+
|
|
697
|
+
prog.Update(95,"Nearly Done")
|
|
698
|
+
if channels is not None:
|
|
699
|
+
self.OnChannelMeasurementRatios(None,channels=channels,cals=cals)
|
|
700
|
+
prog.Update(100)
|
|
701
|
+
prog.Destroy()
|
|
702
|
+
|
|
703
|
+
def OnMeasureVol(self, event):
|
|
704
|
+
from PYMEcs.recipes import localisations
|
|
705
|
+
# fixme: this is currently 2D only but allows straightforward extension to 3D
|
|
706
|
+
VolMeasurer = localisations.ObjectVolume()
|
|
707
|
+
if VolMeasurer.configure_traits(kind='modal'):
|
|
708
|
+
# we call this with the pipeline to allow filtering etc
|
|
709
|
+
namespace = {VolMeasurer.inputName: self.pipeline}
|
|
710
|
+
VolMeasurer.execute(namespace)
|
|
711
|
+
# FIXME: scaling factor is correct for 2D only
|
|
712
|
+
self.pipeline.addColumn('volume', namespace[VolMeasurer.outputName]['volume']/1e3) # area in 1000 nm^2
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
def OnGeneralHist(self, event):
|
|
716
|
+
from PYMEcs.recipes import localisations
|
|
717
|
+
from PYME.recipes.base import ModuleCollection
|
|
718
|
+
|
|
719
|
+
rec = ModuleCollection()
|
|
720
|
+
|
|
721
|
+
HistByID = localisations.HistByID(rec)
|
|
722
|
+
rec.namespace = {HistByID.inputName: self.pipeline}
|
|
723
|
+
if HistByID.configure_traits(kind='modal'):
|
|
724
|
+
# we call this with the pipeline to allow filtering etc
|
|
725
|
+
HistByID.execute(rec.namespace)
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def OnQindexCalibrate(self, event):
|
|
729
|
+
from PYMEcs.recipes import localisations
|
|
730
|
+
from PYME.recipes.base import ModuleCollection
|
|
731
|
+
|
|
732
|
+
rec = ModuleCollection()
|
|
733
|
+
QIScaler = localisations.QindexScale(rec)
|
|
734
|
+
|
|
735
|
+
# we call this with the pipeline to allow filtering etc
|
|
736
|
+
rec.namespace = {QIScaler.inputName: self.pipeline}
|
|
737
|
+
if QIScaler.configure_traits(kind='modal'):
|
|
738
|
+
QIScaler.execute(rec.namespace)
|
|
739
|
+
self.pipeline.addColumn(QIScaler.newKey, rec.namespace[QIScaler.outputName][QIScaler.newKey])
|
|
740
|
+
|
|
741
|
+
def OnQindexRatio(self, event):
|
|
742
|
+
from PYMEcs.recipes import localisations
|
|
743
|
+
from PYME.recipes.base import ModuleCollection
|
|
744
|
+
|
|
745
|
+
rec = ModuleCollection()
|
|
746
|
+
QIRatio = localisations.QindexRatio(rec)
|
|
747
|
+
|
|
748
|
+
# we call this with the pipeline to allow filtering etc
|
|
749
|
+
rec.namespace = {QIRatio.inputName: self.pipeline}
|
|
750
|
+
if QIRatio.configure_traits(kind='modal'):
|
|
751
|
+
QIRatio.execute(rec.namespace)
|
|
752
|
+
self.pipeline.addColumn(QIRatio.qIndexRatio, rec.namespace[QIRatio.outputName][QIRatio.qIndexRatio])
|
|
753
|
+
|
|
754
|
+
def OnScatterByID(self, event):
|
|
755
|
+
from PYMEcs.recipes import localisations
|
|
756
|
+
from PYME.recipes.base import ModuleCollection
|
|
757
|
+
|
|
758
|
+
rec = ModuleCollection()
|
|
759
|
+
ScatterbyID = localisations.ScatterbyID(rec)
|
|
760
|
+
rec.namespace = {ScatterbyID.inputName: self.pipeline}
|
|
761
|
+
if ScatterbyID.configure_traits(kind='modal'):
|
|
762
|
+
# we call this with the pipeline to allow filtering etc
|
|
763
|
+
ScatterbyID.execute(rec.namespace)
|
|
764
|
+
|
|
765
|
+
def OnSaveMeasurements(self,event):
|
|
766
|
+
from PYME.recipes import runRecipe
|
|
767
|
+
from PYMEcs.IO.darkTimes import saveDarkTimesCSV
|
|
768
|
+
import os.path
|
|
769
|
+
import yaml
|
|
770
|
+
|
|
771
|
+
if len(self.qpMeasurements.keys()) > 0:
|
|
772
|
+
filename = wx.FileSelector('Save all measurements (select basename)...',
|
|
773
|
+
wildcard="CSV files (*.csv)|*.csv|Excell files (*.xlsx)|*.xlsx|HDF5 files (*.hdf)|*.hdf",
|
|
774
|
+
flags = wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
|
|
775
|
+
|
|
776
|
+
if not filename == '':
|
|
777
|
+
base, ext = os.path.splitext(filename)
|
|
778
|
+
for ds in self.qpMeasurements.keys():
|
|
779
|
+
for chan in self.qpMeasurements[ds]:
|
|
780
|
+
qpm = self.qpMeasurements[ds][chan]
|
|
781
|
+
runRecipe.saveOutput(qpm['measures'], base + '_' + ds + '_' + chan + ext)
|
|
782
|
+
saveDarkTimesCSV(qpm['measures']['objectID'],qpm['darkTimes'],
|
|
783
|
+
base + '_' + ds + '_' + chan + '_' + 'dts.csv')
|
|
784
|
+
saveDarkTimesCSV(qpm['measures']['objectID'],qpm['times'],
|
|
785
|
+
base + '_' + ds + '_' + chan + '_' + 'times.csv')
|
|
786
|
+
with open(base + '_' + ds + '_' + chan + '_' + 'state.yaml', 'w') as stream:
|
|
787
|
+
yaml.dump(qpm['state'], stream)
|
|
788
|
+
|
|
789
|
+
def OnLoadMeasurements(self,event):
|
|
790
|
+
import PYMEcs.IO.tabular as tb
|
|
791
|
+
import os.path
|
|
792
|
+
|
|
793
|
+
filename = wx.FileSelector('Load measurements (select basename)...',
|
|
794
|
+
wildcard="CSV files (*.csv)|*.csv",
|
|
795
|
+
flags = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
|
796
|
+
|
|
797
|
+
if not filename == '':
|
|
798
|
+
self.qpMeasurements['Everything'] = tb.tabularFromCSV(filename)
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def OnFitSettings(self,event):
|
|
802
|
+
self.fitSettings.configure_traits(kind='modal')
|
|
803
|
+
|
|
804
|
+
def OnDarkT(self,event):
|
|
805
|
+
try:
|
|
806
|
+
from StringIO import StringIO ## for Python 2
|
|
807
|
+
except ImportError:
|
|
808
|
+
from io import StringIO ## for Python 3
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
visFr = self.visFr
|
|
812
|
+
pipeline = visFr.pipeline
|
|
813
|
+
mdh = pipeline.mdh
|
|
814
|
+
|
|
815
|
+
NTMIN = 5
|
|
816
|
+
maxPts = 1e5
|
|
817
|
+
|
|
818
|
+
## STEP 1: Extract times of relevant events, preprocess as needed and get darktimes from that (dtg)
|
|
819
|
+
|
|
820
|
+
tc, dtg, nevents, nevents_corrected, usingClumpIndex, usingTminTmax = \
|
|
821
|
+
fitDarkTimes.extractEventTimes(pipeline,
|
|
822
|
+
useTminTmax = (self.fitSettings.coalescedProcessing == 'useTminTmaxIfAvailable'),
|
|
823
|
+
return_modes=True)
|
|
824
|
+
|
|
825
|
+
## STEP 2: from the extracted times get the actual darktimes
|
|
826
|
+
|
|
827
|
+
# determine darktime from gaps and reject zeros (no real gaps)
|
|
828
|
+
#dts = tc[1:]-tc[0:-1]-1
|
|
829
|
+
#dtg = dts[dts>0]
|
|
830
|
+
nts = dtg.size
|
|
831
|
+
|
|
832
|
+
## STEP 3: fit the dark time distribution
|
|
833
|
+
|
|
834
|
+
if nts > NTMIN:
|
|
835
|
+
# now make a cumulative histogram from these
|
|
836
|
+
cumux,cumuy = fitDarkTimes.cumuhist(dtg)
|
|
837
|
+
binctrs,hc,binctrsg,hcg = fitDarkTimes.cumuhistBinnedLog(dtg,dlog=0.05,return_good=True)
|
|
838
|
+
binctrsCoarse,hcCoarse,binctrsgCoarse,hcgCoarse, hCoarse = fitDarkTimes.cumuhistBinnedLog(dtg,dlog=0.2,return_hist=True,return_good=True)
|
|
839
|
+
|
|
840
|
+
# fit theoretical distributions
|
|
841
|
+
from scipy.optimize import curve_fit
|
|
842
|
+
|
|
843
|
+
try:
|
|
844
|
+
tau1, tau1err, chisqr, tau1est = fitDarkTimes.fitTaus(binctrs,hc,return_tau1est=True)
|
|
845
|
+
logger.info("fitTaus: tau1 %f, tau1err %f, chisq %.1g, tau1est %f" % (tau1, tau1err, chisqr, tau1est))
|
|
846
|
+
except RuntimeError:
|
|
847
|
+
logger.warn("fitting tau value from histogram failed")
|
|
848
|
+
tau1ok = False
|
|
849
|
+
else:
|
|
850
|
+
tau1ok = True
|
|
851
|
+
|
|
852
|
+
try:
|
|
853
|
+
tau1c, tau1errc, chisqrc, tau1estc = fitDarkTimes.fitTaus(cumux,cumuy,return_tau1est=True)
|
|
854
|
+
logger.info("fitTaus: tau1 %f, tau1err %f, chisq %.1g, tau1est %f" % (tau1c, tau1errc, chisqrc, tau1estc))
|
|
855
|
+
except RuntimeError:
|
|
856
|
+
logger.warn("fitting tau value from cumulative dist failed")
|
|
857
|
+
tau1cok = False
|
|
858
|
+
else:
|
|
859
|
+
tau1cok = True
|
|
860
|
+
|
|
861
|
+
try:
|
|
862
|
+
tau1m, tau2m, atau1m, tau1errm, tau2errm, atau1errm, chisqrm = fitDarkTimes.fitTaus(binctrs,hc,
|
|
863
|
+
fitTau2=not self.fitSettings.Tau2Constant,
|
|
864
|
+
tau2const=self.fitSettings.Tau2FixedValue)
|
|
865
|
+
logger.info("fitTaus: tau1 %f, tau2 %f, atau1 %f, tau1err %f, tau2err %f, atau1err %f, chisq %.1g" %
|
|
866
|
+
(tau1m, tau2m, atau1m, tau1errm, tau2errm, atau1errm, chisqrm))
|
|
867
|
+
except RuntimeError:
|
|
868
|
+
logger.warn("fitting two tau values failed")
|
|
869
|
+
tau1mok = False
|
|
870
|
+
else:
|
|
871
|
+
tau1mok = True
|
|
872
|
+
|
|
873
|
+
## STEP 4: plotting and reporting
|
|
874
|
+
|
|
875
|
+
import matplotlib.pyplot as plt
|
|
876
|
+
|
|
877
|
+
def safepltshow():
|
|
878
|
+
try:
|
|
879
|
+
plt.show()
|
|
880
|
+
except RuntimeError:
|
|
881
|
+
pass
|
|
882
|
+
|
|
883
|
+
# make a string with info
|
|
884
|
+
outstr = StringIO()
|
|
885
|
+
|
|
886
|
+
if usingClumpIndex:
|
|
887
|
+
if usingTminTmax:
|
|
888
|
+
print("events: %d (raw clumpSize: %d), dark times: %d (using clumpIndices + Tmin & Tmax)"
|
|
889
|
+
% (nevents_corrected,nevents,nts),file=outstr)
|
|
890
|
+
else:
|
|
891
|
+
print("events: %d (from clumpSize), dark times: %d (using clumpIndices)"
|
|
892
|
+
% (nevents,nts),file=outstr)
|
|
893
|
+
else:
|
|
894
|
+
print("events: %d, dark times: %d" % (nevents,nts),file=outstr)
|
|
895
|
+
|
|
896
|
+
print("darktime: %.1f+-%d (%.1f+-%d) frames - chisqr %.2g (%.2g)"
|
|
897
|
+
% (tau1c,tau1errc,
|
|
898
|
+
tau1,tau1err,
|
|
899
|
+
chisqrc,chisqr),file=outstr)
|
|
900
|
+
if tau1mok:
|
|
901
|
+
print("darktime: tau1 %.1f+-%d, tau2 %.1f+-%d"
|
|
902
|
+
% (tau1m, tau1errm, tau2m, tau2errm),file=outstr)
|
|
903
|
+
if tau1mok:
|
|
904
|
+
print("amplitudes: (a %.2f+-%.2f, b %.2f) - chisqr %.2g"
|
|
905
|
+
% (atau1m,atau1errm, 1-atau1m,chisqrm),file=outstr)
|
|
906
|
+
print("darktime: starting estimates: %.1f (%.1f)" % (tau1estc,tau1est),file=outstr)
|
|
907
|
+
print("qunits: %.2f (%.2f)" % (100.0/tau1c, 100.0/tau1),file=outstr)
|
|
908
|
+
|
|
909
|
+
labelstr = outstr.getvalue()
|
|
910
|
+
|
|
911
|
+
# plot data and fitted curves
|
|
912
|
+
plt.figure()
|
|
913
|
+
plt.subplot(211)
|
|
914
|
+
plt.plot(cumux,cumuy,'o')
|
|
915
|
+
if tau1cok: plt.plot(cumux,fitDarkTimes.cumuexpfit(cumux,tau1c))
|
|
916
|
+
|
|
917
|
+
plt.plot(binctrs,hc,'o')
|
|
918
|
+
if tau1ok: plt.plot(binctrs,fitDarkTimes.cumuexpfit(binctrs,tau1))
|
|
919
|
+
|
|
920
|
+
if tau1mok: plt.plot(binctrs,fitDarkTimes.cumumultiexpfit(binctrs,tau1m,tau2m,atau1m),'--')
|
|
921
|
+
|
|
922
|
+
plt.ylim(-0.2,1.2)
|
|
923
|
+
plt.subplot(212)
|
|
924
|
+
plt.semilogx(cumux,cumuy,'o')
|
|
925
|
+
if tau1cok: plt.semilogx(cumux,fitDarkTimes.cumuexpfit(cumux,tau1c))
|
|
926
|
+
plt.semilogx(binctrs,hc,'o')
|
|
927
|
+
if tau1ok: plt.semilogx(binctrs,fitDarkTimes.cumuexpfit(binctrs,tau1))
|
|
928
|
+
|
|
929
|
+
if tau1mok: plt.semilogx(binctrs,fitDarkTimes.cumumultiexpfit(binctrs,tau1m,tau2m,atau1m),'--')
|
|
930
|
+
|
|
931
|
+
plt.ylim(-0.2,1.2)
|
|
932
|
+
safepltshow()
|
|
933
|
+
|
|
934
|
+
plt.annotate(labelstr, xy=(0.5, 0.1), xycoords='axes fraction',
|
|
935
|
+
fontsize=10)
|
|
936
|
+
|
|
937
|
+
plt.figure()
|
|
938
|
+
plt.subplot(211)
|
|
939
|
+
if True:
|
|
940
|
+
plt.semilogx(cumux,cumuy,'o',color='darkblue')
|
|
941
|
+
if tau1ok: plt.semilogx(binctrs,fitDarkTimes.cumuexpfit(binctrs,tau1))
|
|
942
|
+
if tau1mok: plt.semilogx(binctrs,fitDarkTimes.cumumultiexpfit(binctrs,tau1m,tau2m,atau1m),'--')
|
|
943
|
+
plt.ylim(-0.2,1.2)
|
|
944
|
+
plt.annotate(labelstr, xy=(0.5, 0.1), xycoords='axes fraction',
|
|
945
|
+
fontsize=10)
|
|
946
|
+
else:
|
|
947
|
+
plt.semilogx(binctrsCoarse, hCoarse, 'o')
|
|
948
|
+
|
|
949
|
+
plt.subplot(212)
|
|
950
|
+
plt.semilogx(binctrs,hc,'go')
|
|
951
|
+
plt.semilogx(binctrsg,hcg,'bo')
|
|
952
|
+
if tau1ok: plt.semilogx(binctrs,fitDarkTimes.cumuexpfit(binctrs,tau1))
|
|
953
|
+
if tau1mok: plt.semilogx(binctrs,fitDarkTimes.cumumultiexpfit(binctrs,tau1m,tau2m,atau1m),'--',color='orange')
|
|
954
|
+
|
|
955
|
+
plt.ylim(-0.2,1.2)
|
|
956
|
+
safepltshow()
|
|
957
|
+
|
|
958
|
+
else:
|
|
959
|
+
Warn(None, 'not enough data points, only found %d dark times (need at least %d)' % (nts,NTMIN), 'Error')
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
def Plug(visFr):
|
|
963
|
+
"""Plugs this module into the gui"""
|
|
964
|
+
visFr.qPAINTCalc = QPCalc(visFr)
|
|
965
|
+
|