- Timestamp:
- 04/06/11 15:54:04 (15 years ago)
- File:
-
- 1 edited
-
trunk/python/interactivemask.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/python/interactivemask.py
r2108 r2109 5 5 6 6 class interactivemask: 7 """8 The class for interactive mask selection.9 10 Example:11 my_mask=interactivemask(plotter,scan)12 my_mask.set_basemask(masklist=[[0,10],[90,100]],invert=False)13 # Do interactive mask selection14 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()18 19 Modify mask region by selecting a region on a plot with mouse.20 """21 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.26 27 Parameters:28 plotter: an ASAP plotter object for interactive selection29 scan: a scantable to create a mask interactively30 """31 # Return if GUI is not active32 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 return38 # Verify input parameters39 if scan is None and plotter is None:40 msg = "Either scantable or plotter should be defined."41 raise TypeError(msg)42 43 self.scan=None44 self.p=None45 self.newplot=False46 if scan and isinstance(scan, scantable):47 self.scan=scan48 from asap.asapplotter import asapplotter49 if plotter and isinstance(plotter,asapplotter):50 self.p = plotter51 if self.scan is None and isinstance(self.p._data,scantable):52 self.scan=self.p._data53 if self.scan is None:54 msg = "Invalid scantable."55 raise TypeError(msg)56 57 self.mask=_n_bools(self.scan.nchan(),True)58 self.callback=None59 self.event=None60 self.once=False61 self.showmask=True62 self.rect={}63 self.xold=None64 self.yold=None65 self.xdataold=None66 self.ydataold=None67 self._polygons=[]68 69 70 def set_basemask(self,masklist=[],invert=False):71 """72 Set initial channel mask.73 74 Parameters:75 masklist: [[min, max], [min2, max2], ...]76 A list of pairs of start/end points (inclusive)77 specifying the regions to be masked78 invert:optional argument. If specified as True,79 return an inverted mask, i.e. the regions80 specified are excluded81 You can reset the mask selection by running this method with82 the default parameters.83 """84 # Verify input parameters85 if not (isinstance(masklist, list) or isinstance(masklist, tuple)) \86 or not isinstance(invert, bool):87 msg = 'Invalid mask definition'88 raise TypeError(msg)89 90 # Create base mask91 if ( len(masklist) > 0 ):92 self.mask=self.scan.create_mask(masklist,invert=invert)93 elif invert==True:94 self.mask=_n_bools(self.scan.nchan(),False)95 else:96 self.mask=_n_bools(self.scan.nchan(),True)97 98 99 def set_startevent(self,event):100 """101 Inherit an event from the parent function.102 103 Parameters:104 event: 'button_press_event' object to be inherited to105 start interactive region selection .106 """107 from matplotlib.backend_bases import MouseEvent108 if isinstance(event,MouseEvent) and event.name=='button_press_event':109 self.event=event110 else:111 msg="Invalid event."112 raise TypeError(msg)113 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 after118 the mask selections are completed.119 This will be overwritten if callback is defined in120 finish_selection(callback=func)121 """122 self.callback=callback123 124 def select_mask(self,once=False,showmask=True):125 """126 Do interactive mask selection.127 Modify masks interactively by adding/deleting regions with128 mouse drawing.(left-button: mask; right-button: UNmask)129 Note that the interactive region selection is available only130 when GUI plotter is active.131 132 Parameters:133 once:If specified as True, you can modify masks only134 once. Else if False, you can modify them repeatedly.135 showmask: If specified as True, the masked regions are plotted136 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 active141 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 return147 148 self.once = once149 if self.once:150 self.showmask=showmask151 else:152 if not showmask:153 asaplog.post()154 asaplog.push('showmask spcification is ignored. Mask regions are plotted anyway.')155 asaplog.post("WARN")156 self.showmask=True157 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 asapplotter163 self.p=asapplotter()164 self.newplot=True165 166 # Plot selected spectra if needed167 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 replot173 self.p._plotter.legend(1)174 self.p.plot(self.scan)175 # disable casa toolbar176 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']: break185 self.p._plotter.show()186 187 # Plot initial mask region188 #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.'196 197 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)203 204 205 def _region_start(self,event):206 # Do not fire event when in zooming/panning mode207 mode = self.p._plotter.figmgr.toolbar.mode208 if not mode =='':209 return210 # Return if selected point is out of panel211 if event.inaxes == None: return212 # Select mask/unmask region with mask213 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)221 222 def _region_draw(self,event):223 sameaxes=(event.inaxes == self.rect['axes'])224 if sameaxes:225 xnow=event.x226 ynow=event.y227 self.xold=xnow228 self.yold=ynow229 self.xdataold=event.xdata230 self.ydataold=event.ydata231 else:232 xnow=self.xold233 ynow=self.yold234 235 self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y'])236 237 def _region_end(self,event):238 self.p._plotter.register('motion_notify', None)239 self.p._plotter.register('button_release', None)240 241 # Delete the rubber band242 self.p._plotter.figmgr.toolbar.release(event)243 244 if event.inaxes == self.rect['axes']:245 xend=event.x246 yend=event.y247 xdataend=event.xdata248 ydataend=event.ydata249 else:250 xend=self.xold251 yend=self.yold252 xdataend=self.xdataold253 ydataend=self.ydataold254 255 self.rect['world'][2:4] = [xdataend, ydataend]256 self.rect['pixel'][2:4] = [xend, yend]257 self._update_mask()258 # Clear up region selection259 self.rect={}260 self.xold=None261 self.yold=None262 self.xdataold=None263 self.ydataold=None264 if self.once: self.finish_selection(callback=self.callback)265 266 def _update_mask(self):267 # Min and Max for new mask268 xstart=self.rect['world'][0]269 xend=self.rect['world'][2]270 if xstart <= xend: newlist=[xstart,xend]271 else: newlist=[xend,xstart]272 # Mask or unmask273 invmask=None274 if self.rect['button'] == 1:275 invmask=False276 mflg='Mask'277 elif self.rect['button'] == 3:278 invmask=True279 mflg='UNmask'280 asaplog.push(mflg+': '+str(newlist))281 asaplog.post()282 newmask=self.scan.create_mask(newlist,invert=invmask)283 # Logic operation to update mask284 if invmask:285 self.mask=mask_and(self.mask,newmask)286 else:287 self.mask=mask_or(self.mask,newmask)288 # Plot masked regions289 #if self.showmask or not self.once: self._plot_mask()290 if self.showmask: self._plot_mask()291 292 # Plot masked regions293 def _plot_mask(self):294 msks = []295 msks = self.scan.get_masklist(self.mask,row=0)296 # Get projection masks for multi-IF297 ifs=self.scan.getifnos()298 projs = []299 if len(ifs) > 1:300 row0if=self.scan.getif(0)301 for ifno in ifs:302 if ifno == row0if: continue303 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 break307 if len(self._polygons)>0:308 # Remove old polygons309 for polygon in self._polygons: polygon.remove()310 self._polygons=[]311 # Plot new polygons312 if len(msks) > 0:313 npanel=len(self.p._plotter.subplots)314 j=-1315 for iloop in range(len(msks)*npanel):316 i = iloop % len(msks)317 if i == 0 : j += 1318 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()323 324 def finish_selection(self, callback=None):325 """326 Execute callback function, reset or close plotter window as327 necessary.328 329 Parameters:330 callback: The post processing function to run after331 the mask selections are completed.332 Specifying the callback function here will overwrite333 the one set by set_callback(func)334 335 Note this function is automatically called at the end of336 select_mask() if once=True.337 """338 if callback: self.callback=callback339 if self.callback: self.callback()340 if not self.event: self.p._plotter.register('button_press',None)341 # Finish the plot342 if not self.newplot:343 self.clear_polygon()344 else:345 self.p._plotter.unmap()346 self.p._plotter = None347 del self.p348 self.p=None349 self._polygons=[]350 351 352 def clear_polygon(self):353 """354 Erase masks plots from the plotter.355 """356 if len(self._polygons)>0:357 # Remove old polygons358 for polygon in self._polygons: polygon.remove()359 self.p._plotter.show()360 self._polygons=[]361 362 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.mask7 """ 8 The class for interactive mask selection. 9 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() 18 19 Modify mask region by selecting a region on a plot with mouse. 20 """ 21 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. 26 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) 42 43 self.scan=None 44 self.p=None 45 self.newplot=False 46 if scan and isinstance(scan, scantable): 47 self.scan=scan 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): 52 self.scan=self.p._data 53 if self.scan is None: 54 msg = "Invalid scantable." 55 raise TypeError(msg) 56 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=[] 68 69 70 def set_basemask(self,masklist=[],invert=False): 71 """ 72 Set initial channel mask. 73 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) 89 90 # Create base mask 91 if ( len(masklist) > 0 ): 92 self.mask=self.scan.create_mask(masklist,invert=invert) 93 elif invert==True: 94 self.mask=_n_bools(self.scan.nchan(),False) 95 else: 96 self.mask=_n_bools(self.scan.nchan(),True) 97 98 99 def set_startevent(self,event): 100 """ 101 Inherit an event from the parent function. 102 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 108 if isinstance(event,MouseEvent) and event.name=='button_press_event': 109 self.event=event 110 else: 111 msg="Invalid event." 112 raise TypeError(msg) 113 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 """ 122 self.callback=callback 123 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. 131 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 147 148 self.once = once 149 if self.once: 150 self.showmask=showmask 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") 156 self.showmask=True 157 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 163 self.p=asapplotter() 164 self.newplot=True 165 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() 186 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.' 196 197 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) 203 204 205 def _region_start(self,event): 206 # Do not fire event when in zooming/panning mode 207 mode = self.p._plotter.figmgr.toolbar.mode 208 if not mode =='': 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) 221 222 def _region_draw(self,event): 223 sameaxes=(event.inaxes == self.rect['axes']) 224 if sameaxes: 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 231 else: 232 xnow=self.xold 233 ynow=self.yold 234 235 self.p._plotter.figmgr.toolbar.draw_rubberband(event, xnow, ynow, self.rect['x'], self.rect['y']) 236 237 def _region_end(self,event): 238 self.p._plotter.register('motion_notify', None) 239 self.p._plotter.register('button_release', None) 240 241 # Delete the rubber band 242 self.p._plotter.figmgr.toolbar.release(event) 243 244 if event.inaxes == self.rect['axes']: 245 xend=event.x 246 yend=event.y 247 xdataend=event.xdata 248 ydataend=event.ydata 249 else: 250 xend=self.xold 251 yend=self.yold 252 xdataend=self.xdataold 253 ydataend=self.ydataold 254 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 259 self.rect={} 260 self.xold=None 261 self.yold=None 262 self.xdataold=None 263 self.ydataold=None 264 if self.once: self.finish_selection(callback=self.callback) 265 266 def _update_mask(self): 267 # Min and Max for new mask 268 xstart=self.rect['world'][0] 269 xend=self.rect['world'][2] 270 if xstart <= xend: newlist=[xstart,xend] 271 else: newlist=[xend,xstart] 272 # Mask or unmask 273 invmask=None 274 if self.rect['button'] == 1: 275 invmask=False 276 mflg='Mask' 277 elif self.rect['button'] == 3: 278 invmask=True 279 mflg='UNmask' 280 asaplog.push(mflg+': '+str(newlist)) 281 asaplog.post() 282 newmask=self.scan.create_mask(newlist,invert=invmask) 283 # Logic operation to update mask 284 if invmask: 285 self.mask=mask_and(self.mask,newmask) 286 else: 287 self.mask=mask_or(self.mask,newmask) 288 # Plot masked regions 289 #if self.showmask or not self.once: self._plot_mask() 290 if self.showmask: self._plot_mask() 291 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 297 ifs=self.scan.getifnos() 298 projs = [] 299 if len(ifs) > 1: 300 row0if=self.scan.getif(0) 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() 310 self._polygons=[] 311 # Plot new polygons 312 if len(msks) > 0: 313 npanel=len(self.p._plotter.subplots) 314 j=-1 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() 323 324 def finish_selection(self, callback=None): 325 """ 326 Execute callback function, reset or close plotter window as 327 necessary. 328 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) 334 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 348 self.p=None 349 self._polygons=[] 350 351 352 def clear_polygon(self): 353 """ 354 Erase masks plots from the plotter. 355 """ 356 if len(self._polygons)>0: 357 # Remove old polygons 358 for polygon in self._polygons: polygon.remove() 359 self.p._plotter.show() 360 self._polygons=[] 361 362 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 TracChangeset
for help on using the changeset viewer.
