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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. PYMEcs/Acquire/Actions/__init__.py +0 -0
  2. PYMEcs/Acquire/Actions/custom.py +167 -0
  3. PYMEcs/Acquire/Hardware/LPthreadedSimple.py +248 -0
  4. PYMEcs/Acquire/Hardware/LPthreadedSimpleSim.py +246 -0
  5. PYMEcs/Acquire/Hardware/NikonTiFlaskServer.py +45 -0
  6. PYMEcs/Acquire/Hardware/NikonTiFlaskServerT.py +59 -0
  7. PYMEcs/Acquire/Hardware/NikonTiRESTClient.py +73 -0
  8. PYMEcs/Acquire/Hardware/NikonTiSim.py +35 -0
  9. PYMEcs/Acquire/Hardware/__init__.py +0 -0
  10. PYMEcs/Acquire/Hardware/driftTrackGUI.py +329 -0
  11. PYMEcs/Acquire/Hardware/driftTrackGUI_n.py +472 -0
  12. PYMEcs/Acquire/Hardware/driftTracking.py +424 -0
  13. PYMEcs/Acquire/Hardware/driftTracking_n.py +433 -0
  14. PYMEcs/Acquire/Hardware/fakeCamX.py +15 -0
  15. PYMEcs/Acquire/Hardware/offsetPiezoRESTCorrelLog.py +38 -0
  16. PYMEcs/Acquire/__init__.py +0 -0
  17. PYMEcs/Analysis/MBMcollection.py +552 -0
  18. PYMEcs/Analysis/MINFLUX.py +280 -0
  19. PYMEcs/Analysis/MapUtils.py +77 -0
  20. PYMEcs/Analysis/NPC.py +1176 -0
  21. PYMEcs/Analysis/Paraflux.py +218 -0
  22. PYMEcs/Analysis/Simpler.py +81 -0
  23. PYMEcs/Analysis/Sofi.py +140 -0
  24. PYMEcs/Analysis/__init__.py +0 -0
  25. PYMEcs/Analysis/decSofi.py +211 -0
  26. PYMEcs/Analysis/eventProperties.py +50 -0
  27. PYMEcs/Analysis/fitDarkTimes.py +569 -0
  28. PYMEcs/Analysis/objectVolumes.py +20 -0
  29. PYMEcs/Analysis/offlineTracker.py +130 -0
  30. PYMEcs/Analysis/stackTracker.py +180 -0
  31. PYMEcs/Analysis/timeSeries.py +63 -0
  32. PYMEcs/Analysis/trackFiducials.py +186 -0
  33. PYMEcs/Analysis/zerocross.py +91 -0
  34. PYMEcs/IO/MINFLUX.py +851 -0
  35. PYMEcs/IO/NPC.py +117 -0
  36. PYMEcs/IO/__init__.py +0 -0
  37. PYMEcs/IO/darkTimes.py +19 -0
  38. PYMEcs/IO/picasso.py +219 -0
  39. PYMEcs/IO/tabular.py +11 -0
  40. PYMEcs/__init__.py +0 -0
  41. PYMEcs/experimental/CalcZfactor.py +51 -0
  42. PYMEcs/experimental/FRC.py +338 -0
  43. PYMEcs/experimental/ImageJROItools.py +49 -0
  44. PYMEcs/experimental/MINFLUX.py +1537 -0
  45. PYMEcs/experimental/NPCcalcLM.py +560 -0
  46. PYMEcs/experimental/Simpler.py +369 -0
  47. PYMEcs/experimental/Sofi.py +78 -0
  48. PYMEcs/experimental/__init__.py +0 -0
  49. PYMEcs/experimental/binEventProperty.py +187 -0
  50. PYMEcs/experimental/chaining.py +23 -0
  51. PYMEcs/experimental/clusterTrack.py +179 -0
  52. PYMEcs/experimental/combine_maps.py +104 -0
  53. PYMEcs/experimental/eventProcessing.py +93 -0
  54. PYMEcs/experimental/fiducials.py +323 -0
  55. PYMEcs/experimental/fiducialsNew.py +402 -0
  56. PYMEcs/experimental/mapTools.py +271 -0
  57. PYMEcs/experimental/meas2DplotDh5view.py +107 -0
  58. PYMEcs/experimental/mortensen.py +131 -0
  59. PYMEcs/experimental/ncsDenoise.py +158 -0
  60. PYMEcs/experimental/onTimes.py +295 -0
  61. PYMEcs/experimental/procPoints.py +77 -0
  62. PYMEcs/experimental/pyme2caml.py +73 -0
  63. PYMEcs/experimental/qPAINT.py +965 -0
  64. PYMEcs/experimental/randMap.py +188 -0
  65. PYMEcs/experimental/regExtraCmaps.py +11 -0
  66. PYMEcs/experimental/selectROIfilterTable.py +72 -0
  67. PYMEcs/experimental/showErrs.py +51 -0
  68. PYMEcs/experimental/showErrsDh5view.py +58 -0
  69. PYMEcs/experimental/showShiftMap.py +56 -0
  70. PYMEcs/experimental/snrEvents.py +188 -0
  71. PYMEcs/experimental/specLabeling.py +51 -0
  72. PYMEcs/experimental/splitRender.py +246 -0
  73. PYMEcs/experimental/testChannelByName.py +36 -0
  74. PYMEcs/experimental/timedSpecies.py +28 -0
  75. PYMEcs/experimental/utils.py +31 -0
  76. PYMEcs/misc/ExtraCmaps.py +177 -0
  77. PYMEcs/misc/__init__.py +0 -0
  78. PYMEcs/misc/configUtils.py +169 -0
  79. PYMEcs/misc/guiMsgBoxes.py +27 -0
  80. PYMEcs/misc/mapUtils.py +230 -0
  81. PYMEcs/misc/matplotlib.py +136 -0
  82. PYMEcs/misc/rectsFromSVG.py +182 -0
  83. PYMEcs/misc/shellutils.py +1110 -0
  84. PYMEcs/misc/utils.py +205 -0
  85. PYMEcs/misc/versionCheck.py +20 -0
  86. PYMEcs/misc/zcInfo.py +90 -0
  87. PYMEcs/pyme_warnings.py +4 -0
  88. PYMEcs/recipes/__init__.py +0 -0
  89. PYMEcs/recipes/base.py +75 -0
  90. PYMEcs/recipes/localisations.py +2380 -0
  91. PYMEcs/recipes/manipulate_yaml.py +83 -0
  92. PYMEcs/recipes/output.py +177 -0
  93. PYMEcs/recipes/processing.py +247 -0
  94. PYMEcs/recipes/simpler.py +290 -0
  95. PYMEcs/version.py +2 -0
  96. pyme_extra-1.0.4.post0.dist-info/METADATA +114 -0
  97. pyme_extra-1.0.4.post0.dist-info/RECORD +101 -0
  98. pyme_extra-1.0.4.post0.dist-info/WHEEL +5 -0
  99. pyme_extra-1.0.4.post0.dist-info/entry_points.txt +3 -0
  100. pyme_extra-1.0.4.post0.dist-info/licenses/LICENSE +674 -0
  101. pyme_extra-1.0.4.post0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,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
@@ -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
+