21
21
import logging
22
22
logger = logging .getLogger (__name__ )
23
23
from maingui import int_validator , nat_validator
24
+ from fitCurve import fit
24
25
25
26
#### #### #### ####
26
27
@@ -76,15 +77,42 @@ def create_rect_mask(self, image_shape=None):
76
77
else : logger .warning ('ROI tried to create invalid mask.\n ' +
77
78
'shape %s, x %s, y %s, w %s, h %s' % (self .s , self .x , self .y , self .w , self .h ))
78
79
79
- def resize (self , xc , yc , width , height ):
80
+ def create_gauss_mask (self , im = 0 ):
81
+ """Fit a 2D Gaussian to the given image and use it to create a mask.
82
+ The Gaussian is fitted around the maximum intensity pixel."""
83
+ try :
84
+ if np .size (np .shape (im )) == 2 :
85
+ self .s = np .shape (im )
86
+ self .mask = np .zeros (np .shape (im ))
87
+ xc , yc = np .unravel_index (np .argmax (im ), im .shape )
88
+ d = round (np .size (im [im > self .t ])** 0.5 ) # guess PSF size from pixels > threshold
89
+ im2 = im [xc - d :xc + d , yc - d :yc + d ] # better for fitting to use zoom in
90
+ w = []
91
+ for i in range (2 ): # do Gaussian fit along each axis
92
+ vals = np .sum (im2 , axis = i )
93
+ f = fit (np .arange (len (vals )), vals )
94
+ f .estGaussParam ()
95
+ f .p0 = f .p0 + [np .min (vals )]
96
+ f .getBestFit (f .offGauss ) # only interested in the width
97
+ w .append (round (f .ps [2 ]) if f .ps [2 ]> 0 and np .isfinite (f .ps [2 ]) else 1 )
98
+ xy = np .meshgrid (list (reversed (range (- int (w [1 ]* 2 ), int (w [1 ]* 2 )+ 1 ))),
99
+ range (- int (w [0 ]* 2 ), int (w [0 ]* 2 )+ 1 )) # make grid of (x,y) coordinates
100
+ # only include values of the Gaussian within 2 1/e^2 widths
101
+ self .mask [xc - int (w [0 ]* 2 ) : xc + int (w [0 ]* 2 ) + 1 ,
102
+ yc - int (w [1 ]* 2 ) : yc + int (w [1 ]* 2 ) + 1 ] = np .exp ( # fill in 2D Gaussian
103
+ - 2 * xy [0 ]** 2 / w [0 ]** 2 - 2 * xy [1 ]** 2 / w [1 ]** 2 ) / np .pi / w [0 ]/ w [1 ]* 2
104
+ self .resize (* map (int , [xc , yc , w [0 ], w [1 ]]), False ) # update stored ROI values
105
+ except Exception as e : logger .error ('ROI %s failed to set Gaussian mask\n ' % self .i + str (e ))
106
+
107
+ def resize (self , xc , yc , width , height , create_sq_mask = True ):
80
108
"""Reset the position and dimensions of the ROI"""
81
109
self .x , self .y , self .w , self .h = xc , yc , width , height
82
110
self .roi .setPos (xc - width // 2 , yc - height // 2 )
83
111
self .roi .setSize ((width , height ))
84
112
self .label .setPos (xc , yc )
85
113
for key , val in zip (self .edits .keys (), [xc , yc , width , height ]):
86
114
self .edits [key ].setText (str (val ))
87
- self .create_rect_mask ()
115
+ if create_sq_mask : self .create_rect_mask ()
88
116
89
117
def atom (self ):
90
118
"""A list of whether the counts are above threshold"""
@@ -131,11 +159,12 @@ class roi_handler(QWidget):
131
159
im_shape -- dimensions of the image in pixels"""
132
160
trigger = pyqtSignal (int )
133
161
134
- def __init__ (self , rois = [(1 ,1 ,1 ,1 )], im_shape = (512 ,512 )):
162
+ def __init__ (self , rois = [(1 ,1 ,1 ,1 , 1 )], im_shape = (512 ,512 )):
135
163
super ().__init__ ()
136
164
self .ROIs = [ROI (im_shape ,* r , ID = i ) for i , r in enumerate (rois )]
137
- self .shape = im_shape
138
- self .delim = ' '
165
+ self .shape = im_shape # image dimensions in pixels
166
+ self .bias = 697 # bias offset to subtract from image counts
167
+ self .delim = ' ' # delimiter used to save/load files
139
168
140
169
def create_rois (self , n ):
141
170
"""Change the list of ROIs to have length n"""
@@ -146,7 +175,10 @@ def create_rois(self, n):
146
175
def resize_rois (self , ROIlist ):
147
176
"""Convenience function for setting multiple ROIs"""
148
177
for i , roi in enumerate (ROIlist ):
149
- try : self .ROIs [i ].resize (* roi )
178
+ try :
179
+ self .ROIs [i ].resize (* roi [:- 1 ])
180
+ self .ROIs [i ].t = roi [- 1 ]
181
+ self .ROIs [i ].threshedit .setText (str (roi [- 1 ]))
150
182
except (IndexError , ValueError ) as e : logger .warning (
151
183
"Failed to resize ROI " + str (i )+ ": %s\n " % roi + str (e ))
152
184
@@ -162,7 +194,7 @@ def process(self, im, include=True):
162
194
success = 1
163
195
for r in self .ROIs :
164
196
try :
165
- counts = np .sum (im * r .mask )
197
+ counts = np .sum (im * r .mask ) - self . bias
166
198
success = 1 if abs (counts ) // r .t and success else 0
167
199
r .c .append (counts )
168
200
except ValueError as e :
@@ -185,6 +217,10 @@ def set_pic_size(self, im_name):
185
217
self .shape = np .genfromtxt (im_name , delimiter = self .delim ).shape
186
218
try : self .cam_pic_size_changed (self .shape [1 ]- 1 , self .shape [0 ])
187
219
except IndexError : self .cam_pic_size_changed (self .shape [0 ]- 1 , 1 )
220
+
221
+ def set_bias (self , bias ):
222
+ """Update the bias offset subtracted from all image counts."""
223
+ self .bias = bias
188
224
189
225
def cam_pic_size_changed (self , width , height ):
190
226
"""Receive new image dimensions from Andor camera"""
0 commit comments