source: trunk/python/interactivemask.py@ 2305

Last change on this file since 2305 was 2117, checked in by Kana Sugimoto, 14 years ago

New Development: No

JIRA Issue: No

Ready for Test: Yes

Interface Changes: No

What Interface Changed:

Test Programs:

Put in Release Notes: No

Module(s):

Description: tidy up the codes


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