Package multidrizzle :: Module input_image
[hide private]
[frames] | no frames]

Source Code for Module multidrizzle.input_image

  1  # 
  2  #   Authors: Warren Hack, Ivo Busko, Christopher Hanley 
  3  #   Program: input_image.py 
  4  #   Purpose: Super class used to model astronomical data from observatory instruments. 
  5   
  6  from __future__ import division # confidence high 
  7   
  8  import pyfits 
  9  from pytools import fileutil 
 10   
 11  import imagestats 
 12  from imagestats import ImageStats 
 13   
 14  from pytools import imageiter 
 15  from pytools.imageiter import ImageIter 
 16   
 17  import numpy as np 
 18  import quickDeriv 
 19  import driz_cr 
 20   
 21  DEFAULT_SEPARATOR = '_' 
 22   
23 -class InputImage(object):
24 '''The InputImage class is the base class for all of the various 25 types of images 26 ''' 27
28 - def __init__(self, input,dqname,platescale,memmap=0,proc_unit="native"):
29 # These will always be populated by the appropriate 30 # sub-class, however, this insures that these attributes 31 # are not overlooked/forgotten. 32 self.name = input 33 self.memmap = memmap 34 if not self.SEPARATOR: 35 self.sep = DEFAULT_SEPARATOR 36 else: 37 self.sep = self.SEPARATOR 38 39 self.instrument = None 40 _fname,_extn = fileutil.parseFilename(input) 41 self.dqfile_fullname = dqname 42 self.dqfile_name,self.dqfile_extn = fileutil.parseFilename(dqname) 43 self.extn = _extn 44 self.grp = fileutil.parseExtn(_extn)[1] 45 self.rootname = self.getRootname(_fname) 46 self.datafile = _fname 47 self.cr_bits_value = None 48 self._effGain = None 49 self.static_badval = 64 50 self.static_mask = None 51 self.cte_dir = 1 52 53 # read amplifier to be used for HRC or STIS/CCD. 54 try: 55 self.amp = fileutil.getKeyword(input,'CCDAMP') 56 # set default if keyword missing 57 except KeyError: 58 # STIS default should be 'D' but 'C' and 'D' have the same readout direction so it's okay 59 self.amp = 'C' 60 61 # Define the platescale and reference plate scale for the detector. 62 self.platescale = platescale 63 self.refplatescale = platescale # Default is to make each chip it's own reference value 64 65 # Image information 66 handle = fileutil.openImage(self.name,mode='readonly',memmap=self.memmap) 67 sciext = fileutil.getExtn(handle,extn=self.extn) 68 self.image_shape = sciext.data.shape 69 self.image_type = sciext.data.dtype.name 70 self.image_dtype= sciext.data.dtype.name 71 72 # Retrieve a combined primary and extension header 73 self.header = fileutil.getHeader(input,handle=handle) 74 del sciext 75 handle.close() 76 del handle 77 78 # Initialize sky background information keywords 79 self._subtractedsky = 0. 80 self._computedsky = None 81 82 # Get image size information for possible subarray use 83 try: 84 self.ltv1 = self.header['LTV1'] * -1 85 self.ltv2 = self.header['LTV2'] * -1 86 except KeyError: 87 self.ltv1 = 0 88 self.ltv2 = 0 89 self.size1 = self.header['NAXIS1'] + self.ltv1 90 self.size2 = self.header['NAXIS2'] + self.ltv2 91 92 # Set Units used for processing. Options are "native" or "electrons" 93 self.proc_unit = proc_unit
94
95 - def setInstrumentParameters(self, instrpars, pri_header):
96 """ 97 Sets the instrument parameters. 98 """ 99 pass
100
101 - def doUnitConversions(self):
102 """ 103 Convert the sci extension pixels to electrons 104 """ 105 pass
106
107 - def getInstrParameter(self, value, header, keyword):
108 """ This method gets a instrument parameter from a 109 pair of task parameters: a value, and a header keyword. 110 111 The default behavior is: 112 - if the value and header keyword are given, raise an exception. 113 - if the value is given, use it. 114 - if the value is blank and the header keyword is given, use 115 the header keyword. 116 - if both are blank, or if the header keyword is not 117 found, return None. 118 """ 119 if (value != None and value != '') and (keyword != None and keyword.strip() != ''): 120 exceptionMessage = "ERROR: Your input is ambiguous! Please specify either a value or a keyword.\n You specifed both " + str(value) + " and " + str(keyword) 121 raise ValueError, exceptionMessage 122 elif value != None and value != '': 123 return self._averageFromList(value) 124 elif keyword != None and keyword.strip() != '': 125 return self._averageFromHeader(header, keyword) 126 else: 127 return None
128
129 - def _averageFromHeader(self, header, keyword):
130 """ Averages out values taken from header. The keywords where 131 to read values from are passed as a comma-separated list. 132 """ 133 _list = '' 134 for _kw in keyword.split(','): 135 if header.has_key(_kw): 136 _list = _list + ',' + str(header[_kw]) 137 else: 138 return None 139 return self._averageFromList(_list)
140
141 - def _averageFromList(self, param):
142 """ Averages out values passed as a comma-separated 143 list, disregarding the zero-valued entries. 144 """ 145 _result = 0.0 146 _count = 0 147 148 for _param in param.split(','): 149 if _param != '' and float(_param) != 0.0: 150 _result = _result + float(_param) 151 _count += 1 152 153 if _count >= 1: 154 _result = _result / _count 155 return _result
156
157 - def getreferencesky(self):
158 return (self._subtractedsky * (self.refplatescale / self.platescale)**2)
159
160 - def getComputedSky(self):
161 return self._computedsky
162
163 - def setComputedSky(self,newValue):
164 self._computedsky = newValue
165
166 - def getSubtractedSky(self):
167 return self._subtractedsky
168
169 - def setSubtractedSky(self,newValue):
170 self._subtractedsky = newValue
171
172 - def getGain(self):
173 return self._gain
174
175 - def getExpTime(self):
176 return self._exptime
177
178 - def getCRbit(self):
179 return self._crbit
180
181 - def getRootname(self,name):
182 _indx = name.rfind(self.sep) 183 if _indx < 0: _indx = len(name) 184 return name[:_indx]
185
186 - def _isSubArray(self):
187 """ Instrument specific method to determine whether input is 188 a subarray or not. 189 """ 190 pass
191
192 - def _isNotValid(self, par1, par2):
193 """ Method used to determine if a value or keyword is supplied as 194 input for instrument specific parameters. 195 """ 196 if (par1 == None or par1 == '') and (par2 == None or par2 == ''): 197 return True 198 else: 199 return False
200
201 - def updateStaticMask(self, static_mask):
202 203 """ This method updates a static mask passed as a parameter, 204 with mask info derived from the [SCI] array. It also 205 keeps a private static mask array appropriate for 206 use with the [SCI] array when doing sky processing 207 later on. 208 """ 209 # Open input image and get pointer to SCI data 210 handle = fileutil.openImage(self.name,mode='readonly',memmap=self.memmap) 211 sciext = fileutil.getExtn(handle,extn=self.extn) 212 213 # Add SCI array to static mask 214 static_mask.addMember(sciext.data, self.signature()) 215 self.static_mask = static_mask 216 217 # Close input image filehandle 218 handle.close() 219 del sciext,handle
220
221 - def signature(self):
222 223 """ Generates a signature unique to this image. """ 224 225 # Shape is taken from PyFITS object. Subclasses that 226 # depend on other types of files must override this. 227 return (self.instrument, self.image_shape, self.grp)
228
229 - def computeSky(self, skypars):
230 231 """ Compute the sky value based upon the sci array of the chip""" 232 233 # Open input image and get pointer to SCI data 234 # 235 # 236 try: 237 _handle = fileutil.openImage(self.name,mode='update',memmap=self.memmap) 238 _sciext = fileutil.getExtn(_handle,extn=self.extn) 239 except: 240 raise IOError, "Unable to open %s for sky level computation"%self.name 241 242 _tmp = ImageStats(_sciext.data, 243 fields = skypars['skystat'], 244 lower = skypars['skylower'], 245 upper = skypars['skyupper'], 246 nclip = skypars['skyclip'], 247 lsig = skypars['skylsigma'], 248 usig = skypars['skyusigma'], 249 binwidth = skypars['skywidth'] 250 ) 251 252 self._computedsky = self._extractSkyValue(_tmp,skypars['skystat'].lower()) 253 print "Computed sky value for ",self.name," : ",self._computedsky 254 255 # Close input image filehandle 256 _handle.close() 257 del _sciext,_handle
258
259 - def _extractSkyValue(self,ImageStatsObject,skystat):
260 if (skystat =="mode"): 261 return ImageStatsObject.mode 262 elif (skystat == "mean"): 263 return ImageStatsObject.mean 264 else: 265 return ImageStatsObject.median
266
267 - def subtractSky(self):
268 try: 269 try: 270 _handle = fileutil.openImage(self.name,mode='update',memmap=self.memmap) 271 _sciext = fileutil.getExtn(_handle,extn=self.extn) 272 print "%s (computed sky,subtracted sky) : (%f,%f)"%(self.name,self.getComputedSky(),self.getSubtractedSky()) 273 np.subtract(_sciext.data,self.getSubtractedSky(),_sciext.data) 274 except: 275 raise IOError, "Unable to open %s for sky subtraction"%self.name 276 finally: 277 _handle.close() 278 del _sciext,_handle
279
280 - def updateMDRIZSKY(self,filename=None):
281 282 if (filename == None): 283 filename = self.name 284 285 try: 286 _handle = fileutil.openImage(filename,mode='update',memmap=self.memmap) 287 except: 288 raise IOError, "Unable to open %s for sky level computation"%filename 289 try: 290 try: 291 # Assume MDRIZSKY lives in primary header 292 print "Updating MDRIZSKY in %s with %f"%(filename,self.getSubtractedSky()) 293 _handle[0].header['MDRIZSKY'] = self.getSubtractedSky() 294 except: 295 print "Cannot find keyword MDRIZSKY in %s to update"%filename 296 print "Adding MDRIZSKY keyword to primary header with value %f"%self.getSubtractedSky() 297 _handle[0].header.update('MDRIZSKY',self.getSubtractedSky(), 298 comment="Sky value subtracted by Multidrizzle") 299 finally: 300 _handle.close()
301
302 - def runDrizCR(self, blotted_array, mask_array, drizcrpars, skypars, corr_file, cr_file):
303 """ Run 'deriv' and 'driz_cr' to create cosmic-ray mask for this image. """ 304 305 _deriv_array = None 306 307 print "Working on image ",self.datafile,"..." 308 _deriv_array = quickDeriv.qderiv(blotted_array) 309 310 # Open input image and get pointer to SCI data 311 handle = fileutil.openImage(self.name,mode='readonly',memmap=self.memmap) 312 scihdu = fileutil.getExtn(handle,extn=self.extn) 313 314 tmpDriz_cr = driz_cr.DrizCR(scihdu.data, 315 scihdu.header, 316 blotted_array, 317 _deriv_array, 318 mask_array, 319 gain = self.getEffGain(), 320 grow = drizcrpars['driz_cr_grow'], 321 ctegrow = drizcrpars['driz_cr_ctegrow'], 322 ctedir = self.cte_dir, 323 amp = self.amp, 324 rn = self.getReadNoise(), 325 SNR = drizcrpars['driz_cr_snr'], 326 backg = self.getSubtractedSky(), 327 scale = drizcrpars['driz_cr_scale']) 328 329 330 # If the user provided a None value for the cr bit, don't 331 # update the dqarray 332 if (self.getCRbit() != 0): 333 # Update the dq information if there is a dq array to update. 334 # In the case of WFPC2 input, no DQ file may have been provided. 335 # For ACS, there will always be DQ array information in the FLT file. 336 if fileutil.findFile(self.dqfile_fullname): 337 dqhandle = fileutil.openImage(self.dqfile_name,mode='update',memmap=self.memmap) 338 dqarray = fileutil.getExtn(dqhandle,extn=self.dqfile_extn) 339 tmpDriz_cr.updatedqarray(dqarray.data,self.getCRbit()) 340 dqhandle.close() 341 else: 342 print " CR bit value of 0 specified. Skipping DQ array updates." 343 344 if (corr_file != None): 345 tmpDriz_cr.createcorrfile(corr_file,self.header) 346 if (cr_file != None): 347 tmpDriz_cr.createcrmaskfile(cr_file,self.header) 348 349 del tmpDriz_cr 350 351 # Close input image filehandle 352 if handle: 353 del scihdu 354 handle.close() 355 del handle 356 if _deriv_array != None: 357 del _deriv_array
358
359 - def getflat(self):
360 """ 361 362 Purpose 363 ======= 364 Placeholder method for retrieving a detector's flat field. 365 This is an abstract method since each detector will be 366 handled differently. 367 368 This method will return an array the same shape as the 369 image. 370 """ 371 pass
372
373 - def getEffGain(self):
374 """ 375 376 Purpose 377 ======= 378 Method used to return the effective gain of a instrument's 379 detector. 380 381 This method returns a single floating point value. 382 383 """ 384 385 return self._effGain
386
387 - def getReadNoise(self):
388 """ 389 390 Purpose 391 ======= 392 Method for trturning the readnoise of a detector (in electrons). 393 394 :units: electrons 395 396 """ 397 return self._rdnoise
398
399 - def getReadNoiseImage(self):
400 """ 401 402 Purpose 403 ======= 404 Method for returning the readnoise image of a detector 405 (in electrons). 406 407 The method will return an array of the same shape as the image. 408 409 :units: electrons 410 411 """ 412 return np.ones(self.image_shape,dtype=self.image_dtype) * self._rdnoise
413
414 - def getdarkcurrent(self):
415 """ 416 417 Purpose 418 ======= 419 Return the dark current for the detector. This value 420 will be contained within an instrument specific keyword. 421 The value in the image header will be converted to units 422 of electrons. 423 424 :units: electrons 425 426 """ 427 pass
428 429
430 - def getdarkimg(self):
431 """ 432 433 Purpose 434 ======= 435 Return an array representing the dark image for the detector. 436 437 :units: electrons 438 439 """ 440 return np.ones(self.image_shape,dtype=self.image_dtype)*self.getdarkcurrent()
441
442 - def getskyimg(self):
443 """ 444 445 Purpose 446 ======= 447 Return an array representing the sky image for the detector. The value 448 of the sky is what would actually be subtracted from the exposure by 449 the skysub step. 450 451 :units: electrons 452 453 """ 454 return np.ones(self.image_shape,dtype=self.image_dtype)*self.getSubtractedSky()
455