1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 from __future__ import division
20
21 import os
22 import buildmask, drutil, arrdriz
23 from obsgeometry import ObsGeometry
24 from stsci.tools import fileutil, wcsutil
25 from math import ceil,floor
26
27 import numpy as np
28
29 yes = True
30 no = False
31
32 __version__ = '0.1.4'
33
34
35
36
37
38
39
40
42 """
43 This class will provide the basic functionality for keeping
44 track of an exposure's parameters, including distortion model,
45 WCS information, and metachip shape.
46 """
47
48 - def __init__(self,expname, handle=None, dqname=None, idckey=None,
49 new=no,wcs=None,mask=None,pa_key=None, parity=None,
50 idcdir=None, rot=None, extver=1, exptime=None,
51 ref_pscale=1.0, binned=1, mt_wcs=None, group_indx = None):
52
53
54 self.name = fileutil.osfn(expname)
55
56
57
58
59 _path,_name = os.path.split(self.name)
60
61
62 if _path == os.getcwd(): self.name = _name
63
64
65
66
67 _fname,_extn = fileutil.parseFilename(expname)
68 _open = False
69
70
71
72 if not handle and not new:
73 handle = fileutil.openImage(expname)
74 _open = True
75
76
77
78
79 if handle and _extn == None:
80 if handle[0].data == None:
81
82
83 if len(handle) > 1 and handle[1].data != None:
84 _extn = 1
85 expname += '[1]'
86 else:
87 raise IOError, "No valid image data in %s.\n"%expname
88 else:
89 _extn = 0
90
91 self.dgeoname = None
92 self.xgeoim = ""
93 self.ygeoim = ""
94 self.exptime = exptime
95 self.group_indx = group_indx
96 if not new:
97
98 _header = fileutil.getHeader(expname,handle=handle)
99 _chip = drutil.getChipId(_header)
100 self.chip = str(_chip)
101
102
103 self.dgeoname = fileutil.getKeyword(expname,'DGEOFILE',handle=handle)
104 self.xgeoim,self.ygeoim = self.getDGEOExtn()
105 if self.exptime == None:
106 self.exptime = float(_header['EXPTIME'])
107 if self.exptime == 0.: self.exptime = 1.0
108
109
110
111
112 self.plam = float(fileutil.getKeyword(expname,'PHOTPLAM',handle=handle)) / 10.
113 if self.plam == None:
114
115 self.plam = 555.
116 self.photzpt = float(fileutil.getKeyword(expname,'PHOTZPT',handle=handle))
117 if self.photzpt == None: self.photzpt = 0.0
118 self.photflam = float(fileutil.getKeyword(expname,'PHOTFLAM',handle=handle))
119 if self.photflam == None: self.photflam = 1.0
120
121
122 if _header:
123 if _header.has_key('date-obs'):
124 self.dateobs = _header['date-obs']
125 elif _header.has_key('date_obs'):
126 self.dateobs = _header['date_obs']
127 else:
128 self.dateobs = None
129 else:
130 self.dateobs = None
131
132
133
134 if _header.has_key('BUNIT') and _header['BUNIT'].find('ergs') < 0:
135 self.bunit = _header['BUNIT']
136 else:
137 self.bunit = 'ELECTRONS'
138
139 else:
140 _chip = 1
141 _header = None
142 self.chip = str(_chip)
143
144 self.plam = 555.
145 self.photzpt = 0.0
146 self.photflam = 1.0
147 self.dateobs = None
148 if self.exptime == None:
149 self.exptime = 1.
150
151 self.parity = parity
152 self.header = _header
153 self.extver = extver
154
155
156
157 self.maskname = None
158 self.singlemaskname = None
159 self.masklist = None
160 if mask != None:
161
162 self.maskname = mask[0]
163 self.singlemaskname = mask[1]
164 self.masklist = mask[2]
165
166 self.dqname = dqname
167
168
169 self.coeffs = self.buildCoeffsName()
170
171
172
173 if idckey != None and idckey.lower() != 'wcs':
174 _indx = expname.find('[')
175 if _indx > -1:
176 _idc_fname = expname[:_indx]+'[0]'
177 else: _idc_fname = expname+'[0]'
178
179 idcfile, idctype = drutil.getIDCFile(self.header,keyword=idckey,
180 directory=idcdir)
181 else:
182 idcfile = None
183 idctype = None
184
185 if (idckey != None) and (idckey.lower() == 'header'):
186 idckey = idctype
187
188
189 self.geometry = ObsGeometry(expname, idcfile, idckey=idckey,
190 chip=_chip, new=new, header=self.header,
191 pa_key=pa_key, rot=rot, date=self.dateobs,
192 ref_pscale=ref_pscale, binned=binned, mt_wcs=mt_wcs)
193
194
195 self.idcfile = idcfile
196 self.idctype = idctype
197
198
199 self.filters = self.geometry.filter1+','+self.geometry.filter2
200
201
202
203
204 if wcs != None:
205
206 self.geometry.wcs = wcs
207 self.geometry.model.pscale = wcs.pscale
208 if expname != None:
209 self.geometry.wcs.rootname = expname
210
211 self.naxis1 = self.geometry.wcs.naxis1
212 self.naxis2 = self.geometry.wcs.naxis2
213 self.pscale = self.geometry.wcs.pscale
214 self.shape = (self.naxis1,self.naxis2,self.pscale)
215
216
217
218
219 self.corners = {'raw':np.zeros((4,2),dtype=np.float64),'corrected':np.zeros((4,2),dtype=np.float64)}
220 self.setCorners()
221
222
223 _blot_extn = '_sci'+repr(extver)+'_blt.fits'
224 self.outblot = fileutil.buildNewRootname(self.name,extn=_blot_extn)
225
226
227
228
229
230 self.product_wcs = self.geometry.wcslin
231 self.xzero = 0.
232 self.yzero = 0.
233 self.chip_shape = (0.,0.)
234 self.xsh2 = 0.
235 self.ysh2 = 0.
236
237 if _open:
238 handle.close()
239 del handle
240
242 """Sets the value of bunit to user specified value, if one is provided.
243 """
244 if value is not None and self.bunit is not None:
245 self.bunit = value
246
248 """ Initializes corners for the raw image. """
249 self.corners['raw'] = np.array([(1.,1.),(1.,self.naxis2),(self.naxis1,1.),(self.naxis1,self.naxis2)],dtype=np.float64)
250
252 """ Computes the zero-point offset and shape of undistorted single chip relative
253 to the full final output product metachip.
254 """
255 _wcs = self.geometry.wcs
256 _corners = np.array([(1.,1.),(1.,_wcs.naxis2),(_wcs.naxis1,1.),(_wcs.naxis1,_wcs.naxis2)],dtype=np.int64)
257 _wc = self.geometry.wtraxy(_corners,self.product_wcs)
258
259 _xrange = (_wc[:,0].min(),_wc[:,0].max())
260 _yrange = (_wc[:,1].min(),_wc[:,1].max())
261
262 self.xzero = int(_xrange[0] - 1)
263 self.yzero = int(_yrange[0] - 1)
264 if self.xzero < 0: self.xzero = 0
265 if self.yzero < 0: self.yzero = 0
266
267 _out_naxis1 = int(ceil(_xrange[1]) - floor(_xrange[0]))
268 _out_naxis2 = int(ceil(_yrange[1]) - floor(_yrange[0]))
269 _max_x = _out_naxis1 + self.xzero
270 _max_y = _out_naxis2 + self.yzero
271 if _max_x > self.product_wcs.naxis1: _out_naxis1 -= (_max_x - self.product_wcs.naxis1)
272 if _max_y > self.product_wcs.naxis2: _out_naxis2 -= (_max_y - self.product_wcs.naxis2)
273
274 self.chip_shape = (_out_naxis1,_out_naxis2)
275 self.xsh2 = int(ceil((self.product_wcs.naxis1 - _out_naxis1)/2.)) - self.xzero
276 self.ysh2 = int(ceil((self.product_wcs.naxis2 - _out_naxis2)/2.)) - self.yzero
277
279 """
280 This method gets the shape after opening and reading
281 the input image. This version will be the default
282 way of doing this, but each instrument may override
283 this method with one specific to their data format.
284 """
285 return self.shape
286
288 """
289 This method will set the shape for a new file if
290 there is no image header information.
291
292 Size will be defined as (nx,ny) and pixel size
293 """
294 self.shape = (size[0],size[1],pscale)
295
296
298 """ Define the name of the coeffs file used for this chip. """
299 indx = self.name.rfind('.')
300 return self.name[:indx]+'_coeffs'+self.chip+'.dat'
301
303 """ Write out coeffs file for this chip. """
304
305
306
307
308
309
310 if not self.geometry.model.refpix.has_key('empty_model'):
311 _xref = self.geometry.wcs.chip_xref
312 _yref = self.geometry.wcs.chip_yref
313 else:
314 _xref = None
315 _yref = None
316
317
318 _delta = not (self.geometry.wcslin.subarray)
319
320
321 self.geometry.model.convert(self.coeffs,xref=_xref,yref=_yref,delta=_delta)
322
323
324 self.setSingleOffsets()
325
327 """ Builds filename with extension to access distortion
328 correction image extension appropriate to each chip.
329 """
330
331
332 if not self.dgeoname or self.dgeoname == 'N/A':
333 return '',''
334
335
336 fimg = fileutil.openImage(self.dgeoname)
337 dx_extver = None
338 dy_extver = None
339
340
341 for hdu in fimg:
342 hdr = hdu.header
343 if not hdr.has_key('CCDCHIP'):
344 _chip = 1
345 else:
346 _chip = int(hdr['CCDCHIP'])
347
348 if hdr.has_key('EXTNAME'):
349 _extname = hdr['EXTNAME'].lower()
350
351 if _chip == int(self.chip):
352 if _extname == 'dx':
353 dx_extver = hdr['EXTVER']
354
355 if _extname == 'dy':
356 dy_extver = hdr['EXTVER']
357
358 fimg.close()
359 del fimg
360
361
362 _dxgeo = self.dgeoname+'[DX,'+str(dx_extver)+']'
363 _dygeo = self.dgeoname+'[DY,'+str(dy_extver)+']'
364
365 return _dxgeo,_dygeo
366
368 """ Return numpy objects for the distortion correction
369 image arrays.
370
371 If no DGEOFILE is specified, it will return
372 empty 2x2 arrays.
373 """
374
375
376 if self.xgeoim == '':
377
378
379 xgdim = ygdim = 2
380 _pxg = np.zeros((ygdim,xgdim),dtype=np.float32)
381 _pyg = np.zeros((ygdim,xgdim),dtype=np.float32)
382 else:
383
384 _xgfile = fileutil.openImage(self.xgeoim)
385
386
387
388 _xgname,_xgext = fileutil.parseFilename(self.xgeoim)
389 _ygname,_ygext = fileutil.parseFilename(self.ygeoim)
390
391 _pxgext = fileutil.getExtn(_xgfile,extn=_xgext)
392 _pygext = fileutil.getExtn(_xgfile,extn=_ygext)
393
394
395 _ltv1 = int(self.geometry.wcs.offset_x)
396 _ltv2 = int(self.geometry.wcs.offset_y)
397 if _ltv1 != 0. or _ltv2 != 0.:
398
399 _pxg = _pxgext.data[_ltv2:_ltv2+self.naxis2,_ltv1:_ltv1+self.naxis1].copy()
400 _pyg = _pygext.data[_ltv2:_ltv2+self.naxis2,_ltv1:_ltv1+self.naxis1].copy()
401 else:
402
403 _pxg = _pxgext.data.copy()
404 _pyg = _pygext.data.copy()
405
406
407 _xgfile.close()
408 del _xgfile
409
410 return _pxg,_pyg
411
412
413 - def applyDeltaWCS(self,dcrval=None,dcrpix=None,drot=None,dscale=None):
414 """
415 Apply shifts to the WCS of this exposure.
416
417 Shifts are always relative to the current value and in the
418 same reference frame.
419 """
420 in_wcs = self.geometry.wcs
421 in_wcslin = self.geometry.wcslin
422
423 if dcrpix:
424 _crval = in_wcs.xy2rd((in_wcs.crpix1-dcrpix[0],in_wcs.crpix2-dcrpix[1]))
425 elif dcrval:
426 _crval = (in_wcs.crval1 - dcrval[0],in_wcs.crval2 - dcrval[1])
427 else:
428 _crval = None
429
430 _orient = None
431 _scale = None
432 if drot:
433 _orient = in_wcs.orient + drot
434 if dscale:
435 _scale = in_wcs.pscale * dscale
436
437 _crpix = (in_wcs.crpix1,in_wcs.crpix2)
438
439 in_wcs.updateWCS(pixel_scale=_scale,orient=_orient,refval=_crval,refpos=_crpix)
440 in_wcslin.updateWCS(pixel_scale=_scale,orient=_orient,refval=_crval,refpos=_crpix)
441
442
444 """
445 This method will compute arrays for all the pixels around
446 the edge of an image AFTER applying the geometry model.
447
448 Parameter: delta - offset from nominal crpix center position
449
450 Output: xsides - array which contains the new positions for
451 all pixels along the LEFT and RIGHT edges
452 ysides - array which contains the new positions for
453 all pixels along the TOP and BOTTOM edges
454 The new position for each pixel is calculated by calling
455 self.geometry.apply() on each position.
456 """
457
458
459 numpix = self.naxis1*2 + self.naxis2 * 2
460 border = np.zeros(shape=(numpix,2),dtype=np.float64)
461
462
463
464 xmin = 1.
465 xmax = self.naxis1
466 ymin = 1.
467 ymax = self.naxis2
468
469
470
471
472
473 xside = np.arange(self.naxis1) + xmin
474 yside = np.arange(self.naxis2) + ymin
475
476
477
478 _range0 = 0
479 _range1 = self.naxis1
480 border[_range0:_range1,0] = xside
481 border[_range0:_range1,1] = ymin
482
483 _range0 = _range1
484 _range1 = _range0 + self.naxis1
485 border[_range0:_range1,0] = xside
486 border[_range0:_range1,1] = ymax
487
488 _range0 = _range1
489 _range1 = _range0 + self.naxis2
490 border[_range0:_range1,0] = xmin
491 border[_range0:_range1,1] = yside
492
493 _range0 = _range1
494 _range1 = _range0 + self.naxis2
495 border[_range0:_range1,0] = xmax
496 border[_range0:_range1,1] = yside
497
498
499 border[:,0],border[:,1] = self.geometry.apply(border,pscale=pscale)
500
501
502
503 _refpix = self.geometry.model.refpix
504 if _refpix != None:
505 _ratio = pscale / _refpix['PSCALE']
506 _delta = (_refpix['XDELTA']/_ratio, _refpix['YDELTA']/_ratio)
507 else:
508 _delta = (0.,0.)
509
510 return border + _delta
511
513 return self.geometry.wcs
515 print self.geometry.wcs
516
517 - def runDriz(self,pixfrac=1.0,kernel='turbo',fillval='INDEF'):
518 """ Runs the 'drizzle' algorithm on this specific chip to create
519 a numpy object of the undistorted image.
520
521 The resulting drizzled image gets returned as a numpy object.
522 """
523
524
525
526 _wcs = self.geometry.wcs
527 _wcsout = self.product_wcs
528
529
530
531 abxt,cdyt = drutil.wcsfit(self.geometry, self.product_wcs)
532
533
534 xsh = abxt[2]
535 ysh = cdyt[2]
536 rot = fileutil.RADTODEG(np.arctan2(abxt[1],cdyt[0]))
537 scale = self.product_wcs.pscale / self.geometry.wcslin.pscale
538
539
540 _out_naxis1,_out_naxis2 = self.chip_shape
541
542
543
544
545 if not os.path.exists(self.coeffs):
546 self.writeCoeffs()
547
548
549
550
551
552
553 _outsci = np.zeros((_out_naxis2,_out_naxis1),dtype=np.float32)
554 _outwht = np.zeros((_out_naxis2,_out_naxis1),dtype=np.float32)
555 _inwcs = np.zeros([8],dtype=np.float64)
556
557
558
559 _outctx = np.zeros((_out_naxis2,_out_naxis1),dtype=np.int32)
560
561
562 _pxg,_pyg = self.getDGEOArrays()
563
564
565 _expname = self.name
566 _handle = fileutil.openImage(_expname,mode='readonly',memmap=0)
567 _fname,_extn = fileutil.parseFilename(_expname)
568 _sciext = fileutil.getExtn(_handle,extn=_extn)
569
570
571
572 _inwcs = drutil.convertWCS(wcsutil.WCSObject(_fname,header=_sciext.header),_inwcs)
573
574
575
576 _planeid = 1
577
578
579 _inwht = np.ones((self.naxis2,self.naxis1),dtype=np.float32)
580
581
582 _wtscl = self.exptime
583
584
585 _expin = self.exptime
586
587
588
589 _uniqid = 1
590 ystart = 0
591 nmiss = 0
592 nskip = 0
593 _vers = ''
594
595
596
597
598 _dny = self.naxis2
599
600 _vers,nmiss,nskip = arrdriz.tdriz(_sciext.data.copy(),_inwht,
601 _outsci, _outwht, _outctx,
602 _uniqid, ystart, 1, 1, _dny,
603 xsh,ysh, 'output','output', rot,scale,
604 self.xsh2,self.ysh2, 1.0, 1.0, 0.0, 'output',
605 _pxg,_pyg,
606 'center', pixfrac, kernel,
607 self.coeffs, 'counts', _expin,_wtscl,
608 fillval, _inwcs, nmiss, nskip, 1,0.,0.)
609
610
611
612
613 if nmiss > 0:
614 print '! Warning, ',nmiss,' points were outside the output image.'
615 if nskip > 0:
616 print '! Note, ',nskip,' input lines were skipped completely.'
617
618
619 _handle.close()
620 del _handle,_fname,_extn,_sciext
621 del _inwht
622
623 del _pxg,_pyg
624
625 del _outwht,_outctx
626
627 return _outsci
628