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,83 @@
|
|
|
1
|
+
# a few routines to manipulate recipes in yaml format
|
|
2
|
+
# this is supposed to make programmatic changing of parameters a little easier
|
|
3
|
+
|
|
4
|
+
# note that we work on the python data structure which is a mix of dicts and lists
|
|
5
|
+
# as it comes in from the yaml reader routines
|
|
6
|
+
|
|
7
|
+
# we initially implement a minimal set of routines with the goal to uniquely identify
|
|
8
|
+
# a module in the recipe by module name and the name of inputs and outputs
|
|
9
|
+
# if a unique module was identified then the keyword parameters are used to set values
|
|
10
|
+
# in the identified module entry. Currently, this is simplistic and mostly for scalar values.
|
|
11
|
+
# TODO: For further tweaking we may need to make the modParams class aware of more complex data structures
|
|
12
|
+
# like lists and dicts for certain modules...
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
import yaml
|
|
17
|
+
import warnings
|
|
18
|
+
|
|
19
|
+
def modname(entry):
|
|
20
|
+
return list(entry.keys())[0]
|
|
21
|
+
|
|
22
|
+
def modinputs(entry):
|
|
23
|
+
mname = modname(entry)
|
|
24
|
+
inputnames = [entry[mname][key] for key in entry[mname].keys() if key.startswith('input')]
|
|
25
|
+
return inputnames
|
|
26
|
+
|
|
27
|
+
def modoutputs(entry):
|
|
28
|
+
mname = modname(entry)
|
|
29
|
+
outputnames = [entry[mname][key] for key in entry[mname].keys() if key.startswith('output')]
|
|
30
|
+
return outputnames
|
|
31
|
+
|
|
32
|
+
def modmatch(entry,name,inputDS,outputDS):
|
|
33
|
+
if modname(entry) != name:
|
|
34
|
+
return False
|
|
35
|
+
if inputDS is not None:
|
|
36
|
+
if inputDS not in modinputs(entry):
|
|
37
|
+
return False
|
|
38
|
+
if outputDS is not None:
|
|
39
|
+
if outputDS not in modoutputs(entry):
|
|
40
|
+
return False
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
class modParams(object):
|
|
44
|
+
"""A class to modify parameters of identified module entries in a recipe
|
|
45
|
+
|
|
46
|
+
Typical usage:
|
|
47
|
+
|
|
48
|
+
modified_rec = load_recipe('base_recipe.yaml',
|
|
49
|
+
recmy.modParams('PYMEcs.NNfilter',
|
|
50
|
+
nnMin=10.0, nnMax=30.0),
|
|
51
|
+
recmy.modParams('PYMEcs.DBSCANClustering2',
|
|
52
|
+
inputDS='coalesced_nz',searchRadius=7.0)))
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self,name,inputDS=None,outputDS=None,**kwargs):
|
|
56
|
+
self.name = name
|
|
57
|
+
self.inputDS = inputDS
|
|
58
|
+
self.outputDS = outputDS
|
|
59
|
+
self.paramdict = kwargs
|
|
60
|
+
|
|
61
|
+
def modify_recipe(self,rec,inplace=False):
|
|
62
|
+
if not inplace:
|
|
63
|
+
rec = rec.copy()
|
|
64
|
+
matches = [entry for entry in rec if modmatch(entry,self.name,self.inputDS,self.outputDS)]
|
|
65
|
+
if len(matches) == 0:
|
|
66
|
+
warnings.warn("no matching module entry, ignoring parameters")
|
|
67
|
+
elif len(matches) > 1:
|
|
68
|
+
raise RuntimeError("more than one matched entry, need exactly one match")
|
|
69
|
+
else:
|
|
70
|
+
entry = matches[0]
|
|
71
|
+
for key in self.paramdict:
|
|
72
|
+
entry[self.name][key] = self.paramdict[key]
|
|
73
|
+
return rec
|
|
74
|
+
|
|
75
|
+
def load_recipe(filename, *modpars, return_yamldumped=True):
|
|
76
|
+
with open(filename) as fi:
|
|
77
|
+
rec = yaml.safe_load(fi)
|
|
78
|
+
for mp in modpars:
|
|
79
|
+
rec = mp.modify_recipe(rec)
|
|
80
|
+
if return_yamldumped:
|
|
81
|
+
return yaml.dump(rec)
|
|
82
|
+
else:
|
|
83
|
+
return rec
|
PYMEcs/recipes/output.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from PYME.recipes.base import register_module, ModuleBase, OutputModule
|
|
2
|
+
from PYME.recipes.traits import Input, Output, Float, Enum, CStr, Bool, Int, DictStrStr, File, Button
|
|
3
|
+
#from traitsui.file_dialog import save_file # this call seems to shut down the gui of VisGUI
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
@register_module('CSVOutputFileBrowse2')
|
|
9
|
+
class CSVOutputFileBrowse2(OutputModule):
|
|
10
|
+
"""
|
|
11
|
+
Save tabular data as csv. This module uses a File Browser to set the fileName
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
|
|
16
|
+
inputName : basestring
|
|
17
|
+
the name (in the recipe namespace) of the table to save.
|
|
18
|
+
|
|
19
|
+
fileName : File
|
|
20
|
+
a full path to the file
|
|
21
|
+
|
|
22
|
+
Notes
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
We convert the data to a pandas `DataFrame` and uses the `to_csv`
|
|
26
|
+
method to save.
|
|
27
|
+
|
|
28
|
+
This version of the output module uses the pyface.FileDialog.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
inputName = Input('output')
|
|
32
|
+
fileName = File('out.csv')
|
|
33
|
+
saveAs = Button('Save as...')
|
|
34
|
+
|
|
35
|
+
def save(self, namespace, context={}):
|
|
36
|
+
"""
|
|
37
|
+
Save recipes output(s) to CSV
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
namespace : dict
|
|
42
|
+
The recipe namespace
|
|
43
|
+
context : dict
|
|
44
|
+
Information about the source file to allow pattern substitution to generate the output name. At least
|
|
45
|
+
'basedir' (which is the fully resolved directory name in which the input file resides) and
|
|
46
|
+
'filestub' (which is the filename without any extension) should be resolved.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
out_filename = self.fileName
|
|
54
|
+
v = namespace[self.inputName]
|
|
55
|
+
|
|
56
|
+
if not isinstance(v, pd.DataFrame):
|
|
57
|
+
v = v.toDataFrame()
|
|
58
|
+
|
|
59
|
+
v.to_csv(out_filename)
|
|
60
|
+
|
|
61
|
+
def _saveAs_changed(self):
|
|
62
|
+
""" Handles the user clicking the 'Save as...' button.
|
|
63
|
+
"""
|
|
64
|
+
from pyface.api import FileDialog, OK
|
|
65
|
+
import os
|
|
66
|
+
dirname = os.path.dirname(self.fileName)
|
|
67
|
+
filename = os.path.basename(self.fileName)
|
|
68
|
+
if not dirname:
|
|
69
|
+
dirname = os.getcwd()
|
|
70
|
+
|
|
71
|
+
dlg = FileDialog(action='save as', default_directory=dirname, default_filename=filename)
|
|
72
|
+
if dlg.open() == OK:
|
|
73
|
+
self.fileName = dlg.path
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def default_view(self):
|
|
77
|
+
from traitsui.api import View, Group, Item, HGroup
|
|
78
|
+
from PYME.ui.custom_traits_editors import CBEditor
|
|
79
|
+
|
|
80
|
+
editable = self.class_editable_traits()
|
|
81
|
+
inputs = [tn for tn in editable if tn.startswith('input')]
|
|
82
|
+
return View(
|
|
83
|
+
Group(Item('inputName', editor=CBEditor(choices=self._namespace_keys)),
|
|
84
|
+
HGroup(
|
|
85
|
+
Item('saveAs', show_label=False),
|
|
86
|
+
'_',
|
|
87
|
+
Item('fileName', style='readonly', springy=True)
|
|
88
|
+
)
|
|
89
|
+
), buttons=['OK'])
|
|
90
|
+
|
|
91
|
+
@register_module('CSVOutputFileBrowse')
|
|
92
|
+
class CSVOutputFileBrowse(OutputModule):
|
|
93
|
+
"""
|
|
94
|
+
Save tabular data as csv. This module uses a File Browser to set the fileName
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
|
|
99
|
+
inputName : basestring
|
|
100
|
+
the name (in the recipe namespace) of the table to save.
|
|
101
|
+
|
|
102
|
+
fileName : File
|
|
103
|
+
a full path to the file
|
|
104
|
+
|
|
105
|
+
Notes
|
|
106
|
+
-----
|
|
107
|
+
|
|
108
|
+
We convert the data to a pandas `DataFrame` and uses the `to_csv`
|
|
109
|
+
method to save.
|
|
110
|
+
|
|
111
|
+
This version of the output module uses the wx.FileDialog.
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
inputName = Input('output')
|
|
116
|
+
fileName = File('output.csv')
|
|
117
|
+
saveAs = Button('Save as...')
|
|
118
|
+
|
|
119
|
+
def save(self, namespace, context={}):
|
|
120
|
+
"""
|
|
121
|
+
Save recipes output(s) to CSV
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
namespace : dict
|
|
126
|
+
The recipe namespace
|
|
127
|
+
context : dict
|
|
128
|
+
Information about the source file to allow pattern substitution to generate the output name. At least
|
|
129
|
+
'basedir' (which is the fully resolved directory name in which the input file resides) and
|
|
130
|
+
'filestub' (which is the filename without any extension) should be resolved.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
out_filename = self.fileName
|
|
138
|
+
v = namespace[self.inputName]
|
|
139
|
+
|
|
140
|
+
if not isinstance(v, pd.DataFrame):
|
|
141
|
+
v = v.toDataFrame()
|
|
142
|
+
|
|
143
|
+
v.to_csv(out_filename)
|
|
144
|
+
|
|
145
|
+
def _saveAs_changed(self):
|
|
146
|
+
""" Handles the user clicking the 'Save as...' button.
|
|
147
|
+
"""
|
|
148
|
+
import wx
|
|
149
|
+
import os
|
|
150
|
+
dirname = os.path.dirname(self.fileName)
|
|
151
|
+
filename = os.path.basename(self.fileName)
|
|
152
|
+
if not dirname:
|
|
153
|
+
dirname = os.getcwd()
|
|
154
|
+
dlg = wx.FileDialog(None, "Save as...", dirname, filename, "*.csv",
|
|
155
|
+
wx.SAVE|wx.OVERWRITE_PROMPT)
|
|
156
|
+
result = dlg.ShowModal()
|
|
157
|
+
inFile = dlg.GetPath()
|
|
158
|
+
dlg.Destroy()
|
|
159
|
+
|
|
160
|
+
if result == wx.ID_OK: #Save button was pressed
|
|
161
|
+
self.fileName = inFile
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def default_view(self):
|
|
165
|
+
from traitsui.api import View, Group, Item, HGroup
|
|
166
|
+
from PYME.ui.custom_traits_editors import CBEditor
|
|
167
|
+
|
|
168
|
+
editable = self.class_editable_traits()
|
|
169
|
+
inputs = [tn for tn in editable if tn.startswith('input')]
|
|
170
|
+
return View(
|
|
171
|
+
Group(Item('inputName', editor=CBEditor(choices=self._namespace_keys)),
|
|
172
|
+
HGroup(
|
|
173
|
+
Item('saveAs', show_label=False),
|
|
174
|
+
'_',
|
|
175
|
+
Item('fileName', style='readonly', springy=True)
|
|
176
|
+
)
|
|
177
|
+
), buttons=['OK'])
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
from PYME.recipes.base import ModuleBase, register_module, Filter
|
|
2
|
+
from PYME.recipes.traits import Input, Output, Float, Enum, CStr, Bool, Int, File
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import skimage.filters as skf
|
|
6
|
+
from scipy import ndimage
|
|
7
|
+
|
|
8
|
+
from PYME.IO.image import ImageStack
|
|
9
|
+
from PYME.IO import MetaDataHandler
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@register_module('FlexiThreshold')
|
|
13
|
+
class FlexiThreshold(Filter):
|
|
14
|
+
"""Chose a threshold using a range of available thresholding methods.
|
|
15
|
+
Currently we can chose from: simple, fractional, otsu, isodata
|
|
16
|
+
"""
|
|
17
|
+
method = Enum('simple','fractional','otsu','isodata',
|
|
18
|
+
'li','yen') # newer skimage has minimum, mean and triangle as well
|
|
19
|
+
parameter = Float(0.5)
|
|
20
|
+
clipAt = Float(2e6) # used to be 10 - increase to large value for newer PYME renderings
|
|
21
|
+
|
|
22
|
+
def fractionalThreshold(self, data):
|
|
23
|
+
N, bins = np.histogram(data, bins=5000)
|
|
24
|
+
#calculate bin centres
|
|
25
|
+
bin_mids = (bins[:-1] )
|
|
26
|
+
cN = np.cumsum(N*bin_mids)
|
|
27
|
+
i = np.argmin(abs(cN - cN[-1]*(1-self.parameter)))
|
|
28
|
+
threshold = bins[i]
|
|
29
|
+
return threshold
|
|
30
|
+
|
|
31
|
+
def applyFilter(self, data, chanNum, frNum, im):
|
|
32
|
+
|
|
33
|
+
if self.method == 'fractional':
|
|
34
|
+
threshold = self.fractionalThreshold(np.clip(data,None,self.clipAt))
|
|
35
|
+
elif self.method == 'simple':
|
|
36
|
+
threshold = self.parameter
|
|
37
|
+
else:
|
|
38
|
+
method = getattr(skf,'threshold_%s' % self.method)
|
|
39
|
+
threshold = method(np.clip(data,None,self.clipAt))
|
|
40
|
+
|
|
41
|
+
mask = data > threshold
|
|
42
|
+
return mask
|
|
43
|
+
|
|
44
|
+
def completeMetadata(self, im):
|
|
45
|
+
im.mdh['Processing.ThresholdParameter'] = self.parameter
|
|
46
|
+
im.mdh['Processing.ThresholdMethod'] = self.method
|
|
47
|
+
|
|
48
|
+
@register_module('LabelRange')
|
|
49
|
+
class LabelRange(Filter):
|
|
50
|
+
"""Asigns a unique integer label to each contiguous region in the input mask.
|
|
51
|
+
Throws away all regions which are outside of given number of pixel range.
|
|
52
|
+
Also uses the number of sites from a second input channel to decide if region is retained,
|
|
53
|
+
retaining only those with the number sites in a given range.
|
|
54
|
+
"""
|
|
55
|
+
inputSitesLabeled = Input("sites") # sites and the main input must have the same shape!
|
|
56
|
+
minRegionPixels = Int(10)
|
|
57
|
+
maxRegionPixels = Int(100)
|
|
58
|
+
minSites = Int(4)
|
|
59
|
+
maxSites = Int(6)
|
|
60
|
+
sitesAsMaxima = Bool(False)
|
|
61
|
+
|
|
62
|
+
def filter(self, image, imagesites):
|
|
63
|
+
#from PYME.util.shmarray import shmarray
|
|
64
|
+
#import multiprocessing
|
|
65
|
+
|
|
66
|
+
if self.processFramesIndividually:
|
|
67
|
+
filt_ims = []
|
|
68
|
+
for chanNum in range(image.data.shape[3]):
|
|
69
|
+
filt_ims.append(np.concatenate([np.atleast_3d(self.applyFilter(image.data[:,:,i,chanNum].squeeze().astype('f'), imagesites.data[:,:,i,chanNum].squeeze().astype('f'), chanNum, i, image)) for i in range(image.data.shape[2])], 2))
|
|
70
|
+
else:
|
|
71
|
+
filt_ims = [np.atleast_3d(self.applyFilter(image.data[:,:,:,chanNum].squeeze().astype('f'), imagesites.data[:,:,:,chanNum].squeeze().astype('f'), chanNum, 0, image)) for chanNum in range(image.data.shape[3])]
|
|
72
|
+
|
|
73
|
+
im = ImageStack(filt_ims, titleStub = self.outputName)
|
|
74
|
+
im.mdh.copyEntriesFrom(image.mdh)
|
|
75
|
+
im.mdh['Parent'] = image.filename
|
|
76
|
+
|
|
77
|
+
self.completeMetadata(im)
|
|
78
|
+
|
|
79
|
+
return im
|
|
80
|
+
|
|
81
|
+
def execute(self, namespace):
|
|
82
|
+
namespace[self.outputName] = self.filter(namespace[self.inputName],namespace[self.inputSitesLabeled])
|
|
83
|
+
|
|
84
|
+
def applyFilter(self, data, sites, chanNum, frNum, im):
|
|
85
|
+
|
|
86
|
+
# siteLabels = self.recipe.namespace[self.sitesLabeled]
|
|
87
|
+
|
|
88
|
+
mask = data > 0.5
|
|
89
|
+
labs, nlabs = ndimage.label(mask)
|
|
90
|
+
|
|
91
|
+
rSize = self.minRegionPixels
|
|
92
|
+
rMax = self.maxRegionPixels
|
|
93
|
+
|
|
94
|
+
minSites = self.minSites
|
|
95
|
+
maxSites = self.maxSites
|
|
96
|
+
|
|
97
|
+
m2 = 0*mask
|
|
98
|
+
objs = ndimage.find_objects(labs)
|
|
99
|
+
for i, o in enumerate(objs):
|
|
100
|
+
r = labs[o] == i+1
|
|
101
|
+
#print r.shape
|
|
102
|
+
area = r.sum()
|
|
103
|
+
if (area >= rSize) and (area <= rMax):
|
|
104
|
+
if self.sitesAsMaxima:
|
|
105
|
+
nsites = sites[o][r].sum()
|
|
106
|
+
else:
|
|
107
|
+
nsites = (np.unique(sites[o][r]) > 0).sum() # count the unique labels (excluding label 0 which is background)
|
|
108
|
+
if (nsites >= minSites) and (nsites <= maxSites):
|
|
109
|
+
m2[o] += r
|
|
110
|
+
|
|
111
|
+
labs, nlabs = ndimage.label(m2 > 0)
|
|
112
|
+
|
|
113
|
+
return labs
|
|
114
|
+
|
|
115
|
+
def completeMetadata(self, im):
|
|
116
|
+
im.mdh['Labelling.MinSize'] = self.minRegionPixels
|
|
117
|
+
im.mdh['Labelling.MaxSize'] = self.maxRegionPixels
|
|
118
|
+
im.mdh['Labelling.MinSites'] = self.minSites
|
|
119
|
+
im.mdh['Labelling.MaxSites'] = self.maxSites
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@register_module('LabelByArea')
|
|
123
|
+
class LabelByArea(Filter):
|
|
124
|
+
"""Asigns a unique integer label to each contiguous region in the input mask.
|
|
125
|
+
Optionally throws away all regions which are smaller than a cutoff size.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def applyFilter(self, data, chanNum, frNum, im):
|
|
129
|
+
mask = data > 0.5
|
|
130
|
+
labs, nlabs = ndimage.label(mask)
|
|
131
|
+
|
|
132
|
+
m2 = 0*mask
|
|
133
|
+
objs = ndimage.find_objects(labs)
|
|
134
|
+
for i, o in enumerate(objs):
|
|
135
|
+
r = labs[o] == i+1
|
|
136
|
+
#print r.shape
|
|
137
|
+
area = r.sum()
|
|
138
|
+
m2[o] += r*area
|
|
139
|
+
|
|
140
|
+
return m2
|
|
141
|
+
|
|
142
|
+
def completeMetadata(self, im):
|
|
143
|
+
im.mdh['Labelling.Property'] = 'area'
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
import skimage.measure
|
|
147
|
+
import math
|
|
148
|
+
|
|
149
|
+
@register_module('LabelByRegionProperty')
|
|
150
|
+
class LabelByRegionProperty(Filter):
|
|
151
|
+
"""Asigns a region property to each contiguous region in the input mask.
|
|
152
|
+
Optionally throws away all regions for which property is outside a given range.
|
|
153
|
+
"""
|
|
154
|
+
regionProperty = Enum(['area','circularity','aspectratio'])
|
|
155
|
+
filterByProperty = Bool(False)
|
|
156
|
+
propertyMin = Float(0)
|
|
157
|
+
propertyMax = Float(1e6)
|
|
158
|
+
|
|
159
|
+
def applyFilter(self, data, chanNum, frNum, im):
|
|
160
|
+
mask = data > 0.5
|
|
161
|
+
labs, nlabs = ndimage.label(mask)
|
|
162
|
+
rp = skimage.measure.regionprops(labs,None,cache=True)
|
|
163
|
+
|
|
164
|
+
m2 = np.zeros_like(mask,dtype='float')
|
|
165
|
+
objs = ndimage.find_objects(labs)
|
|
166
|
+
for region in rp:
|
|
167
|
+
oslices = objs[region.label-1]
|
|
168
|
+
r = labs[oslices] == region.label
|
|
169
|
+
#print r.shape
|
|
170
|
+
if self.regionProperty == 'area':
|
|
171
|
+
propValue = region.area
|
|
172
|
+
elif self.regionProperty == 'aspectratio':
|
|
173
|
+
propValue = region.major_axis_length / region.minor_axis_length
|
|
174
|
+
elif self.regionProperty == 'circularity':
|
|
175
|
+
propValue = 4 * math.pi * region.area / (region.perimeter*region.perimeter)
|
|
176
|
+
if self.filterByProperty:
|
|
177
|
+
if (propValue >= self.propertyMin) and (propValue <= self.propertyMax):
|
|
178
|
+
m2[oslices] += r*propValue
|
|
179
|
+
else:
|
|
180
|
+
m2[oslices] += r*propValue
|
|
181
|
+
|
|
182
|
+
return m2
|
|
183
|
+
|
|
184
|
+
def completeMetadata(self, im):
|
|
185
|
+
im.mdh['Labelling.Property'] = self.regionProperty
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
from scipy.signal import fftconvolve
|
|
189
|
+
def circle_kernel(diam=112.0, sigma=10.0, extent_nm=150.0,voxelsize_nm=5.0,eps=15.0):
|
|
190
|
+
x = np.arange(-extent_nm/2.0, extent_nm/2.0+1.0, voxelsize_nm, dtype='f')
|
|
191
|
+
y = x.copy()
|
|
192
|
+
x2d,y2d = np.meshgrid(x,y)
|
|
193
|
+
circ2d = (x2d**2 + y2d**2 -0.25*diam**2 <= eps**2) & (x2d**2 + y2d**2 -0.25*diam**2 >= -eps**2)
|
|
194
|
+
g2d = np.exp(-(x2d**2+y2d**2)/2.0/(sigma**2))
|
|
195
|
+
ckernel = np.clip(fftconvolve(circ2d,g2d,mode='same'),0,None)
|
|
196
|
+
ckernel /= ckernel.sum()
|
|
197
|
+
|
|
198
|
+
return ckernel
|
|
199
|
+
|
|
200
|
+
@register_module('CircleConvolution')
|
|
201
|
+
class CircleConvolution(ModuleBase):
|
|
202
|
+
inputImage = Input('input')
|
|
203
|
+
outputImage = Output('circle_convol')
|
|
204
|
+
outputKernel = Output('circle_kernel')
|
|
205
|
+
|
|
206
|
+
circleDiameter = Float(112.0)
|
|
207
|
+
circleBlurRadius = Float(15.0)
|
|
208
|
+
|
|
209
|
+
def run(self, inputImage):
|
|
210
|
+
|
|
211
|
+
img = inputImage.data_xyztc[:,:,0,0,0].squeeze()
|
|
212
|
+
ckern = circle_kernel(diam=self.circleDiameter,extent_nm=250.0,eps=30.0,
|
|
213
|
+
sigma=self.circleBlurRadius,voxelsize_nm=inputImage.voxelsize_nm.x)
|
|
214
|
+
|
|
215
|
+
conv = np.clip(fftconvolve(img,ckern,mode='same'),0,None)
|
|
216
|
+
|
|
217
|
+
imconvol = ImageStack(conv,mdh=MetaDataHandler.NestedClassMDHandler(inputImage.mdh), titleStub = self.outputImage)
|
|
218
|
+
imconvol.mdh['CircleConvolution.Diameter'] = self.circleDiameter
|
|
219
|
+
imconvol.mdh['CircleConvolution.BlurRadius'] = self.circleBlurRadius
|
|
220
|
+
circleKernel = ImageStack(ckern, titleStub = self.outputKernel)
|
|
221
|
+
circleKernel.mdh['Diameter'] = self.circleDiameter
|
|
222
|
+
circleKernel.mdh['BlurRadius'] = self.circleBlurRadius
|
|
223
|
+
|
|
224
|
+
return {'outputImage' : imconvol, 'outputKernel' : circleKernel}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# split single channel image to two channels for FRC
|
|
228
|
+
# following paper from Riegher et al:
|
|
229
|
+
# Rieger, B., Droste, I., Gerritsma, F., Ten Brink, T. & Stallinga, S.
|
|
230
|
+
# Single image Fourier ring correlation. Opt. Express 32, 21767 (2024).
|
|
231
|
+
@register_module('Split1FRC')
|
|
232
|
+
class CircleConvolution(ModuleBase):
|
|
233
|
+
inputImage = Input('input')
|
|
234
|
+
outputImage = Output('split_1frc')
|
|
235
|
+
|
|
236
|
+
splitProb = Float(0.5)
|
|
237
|
+
|
|
238
|
+
def run(self, inputImage):
|
|
239
|
+
im0 = inputImage.data_xyztc[:,:,0,0,0]
|
|
240
|
+
rng = np.random.default_rng()
|
|
241
|
+
im0_1 = rng.binomial(im0,self.splitProb)
|
|
242
|
+
im0_2 = im0 - im0_1
|
|
243
|
+
im_1frc = np.stack([im0_1,im0_2],axis=2).astype(inputImage.data_xyztc.dtype)
|
|
244
|
+
imstack1frc = ImageStack(im_1frc[:,:,None,None,:],mdh=MetaDataHandler.NestedClassMDHandler(inputImage.mdh),titleStub = self.outputImage)
|
|
245
|
+
|
|
246
|
+
return imstack1frc
|
|
247
|
+
|