source: branches/alma/python/interactivemask.py@ 1621

Last change on this file since 1621 was 1621, checked in by Kana Sugimoto, 16 years ago

New Development: No

JIRA Issue: No

Ready to Release: Yes

Interface Changes: Yes

What Interface Changed: input parameters changed for methods 'init', 'select_mask'.

new methods added, i.e., 'set_basemask', 'set_startevent',

'set_callback', and 'finish_selection'

Test Programs:

Put in Release Notes: Yes

Module(s): sdbaseline & sdstat

Description:

The class interactivemask is reconstructed so that it can be worked both for
statistics calculations on ASAP plotter and interactive mask selection
in sdbaseline and sdstat.
!!!NOTE!!! both sdbaseline and sdstat should be updated at the same time.
input parameters changed: 'init', 'select_mask'.
new methods: 'set_basemask', 'set_startevent', 'set_callback', and 'finish_selection'

File size: 9.5 KB
Line 
1from asap import rcParams
2from asap import _n_bools, mask_and, mask_or
3from asap.scantable import scantable
4
5class interactivemask:
6 """
7 The class for interactive mask selection.
8
9 Example:
10 my_mask=interactivemask(plotter,scan)
11 my_mask.set_basemask(masklist=[[0,10],[90,100]],invert=False)
12 # Do interactive mask selection
13 my_mask.select_mask()
14 finish=raw_input('Press return to finish selection.\n')
15 my_mask.finish_selection(callback=func)
16 mask=my_mask.get_mask()
17
18 Modify mask region by selecting a region on a plot with mouse.
19 """
20
21 def __init__(self,plotter=None, scan=None):
22 """
23 Create a interactive masking object.
24 Either or both 'plotter' or/and 'scan' should be defined.
25
26 Parameters:
27 plotter: an ASAP plotter object for interactive selection
28 scan: a scantable to create a mask interactively
29 """
30 # Return if GUI is not active
31 if not rcParams['plotter.gui']:
32 print 'GUI plotter is disabled.\n'
33 print 'Exit interactive mode.'
34 return
35 # Verify input parameters
36 if scan is None and plotter is None:
37 msg = "Either scantable or plotter should be defined."
38 raise TypeError(msg)
39
40 self.scan=None
41 self.p=None
42 self.newplot=False
43 if scan and isinstance(scan, scantable):
44 self.scan=scan
45 from asap.asapplotter import asapplotter
46 if plotter and isinstance(plotter,asapplotter):
47 self.p = plotter
48 if self.scan is None and isinstance(self.p._data,scantable):
49 self.scan=self.p._data
50 if self.scan is None:
51 msg = "Invalid scantable."
52 raise TypeError(msg)
53
54 self.mask=_n_bools(self.scan.nchan(),True)
55 self.callback=None
56 self.event=None
57 self.once=False
58 self.showmask=True
59 self.rect={}
60 self.xold=None
61 self.yold=None
62 self.xdataold=None
63 self.ydataold=None
64 self._polygons=[]
65
66
67 def set_basemask(self,masklist=[],invert=False):
68 """
69 Set initial channel mask.
70
71 Parameters:
72 masklist: [[min, max], [min2, max2], ...]
73 A list of pairs of start/end points (inclusive)
74 specifying the regions to be masked
75 invert: optional argument. If specified as True,
76 return an inverted mask, i.e. the regions
77 specified are excluded
78 You can reset the mask selection by running this method with
79 the default parameters.
80 """
81 # Verify input parameters
82 if not (isinstance(masklist, list) or isinstance(masklist, tuple)) \
83 or not isinstance(invert, bool):
84 msg = 'Invalid mask definition'
85 raise TypeError(msg)
86
87 # Create base mask
88 if ( len(masklist) > 0 ):
89 self.mask=self.scan.create_mask(masklist,invert=invert)
90 elif invert==True:
91 self.mask=_n_bools(self.scan.nchan(),False)
92 else:
93 self.mask=_n_bools(self.scan.nchan(),True)
94
95
96 def set_startevent(self,event):
97 """
98 Inherit an event from the parent function.
99
100 Parameters:
101 event: 'button_press_event' object to be inherited to
102 start interactive region selection .
103 """
104 from matplotlib.backend_bases import MouseEvent
105 if isinstance(event,MouseEvent) and event.name=='button_press_event':
106 self.event=event
107 else:
108 msg="Invalid event."
109 raise TypeError(msg)
110
111 def set_callback(self,callback):
112 """
113 Set callback function to run when finish_selection() is executed.
114 callback: The post processing function to run after
115 the mask selections are completed.
116 This will be overwritten if callback is defined in
117 finish_selection(callback=func)
118 """
119 self.callback=callback
120
121 def select_mask(self,once=False,showmask=True):
122 """
123 Do interactive mask selection.
124 Modify masks interactively by adding/deleting regions with
125 mouse drawing.(left-button: mask; right-button: UNmask)
126 Note that the interactive region selection is available only
127 when GUI plotter is active.
128
129 Parameters:
130 once: If specified as True, you can modify masks only
131 once. Else if False, you can modify them repeatedly.
132 showmask: If specified as True, the masked regions are plotted
133 on the plotter.
134 Note this parameter is valid only when once=True.
135 Otherwise, maskes are forced to be plotted for reference.
136 """
137 # Return if GUI is not active
138 if not rcParams['plotter.gui']:
139 print 'GUI plotter is disabled.\n'
140 print 'Exit interactive mode.'
141 return
142
143 self.once = once
144 if self.once:
145 self.showmask=showmask
146 else:
147 if not showmask: print 'Warning: showmask spcification is ignored. Mask regions are plotted anyway.'
148 self.showmask=True
149
150 #if not self.p._plotter or self.p._plotter.is_dead:
151 if not self.p or self.p._plotter.is_dead:
152 print 'A new ASAP plotter will be loaded'
153 from asap.asapplotter import asapplotter
154 self.p=asapplotter()
155 self.newplot=True
156
157 # Plot selected spectra if needed
158 if self.scan != self.p._data:
159 # Need replot
160 self.p.plot(self.scan)
161 for panel in self.p._plotter.subplots:
162 xmin, xmax = panel['axes'].get_xlim()
163 marg = 0.05*(xmax-xmin)
164 panel['axes'].set_xlim(xmin-marg, xmax+marg)
165 self.p._plotter.show()
166
167 # Plot initial mask region
168 #if self.showmask or not self.once:
169 if self.showmask:
170 self._plot_mask()
171 print ''
172 print 'Selected regions are shaded with yellow. (gray: projections)'
173 print 'Now you can modify the selection.'
174 print 'Draw rectangles with Left-mouse to add the regions,'
175 print 'or with Right-mouse to exclude the regions.'
176
177
178 if self.event != None:
179 self._region_start(self.event)
180 else:
181 self.p._plotter.register('button_press',None)
182 self.p._plotter.register('button_press',self._region_start)
183
184
185 def _region_start(self,event):
186 # Do not fire event when in zooming/panning mode
187 mode = self.p._plotter.figmgr.toolbar.mode
188 if not mode =='':
189 return
190 # Return if selected point is out of panel
191 if event.inaxes == None: return
192 # Select mask/unmask region with mask
193 self.rect = {'button': event.button, 'axes': event.inaxes,
194 'x': event.x, 'y': event.y,
195 'world': [event.xdata, event.ydata,
196 event.xdata, event.ydata],
197 'pixel': [event.x, event.y,
198 event.x, event.y]}
199 self.p._plotter.register('motion_notify', self._region_draw)
200 self.p._plotter.register('button_release', self._region_end)
201
202 def _region_draw(self,event):
203 sameaxes=(event.inaxes == self.rect['axes'])
204 if sameaxes:
205 xnow=event.x
206 ynow=event.y
207 self.xold=xnow
208 self.yold=ynow
209 self.xdataold=event.xdata
210 self.ydataold=event.ydata
211 else:
212 xnow=self.xold
213 ynow=self.yold
214
215 self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])
216
217
218 def _region_end(self,event):
219 self.p._plotter.register('motion_notify', None)
220 self.p._plotter.register('button_release', None)
221
222 self.p._plotter.figmgr.toolbar.release(event)
223
224 if event.inaxes == self.rect['axes']:
225 xend=event.x
226 yend=event.y
227 xdataend=event.xdata
228 ydataend=event.ydata
229 else:
230 xend=self.xold
231 yend=self.yold
232 xdataend=self.xdataold
233 ydataend=self.ydataold
234
235 self.rect['world'][2:4] = [xdataend, ydataend]
236 self.rect['pixel'][2:4] = [xend, yend]
237 self._update_mask()
238 if self.once: self.finish_selection(callback=self.callback)
239
240 def _update_mask(self):
241 # Min and Max for new mask
242 xstart=self.rect['world'][0]
243 xend=self.rect['world'][2]
244 if xstart <= xend: newlist=[xstart,xend]
245 else: newlist=[xend,xstart]
246 # Mask or unmask
247 invmask=None
248 if self.rect['button'] == 1:
249 invmask=False
250 mflg='Mask'
251 elif self.rect['button'] == 3:
252 invmask=True
253 mflg='UNmask'
254 print mflg+': ',newlist
255 newmask=self.scan.create_mask(newlist,invert=invmask)
256 # Logic operation to update mask
257 if invmask:
258 self.mask=mask_and(self.mask,newmask)
259 else:
260 self.mask=mask_or(self.mask,newmask)
261 # Plot masked regions
262 #if self.showmask or not self.once: self._plot_mask()
263 if self.showmask: self._plot_mask()
264
265 # Plot masked regions
266 def _plot_mask(self):
267 msks = []
268 msks = self.scan.get_masklist(self.mask,row=0)
269 # Get projection masks for multi-IF
270 ifs=self.scan.getifnos()
271 projs = []
272 if len(ifs) > 1:
273 row0if=self.scan.getif(0)
274 for ifno in ifs:
275 if ifno == row0if: continue
276 for row in xrange(self.scan.nrow()):
277 if self.scan.getif(row) == ifno:
278 projs.append(self.scan.get_masklist(self.mask,row=row))
279 break
280 if len(self._polygons)>0:
281 # Remove old polygons
282 for polygon in self._polygons: polygon.remove()
283 self._polygons=[]
284 # Plot new polygons
285 if len(msks) > 0:
286 npanel=len(self.p._plotter.subplots)
287 j=-1
288 for iloop in range(len(msks)*npanel):
289 i = iloop % len(msks)
290 if i == 0 : j += 1
291 if len(ifs) > 1:
292 for k in xrange(len(ifs)-1):
293 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(projs[k][i][0],projs[k][i][1],facecolor='#aaaaaa'))
294 self._polygons.append(self.p._plotter.subplots[j]['axes'].axvspan(msks[i][0],msks[i][1],facecolor='yellow'))
295 self.p._plotter.canvas.draw()
296
297 def finish_selection(self, callback=None):
298 if callback: self.callback=callback
299 if self.callback: self.callback()
300 self.p._plotter.register('button_press',None)
301 # Finish the plot
302 if not self.newplot:
303 self.clear_polygon()
304 else:
305 self.p._plotter.unmap()
306 self.p._plotter = None
307 del self.p
308 self.p=None
309
310
311 def clear_polygon(self):
312 """
313 Erase masks plots from the plotter.
314 """
315 if len(self._polygons)>0:
316 # Remove old polygons
317 for polygon in self._polygons: polygon.remove()
318 self.p._plotter.show()
319 self._polygons=[]
320
321
322 def get_mask(self):
323 """
324 Get the interactively selected channel mask.
325 Returns:
326 A list of channel mask.
327 """
328 return self.mask
329
330
Note: See TracBrowser for help on using the repository browser.