def createobslogfits(headerDict): """Create the fits table for the observation log""" # define generic columns of output table col=[] for k, f in zip(headerList, formatList): print k,f, headerDict[k] col.append(pyfits.Column(name=k, format=f, array=headerDict[k])) for k, f in zip(scamheaderList, scamformatList): print k,f, headerDict[k] col.append(pyfits.Column(name=k, format=f, array=headerDict[k])) for k, f in zip(rssheaderList, rssformatList): print k,f, headerDict[k] col.append(pyfits.Column(name=k, format=f, array=headerDict[k])) # construct FITS table from columns table = saltio.fitscolumns(col) # write FITS table to output file struct=saltio.newfitstable(table) # name the table extension saltkey.new('EXTNAME','OBSLOG','extension name', struct) # housekeeping keywords saltkey.put('SAL-TLM',time.asctime(time.localtime()), struct) return struct
def addhousekeeping(struct, outhdu, outfile): """housekeeping keywords""" saltsafekey.put('SAL-TLM', time.asctime(time.localtime()), struct, outfile) saltsafekey.new('EXTNAME', 'SCI', 'Extension name', struct, outfile) saltsafekey.new('EXTVER', outhdu, 'Extension number', struct, outfile) return struct
def addhousekeeping(struct, outhdu, outfile): """housekeeping keywords""" saltsafekey.put('SAL-TLM',time.asctime(time.localtime()),struct,outfile) saltsafekey.new('EXTNAME','SCI','Extension name',struct,outfile) saltsafekey.new('EXTVER',outhdu,'Extension number',struct,outfile) return struct
def saltcrclean(images,outimages,outpref,crtype='fast',thresh=5,mbox=3, \ bthresh=3, flux_ratio=0.2, bbox=11, gain=1, rdnoise=5, fthresh=5,\ bfactor=2, gbox=3, maxiter=5, multithread=False, update=True, clobber=True, logfile='salt.log', verbose=True): with logging(logfile,debug) as log: # Check the input images infiles = saltio.argunpack ('Input',images) # create list of output files outfiles=saltio.listparse('Outfile', outimages, outpref,infiles,'') #verify that the input and output lists are the same length saltio.comparelists(infiles,outfiles,'Input','output') #check to see if multithreading is available if mp: pass else: multithread=False log.warning('multiprocessing module is not available. Setting multiththread=False') # Begin processes each file for infile,outfile in zip(infiles,outfiles): #open the infile struct=saltio.openfits(infile) #clean the cosmic rays if multithread and len(struct)>1: struct=multicrclean(struct, crtype, thresh, mbox, bbox, bthresh, flux_ratio, \ gain, rdnoise, bfactor, fthresh, gbox, maxiter, log, verbose=verbose) else: struct=crclean(struct, crtype, thresh, mbox, bbox, bthresh, flux_ratio, \ gain, rdnoise, bfactor, fthresh, gbox, maxiter, update, log, verbose=verbose) #log the call #log.message('Cleaned %i cosmic rays from %s using %s method' % (totcr, infile, crtype), with_header=False) log.message('', with_header=False, with_stdout=verbose) #add house keeping keywords saltkey.put('SAL-TLM',time.asctime(time.localtime()), struct[0]) #add the history keyword fname, hist=history(level=1, wrap=False) saltkey.history(struct[0],hist) #write out the file saltio.writefits(struct, outfile, clobber=clobber) #close the image saltio.closefits(struct)
def saltcrclean(images,outimages,outpref,crtype='fast',thresh=5,mbox=3, \ bthresh=3, flux_ratio=0.2, bbox=11, gain=1, rdnoise=5, fthresh=5,\ bfactor=2, gbox=3, maxiter=5, multithread=False, update=True, clobber=True, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # Check the input images infiles = saltio.argunpack('Input', images) # create list of output files outfiles = saltio.listparse('Outfile', outimages, outpref, infiles, '') #verify that the input and output lists are the same length saltio.comparelists(infiles, outfiles, 'Input', 'output') #check to see if multithreading is available if mp: pass else: multithread = False log.warning( 'multiprocessing module is not available. Setting multiththread=False' ) # Begin processes each file for infile, outfile in zip(infiles, outfiles): #open the infile struct = saltio.openfits(infile) #clean the cosmic rays if multithread and len(struct) > 1: struct=multicrclean(struct, crtype, thresh, mbox, bbox, bthresh, flux_ratio, \ gain, rdnoise, bfactor, fthresh, gbox, maxiter, log, verbose=verbose) else: struct=crclean(struct, crtype, thresh, mbox, bbox, bthresh, flux_ratio, \ gain, rdnoise, bfactor, fthresh, gbox, maxiter, update, log, verbose=verbose) #log the call #log.message('Cleaned %i cosmic rays from %s using %s method' % (totcr, infile, crtype), with_header=False) log.message('', with_header=False, with_stdout=verbose) #add house keeping keywords saltkey.put('SAL-TLM', time.asctime(time.localtime()), struct[0]) #add the history keyword fname, hist = history(level=1, wrap=False) saltkey.history(struct[0], hist) #write out the file saltio.writefits(struct, outfile, clobber=clobber) #close the image saltio.closefits(struct)
def readtimefix(hdu, dsteps=7, transtime=4e-3): """Update the hdu with the correct time for when the exposure started and add the READTIME keyword dsteps--the number of readouts to correct for transtime--the transfer time between each frame """ #check for if the data has already been processed if saltkey.found('READTIME', hdu): raise SaltIOError(' has already been processed') #determine the UTC time utctime=saltkey.get('UTC-OBS', hdu) timeobs=saltkey.get('TIME-OBS', hdu) dateobs=saltkey.get('DATE-OBS', hdu) exptime=float(saltkey.get('EXPTIME', hdu)) #add the readtime header saltkey.new("READTIME",utctime,'Time of the readout of the frame', hdu) #correct the utctime--first switch to datetime to properly handle #dates around changes in hours y,m,d=dateobs.split('-') H,M,S=utctime.split(':') s=int(float(S)) ms=int(1e6*(float(S)-s)) newtime=datetime.datetime(int(y),int(m),int(d),int(H),int(M),s,ms) #correct the datetime dtime=dsteps*(exptime+transtime) s=int(dtime) ms=int(1e6*(dtime-s)) newtime=newtime-datetime.timedelta(0, s, ms) #update the headkeywords hdu.header.update("UTC-OBS", str(newtime.time())) saltkey.put("UTC-OBS", str(newtime.time()), hdu) saltkey.put("TIME-OBS", str(newtime.time()), hdu) saltkey.put("DATE-OBS", str(newtime.date()), hdu) return hdu
def readtimefix(hdu, dsteps=7, transtime=4e-3): """Update the hdu with the correct time for when the exposure started and add the READTIME keyword dsteps--the number of readouts to correct for transtime--the transfer time between each frame """ #check for if the data has already been processed if saltkey.found('READTIME', hdu): raise SaltIOError(' has already been processed') #determine the UTC time utctime = saltkey.get('UTC-OBS', hdu) timeobs = saltkey.get('TIME-OBS', hdu) dateobs = saltkey.get('DATE-OBS', hdu) exptime = float(saltkey.get('EXPTIME', hdu)) #add the readtime header saltkey.new("READTIME", utctime, 'Time of the readout of the frame', hdu) #correct the utctime--first switch to datetime to properly handle #dates around changes in hours y, m, d = dateobs.split('-') H, M, S = utctime.split(':') s = int(float(S)) ms = int(1e6 * (float(S) - s)) newtime = datetime.datetime(int(y), int(m), int(d), int(H), int(M), s, ms) #correct the datetime dtime = dsteps * (exptime + transtime) s = int(dtime) ms = int(1e6 * (dtime - s)) newtime = newtime - datetime.timedelta(0, s, ms) #update the headkeywords hdu.header["UTC-OBS"] = str(newtime.time()) saltkey.put("UTC-OBS", str(newtime.time()), hdu) saltkey.put("TIME-OBS", str(newtime.time()), hdu) saltkey.put("DATE-OBS", str(newtime.date()), hdu) return hdu
def slotmerge(images, outimages, outpref, geomfile, clobber, logfile, verbose): with logging(logfile, debug) as log: # are the arguments defined saltsafeio.argdefined('images', images) saltsafeio.argdefined('geomfile', geomfile) saltsafeio.argdefined('logfile', logfile) # if the input file is a list, does it exist? if images[0] == '@': saltsafeio.listexists('Input', images) # parse list of input files infiles = saltsafeio.listparse('Raw image', images, '', '', '') # check input files exist saltsafeio.filesexist(infiles, '', 'r') # load output name list: @list, * and comma separated outimages = outimages.strip() outpref = outpref.strip() if len(outpref) == 0 and len(outimages) == 0: raise SaltIOError('Output file(s) not specified') # test output @filelist exists if len(outimages) > 0 and outimages[0] == '@': saltsafeio.listexists('Output', outimages) # parse list of output files outfiles = saltsafeio.listparse('Output image', outimages, outpref, infiles, '') # are input and output lists the same length? saltsafeio.comparelists(infiles, outfiles, 'Input', 'output') # do the output files already exist? if not clobber: saltsafeio.filesexist(outfiles, '', 'w') # does CCD geometry definition file exist geomfilefile = geomfile.strip() saltsafeio.fileexists(geomfile) # read geometry definition file gap = 0 xshift = [0, 0] yshift = [0, 0] rotation = [0, 0] gap, xshift, yshift, rotation = saltsafeio.readccdgeom(geomfile) for ro in rotation: if ro != 0: log.warning('SLOTMERGE currently ignores CCD rotation') # Begin processes each file for infile, outfile in zip(infiles, outfiles): # determine the name for the output file outpath = outfile.rstrip(os.path.basename(outfile)) if (len(outpath) == 0): outpath = '.' # open each raw image struct = saltsafeio.openfits(infile) # identify instrument instrume, keyprep, keygain, keybias, keyxtalk, keyslot = saltsafekey.instrumid( struct, infile) # how many amplifiers? nccds = saltsafekey.get('NCCDS', struct[0], infile) amplifiers = nccds * 2 #if (nccds != 2): # raise SaltError('Can not currently handle more than two CCDs') # CCD geometry coefficients if instrume == 'RSS' or instrume == 'PFIS': xsh = [xshift[0], 0., xshift[1]] ysh = [yshift[0], 0., yshift[1]] rot = [rotation[0], 0., rotation[1]] refid = 1 if instrume == 'SALTICAM': xsh = [xshift[0], 0.] ysh = [yshift[0], 0.] rot = [rotation[0], 0] refid = 1 # how many extensions? nextend = saltsafekey.get('NEXTEND', struct[0], infile) # how many exposures exposures = nextend / amplifiers # CCD on-chip binning xbin, ybin = saltsafekey.ccdbin(struct[0], infile) gp = int(gap / xbin) # create output hdu structure outstruct = [None] * int(exposures + 1) outstruct[0] = struct[0] # iterate over exposures, stitch them to produce file of CCD images for i in range(exposures): # Determine the total size of the image xsize = 0 ysize = 0 for j in range(amplifiers): hdu = i * amplifiers + j + 1 try: xsize += len(struct[hdu].data[0]) if ysize < len(struct[hdu].data): ysize = len(struct[hdu].data) except: msg = 'Unable to access extension %i ' % hdu raise SaltIOError(msg) xsize += gp * (nccds - 1) maxxsh, minxsh = determineshifts(xsh) maxysh, minysh = determineshifts(ysh) xsize += (maxxsh - minxsh) ysize += (maxysh - minysh) # Determine the x and y origins for each frame xdist = 0 ydist = 0 shid = 0 x0 = np.zeros(amplifiers) y0 = np.zeros(amplifiers) for j in range(amplifiers): x0[j] = xdist + xsh[shid] - minxsh y0[j] = ysh[shid] - minysh hdu = i * amplifiers + j + 1 darr = struct[hdu].data xdist += len(darr[0]) if j % 2 == 1: xdist += gp shid += 1 # make the out image outarr = np.zeros((ysize, xsize), np.float64) # Embed each frame into the output array for j in range(amplifiers): hdu = i * amplifiers + j + 1 darr = struct[hdu].data outarr = salttran.embed(darr, x0[j], y0[j], outarr) # Add the outimage to the output structure hdu = i * amplifiers + 1 outhdu = i + 1 outstruct[outhdu] = pyfits.ImageHDU(outarr) outstruct[outhdu].header = struct[hdu].header # Fix the headers in each extension datasec = '[1:%4i,1:%4i]' % (xsize, ysize) saltsafekey.put('DATASEC', datasec, outstruct[outhdu], outfile) saltsafekey.rem('DETSIZE', outstruct[outhdu], outfile) saltsafekey.rem('DETSEC', outstruct[outhdu], outfile) saltsafekey.rem('CCDSEC', outstruct[outhdu], outfile) saltsafekey.rem('AMPSEC', outstruct[outhdu], outfile) # add housekeeping key words outstruct[outhdu] = addhousekeeping(outstruct[outhdu], outhdu, outfile) # close input FITS file saltsafeio.closefits(struct) # housekeeping keywords keymosaic = 'SLOTMERG' fname, hist = history(level=1, wrap=False) saltsafekey.housekeeping(struct[0], keymosaic, 'Amplifiers have been mosaiced', hist) #saltsafekey.history(outstruct[0],hist) # this is added for later use by saltsafekey.put('NCCDS', 0.5, outstruct[0]) saltsafekey.put('NSCIEXT', exposures, outstruct[0]) saltsafekey.put('NEXTEND', exposures, outstruct[0]) # write FITS file of mosaiced image outstruct = pyfits.HDUList(outstruct) saltsafeio.writefits(outstruct, outfile, clobber=clobber)
def salteditkey(images,outimages,outpref, keyfile, recfile=None,clobber=False,logfile='salt.log',verbose=True): with logging(logfile,debug) as log: # Check the input images infiles = saltio.argunpack ('Input',images) # create list of output files outfiles=saltio.listparse('Outfile', outimages, outpref,infiles,'') #verify that the input and output lists are the same length saltio.comparelists(infiles,outfiles,'Input','output') #is key file defined saltio.argdefined('keyfile',keyfile) keyfile = keyfile.strip() saltio.fileexists(keyfile) # if the data are the same, set up to use update instead of write openmode='copyonwrite' if (infiles!=outfiles): openmode='copyonwrite' # determine the date of the observations obsdate=saltstring.makeobsdatestr(infiles, 1,9) if len(obsdate)!=8: message = 'Either FITS files from multiple dates exist, ' message += 'or raw FITS files exist with non-standard names.' log.warning(message) # FITS file columns to record keyword changes fitcol = [] keycol = [] oldcol = [] newcol = [] # Set up the rules to change the files keyedits=readkeyfile(keyfile, log=log, verbose=verbose) #now step through the images for img, oimg in zip(infiles, outfiles): #determine the appropriate keyword edits for the image klist=[] for frange in keyedits: if checkfitsfile(img, frange, keyedits[frange]): klist.append(keyedits[frange][3]) if klist: #open up the new files struct = saltio.openfits(img,mode=openmode) struct.verify('fix') for kdict in klist: for keyword in kdict: #record the changes value=kdict[keyword] fitcol.append(img) keycol.append(keyword) newcol.append(value) try: oldcol.append(struct[0].header[keyword].lstrip()) except: oldcol.append('None') #update the keyword if saltkey.found(keyword, struct[0]): try: saltkey.put(keyword,value,struct[0]) message='\tUpdating %s in %s to %s' % (keyword, os.path.basename(img), value) log.message(message, with_header=False, with_stdout=verbose) except Exception, e: message = 'Could not update %s in %s because %s' % (keyword, img, str(e)) raise SaltError(message) else: try: saltkey.new(keyword.strip(),value,'Added Comment',struct[0]) message='\tAdding %s in %s to %s' % (keyword, os.path.basename(img), value) log.message(message, with_header=False, with_stdout=verbose) except Exception,e : message = 'Could not update %s in %s because %s' % (keyword, img, str(e)) raise SaltError(message) #updat the history keywords #fname, hist=history(level=1, wrap=False, exclude=['images', 'outimages', 'outpref']) #saltkey.housekeeping(struct[0],'SAL-EDT', 'Keywords updated by SALTEDITKEY', hist) #write the file out if openmode=='update': saltio.updatefits(struct) message = 'Updated file ' + os.path.basename(oimg) else: saltio.writefits(struct, oimg, clobber) message = 'Created file ' + os.path.basename(oimg) log.message(message, with_header=False, with_stdout=True) struct.close()
ntime = ntime - tdiff / 3600.0 newutc = salttime.dec2sex(ntime) except Exception, e: msg = 'Could not update UTC in %i header of image %s because %s' % ( ext, infile, e) raise SaltError(msg) return struct # update the headers if utc == saltsafekey.get('UTC-OBS', struct): expt_string = '%5.4f' % real_expt td_string = '%5.4f' % tdiff if not saltsafekey.found('DUTC', struct): try: saltsafekey.put('UTC-OBS', newutc, struct, infile) saltsafekey.put('TIME-OBS', newutc, struct, infile) saltsafekey.new('DWETIME', expt_string, 'Dwell Time', struct, infile) saltsafekey.new('DUTC', td_string, 'Change in UTC time', struct, infile) except Exception, e: msg = 'Could not update %i header of image %s because %s' % ( ext, infile, e) raise SaltIOError(msg) else: try: saltsafekey.put('UTC-OBS', newutc, struct, infile) saltsafekey.put('TIME-OBS', newutc, struct, infile) saltsafekey.put('DWETIME', real_expt, struct, infile) saltsafekey.put('DUTC', tdiff, struct, infile)
ntime=salttime.sex2dec(utc) ntime=ntime-tdiff/3600.0 newutc=salttime.dec2sex(ntime) except Exception,e: msg='Could not update UTC in %i header of image %s because %s' % (ext, infile, e) raise SaltError(msg) return struct # update the headers if utc==saltsafekey.get('UTC-OBS', struct): expt_string='%5.4f' % real_expt td_string='%5.4f' % tdiff if not saltsafekey.found('DUTC', struct): try: saltsafekey.put('UTC-OBS', newutc, struct, infile) saltsafekey.put('TIME-OBS', newutc, struct, infile) saltsafekey.new('DWETIME', expt_string, 'Dwell Time', struct, infile) saltsafekey.new('DUTC', td_string, 'Change in UTC time', struct, infile) except Exception, e: msg='Could not update %i header of image %s because %s' % (ext, infile, e) raise SaltIOError(msg) else: try: saltsafekey.put('UTC-OBS', newutc, struct, infile) saltsafekey.put('TIME-OBS', newutc, struct, infile) saltsafekey.put('DWETIME', real_expt, struct, infile) saltsafekey.put('DUTC', tdiff, struct, infile) except Exception, e: msg='Could not update %i header of image %s because %s' % (ext, infile, e) raise SaltError(msg)
if saltkey.found('VAREXT', struct[hdu]): vhdu = saltkey.get('VAREXT', struct[hdu]) try: vdata = struct[vhdu].data struct[vhdu].data = vdata * gain * ( 1 + 2 * gain1 * 1e-6 * data) except Exception, e: msg = 'Cannot update the variance frame in %s[%i] because %s' % ( infile, vhdu, e) raise SaltError(msg) else: gainmult = gain #update the headers if usedb: saltkey.put('GAIN', gain, struct[hdu]) saltkey.put('RDNOISE', rdnoise, struct[hdu]) #add a keyword indicating what action was taken saltkey.new('GAINMULT', gainmult, 'Gain multiplication', struct[hdu]) #if logging is true, then print out the following information if log: message = '%25s[%1d] %6s %5s %2s %6.2f %5.2f' \ % (infile,hdu,gainset,rospeed,amp, gain, rdnoise) log.message(message, with_header=False, with_stdout=verbose) #just to make it look pretty if log: log.message('', with_header=False, with_stdout=verbose)
def slot(struct, infile, dbspeed, dbrate, dbgain, dbnoise, dbbias, dbamp, xcoeff, gaindb, xtalkfile, logfile, verbose): import saltprint, saltkey, saltio, saltstat, time # identify instrument instrume, keyprep, keygain, keybias, keyxtalk, keyslot, status = saltkey.instrumid( struct, infile, logfile) # number of image HDU nextend = 0 while (status == 0): try: struct[nextend + 1].header['XTENSION'] nextend += 1 except: break nccds, status = saltkey.get('NCCDS', struct[0], infile, logfile) amplifiers = nccds * 2 if (nextend % (amplifiers) != 0): message = '\nERROR -- SALTSLOT: Number of image extensions and' message += 'number of amplifiers are not consistent' status = saltprint.err(saltlog, message) status = saltkey.new('NSCIEXT', nextend, 'Number of science extensions', struct[0], infile, logfile) status = saltkey.new('NEXTEND', nextend, 'Number of data extensions', struct[0], infile, logfile) # check image file and gain database are compatible if (status == 0): ngains = len(dbgain) if (int(max(dbamp)) != amplifiers): message = '\nERROR -- SALTGSLOT: ' + infile + ' contains ' + str( amplifiers) + ' amplifiers' message += ', the gaindb file ' + gaindb + ' contains ' + str( max(dbamp)) + ' amplifiers' status = saltprint.err(logfile, message) # check image file and cross talk database are compatible if (status == 0): if (len(xcoeff) - 1 != amplifiers): message = '\nERROR -- SALTSLOT: ' + infile + ' contains ' + str( amplifiers) + ' amplifiers' message += ', the cross talk file ' + xtalkfile + ' contains ' + str( len(xcoeff) - 1) + ' amplifiers' status = saltprint.err(logfile, message) # housekeeping keywords if (status == 0): status = saltkey.put('SAL-TLM', time.asctime(time.localtime()), struct[0], infile, logfile) status = saltkey.new(keyslot, time.asctime(time.localtime()), 'Data have been cleaned by SALTSLOT', struct[0], infile, logfile) # keywords for image extensions for i in range(nextend): hdu = i + 1 status = saltkey.new('EXTNAME', 'SCI', 'Extension name', struct[hdu], infile, logfile) status = saltkey.new('EXTVER', hdu, 'Extension number', struct[hdu], infile, logfile) # log coefficent table if (status == 0): message = '%30s %5s %4s %8s' % ('HDU', 'Gain', 'Bias', 'Xtalk') saltprint.log(logfile, '\n ---------------------------------------------', verbose) saltprint.log(logfile, message, verbose) saltprint.log(logfile, ' ---------------------------------------------', verbose) # loop over image extensions if (status == 0): for i in range(nextend / 2): hdu = i * 2 + 1 amplifier = hdu % amplifiers if (amplifier == 0): amplifier = amplifiers if (status == 0): value, status = saltkey.get('NAXIS1', struct[hdu], infile, logfile) naxis1 = int(value) if (status == 0): value, status = saltkey.get('NAXIS1', struct[hdu + 1], infile, logfile) naxis2 = int(value) if (status == 0 and hdu == 1): biassec, status = saltkey.get('BIASSEC', struct[hdu], infile, logfile) if (status == 0 and hdu == 1): ranges = biassec.lstrip('[').rstrip(']').split(',') x1_1 = int(ranges[0].split(':')[0]) - 1 x2_1 = int(ranges[0].split(':')[1]) - 1 y1_1 = int(ranges[1].split(':')[0]) - 1 y2_1 = int(ranges[1].split(':')[1]) - 1 if (status == 0 and hdu == 1): biassec, status = saltkey.get('BIASSEC', struct[hdu + 1], infile, logfile) if (status == 0 and hdu == 1): ranges = biassec.lstrip('[').rstrip(']').split(',') x1_2 = int(ranges[0].split(':')[0]) - 1 x2_2 = int(ranges[0].split(':')[1]) - 1 y1_2 = int(ranges[1].split(':')[0]) - 1 y2_2 = int(ranges[1].split(':')[1]) - 1 if (status == 0 and hdu == 1): datasec, status = saltkey.get('DATASEC', struct[hdu], infile, logfile) if (status == 0 and hdu == 1): ranges = datasec.lstrip('[').rstrip(']').split(',') dx1_1 = int(ranges[0].split(':')[0]) - 1 dx2_1 = int(ranges[0].split(':')[1]) dy1_1 = int(ranges[1].split(':')[0]) - 1 dy2_1 = int(ranges[1].split(':')[1]) if (status == 0 and hdu == 1): datasec, status = saltkey.get('DATASEC', struct[hdu + 1], infile, logfile) if (status == 0 and hdu == 1): ranges = datasec.lstrip('[').rstrip(']').split(',') dx1_2 = int(ranges[0].split(':')[0]) - 1 dx2_2 = int(ranges[0].split(':')[1]) dy1_2 = int(ranges[1].split(':')[0]) - 1 dy2_2 = int(ranges[1].split(':')[1]) if (status == 0 and dx2_1 - dx1_1 != dx2_2 - dx1_2): message = 'ERROR -- SALTSLOT: HDUs ' + infile message += '[' + str(hdu) + '] and ' + infile + '[' + str( hdu + 1) + ']' message += ' have different dimensions' status = saltprint.err(logfile, message) # read speed and gain of each exposure if (status == 0 and hdu == 1): gainset, status = saltkey.get('GAINSET', struct[0], infile, logfile) rospeed, status = saltkey.get('ROSPEED', struct[0], infile, logfile) if (rospeed == 'NONE'): saltprint.log(logfile, " ", verbose) message = "ERROR -- SALTSLOT: Readout speed is 'NONE' in " message += "primary keywords of " + infile status = saltprint.err(logfile, message) # read raw images if (status == 0): imagedata1, status = saltio.readimage(struct, hdu, logfile) imagedata2, status = saltio.readimage(struct, hdu + 1, logfile) # gain correction if (status == 0): for j in range(len(dbgain)): if (gainset == dbrate[j] and rospeed == dbspeed[j] and amplifier == int(dbamp[j])): try: gain1 = float(dbgain[j]) imagedata1 *= gain1 except: mesage = 'ERROR -- SALTSLOT: Cannot perform gain correction on image ' message += infile + '[' + str(hdu) + ']' status = saltprint.err(logfile, message) elif (gainset == dbrate[j] and rospeed == dbspeed[j] and amplifier + 1 == int(dbamp[j])): try: gain2 = float(dbgain[j]) imagedata2 *= gain2 except: mesage = 'ERROR -- SALTSLOT: Cannot perform gain correction on image ' message += infile + '[' + str(hdu + 1) + ']' status = saltprint.err(logfile, message) # crosstalk correction if (status == 0): revimage1 = imagedata1 * float(xcoeff[amplifier]) revimage2 = imagedata2 * float(xcoeff[amplifier + 1]) for j in range(dx2_1 - dx1_1 + 1): imagedata1[:, j] -= revimage2[:, dx2_2 - j - 1] imagedata2[:, j] -= revimage1[:, dx2_1 - j - 1] # bias subtraction if (status == 0): overx_val_1 = [] overx_val_2 = [] for x in range(x1_1, x2_1 + 1): list_1 = imagedata1[y1_1:y2_1, x] * 1.0 overx_val_1.append(saltstat.median(list_1, logfile)) overlevel_1 = saltstat.median(overx_val_1, logfile) for x in range(x1_2, x2_2 + 1): list_2 = imagedata2[y1_2:y2_2, x] * 1.0 overx_val_2.append(saltstat.median(list_2, logfile)) overlevel_2 = saltstat.median(overx_val_2, logfile) imagedata1 -= overlevel_1 imagedata2 -= overlevel_2 # trim overscan if (status == 0): imagedata1 = imagedata1[dy1_1:dy2_1, dx1_1:dx2_1] imagedata2 = imagedata2[dy1_2:dy2_2, dx1_2:dx2_2] datasec = '[1:' + str(dx2_1 - dx1_1) + ',1:' + str(dy2_1 - dy1_1) + ']' status = saltkey.put('DATASEC', datasec, struct[hdu], infile, logfile) status = saltkey.rem('BIASSEC', struct[hdu], infile, logfile) datasec = '[1:' + str(dx2_2 - dx1_2) + ',1:' + str(dy2_2 - dy1_2) + ']' status = saltkey.put('DATASEC', datasec, struct[hdu + 1], infile, logfile) status = saltkey.rem('BIASSEC', struct[hdu + 1], infile, logfile) # log coefficient table if (status == 0): infilename = infile.split('/') infilename = infilename[len(infilename) - 1] message = '%25s[%3d] %5.2f %4d %8.6f' % \ (infilename, hdu, gain1, overlevel_1, float(xcoeff[amplifier+1])) saltprint.log(logfile, message, verbose) message = '%25s[%3d] %5.2f %4d %8.6f' % \ (infilename, hdu+1, gain2, overlevel_2,float(xcoeff[amplifier])) saltprint.log(logfile, message, verbose) # update image in HDU structure if (status == 0): struct, status = saltio.writeimage(struct, hdu, imagedata1, logfile) struct, status = saltio.writeimage(struct, hdu + 1, imagedata2, logfile) return struct, status
def gain(struct,mult=True,usedb=False, dblist=None, ampccd=2, log=None, verbose=True): """gain processes a image hduList and gain corrects each amplifier. It can either use gain settings in the header or those supplied in a config file which would be suppleid in the dblist (see helpfile for structure of the config file). If variance frames exist, it will update those for changes in the header value as well. In the end, it will update the gain with a value of one signfing the data has been transformed into e- from ADU The program will look for the non-linear gain settings which are given by: e = GAIN*(1 + GAIN1*E-6*ADU)*ADU mult--if true, multiple the gains usedb--use the values in the dblist, if false use the header values dblist--values for the gain and readnoise from the ampccd--number of amplifiers per ccd dblist should have the following lists: speed, rate, gain, noise, bias, amp """ #get the infile name infile=saltkey.getimagename(struct[0]) #how many science extensions nsciext = saltkey.get('NSCIEXT',struct[0]) #how many data extensions nextend = saltkey.get('NSCIEXT',struct[0]) # how many amplifiers? amplifiers = ampccd*saltkey.get('NCCDS',struct[0]) #read the gain and rospeed for the image gainset = saltkey.get('GAINSET',struct[0]) rospeed = saltkey.get('ROSPEED',struct[0]) #loop through each amplifier and gain correct it if log: message = '%28s %6s %5s %3s %5s %5s' \ % ('HDU','GAIN','SPEED','AMP','GAIN','NOISE') log.message('\n ---------------------------------------------------', \ with_header=False, with_stdout=verbose) log.message(message, with_header=False, with_stdout=verbose) log.message(' ---------------------------------------------------', \ with_header=False, with_stdout=verbose) for i in range(nsciext): hdu = i + 1 amp = i%amplifiers+1 #get the gain and rdnoise values for the array if usedb: gain, rdnoise=get_values(dblist, gainset, rospeed, amp) gain1=0 else: gain = saltkey.get('GAIN',struct[hdu]) rdnoise = saltkey.get('RDNOISE',struct[hdu]) try: gain1=saltkey.get('GAIN1',struct[hdu]) except: gain1=0 if mult: #correct the gain gainmult=1 try: data=struct[hdu].data struct[hdu].data=gain*data+gain1*data**2 except Exception as e: msg='Cannot gain correct %s[%i] because %s' % (infile, hdu, e) raise SaltError(msg) #correct the variance frame if saltkey.found('VAREXT', struct[hdu]): vhdu=saltkey.get('VAREXT', struct[hdu]) try: vdata=struct[vhdu].data struct[vhdu].data=vdata*gain*(1+2*gain1*1e-6*data) except Exception as e: msg='Cannot update the variance frame in %s[%i] because %s' % (infile, vhdu, e) raise SaltError(msg) else: gainmult=gain #update the headers if usedb: saltkey.put('GAIN',gain,struct[hdu]) saltkey.put('RDNOISE',rdnoise,struct[hdu]) #add a keyword indicating what action was taken saltkey.new('GAINMULT',gainmult,'Gain multiplication', struct[hdu]) #if logging is true, then print out the following information if log: message = '%25s[%1d] %6s %5s %2s %6.2f %5.2f' \ % (infile,hdu,gainset,rospeed,amp, gain, rdnoise) log.message(message, with_header=False, with_stdout=verbose) #just to make it look pretty if log: log.message('', with_header=False, with_stdout=verbose) return struct
def updateheaders(struct, ext, tdiff, real_expt, utc, infile): # exit if tdiff wasn't updated if tdiff == real_expt: msg='No adequate correction found for frame %i in file %s' % (ext, infile) raise SaltError(msg) return struct # calculate the new utc value try: ntime=salttime.sex2dec(utc) ntime=ntime-tdiff/3600.0 newutc=salttime.dec2sex(ntime) except Exception as e: msg='Could not update UTC in %i header of image %s because %s' % (ext, infile, e) raise SaltError(msg) return struct # update the headers if utc==saltsafekey.get('UTC-OBS', struct): expt_string='%5.4f' % real_expt td_string='%5.4f' % tdiff if not saltsafekey.found('DUTC', struct): try: saltsafekey.put('UTC-OBS', newutc, struct, infile) saltsafekey.put('TIME-OBS', newutc, struct, infile) saltsafekey.new('DWETIME', expt_string, 'Dwell Time', struct, infile) saltsafekey.new('DUTC', td_string, 'Change in UTC time', struct, infile) except Exception as e: msg='Could not update %i header of image %s because %s' % (ext, infile, e) raise SaltIOError(msg) else: try: saltsafekey.put('UTC-OBS', newutc, struct, infile) saltsafekey.put('TIME-OBS', newutc, struct, infile) saltsafekey.put('DWETIME', real_expt, struct, infile) saltsafekey.put('DUTC', tdiff, struct, infile) except Exception as e: msg='Could not update %i header of image %s because %s' % (ext, infile, e) raise SaltError(msg) else: raise SaltIOError('Frame missing from list of times') return struct
def bias(struct, subover=True, trim=True, subbias=False, bstruct=None, median=False, function='polynomial', order=3, rej_lo=3, rej_hi=3, niter=10, plotover=False, log=None, verbose=True): """Bias subtracts the bias levels from a frame. It will fit and subtract the overscan region, trim the images, and subtract a master bias if required. struct--image structure subover--subtract the overscan region trim--trim the image subbias--subtract master bias bstruct--master bias image structure median--use the median instead of mean in image statistics function--form to fit to the overscan region order--order for the function rej_lo--sigma of low points to reject in the fit rej_hi--sigma of high points to reject in the fit niter--number of iterations log--saltio log for recording information verbose--whether to print to stdout """ infile = saltkey.getimagename(struct[0]) # how many extensions? nsciext = saltkey.get('NSCIEXT', struct[0]) nextend = saltkey.get('NEXTEND', struct[0]) nccd = saltkey.get('NCCDS', struct[0]) # how many amplifiers?--this is hard wired amplifiers = 2 * nccd #log the process if subover and log: message = '%28s %7s %5s %4s %6s' % \ ('HDU','Overscan','Order','RMS','Niter') log.message( '\n --------------------------------------------------', with_header=False, with_stdout=verbose) log.message(message, with_header=False, with_stdout=verbose) log.message(' --------------------------------------------------', with_header=False, with_stdout=verbose) if (plotover): plt.figure(1) plt.axes([0.1, 0.1, 0.8, 0.8]) plt.xlabel('CCD Column') plt.ylabel('Pixel Counts (e-)') plt.ion() #loop through the extensions and subtract the bias for i in range(1, nsciext + 1): if struct[i].name == 'SCI': #get the bias section biassec = saltkey.get('BIASSEC', struct[i]) y1, y2, x1, x2 = saltio.getSection(biassec, iraf_format=True) #get the data section datasec = saltkey.get('DATASEC', struct[i]) dy1, dy2, dx1, dx2 = saltio.getSection(datasec, iraf_format=True) #setup the overscan region if subover: yarr = np.arange(y1, y2, dtype=float) data = struct[i].data odata = struct[i].data[y1:y2, x1:x2] if median: odata = np.median((struct[i].data[y1:y2, x1:x2]), axis=1) olevel = np.median((struct[i].data[y1:y2, x1:x2])) saltkey.new('OVERSCAN', '%f' % (olevel), 'Overscan median value', struct[i]) else: odata = np.mean((struct[i].data[y1:y2, x1:x2]), axis=1) olevel = np.mean((struct[i].data[y1:y2, x1:x2])) saltkey.new('OVERSCAN', '%f' % (olevel), 'Overscan mean value', struct[i]) #fit the overscan region ifit=saltfit.interfit(yarr, odata, function=function, \ order=order, thresh=rej_hi, niter=niter) try: ifit.interfit() coeffs = ifit.coef ofit = ifit(yarr) omean, omed, osigma = saltstat.iterstat((odata - ofit), sig=3, niter=5) except ValueError: #catch the error if it is a zero array ofit = np.array(yarr) * 0.0 osigma = 0.0 except TypeError: #catch the error if it is a zero array ofit = np.array(yarr) * 0.0 osigma = 0.0 #if it hasn't been already, convert image to #double format struct[i].data = 1.0 * struct[i].data try: struct[i].header.remove('BZERO') struct[i].header.remove('BSCALE') except: pass #subtract the overscan region for j in range(len(struct[i].data[0])): struct[i].data[y1:y2, j] -= ofit #report the information if log: message = '%25s[%1d] %8.2f %3d %7.2f %3d' % \ (infile, i, olevel, order, osigma, niter) log.message(message, with_stdout=verbose, with_header=False) #add the statistics to the image header saltkey.new('OVERRMS', '%f' % (osigma), 'Overscan RMS value', struct[i]) #update the variance frame if saltkey.found('VAREXT', struct[i]): vhdu = saltkey.get('VAREXT', struct[i]) try: vdata = struct[vhdu].data #The bias level should not be included in the noise from the signal for j in range(len(struct[i].data[0])): vdata[y1:y2, j] -= ofit #add a bit to make sure that the minimum error is the rednoise rdnoise = saltkey.get('RDNOISE', struct[i]) vdata[vdata < rdnoise**2] = rdnoise**2 struct[vhdu].data = vdata + osigma**2 except Exception, e: msg = 'Cannot update the variance frame in %s[%i] because %s' % ( infile, vhdu, e) raise SaltError(msg) #plot the overscan region if plotover: plt.plot(yarr, odata) plt.plot(yarr, ofit) #trim the data and update the headers if trim: struct[i].data = struct[i].data[dy1:dy2, dx1:dx2] datasec = '[1:' + str(dx2 - dx1) + ',1:' + str(dy2 - dy1) + ']' saltkey.put('DATASEC', datasec, struct[i]) #update the variance frame if saltkey.found('VAREXT', struct[i]): vhdu = saltkey.get('VAREXT', struct[i]) struct[vhdu].data = struct[vhdu].data[dy1:dy2, dx1:dx2] datasec = '[1:' + str(dx2 - dx1) + ',1:' + str(dy2 - dy1) + ']' saltkey.put('DATASEC', datasec, struct[vhdu]) #update the BPM frame if saltkey.found('BPMEXT', struct[i]): bhdu = saltkey.get('BPMEXT', struct[i]) struct[bhdu].data = struct[bhdu].data[dy1:dy2, dx1:dx2] datasec = '[1:' + str(dx2 - dx1) + ',1:' + str(dy2 - dy1) + ']' saltkey.put('DATASEC', datasec, struct[bhdu]) #subtract the master bias if necessary if subbias and bstruct: struct[i].data -= bstruct[i].data #update the variance frame if saltkey.found('VAREXT', struct[i]): vhdu = saltkey.get('VAREXT', struct[i]) try: vdata = struct[vhdu].data struct[vhdu].data = vdata + bstruct[vhdu].data except Exception, e: msg = 'Cannot update the variance frame in %s[%i] because %s' % ( infile, vhdu, e) raise SaltError(msg)
def make_mosaic(struct, gap, xshift, yshift, rotation, interp_type='linear', boundary='constant', constant=0, geotran=True, fill=False, cleanup=True, log=None, verbose=False): """Given a SALT image struct, combine each of the individual amplifiers and apply the geometric CCD transformations to the image """ # get the name of the file infile = saltkey.getimagename(struct[0], base=True) outpath = './' # identify instrument instrume, keyprep, keygain, keybias, keyxtalk, keyslot = \ saltkey.instrumid(struct) # how many amplifiers? nsciext = saltkey.get('NSCIEXT', struct[0]) nextend = saltkey.get('NEXTEND', struct[0]) nccds = saltkey.get('NCCDS', struct[0]) amplifiers = nccds * 2 if nextend > nsciext: varframe = True else: varframe = False # CCD geometry coefficients if (instrume == 'RSS' or instrume == 'PFIS'): xsh = [0., xshift[0], 0., xshift[1]] ysh = [0., yshift[0], 0., yshift[1]] rot = [0., rotation[0], 0., rotation[1]] elif instrume == 'SALTICAM': xsh = [0., xshift[0], 0.] ysh = [0., yshift[0], 0.] rot = [0., rotation[0], 0] # how many extensions? nextend = saltkey.get('NEXTEND', struct[0]) # CCD on-chip binning xbin, ybin = saltkey.ccdbin(struct[0]) # create temporary primary extension outstruct = [] outstruct.append(struct[0]) # define temporary FITS file store tiled CCDs tilefile = saltio.tmpfile(outpath) tilefile += 'tile.fits' if varframe: tilehdu = [None] * (3 * int(nsciext / 2) + 1) else: tilehdu = [None] * int(nsciext / 2 + 1) tilehdu[0] = fits.PrimaryHDU() #tilehdu[0].header = struct[0].header if log: log.message('', with_stdout=verbose) # iterate over amplifiers, stich them to produce file of CCD images for i in range(int(nsciext / 2)): hdu = i * 2 + 1 # amplifier = hdu%amplifiers # if (amplifier == 0): amplifier = amplifiers # read DATASEC keywords datasec1 = saltkey.get('DATASEC', struct[hdu]) datasec2 = saltkey.get('DATASEC', struct[hdu + 1]) xdsec1, ydsec1 = saltstring.secsplit(datasec1) xdsec2, ydsec2 = saltstring.secsplit(datasec2) # read images imdata1 = saltio.readimage(struct, hdu) imdata2 = saltio.readimage(struct, hdu + 1) # tile 2n amplifiers to yield n CCD images outdata = numpy.zeros( (int(ydsec1[1] + abs(ysh[i + 1] / ybin)), int(xdsec1[1] + xdsec2[1] + abs(xsh[i + 1] / xbin))), numpy.float32) # set up the variance frame if varframe: vardata = outdata.copy() vdata1 = saltio.readimage(struct, struct[hdu].header['VAREXT']) vdata2 = saltio.readimage(struct, struct[hdu + 1].header['VAREXT']) bpmdata = outdata.copy() bdata1 = saltio.readimage(struct, struct[hdu].header['BPMEXT']) bdata2 = saltio.readimage(struct, struct[hdu + 1].header['BPMEXT']) x1 = xdsec1[0] - 1 if x1 != 0: msg = 'The data in %s have not been trimmed prior to mosaicking.' \ % infile log.error(msg) if xsh[i + 1] < 0: x1 += int(abs(xsh[i + 1] / xbin)) x2 = x1 + xdsec1[1] y1 = ydsec1[0] - 1 if ysh[i + 1] < 0: y1 += int(abs(ysh[i + 1] / ybin)) y2 = y1 + ydsec1[1] outdata[y1:y2, x1:x2] =\ imdata1[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] if varframe: vardata[y1:y2, x1:x2] =\ vdata1[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] bpmdata[y1:y2, x1:x2] =\ bdata1[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] x1 = x2 x2 = x1 + xdsec2[1] y1 = ydsec2[0] - 1 if ysh[i + 1] < 0: y1 += abs(ysh[i + 1] / ybin) y2 = y1 + ydsec2[1] outdata[y1:y2, x1:x2] =\ imdata2[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] if varframe: vardata[y1:y2, x1:x2] =\ vdata2[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] bpmdata[y1:y2, x1:x2] =\ bdata2[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] # size of new image naxis1 = str(xdsec1[1] + xdsec2[1]) naxis2 = str(ydsec1[1]) # add image and keywords to HDU list tilehdu[i + 1] = fits.ImageHDU(outdata) tilehdu[i + 1].header = struct[hdu].header #tilehdu[ # i + 1].header['DATASEC'] = '[1:' + naxis1 + ',1:' + naxis2 + ']' if varframe: vext = i + 1 + int(nsciext / 2.) tilehdu[vext] = fits.ImageHDU(vardata) #tilehdu[vext].header = struct[struct[hdu].header['VAREXT']].header #tilehdu[vext].header[ # 'DATASEC'] = '[1:' + naxis1 + ',1:' + naxis2 + ']' bext = i + 1 + 2 * int(nsciext / 2.) tilehdu[bext] = fits.ImageHDU(bpmdata) #tilehdu[bext].header = struct[struct[hdu].header['BPMEXT']].header #tilehdu[bext].header[ # 'DATASEC'] = '[1:' + naxis1 + ',1:' + naxis2 + ']' # image tile log message #1 if log: message = os.path.basename(infile) + '[' + str(hdu) + '][' message += str(xdsec1[0]) + ':' + str(xdsec1[1]) + ',' message += str(ydsec1[0]) + ':' + str(ydsec1[1]) + '] --> ' message += os.path.basename(tilefile) + '[' + str(i + 1) + '][' message += str(xdsec1[0]) + ':' + str(xdsec1[1]) + ',' message += str(ydsec1[0]) + ':' + str(ydsec1[1]) + ']' log.message(message, with_stdout=verbose, with_header=False) message = os.path.basename(infile) + '[' + str(hdu + 1) + '][' message += str(xdsec1[0]) + ':' + str(xdsec1[1]) + ',' message += str(ydsec1[0]) + ':' + str(ydsec1[1]) + '] --> ' message += os.path.basename(tilefile) + '[' + str(i + 1) + '][' message += str(xdsec1[1] + 1) + ':' + \ str(xdsec1[1] + xdsec2[1]) + ',' message += str(ydsec2[0]) + ':' + str(ydsec2[1]) + ']' log.message(message, with_stdout=verbose, with_header=False) # write temporary file of tiled CCDs hdulist = fits.HDUList(tilehdu) hdulist.writeto(tilefile) # iterate over CCDs, transform and rotate images yrot = [None] * 4 xrot = [None] * 4 tranfile = [' '] tranhdu = [0] if varframe: tranfile = [''] * (3 * int(nsciext / 2) + 1) tranhdu = [0] * (3 * int(nsciext / 2) + 1) else: tranfile = [''] * int(nsciext / 2 + 1) tranhdu = [0] * int(nsciext / 2 + 1) # this is hardwired for SALT where the second CCD is considered the # fiducial for hdu in range(1, int(nsciext / 2 + 1)): tranfile[hdu] = saltio.tmpfile(outpath) tranfile[hdu] += 'tran.fits' if varframe: tranfile[hdu + nccds] = saltio.tmpfile(outpath) + 'tran.fits' tranfile[hdu + 2 * nccds] = saltio.tmpfile(outpath) + 'tran.fits' ccd = hdu % nccds if (ccd == 0): ccd = nccds # correct rotation for CCD binning yrot[ccd] = rot[ccd] * ybin / xbin xrot[ccd] = rot[ccd] * xbin / ybin dxshift = xbin * int(float(int(gap) / xbin) + 0.5) - gap # transformation using geotran IRAF task # if (ccd == 1): if (ccd != 2): if geotran: message = '\nSALTMOSAIC -- geotran ' + tilefile + \ '[' + str(ccd) + '] ' + tranfile[hdu] message += ' \"\" \"\" xshift=' + \ str((xsh[ccd] + (2 - ccd) * dxshift) / xbin) + ' ' message += 'yshift=' + \ str(ysh[ccd] / ybin) + ' xrotation=' + str(xrot[ccd]) + ' ' message += 'yrotation=' + \ str(yrot[ccd]) + ' xmag=1 ymag=1 xmin=\'INDEF\'' message += 'xmax=\'INDEF\' ymin=\'INDEF\' ymax=\'INDEF\' ' message += 'ncols=\'INDEF\' ' message += 'nlines=\'INDEF\' verbose=\'no\' ' message += 'fluxconserve=\'yes\' nxblock=2048 ' message += 'nyblock=2048 interpolant=\'' + \ interp_type + '\' boundary=\'constant\' constant=0' log.message(message, with_stdout=verbose) yd, xd = tilehdu[ccd].data.shape ncols = 'INDEF' # ncols=xd+abs(xsh[ccd]/xbin) nlines = 'INDEF' # nlines=yd+abs(ysh[ccd]/ybin) geo_xshift = xsh[ccd] + (2 - ccd) * dxshift / xbin geo_yshift = ysh[ccd] / ybin iraf.images.immatch.geotran(tilefile + "[" + str(ccd) + "]", tranfile[hdu], "", "", xshift=geo_xshift, yshift=geo_yshift, xrotation=xrot[ccd], yrotation=yrot[ccd], xmag=1, ymag=1, xmin='INDEF', xmax='INDEF', ymin='INDEF', ymax='INDEF', ncols=ncols, nlines=nlines, verbose='no', fluxconserve='yes', nxblock=2048, nyblock=2048, interpolant="linear", boundary="constant", constant=0) if varframe: var_infile = tilefile + "[" + str(ccd + nccds) + "]" iraf.images.immatch.geotran(var_infile, tranfile[hdu + nccds], "", "", xshift=geo_xshift, yshift=geo_yshift, xrotation=xrot[ccd], yrotation=yrot[ccd], xmag=1, ymag=1, xmin='INDEF', xmax='INDEF', ymin='INDEF', ymax='INDEF', ncols=ncols, nlines=nlines, verbose='no', fluxconserve='yes', nxblock=2048, nyblock=2048, interpolant="linear", boundary="constant", constant=0) var2_infile = tilefile + "[" + str(ccd + 2 * nccds) + "]" iraf.images.immatch.geotran(var2_infile, tranfile[hdu + 2 * nccds], "", "", xshift=geo_xshift, yshift=geo_yshift, xrotation=xrot[ccd], yrotation=yrot[ccd], xmag=1, ymag=1, xmin='INDEF', xmax='INDEF', ymin='INDEF', ymax='INDEF', ncols=ncols, nlines=nlines, verbose='no', fluxconserve='yes', nxblock=2048, nyblock=2048, interpolant="linear", boundary="constant", constant=0) # open the file and copy the data to tranhdu tstruct = fits.open(tranfile[hdu]) tranhdu[hdu] = tstruct[0].data tstruct.close() if varframe: tranhdu[hdu + nccds] = fits.open(tranfile[hdu + nccds])[0].data tranhdu[hdu + 2 * nccds] = fits.open( tranfile[hdu + 2 * nccds])[0].data else: log.message("Transform CCD #%i using dx=%s, dy=%s, rot=%s" % (ccd, xsh[ccd] / 2.0, ysh[ccd] / 2.0, xrot[ccd]), with_stdout=verbose, with_header=False) tranhdu[hdu] = geometric_transform( tilehdu[ccd].data, tran_func, prefilter=False, order=1, extra_arguments=(xsh[ccd] / 2, ysh[ccd] / 2, 1, 1, xrot[ccd], yrot[ccd])) tstruct = fits.PrimaryHDU(tranhdu[hdu]) tstruct.writeto(tranfile[hdu]) if varframe: tranhdu[hdu + nccds] = geometric_transform( tilehdu[hdu + 3].data, tran_func, prefilter=False, order=1, extra_arguments=(xsh[ccd] / 2, ysh[ccd] / 2, 1, 1, xrot[ccd], yrot[ccd])) tranhdu[hdu + 2 * nccds] = geometric_transform( tilehdu[hdu + 6].data, tran_func, prefilter=False, order=1, extra_arguments=(xsh[ccd] / 2, ysh[ccd] / 2, 1, 1, xrot[ccd], yrot[ccd])) else: log.message("Transform CCD #%i using dx=%s, dy=%s, rot=%s" % (ccd, 0, 0, 0), with_stdout=verbose, with_header=False) tranhdu[hdu] = tilehdu[ccd].data if varframe: tranhdu[hdu + nccds] = tilehdu[ccd + nccds].data tranhdu[hdu + 2 * nccds] = tilehdu[ccd + 2 * nccds].data # open outfile if varframe: outlist = 4 * [None] else: outlist = 2 * [None] #outlist[0] = struct[0].copy() outlist[0] = fits.PrimaryHDU() outlist[0].header = struct[0].header naxis1 = int(gap / xbin * (nccds - 1)) naxis2 = 0 for i in range(1, nccds + 1): yw, xw = tranhdu[i].shape naxis1 += xw + int(abs(xsh[ccd] / xbin)) + 1 naxis2 = max(naxis2, yw) outdata = numpy.zeros((naxis2, naxis1), numpy.float32) outdata.shape = naxis2, naxis1 if varframe: vardata = outdata * 0 bpmdata = outdata * 0 + 1 # iterate over CCDs, stich them to produce a full image hdu = 0 totxshift = 0 for hdu in range(1, nccds + 1): # read DATASEC keywords ydsec, xdsec = tranhdu[hdu].shape # define size and shape of final image # tile CCDs to yield mosaiced image x1 = int((hdu - 1) * (xdsec + gap / xbin)) + int(totxshift) x2 = xdsec + x1 y1 = int(0) y2 = int(ydsec) outdata[y1:y2, x1:x2] = tranhdu[hdu] totxshift += int(abs(xsh[hdu] / xbin)) + 1 if varframe: vardata[y1:y2, x1:x2] = tranhdu[hdu + nccds] bpmdata[y1:y2, x1:x2] = tranhdu[hdu + 2 * nccds] # make sure to cover up all the gaps include bad areas if varframe: baddata = (outdata == 0) baddata = nd.maximum_filter(baddata, size=3) bpmdata[baddata] = 1 # fill in the gaps if requested if fill: if varframe: outdata = fill_gaps(outdata, 0) else: outdata = fill_gaps(outdata, 0) # add to the file outlist[1] = fits.ImageHDU(outdata) if varframe: outlist[2] = fits.ImageHDU(vardata, name='VAR') outlist[3] = fits.ImageHDU(bpmdata, name='BPM') # create the image structure outstruct = fits.HDUList(outlist) # update the head informaation # housekeeping keywords saltkey.put('NEXTEND', 2, outstruct[0]) saltkey.new('EXTNAME', 'SCI', 'Extension name', outstruct[1]) saltkey.new('EXTVER', 1, 'Extension number', outstruct[1]) if varframe: saltkey.new('VAREXT', 2, 'Variance frame extension', outstruct[1]) saltkey.new('BPMEXT', 3, 'BPM Extension', outstruct[1]) try: saltkey.copy(struct[1], outstruct[1], 'CCDSUM') except: pass # Add keywords associated with geometry saltkey.new('SGEOMGAP', gap, 'SALT Chip Gap', outstruct[0]) c1str = '{:3.2f} {:3.2f} {:3.4f}'.format(xshift[0], yshift[0], rotation[0]) saltkey.new('SGEOM1', c1str, 'SALT Chip 1 Transform', outstruct[0]) c2str = '{:3.2f} {:3.2f} {:3.4f}'.format(xshift[1], yshift[1], rotation[1]) saltkey.new('SGEOM2', c2str, 'SALT Chip 2 Transform', outstruct[0]) # WCS keywords saltkey.new('CRPIX1', 0, 'WCS: X reference pixel', outstruct[1]) saltkey.new('CRPIX2', 0, 'WCS: Y reference pixel', outstruct[1]) saltkey.new('CRVAL1', float(xbin), 'WCS: X reference coordinate value', outstruct[1]) saltkey.new('CRVAL2', float(ybin), 'WCS: Y reference coordinate value', outstruct[1]) saltkey.new('CDELT1', float(xbin), 'WCS: X pixel size', outstruct[1]) saltkey.new('CDELT2', float(ybin), 'WCS: Y pixel size', outstruct[1]) saltkey.new('CTYPE1', 'pixel', 'X type', outstruct[1]) saltkey.new('CTYPE2', 'pixel', 'Y type', outstruct[1]) # cleanup temporary files if cleanup: for tfile in tranfile: if os.path.isfile(tfile): saltio.delete(tfile) if os.path.isfile(tilefile): status = saltio.delete(tilefile) # return the file return outstruct
def slot(struct,infile,dbspeed,dbrate,dbgain,dbnoise,dbbias,dbamp,xcoeff,gaindb,xtalkfile, logfile,verbose): import saltprint, saltkey, saltio, saltstat, time # identify instrument instrume,keyprep,keygain,keybias,keyxtalk,keyslot,status = saltkey.instrumid(struct,infile,logfile) # number of image HDU nextend = 0 while (status == 0): try: struct[nextend+1].header['XTENSION'] nextend += 1 except: break nccds,status = saltkey.get('NCCDS',struct[0],infile,logfile) amplifiers = nccds * 2 if (nextend%(amplifiers) != 0): message = '\nERROR -- SALTSLOT: Number of image extensions and' message += 'number of amplifiers are not consistent' status = saltprint.err(saltlog,message) status = saltkey.new('NSCIEXT',nextend,'Number of science extensions',struct[0],infile,logfile) status = saltkey.new('NEXTEND',nextend,'Number of data extensions',struct[0],infile,logfile) # check image file and gain database are compatible if (status == 0): ngains = len(dbgain) if (int(max(dbamp)) != amplifiers): message = '\nERROR -- SALTGSLOT: ' + infile + ' contains ' + str(amplifiers) + ' amplifiers' message += ', the gaindb file ' + gaindb + ' contains ' + str(max(dbamp)) + ' amplifiers' status = saltprint.err(logfile,message) # check image file and cross talk database are compatible if (status == 0): if (len(xcoeff)-1 != amplifiers): message = '\nERROR -- SALTSLOT: ' + infile + ' contains ' + str(amplifiers) + ' amplifiers' message += ', the cross talk file ' + xtalkfile + ' contains ' + str(len(xcoeff)-1) + ' amplifiers' status = saltprint.err(logfile,message) # housekeeping keywords if (status == 0): status = saltkey.put('SAL-TLM',time.asctime(time.localtime()),struct[0],infile,logfile) status = saltkey.new(keyslot,time.asctime(time.localtime()), 'Data have been cleaned by SALTSLOT',struct[0],infile,logfile) # keywords for image extensions for i in range(nextend): hdu = i + 1 status = saltkey.new('EXTNAME','SCI','Extension name',struct[hdu],infile,logfile) status = saltkey.new('EXTVER',hdu,'Extension number',struct[hdu],infile,logfile) # log coefficent table if (status == 0): message = '%30s %5s %4s %8s' % ('HDU','Gain','Bias','Xtalk') saltprint.log(logfile,'\n ---------------------------------------------',verbose) saltprint.log(logfile,message,verbose) saltprint.log(logfile,' ---------------------------------------------',verbose) # loop over image extensions if (status == 0): for i in range(nextend/2): hdu = i * 2 + 1 amplifier = hdu%amplifiers if (amplifier == 0): amplifier = amplifiers if (status == 0): value,status = saltkey.get('NAXIS1',struct[hdu],infile,logfile) naxis1 = int(value) if (status == 0): value,status = saltkey.get('NAXIS1',struct[hdu+1],infile,logfile) naxis2 = int(value) if (status == 0 and hdu == 1): biassec, status = saltkey.get('BIASSEC',struct[hdu],infile,logfile) if (status == 0 and hdu == 1): ranges = biassec.lstrip('[').rstrip(']').split(',') x1_1 = int(ranges[0].split(':')[0]) - 1 x2_1 = int(ranges[0].split(':')[1]) - 1 y1_1 = int(ranges[1].split(':')[0]) - 1 y2_1 = int(ranges[1].split(':')[1]) - 1 if (status == 0 and hdu == 1): biassec, status = saltkey.get('BIASSEC',struct[hdu+1],infile,logfile) if (status == 0 and hdu == 1): ranges = biassec.lstrip('[').rstrip(']').split(',') x1_2 = int(ranges[0].split(':')[0]) - 1 x2_2 = int(ranges[0].split(':')[1]) - 1 y1_2 = int(ranges[1].split(':')[0]) - 1 y2_2 = int(ranges[1].split(':')[1]) - 1 if (status == 0 and hdu == 1): datasec,status = saltkey.get('DATASEC',struct[hdu],infile,logfile) if (status == 0 and hdu == 1): ranges = datasec.lstrip('[').rstrip(']').split(',') dx1_1 = int(ranges[0].split(':')[0]) - 1 dx2_1 = int(ranges[0].split(':')[1]) dy1_1 = int(ranges[1].split(':')[0]) - 1 dy2_1 = int(ranges[1].split(':')[1]) if (status == 0 and hdu == 1): datasec,status = saltkey.get('DATASEC',struct[hdu+1],infile,logfile) if (status == 0 and hdu == 1): ranges = datasec.lstrip('[').rstrip(']').split(',') dx1_2 = int(ranges[0].split(':')[0]) - 1 dx2_2 = int(ranges[0].split(':')[1]) dy1_2 = int(ranges[1].split(':')[0]) - 1 dy2_2 = int(ranges[1].split(':')[1]) if (status == 0 and dx2_1 - dx1_1 != dx2_2 - dx1_2): message = 'ERROR -- SALTSLOT: HDUs '+infile message += '['+str(hdu)+'] and '+infile+'['+str(hdu+1)+']' message += ' have different dimensions' status = saltprint.err(logfile,message) # read speed and gain of each exposure if (status == 0 and hdu == 1): gainset,status = saltkey.get('GAINSET',struct[0],infile,logfile) rospeed,status = saltkey.get('ROSPEED',struct[0],infile,logfile) if (rospeed == 'NONE'): saltprint.log(logfile," ",verbose) message = "ERROR -- SALTSLOT: Readout speed is 'NONE' in " message += "primary keywords of " + infile status = saltprint.err(logfile,message) # read raw images if (status == 0): imagedata1,status = saltio.readimage(struct,hdu,logfile) imagedata2,status = saltio.readimage(struct,hdu+1,logfile) # gain correction if (status == 0): for j in range(len(dbgain)): if (gainset == dbrate[j] and rospeed == dbspeed[j] and amplifier == int(dbamp[j])): try: gain1 = float(dbgain[j]) imagedata1 *= gain1 except: mesage = 'ERROR -- SALTSLOT: Cannot perform gain correction on image ' message += infile+'['+str(hdu)+']' status = saltprint.err(logfile,message) elif (gainset == dbrate[j] and rospeed == dbspeed[j] and amplifier + 1 == int(dbamp[j])): try: gain2 = float(dbgain[j]) imagedata2 *= gain2 except: mesage = 'ERROR -- SALTSLOT: Cannot perform gain correction on image ' message += infile+'['+str(hdu+1)+']' status = saltprint.err(logfile,message) # crosstalk correction if (status == 0): revimage1 = imagedata1 * float(xcoeff[amplifier]) revimage2 = imagedata2 * float(xcoeff[amplifier+1]) for j in range(dx2_1-dx1_1+1): imagedata1[:,j] -= revimage2[:,dx2_2-j-1] imagedata2[:,j] -= revimage1[:,dx2_1-j-1] # bias subtraction if (status == 0): overx_val_1 = [] overx_val_2 = [] for x in range(x1_1,x2_1+1): list_1 = imagedata1[y1_1:y2_1,x] * 1.0 overx_val_1.append(saltstat.median(list_1,logfile)) overlevel_1 = saltstat.median(overx_val_1,logfile) for x in range(x1_2,x2_2+1): list_2 = imagedata2[y1_2:y2_2,x] * 1.0 overx_val_2.append(saltstat.median(list_2,logfile)) overlevel_2 = saltstat.median(overx_val_2,logfile) imagedata1 -= overlevel_1 imagedata2 -= overlevel_2 # trim overscan if (status == 0): imagedata1 = imagedata1[dy1_1:dy2_1,dx1_1:dx2_1] imagedata2 = imagedata2[dy1_2:dy2_2,dx1_2:dx2_2] datasec = '[1:'+str(dx2_1-dx1_1)+',1:'+str(dy2_1-dy1_1)+']' status = saltkey.put('DATASEC',datasec,struct[hdu],infile,logfile) status = saltkey.rem('BIASSEC',struct[hdu],infile,logfile) datasec = '[1:'+str(dx2_2-dx1_2)+',1:'+str(dy2_2-dy1_2)+']' status = saltkey.put('DATASEC',datasec,struct[hdu+1],infile,logfile) status = saltkey.rem('BIASSEC',struct[hdu+1],infile,logfile) # log coefficient table if (status == 0): infilename = infile.split('/') infilename = infilename[len(infilename)-1] message = '%25s[%3d] %5.2f %4d %8.6f' % \ (infilename, hdu, gain1, overlevel_1, float(xcoeff[amplifier+1])) saltprint.log(logfile,message,verbose) message = '%25s[%3d] %5.2f %4d %8.6f' % \ (infilename, hdu+1, gain2, overlevel_2,float(xcoeff[amplifier])) saltprint.log(logfile,message,verbose) # update image in HDU structure if (status == 0): struct,status = saltio.writeimage(struct,hdu,imagedata1,logfile) struct,status = saltio.writeimage(struct,hdu+1,imagedata2,logfile) return struct, status
def make_mosaic(struct, gap, xshift, yshift, rotation, interp_type='linear', boundary='constant', constant=0, geotran=True, fill=False, cleanup=True, log=None, verbose=False): """Given a SALT image struct, combine each of the individual amplifiers and apply the geometric CCD transformations to the image """ # get the name of the file infile = saltkey.getimagename(struct[0], base=True) outpath = './' # identify instrument instrume, keyprep, keygain, keybias, keyxtalk, keyslot = \ saltkey.instrumid(struct) # how many amplifiers? nsciext = saltkey.get('NSCIEXT', struct[0]) nextend = saltkey.get('NEXTEND', struct[0]) nccds = saltkey.get('NCCDS', struct[0]) amplifiers = nccds * 2 if nextend > nsciext: varframe = True else: varframe = False # CCD geometry coefficients if (instrume == 'RSS' or instrume == 'PFIS'): xsh = [0., xshift[0], 0., xshift[1]] ysh = [0., yshift[0], 0., yshift[1]] rot = [0., rotation[0], 0., rotation[1]] elif instrume == 'SALTICAM': xsh = [0., xshift[0], 0.] ysh = [0., yshift[0], 0.] rot = [0., rotation[0], 0] # how many extensions? nextend = saltkey.get('NEXTEND', struct[0]) # CCD on-chip binning xbin, ybin = saltkey.ccdbin(struct[0]) # create temporary primary extension outstruct = [] outstruct.append(struct[0]) # define temporary FITS file store tiled CCDs tilefile = saltio.tmpfile(outpath) tilefile += 'tile.fits' if varframe: tilehdu = [None] * (3 * int(nsciext / 2) + 1) else: tilehdu = [None] * int(nsciext / 2 + 1) tilehdu[0] = fits.PrimaryHDU() #tilehdu[0].header = struct[0].header if log: log.message('', with_stdout=verbose) # iterate over amplifiers, stich them to produce file of CCD images for i in range(int(nsciext / 2)): hdu = i * 2 + 1 # amplifier = hdu%amplifiers # if (amplifier == 0): amplifier = amplifiers # read DATASEC keywords datasec1 = saltkey.get('DATASEC', struct[hdu]) datasec2 = saltkey.get('DATASEC', struct[hdu + 1]) xdsec1, ydsec1 = saltstring.secsplit(datasec1) xdsec2, ydsec2 = saltstring.secsplit(datasec2) # read images imdata1 = saltio.readimage(struct, hdu) imdata2 = saltio.readimage(struct, hdu + 1) # tile 2n amplifiers to yield n CCD images outdata = numpy.zeros((ydsec1[1] + abs(ysh[i + 1] / ybin), xdsec1[1] + xdsec2[1] + abs(xsh[i + 1] / xbin)), numpy.float32) # set up the variance frame if varframe: vardata = outdata.copy() vdata1 = saltio.readimage(struct, struct[hdu].header['VAREXT']) vdata2 = saltio.readimage(struct, struct[hdu + 1].header['VAREXT']) bpmdata = outdata.copy() bdata1 = saltio.readimage(struct, struct[hdu].header['BPMEXT']) bdata2 = saltio.readimage(struct, struct[hdu + 1].header['BPMEXT']) x1 = xdsec1[0] - 1 if x1 != 0: msg = 'The data in %s have not been trimmed prior to mosaicking.' \ % infile log.error(msg) if xsh[i + 1] < 0: x1 += abs(xsh[i + 1] / xbin) x2 = x1 + xdsec1[1] y1 = ydsec1[0] - 1 if ysh[i + 1] < 0: y1 += abs(ysh[i + 1] / ybin) y2 = y1 + ydsec1[1] outdata[y1:y2, x1:x2] =\ imdata1[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] if varframe: vardata[y1:y2, x1:x2] =\ vdata1[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] bpmdata[y1:y2, x1:x2] =\ bdata1[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] x1 = x2 x2 = x1 + xdsec2[1] y1 = ydsec2[0] - 1 if ysh[i + 1] < 0: y1 += abs(ysh[i + 1] / ybin) y2 = y1 + ydsec2[1] outdata[y1:y2, x1:x2] =\ imdata2[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] if varframe: vardata[y1:y2, x1:x2] =\ vdata2[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] bpmdata[y1:y2, x1:x2] =\ bdata2[ydsec1[0] - 1:ydsec1[1], xdsec1[0] - 1:xdsec1[1]] # size of new image naxis1 = str(xdsec1[1] + xdsec2[1]) naxis2 = str(ydsec1[1]) # add image and keywords to HDU list tilehdu[i + 1] = fits.ImageHDU(outdata) tilehdu[i + 1].header = struct[hdu].header #tilehdu[ # i + 1].header['DATASEC'] = '[1:' + naxis1 + ',1:' + naxis2 + ']' if varframe: vext = i + 1 + int(nsciext / 2.) tilehdu[vext] = fits.ImageHDU(vardata) #tilehdu[vext].header = struct[struct[hdu].header['VAREXT']].header #tilehdu[vext].header[ # 'DATASEC'] = '[1:' + naxis1 + ',1:' + naxis2 + ']' bext = i + 1 + 2 * int(nsciext / 2.) tilehdu[bext] = fits.ImageHDU(bpmdata) #tilehdu[bext].header = struct[struct[hdu].header['BPMEXT']].header #tilehdu[bext].header[ # 'DATASEC'] = '[1:' + naxis1 + ',1:' + naxis2 + ']' # image tile log message #1 if log: message = os.path.basename(infile) + '[' + str(hdu) + '][' message += str(xdsec1[0]) + ':' + str(xdsec1[1]) + ',' message += str(ydsec1[0]) + ':' + str(ydsec1[1]) + '] --> ' message += os.path.basename(tilefile) + '[' + str(i + 1) + '][' message += str(xdsec1[0]) + ':' + str(xdsec1[1]) + ',' message += str(ydsec1[0]) + ':' + str(ydsec1[1]) + ']' log.message(message, with_stdout=verbose, with_header=False) message = os.path.basename(infile) + '[' + str(hdu + 1) + '][' message += str(xdsec1[0]) + ':' + str(xdsec1[1]) + ',' message += str(ydsec1[0]) + ':' + str(ydsec1[1]) + '] --> ' message += os.path.basename(tilefile) + '[' + str(i + 1) + '][' message += str(xdsec1[1] + 1) + ':' + \ str(xdsec1[1] + xdsec2[1]) + ',' message += str(ydsec2[0]) + ':' + str(ydsec2[1]) + ']' log.message(message, with_stdout=verbose, with_header=False) # write temporary file of tiled CCDs hdulist = fits.HDUList(tilehdu) hdulist.writeto(tilefile) # iterate over CCDs, transform and rotate images yrot = [None] * 4 xrot = [None] * 4 tranfile = [' '] tranhdu = [0] if varframe: tranfile = [''] * (3 * int(nsciext / 2) + 1) tranhdu = [0] * (3 * int(nsciext / 2) + 1) else: tranfile = [''] * int(nsciext / 2 + 1) tranhdu = [0] * int(nsciext / 2 + 1) # this is hardwired for SALT where the second CCD is considered the # fiducial for hdu in range(1, int(nsciext / 2 + 1)): tranfile[hdu] = saltio.tmpfile(outpath) tranfile[hdu] += 'tran.fits' if varframe: tranfile[hdu + nccds] = saltio.tmpfile(outpath) + 'tran.fits' tranfile[hdu + 2 * nccds] = saltio.tmpfile(outpath) + 'tran.fits' ccd = hdu % nccds if (ccd == 0): ccd = nccds # correct rotation for CCD binning yrot[ccd] = rot[ccd] * ybin / xbin xrot[ccd] = rot[ccd] * xbin / ybin dxshift = xbin * int(float(int(gap) / xbin) + 0.5) - gap # transformation using geotran IRAF task # if (ccd == 1): if (ccd != 2): if geotran: message = '\nSALTMOSAIC -- geotran ' + tilefile + \ '[' + str(ccd) + '] ' + tranfile[hdu] message += ' \"\" \"\" xshift=' + \ str((xsh[ccd] + (2 - ccd) * dxshift) / xbin) + ' ' message += 'yshift=' + \ str(ysh[ccd] / ybin) + ' xrotation=' + str(xrot[ccd]) + ' ' message += 'yrotation=' + \ str(yrot[ccd]) + ' xmag=1 ymag=1 xmin=\'INDEF\'' message += 'xmax=\'INDEF\' ymin=\'INDEF\' ymax=\'INDEF\' ' message += 'ncols=\'INDEF\' ' message += 'nlines=\'INDEF\' verbose=\'no\' ' message += 'fluxconserve=\'yes\' nxblock=2048 ' message += 'nyblock=2048 interpolant=\'' + \ interp_type + '\' boundary=\'constant\' constant=0' log.message(message, with_stdout=verbose) yd, xd = tilehdu[ccd].data.shape ncols = 'INDEF' # ncols=xd+abs(xsh[ccd]/xbin) nlines = 'INDEF' # nlines=yd+abs(ysh[ccd]/ybin) geo_xshift = xsh[ccd] + (2 - ccd) * dxshift / xbin geo_yshift = ysh[ccd] / ybin iraf.images.immatch.geotran(tilefile + "[" + str(ccd) + "]", tranfile[hdu], "", "", xshift=geo_xshift, yshift=geo_yshift, xrotation=xrot[ccd], yrotation=yrot[ccd], xmag=1, ymag=1, xmin='INDEF', xmax='INDEF', ymin='INDEF', ymax='INDEF', ncols=ncols, nlines=nlines, verbose='no', fluxconserve='yes', nxblock=2048, nyblock=2048, interpolant="linear", boundary="constant", constant=0) if varframe: var_infile = tilefile + "[" + str(ccd + nccds) + "]" iraf.images.immatch.geotran(var_infile, tranfile[hdu + nccds], "", "", xshift=geo_xshift, yshift=geo_yshift, xrotation=xrot[ccd], yrotation=yrot[ccd], xmag=1, ymag=1, xmin='INDEF', xmax='INDEF', ymin='INDEF', ymax='INDEF', ncols=ncols, nlines=nlines, verbose='no', fluxconserve='yes', nxblock=2048, nyblock=2048, interpolant="linear", boundary="constant", constant=0) var2_infile = tilefile + "[" + str(ccd + 2 * nccds) + "]" iraf.images.immatch.geotran(var2_infile, tranfile[hdu + 2 * nccds], "", "", xshift=geo_xshift, yshift=geo_yshift, xrotation=xrot[ccd], yrotation=yrot[ccd], xmag=1, ymag=1, xmin='INDEF', xmax='INDEF', ymin='INDEF', ymax='INDEF', ncols=ncols, nlines=nlines, verbose='no', fluxconserve='yes', nxblock=2048, nyblock=2048, interpolant="linear", boundary="constant", constant=0) # open the file and copy the data to tranhdu tstruct = fits.open(tranfile[hdu]) tranhdu[hdu] = tstruct[0].data tstruct.close() if varframe: tranhdu[ hdu + nccds] = fits.open( tranfile[ hdu + nccds])[0].data tranhdu[ hdu + 2 * nccds] = fits.open( tranfile[ hdu + 2 * nccds])[0].data else: log.message( "Transform CCD #%i using dx=%s, dy=%s, rot=%s" % (ccd, xsh[ccd] / 2.0, ysh[ccd] / 2.0, xrot[ccd]), with_stdout=verbose, with_header=False) tranhdu[hdu] = geometric_transform( tilehdu[ccd].data, tran_func, prefilter=False, order=1, extra_arguments=( xsh[ccd] / 2, ysh[ccd] / 2, 1, 1, xrot[ccd], yrot[ccd])) tstruct = fits.PrimaryHDU(tranhdu[hdu]) tstruct.writeto(tranfile[hdu]) if varframe: tranhdu[hdu + nccds] = geometric_transform( tilehdu[hdu + 3].data, tran_func, prefilter=False, order=1, extra_arguments=( xsh[ccd] / 2, ysh[ccd] / 2, 1, 1, xrot[ccd], yrot[ccd])) tranhdu[hdu + 2 * nccds] = geometric_transform( tilehdu[hdu + 6].data, tran_func, prefilter=False, order=1, extra_arguments=( xsh[ccd] / 2, ysh[ccd] / 2, 1, 1, xrot[ccd], yrot[ccd])) else: log.message( "Transform CCD #%i using dx=%s, dy=%s, rot=%s" % (ccd, 0, 0, 0), with_stdout=verbose, with_header=False) tranhdu[hdu] = tilehdu[ccd].data if varframe: tranhdu[hdu + nccds] = tilehdu[ccd + nccds].data tranhdu[hdu + 2 * nccds] = tilehdu[ccd + 2 * nccds].data # open outfile if varframe: outlist = 4 * [None] else: outlist = 2 * [None] #outlist[0] = struct[0].copy() outlist[0] = fits.PrimaryHDU() outlist[0].header = struct[0].header naxis1 = int(gap / xbin * (nccds - 1)) naxis2 = 0 for i in range(1, nccds + 1): yw, xw = tranhdu[i].shape naxis1 += xw + int(abs(xsh[ccd] / xbin)) + 1 naxis2 = max(naxis2, yw) outdata = numpy.zeros((naxis2, naxis1), numpy.float32) outdata.shape = naxis2, naxis1 if varframe: vardata = outdata * 0 bpmdata = outdata * 0 + 1 # iterate over CCDs, stich them to produce a full image hdu = 0 totxshift = 0 for hdu in range(1, nccds + 1): # read DATASEC keywords ydsec, xdsec = tranhdu[hdu].shape # define size and shape of final image # tile CCDs to yield mosaiced image x1 = int((hdu - 1) * (xdsec + gap / xbin)) + int(totxshift) x2 = xdsec + x1 y1 = int(0) y2 = int(ydsec) outdata[y1:y2, x1:x2] = tranhdu[hdu] totxshift += int(abs(xsh[hdu] / xbin)) + 1 if varframe: vardata[y1:y2, x1:x2] = tranhdu[hdu + nccds] bpmdata[y1:y2, x1:x2] = tranhdu[hdu + 2 * nccds] # make sure to cover up all the gaps include bad areas if varframe: baddata = (outdata == 0) baddata = nd.maximum_filter(baddata, size=3) bpmdata[baddata] = 1 # fill in the gaps if requested if fill: if varframe: outdata = fill_gaps(outdata, 0) else: outdata = fill_gaps(outdata, 0) # add to the file outlist[1] = fits.ImageHDU(outdata) if varframe: outlist[2] = fits.ImageHDU(vardata,name='VAR') outlist[3] = fits.ImageHDU(bpmdata,name='BPM') # create the image structure outstruct = fits.HDUList(outlist) # update the head informaation # housekeeping keywords saltkey.put('NEXTEND', 2, outstruct[0]) saltkey.new('EXTNAME', 'SCI', 'Extension name', outstruct[1]) saltkey.new('EXTVER', 1, 'Extension number', outstruct[1]) if varframe: saltkey.new('VAREXT', 2, 'Variance frame extension', outstruct[1]) saltkey.new('BPMEXT', 3, 'BPM Extension', outstruct[1]) try: saltkey.copy(struct[1], outstruct[1], 'CCDSUM') except: pass # Add keywords associated with geometry saltkey.new('SGEOMGAP', gap, 'SALT Chip Gap', outstruct[0]) c1str = '{:3.2f} {:3.2f} {:3.4f}'.format(xshift[0], yshift[0], rotation[0]) saltkey.new('SGEOM1', c1str, 'SALT Chip 1 Transform', outstruct[0]) c2str = '{:3.2f} {:3.2f} {:3.4f}'.format(xshift[1], yshift[1], rotation[1]) saltkey.new('SGEOM2', c2str, 'SALT Chip 2 Transform', outstruct[0]) # WCS keywords saltkey.new('CRPIX1', 0, 'WCS: X reference pixel', outstruct[1]) saltkey.new('CRPIX2', 0, 'WCS: Y reference pixel', outstruct[1]) saltkey.new( 'CRVAL1', float(xbin), 'WCS: X reference coordinate value', outstruct[1]) saltkey.new( 'CRVAL2', float(ybin), 'WCS: Y reference coordinate value', outstruct[1]) saltkey.new('CDELT1', float(xbin), 'WCS: X pixel size', outstruct[1]) saltkey.new('CDELT2', float(ybin), 'WCS: Y pixel size', outstruct[1]) saltkey.new('CTYPE1', 'pixel', 'X type', outstruct[1]) saltkey.new('CTYPE2', 'pixel', 'Y type', outstruct[1]) # cleanup temporary files if cleanup: for tfile in tranfile: if os.path.isfile(tfile): saltio.delete(tfile) if os.path.isfile(tilefile): status = saltio.delete(tilefile) # return the file return outstruct
def slotmerge(images,outimages,outpref,geomfile,clobber,logfile,verbose): with logging(logfile,debug) as log: # are the arguments defined saltsafeio.argdefined('images',images) saltsafeio.argdefined('geomfile',geomfile) saltsafeio.argdefined('logfile',logfile) # if the input file is a list, does it exist? if images[0] == '@': saltsafeio.listexists('Input',images) # parse list of input files infiles=saltsafeio.listparse('Raw image',images,'','','') # check input files exist saltsafeio.filesexist(infiles,'','r') # load output name list: @list, * and comma separated outimages = outimages.strip() outpref = outpref.strip() if len(outpref) == 0 and len(outimages) == 0: raise SaltIOError('Output file(s) not specified') # test output @filelist exists if len(outimages) > 0 and outimages[0] == '@': saltsafeio.listexists('Output',outimages) # parse list of output files outfiles=saltsafeio.listparse('Output image',outimages,outpref,infiles,'') # are input and output lists the same length? saltsafeio.comparelists(infiles,outfiles,'Input','output') # do the output files already exist? if not clobber: saltsafeio.filesexist(outfiles,'','w') # does CCD geometry definition file exist geomfilefile = geomfile.strip() saltsafeio.fileexists(geomfile) # read geometry definition file gap = 0 xshift = [0, 0] yshift = [0, 0] rotation = [0, 0] gap, xshift, yshift, rotation=saltsafeio.readccdgeom(geomfile) for ro in rotation: if ro!=0: log.warning('SLOTMERGE currently ignores CCD rotation') # Begin processes each file for infile, outfile in zip(infiles, outfiles): # determine the name for the output file outpath = outfile.rstrip(os.path.basename(outfile)) if (len(outpath) == 0): outpath = '.' # open each raw image struct=saltsafeio.openfits(infile) # identify instrument instrume,keyprep,keygain,keybias,keyxtalk,keyslot=saltsafekey.instrumid(struct,infile) # how many amplifiers? nccds=saltsafekey.get('NCCDS',struct[0],infile) amplifiers = nccds * 2 #if (nccds != 2): # raise SaltError('Can not currently handle more than two CCDs') # CCD geometry coefficients if instrume == 'RSS' or instrume == 'PFIS': xsh = [xshift[0], 0., xshift[1]] ysh = [yshift[0], 0., yshift[1]] rot = [rotation[0], 0., rotation[1]] refid = 1 if instrume == 'SALTICAM': xsh = [xshift[0], 0.] ysh = [yshift[0], 0.] rot = [rotation[0], 0] refid = 1 # how many extensions? nextend=saltsafekey.get('NEXTEND',struct[0],infile) # how many exposures exposures = nextend/amplifiers # CCD on-chip binning xbin, ybin=saltsafekey.ccdbin(struct[0],infile) gp = int(gap / xbin) # create output hdu structure outstruct = [None] * int(exposures+1) outstruct[0]=struct[0] # iterate over exposures, stitch them to produce file of CCD images for i in range(exposures): # Determine the total size of the image xsize=0 ysize=0 for j in range(amplifiers): hdu=i*amplifiers+j+1 try: xsize += len(struct[hdu].data[0]) if ysize < len(struct[hdu].data): ysize=len(struct[hdu].data) except: msg='Unable to access extension %i ' % hdu raise SaltIOError(msg) xsize += gp* (nccds-1) maxxsh, minxsh = determineshifts(xsh) maxysh, minysh = determineshifts(ysh) xsize += (maxxsh-minxsh) ysize += (maxysh-minysh) # Determine the x and y origins for each frame xdist=0 ydist=0 shid=0 x0=np.zeros(amplifiers) y0=np.zeros(amplifiers) for j in range(amplifiers): x0[j]=xdist+xsh[shid]-minxsh y0[j]=ysh[shid]-minysh hdu=i*amplifiers+j+1 darr=struct[hdu].data xdist += len(darr[0]) if j%2==1: xdist += gp shid += 1 # make the out image outarr=np.zeros((ysize, xsize), np.float64) # Embed each frame into the output array for j in range(amplifiers): hdu=i*amplifiers+j+1 darr=struct[hdu].data outarr=salttran.embed(darr, x0[j], y0[j], outarr) # Add the outimage to the output structure hdu=i*amplifiers+1 outhdu=i+1 outstruct[outhdu] = pyfits.ImageHDU(outarr) outstruct[outhdu].header=struct[hdu].header # Fix the headers in each extension datasec='[1:%4i,1:%4i]' % (xsize, ysize) saltsafekey.put('DATASEC',datasec, outstruct[outhdu], outfile) saltsafekey.rem('DETSIZE',outstruct[outhdu],outfile) saltsafekey.rem('DETSEC',outstruct[outhdu],outfile) saltsafekey.rem('CCDSEC',outstruct[outhdu],outfile) saltsafekey.rem('AMPSEC',outstruct[outhdu],outfile) # add housekeeping key words outstruct[outhdu]=addhousekeeping(outstruct[outhdu], outhdu, outfile) # close input FITS file saltsafeio.closefits(struct) # housekeeping keywords keymosaic='SLOTMERG' fname, hist=history(level=1, wrap=False) saltsafekey.housekeeping(struct[0],keymosaic,'Amplifiers have been mosaiced', hist) #saltsafekey.history(outstruct[0],hist) # this is added for later use by saltsafekey.put('NCCDS', 0.5, outstruct[0]) saltsafekey.put('NSCIEXT', exposures, outstruct[0]) saltsafekey.put('NEXTEND', exposures, outstruct[0]) # write FITS file of mosaiced image outstruct=pyfits.HDUList(outstruct) saltsafeio.writefits(outstruct, outfile, clobber=clobber)
def bias(struct,subover=True,trim=True, subbias=False, bstruct=None, median=False, function='polynomial',order=3,rej_lo=3,rej_hi=3,niter=10, plotover=False, log=None, verbose=True): """Bias subtracts the bias levels from a frame. It will fit and subtract the overscan region, trim the images, and subtract a master bias if required. struct--image structure subover--subtract the overscan region trim--trim the image subbias--subtract master bias bstruct--master bias image structure median--use the median instead of mean in image statistics function--form to fit to the overscan region order--order for the function rej_lo--sigma of low points to reject in the fit rej_hi--sigma of high points to reject in the fit niter--number of iterations log--saltio log for recording information verbose--whether to print to stdout """ infile=saltkey.getimagename(struct[0]) # how many extensions? nsciext = saltkey.get('NSCIEXT',struct[0]) nextend = saltkey.get('NEXTEND',struct[0]) nccd = saltkey.get('NCCDS',struct[0]) # how many amplifiers?--this is hard wired amplifiers = 2 * nccd #log the process if subover and log: message = '%28s %7s %5s %4s %6s' % \ ('HDU','Overscan','Order','RMS','Niter') log.message('\n --------------------------------------------------', with_header=False, with_stdout=verbose) log.message(message, with_header=False, with_stdout=verbose) log.message(' --------------------------------------------------', with_header=False, with_stdout=verbose) if (plotover): plt.figure(1) plt.axes([0.1,0.1,0.8,0.8]) plt.xlabel('CCD Column') plt.ylabel('Pixel Counts (e-)') plt.ion() #loop through the extensions and subtract the bias for i in range(1,nsciext+1): if struct[i].name=='SCI': #get the bias section biassec = saltkey.get('BIASSEC',struct[i]) y1,y2,x1,x2 = saltio.getSection(biassec, iraf_format=True) #get the data section datasec = saltkey.get('DATASEC',struct[i]) dy1,dy2, dx1, dx2 = saltio.getSection(datasec, iraf_format=True) #setup the overscan region if subover: yarr=np.arange(y1,y2, dtype=float) data=struct[i].data odata=struct[i].data[y1:y2,x1:x2] if median: odata=np.median((struct[i].data[y1:y2,x1:x2]),axis=1) olevel=np.median((struct[i].data[y1:y2,x1:x2])) saltkey.new('OVERSCAN','%f' % (olevel),'Overscan median value', struct[i]) else: odata=np.mean((struct[i].data[y1:y2,x1:x2]),axis=1) olevel=np.mean((struct[i].data[y1:y2,x1:x2])) saltkey.new('OVERSCAN','%f' % (olevel),'Overscan mean value', struct[i]) #fit the overscan region ifit=saltfit.interfit(yarr, odata, function=function, \ order=order, thresh=rej_hi, niter=niter) try: ifit.interfit() coeffs=ifit.coef ofit=ifit(yarr) omean, omed, osigma=saltstat.iterstat((odata-ofit), sig=3, niter=5) except ValueError: #catch the error if it is a zero array ofit=np.array(yarr)*0.0 osigma=0.0 except TypeError: #catch the error if it is a zero array ofit=np.array(yarr)*0.0 osigma=0.0 #if it hasn't been already, convert image to #double format struct[i].data = 1.0 * struct[i].data try: struct[i].header.remove('BZERO') struct[i].header.remove('BSCALE') except: pass #subtract the overscan region for j in range(len(struct[i].data[0])): struct[i].data[y1:y2,j] -= ofit #report the information if log: message = '%25s[%1d] %8.2f %3d %7.2f %3d' % \ (infile, i, olevel, order, osigma, niter) log.message(message, with_stdout=verbose, with_header=False) #add the statistics to the image header saltkey.new('OVERRMS','%f' % (osigma),'Overscan RMS value', struct[i]) #update the variance frame if saltkey.found('VAREXT', struct[i]): vhdu=saltkey.get('VAREXT', struct[i]) try: vdata=struct[vhdu].data #The bias level should not be included in the noise from the signal for j in range(len(struct[i].data[0])): vdata[y1:y2,j] -= ofit #add a bit to make sure that the minimum error is the rednoise rdnoise= saltkey.get('RDNOISE',struct[i]) vdata[vdata<rdnoise**2]=rdnoise**2 struct[vhdu].data=vdata+osigma**2 except Exception, e: msg='Cannot update the variance frame in %s[%i] because %s' % (infile, vhdu, e) raise SaltError(msg) #plot the overscan region if plotover: plt.plot(yarr, odata) plt.plot(yarr, ofit) #trim the data and update the headers if trim: struct[i].data=struct[i].data[dy1:dy2,dx1:dx2] datasec = '[1:'+str(dx2-dx1)+',1:'+str(dy2-dy1)+']' saltkey.put('DATASEC',datasec,struct[i]) #update the variance frame if saltkey.found('VAREXT', struct[i]): vhdu=saltkey.get('VAREXT', struct[i]) struct[vhdu].data=struct[vhdu].data[dy1:dy2,dx1:dx2] datasec = '[1:'+str(dx2-dx1)+',1:'+str(dy2-dy1)+']' saltkey.put('DATASEC',datasec,struct[vhdu]) #update the BPM frame if saltkey.found('BPMEXT', struct[i]): bhdu=saltkey.get('BPMEXT', struct[i]) struct[bhdu].data=struct[bhdu].data[dy1:dy2,dx1:dx2] datasec = '[1:'+str(dx2-dx1)+',1:'+str(dy2-dy1)+']' saltkey.put('DATASEC',datasec,struct[bhdu]) #subtract the master bias if necessary if subbias and bstruct: struct[i].data -= bstruct[i].data #update the variance frame if saltkey.found('VAREXT', struct[i]): vhdu=saltkey.get('VAREXT', struct[i]) try: vdata=struct[vhdu].data struct[vhdu].data=vdata+bstruct[vhdu].data except Exception, e: msg='Cannot update the variance frame in %s[%i] because %s' % (infile, vhdu, e) raise SaltError(msg)
#correct the variance frame if saltkey.found('VAREXT', struct[hdu]): vhdu=saltkey.get('VAREXT', struct[hdu]) try: vdata=struct[vhdu].data struct[vhdu].data=vdata*gain*(1+2*gain1*1e-6*data) except Exception, e: msg='Cannot update the variance frame in %s[%i] because %s' % (infile, vhdu, e) raise SaltError(msg) else: gainmult=gain #update the headers if usedb: saltkey.put('GAIN',gain,struct[hdu]) saltkey.put('RDNOISE',rdnoise,struct[hdu]) #add a keyword indicating what action was taken saltkey.new('GAINMULT',gainmult,'Gain multiplication', struct[hdu]) #if logging is true, then print out the following information if log: message = '%25s[%1d] %6s %5s %2s %6.2f %5.2f' \ % (infile,hdu,gainset,rospeed,amp, gain, rdnoise) log.message(message, with_header=False, with_stdout=verbose) #just to make it look pretty if log: log.message('', with_header=False, with_stdout=verbose)