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

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

New Development: Yes

JIRA Issue: Yes (CAS-1080)

Ready to Release: Yes

Interface Changes: Yes

What Interface Changed: This is a brand new module.

Test Programs:

#from ASAP
scan=scantable('A_FILE_NAME') # Read spectral data from 'A_FILE_NAME'.
new_mask=interactivemask() # Create a interactive masking object
new_mask.select_mask(scan) # Do interactive mask selection
mask=new_mask.get_mask() # Get a final channel mask

Put in Release Notes: No

Description:

The class for interactive mask selection, 'interactivemask', is used
to hard coded in CASA tasks 'sdbaseline' and 'sdstat'.
I moved the class from the tasks and defined as a new ASAP module.


File size: 6.6 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 new_mask=interactivemask()
11 new_mask.select_mask(scan,masklist=[[0,10],[90,100]],invert=False)
12 mask=new_mask.get_mask()
13
14 Modify mask region by selecting a region on a plot with mouse.
15 """
16
17 def __init__(self):
18 """
19 Create a interactive masking object
20 """
21 self.scan=None
22 self.rect={}
23 self.xold=None
24 self.yold=None
25 self.xdataold=None
26 self.ydataold=None
27 self.mask=None
28 self._polygons=[]
29 self._p=None
30
31 def select_mask(self,scan,masklist=[],invert=False):
32 """
33 Do interactive mask selection.
34 Calculate initial channel mask based on the parameters and modify
35 it interactively by adding/deleting regions with mouse drawing.
36 When finish modifying, press <Return> to calculate the final mask.
37
38 Parameters:
39 scan: a scantable
40 masklist: [[min, max], [min2, max2], ...]
41 A list of pairs of start/end points (inclusive)
42 specifying the regions to be masked
43 invert: optional argument. If specified as True,
44 return an inverted mask, i.e. the regions
45 specified are excluded
46
47 Interactive region selection is available only when GUI plotter
48 is active. When GUI plotter is disabled, this method only
49 calculates a initial channel mask.
50 """
51 # Verify input parameters
52 if not isinstance(scan, scantable):
53 msg = 'Input is not a scantable'
54 raise TypeError(msg)
55 if not (isinstance(masklist, list) or isinstance(masklist, tuple)) \
56 or not isinstance(invert, bool):
57 msg = 'Invalid mask definition'
58 raise TypeError(msg)
59
60 self.scan=scan
61
62 # Create initial mask
63 if ( len(masklist) > 0 ):
64 self.mask=self.scan.create_mask(masklist,invert=invert)
65 else:
66 self.mask=_n_bools(self.scan.nchan(),True)
67
68 # Return if GUI is not active
69 if not rcParams['plotter.gui']:
70 print 'GUI plotter is disabled.\n'
71 print 'Exit interactive mode.'
72 return
73
74 # Plot selected spectra
75 if not self._p or self._p.is_dead:
76 from asap.asapplotter import asapplotter
77 plotter=asapplotter()
78 self._p = plotter._plotter
79 plotter.plot(self.scan)
80 for panel in self._p.subplots:
81 xmin, xmax = panel['axes'].get_xlim()
82 marg = 0.05*(xmax-xmin)
83 panel['axes'].set_xlim(xmin-marg, xmax+marg)
84 self._plot_mask()
85
86 print ''
87 print 'Masked regions are shaded with yellow. (gray: projections)'
88 print 'Now you can modify mask regions.'
89 print 'Draw rectangles with Left-mouse to add Mask region,'
90 print 'or with Right-mouse to UNMask region.'
91
92 cid = None
93 cid = self._p.canvas.mpl_connect('button_press_event', self._region_start)
94 finish=raw_input('Press return to calculate statistics.\n')
95 if cid is not None:
96 self._p.canvas.mpl_disconnect(cid)
97
98 # Finish the plot
99 self._p.unmap()
100 self._p = None
101 del plotter
102
103 def _region_start(self,event):
104 # Do not fire event when in zooming/panning mode
105 mode = self._p.figmgr.toolbar.mode
106 if not mode =='':
107 return
108 # Return if selected point is out of panel
109 if event.inaxes == None: return
110 # Select mask/unmask region with mask
111 height = self._p.canvas.figure.bbox.height()
112 self.rect = {'button': event.button, 'axes': event.inaxes,
113 'fig': None, 'height': height,
114 'x': event.x, 'y': height - event.y,
115 'world': [event.xdata, event.ydata,
116 event.xdata, event.ydata],
117 'pixel': [event.x, height - event.y,
118 event.x, height -event.y]}
119 self._p.register('motion_notify', self._region_draw)
120 self._p.register('button_release', self._region_end)
121
122 def _region_draw(self,event):
123 self._p.canvas._tkcanvas.delete(self.rect['fig'])
124 sameaxes=(event.inaxes == self.rect['axes'])
125 if sameaxes:
126 xnow=event.x
127 ynow=event.y
128 self.xold=xnow
129 self.yold=ynow
130 self.xdataold=event.xdata
131 self.ydataold=event.ydata
132 else:
133 xnow=self.xold
134 ynow=self.yold
135
136 self.rect['fig'] = self._p.canvas._tkcanvas.create_rectangle(
137 self.rect['x'], self.rect['y'],
138 xnow, self.rect['height'] - ynow)
139
140 def _region_end(self,event):
141 height = self._p.canvas.figure.bbox.height()
142 self._p.register('motion_notify', None)
143 self._p.register('button_release', None)
144
145 self._p.canvas._tkcanvas.delete(self.rect['fig'])
146
147 if event.inaxes == self.rect['axes']:
148 xend=event.x
149 yend=event.y
150 xdataend=event.xdata
151 ydataend=event.ydata
152 else:
153 xend=self.xold
154 yend=self.yold
155 xdataend=self.xdataold
156 ydataend=self.ydataold
157
158 self.rect['world'][2:4] = [xdataend, ydataend]
159 self.rect['pixel'][2:4] = [xend, height - yend]
160 self._update_mask()
161
162 def _update_mask(self):
163 # Min and Max for new mask
164 xstart=self.rect['world'][0]
165 xend=self.rect['world'][2]
166 if xstart <= xend: newlist=[xstart,xend]
167 else: newlist=[xend,xstart]
168 # Mask or unmask
169 invmask=None
170 if self.rect['button'] == 1:
171 invmask=False
172 mflg='Mask'
173 elif self.rect['button'] == 3:
174 invmask=True
175 mflg='UNmask'
176 print mflg+': ',newlist
177 newmask=self.scan.create_mask(newlist,invert=invmask)
178 # Logic operation to update mask
179 if invmask:
180 self.mask=mask_and(self.mask,newmask)
181 else:
182 self.mask=mask_or(self.mask,newmask)
183 # Plot masked regions
184 self._plot_mask()
185
186 # Plot masked regions
187 def _plot_mask(self):
188 msks = []
189 msks = self.scan.get_masklist(self.mask,row=0)
190 # Get projection masks for multi-IF
191 ifs=self.scan.getifnos()
192 projs = []
193 if len(ifs) > 1:
194 row0if=self.scan.getif(0)
195 for ifno in ifs:
196 if ifno == row0if: continue
197 for row in xrange(self.scan.nrow()):
198 if self.scan.getif(row) == ifno:
199 projs.append(self.scan.get_masklist(self.mask,row=row))
200 break
201 if len(self._polygons)>0:
202 # Remove old polygons
203 for polygon in self._polygons: polygon.remove()
204 self._polygons=[]
205 # Plot new polygons
206 if len(msks) < 1: return
207 npanel=len(self._p.subplots)
208 j=-1
209 for iloop in range(len(msks)*npanel):
210 i = iloop % len(msks)
211 if i == 0 : j += 1
212 if len(ifs) > 1:
213 for k in xrange(len(ifs)-1):
214 self._polygons.append(self._p.subplots[j]['axes'].axvspan(projs[k][i][0],projs[k][i][1],facecolor='#aaaaaa'))
215 self._polygons.append(self._p.subplots[j]['axes'].axvspan(msks[i][0],msks[i][1],facecolor='yellow'))
216 self._p.show()
217
218 def get_mask(self):
219 """
220 Get the interactively selected channel mask.
221 Returns:
222 A list of channel mask.
223 """
224 return self.mask
225
Note: See TracBrowser for help on using the repository browser.