def get_tractor_image_dr7(run, camcol, field, bandname, sdssobj=None, release='DR7', retrieve=True, curl=False, psf='kl-gm', useMags=False, roi=None, roiradecsize=None, roiradecbox=None, nanomaggies=False, savepsfimg=None, zrange=[-3, 10]): ''' Creates a tractor.Image given an SDSS field identifier. If not None, roi = (x0, x1, y0, y1) defines a region-of-interest in the image, in zero-indexed pixel coordinates. x1,y1 are NON-inclusive; roi=(0,100,0,100) will yield a 100 x 100 image. psf can be: "dg" for double-Gaussian "kl-gm" for SDSS KL-decomposition approximated as a Gaussian mixture "roiradecsize" = (ra, dec, half-size in pixels) indicates that you want to grab a ROI around the given RA,Dec. Returns: (tractor.Image, dict) dict contains useful details like: 'sky' 'skysig' ''' from astrometry.sdss import DR7, band_index if sdssobj is None: # Ugly if release != 'DR7': raise RuntimeError('We only support DR7 currently') sdss = DR7(curl=curl) else: sdss = sdssobj valid_psf = ['dg', 'kl-gm'] if psf not in valid_psf: raise RuntimeError('PSF must be in ' + str(valid_psf)) # FIXME rerun = 0 bandnum = band_index(bandname) _check_sdss_files(sdss, run, camcol, field, bandname, ['fpC', 'tsField', 'psField', 'fpM'], retrieve=retrieve) fpC = sdss.readFpC(run, camcol, field, bandname) hdr = fpC.getHeader() fpC = fpC.getImage() fpC = fpC.astype(np.float32) - sdss.softbias image = fpC (H, W) = image.shape info = dict() tai = hdr.get('TAI') stripe = hdr.get('STRIPE') strip = hdr.get('STRIP') obj = hdr.get('OBJECT') info.update(tai=tai, stripe=stripe, strip=strip, object=obj, hdr=hdr) tsf = sdss.readTsField(run, camcol, field, rerun) astrans = tsf.getAsTrans(bandnum) wcs = SdssWcs(astrans) #print('Created SDSS Wcs:', wcs) X = interpret_roi(wcs, (H, W), roi=roi, roiradecsize=roiradecsize, roiradecbox=roiradecbox) if X is None: return None, None roi, hasroi = X info.update(roi=roi) x0, x1, y0, y1 = roi # Mysterious half-pixel shift. asTrans pixel coordinates? wcs.setX0Y0(x0 + 0.5, y0 + 0.5) if nanomaggies: zp = tsf.get_zeropoint(bandnum) photocal = LinearPhotoCal(NanoMaggies.zeropointToScale(zp), band=bandname) elif useMags: photocal = SdssMagsPhotoCal(tsf, bandname) else: photocal = SdssFluxPhotoCal() psfield = sdss.readPsField(run, camcol, field) sky = psfield.getSky(bandnum) skysig = sqrt(sky) skyobj = ConstantSky(sky) zr = sky + np.array(zrange) * skysig info.update(sky=sky, skysig=skysig, zr=zr) fpM = sdss.readFpM(run, camcol, field, bandname) gain = psfield.getGain(bandnum) darkvar = psfield.getDarkVariance(bandnum) skyerr = psfield.getSkyErr(bandnum) invvar = sdss.getInvvar(fpC, fpM, gain, darkvar, sky, skyerr) dgpsf = psfield.getDoubleGaussian(bandnum, normalize=True) info.update(dgpsf=dgpsf) if roi is not None: roislice = (slice(y0, y1), slice(x0, x1)) image = image[roislice].copy() invvar = invvar[roislice].copy() if psf == 'kl-gm': from emfit import em_fit_2d from fitpsf import em_init_params # Create Gaussian mixture model PSF approximation. H, W = image.shape klpsf = psfield.getPsfAtPoints(bandnum, x0 + W / 2, y0 + H / 2) S = klpsf.shape[0] # number of Gaussian components K = 3 w, mu, sig = em_init_params(K, None, None, None) II = klpsf.copy() II /= II.sum() # HIDEOUS HACK II = np.maximum(II, 0) #print('Multi-Gaussian PSF fit...') xm, ym = -(S / 2), -(S / 2) if savepsfimg is not None: plt.clf() plt.imshow(II, interpolation='nearest', origin='lower') plt.title('PSF image to fit with EM') plt.savefig(savepsfimg) res = em_fit_2d(II, xm, ym, w, mu, sig) print('em_fit_2d result:', res) if res == 0: # print('w,mu,sig', w,mu,sig) mypsf = GaussianMixturePSF(w, mu, sig) mypsf.computeRadius() else: # Failed! Return 'dg' model instead? print('PSF model fit', psf, 'failed! Returning DG model instead') psf = 'dg' if psf == 'dg': print('Creating double-Gaussian PSF approximation') (a, s1, b, s2) = dgpsf mypsf = NCircularGaussianPSF([s1, s2], [a, b]) timg = Image(data=image, invvar=invvar, psf=mypsf, wcs=wcs, sky=skyobj, photocal=photocal, name=('SDSS (r/c/f/b=%i/%i/%i/%s)' % (run, camcol, field, bandname))) timg.zr = zr return timg, info
def get_tractor_image(run, camcol, field, bandname, sdssobj=None, release='DR7', retrieve=True, curl=False, psf='kl-gm', useMags=False, roi=None, roiradecsize=None, roiradecbox=None, nanomaggies=False, savepsfimg=None, zrange=[-3,10]): ''' Creates a tractor.Image given an SDSS field identifier. If not None, roi = (x0, x1, y0, y1) defines a region-of-interest in the image, in zero-indexed pixel coordinates. x1,y1 are NON-inclusive; roi=(0,100,0,100) will yield a 100 x 100 image. psf can be: "dg" for double-Gaussian "kl-gm" for SDSS KL-decomposition approximated as a Gaussian mixture "roiradecsize" = (ra, dec, half-size in pixels) indicates that you want to grab a ROI around the given RA,Dec. Returns: (tractor.Image, dict) dict contains useful details like: 'sky' 'skysig' ''' from astrometry.sdss import DR7, band_index if sdssobj is None: # Ugly if release != 'DR7': raise RuntimeError('We only support DR7 currently') sdss = DR7(curl=curl) else: sdss = sdssobj valid_psf = ['dg', 'kl-gm'] if psf not in valid_psf: raise RuntimeError('PSF must be in ' + str(valid_psf)) # FIXME rerun = 0 bandnum = band_index(bandname) _check_sdss_files(sdss, run, camcol, field, bandname, ['fpC', 'tsField', 'psField', 'fpM'], retrieve=retrieve) fpC = sdss.readFpC(run, camcol, field, bandname) hdr = fpC.getHeader() fpC = fpC.getImage() fpC = fpC.astype(np.float32) - sdss.softbias image = fpC (H,W) = image.shape info = dict() tai = hdr.get('TAI') stripe = hdr.get('STRIPE') strip = hdr.get('STRIP') obj = hdr.get('OBJECT') info.update(tai=tai, stripe=stripe, strip=strip, object=obj, hdr=hdr) tsf = sdss.readTsField(run, camcol, field, rerun) astrans = tsf.getAsTrans(bandnum) wcs = SdssWcs(astrans) #print('Created SDSS Wcs:', wcs) X = interpret_roi(wcs, (H,W), roi=roi, roiradecsize=roiradecsize, roiradecbox=roiradecbox) if X is None: return None,None roi,hasroi = X info.update(roi=roi) x0,x1,y0,y1 = roi # Mysterious half-pixel shift. asTrans pixel coordinates? wcs.setX0Y0(x0 + 0.5, y0 + 0.5) if nanomaggies: zp = tsf.get_zeropoint(bandnum) photocal = LinearPhotoCal(NanoMaggies.zeropointToScale(zp), band=bandname) elif useMags: photocal = SdssMagsPhotoCal(tsf, bandname) else: photocal = SdssFluxPhotoCal() psfield = sdss.readPsField(run, camcol, field) sky = psfield.getSky(bandnum) skysig = sqrt(sky) skyobj = ConstantSky(sky) zr = sky + np.array(zrange) * skysig info.update(sky=sky, skysig=skysig, zr=zr) fpM = sdss.readFpM(run, camcol, field, bandname) gain = psfield.getGain(bandnum) darkvar = psfield.getDarkVariance(bandnum) skyerr = psfield.getSkyErr(bandnum) invvar = sdss.getInvvar(fpC, fpM, gain, darkvar, sky, skyerr) dgpsf = psfield.getDoubleGaussian(bandnum, normalize=True) info.update(dgpsf=dgpsf) if roi is not None: roislice = (slice(y0,y1), slice(x0,x1)) image = image[roislice].copy() invvar = invvar[roislice].copy() if psf == 'kl-gm': from emfit import em_fit_2d from fitpsf import em_init_params # Create Gaussian mixture model PSF approximation. H,W = image.shape klpsf = psfield.getPsfAtPoints(bandnum, x0+W/2, y0+H/2) S = klpsf.shape[0] # number of Gaussian components K = 3 w,mu,sig = em_init_params(K, None, None, None) II = klpsf.copy() II /= II.sum() # HIDEOUS HACK II = np.maximum(II, 0) #print('Multi-Gaussian PSF fit...') xm,ym = -(S/2), -(S/2) if savepsfimg is not None: plt.clf() plt.imshow(II, interpolation='nearest', origin='lower') plt.title('PSF image to fit with EM') plt.savefig(savepsfimg) res = em_fit_2d(II, xm, ym, w, mu, sig) print('em_fit_2d result:', res) if res == 0: # print('w,mu,sig', w,mu,sig) mypsf = GaussianMixturePSF(w, mu, sig) mypsf.computeRadius() else: # Failed! Return 'dg' model instead? print('PSF model fit', psf, 'failed! Returning DG model instead') psf = 'dg' if psf == 'dg': print('Creating double-Gaussian PSF approximation') (a,s1, b,s2) = dgpsf mypsf = NCircularGaussianPSF([s1, s2], [a, b]) timg = Image(data=image, invvar=invvar, psf=mypsf, wcs=wcs, sky=skyobj, photocal=photocal, name=('SDSS (r/c/f/b=%i/%i/%i/%s)' % (run, camcol, field, bandname))) timg.zr = zr return timg,info
def _get_tractor_image_dr8(run, camcol, field, bandname, sdss=None, roi=None, psf='kl-gm', roiradecsize=None, roiradecbox=None, savepsfimg=None, curl=False, nanomaggies=False, zrange=[-3,10], invvarIgnoresSourceFlux=False, invvarAtCenter=False, invvarAtCenterImage=False, imargs={}): # retry_retrieve=True, ''' Creates a tractor.Image given an SDSS field identifier. If not None, roi = (x0, x1, y0, y1) defines a region-of-interest in the image, in zero-indexed pixel coordinates. x1,y1 are NON-inclusive; roi=(0,100,0,100) will yield a 100 x 100 image. psf can be: "dg" for double-Gaussian "kl-gm" for SDSS KL-decomposition approximated as a Gaussian mixture "bright-*", "*" one of the above PSFs, with special handling at the bright end. "roiradecsize" = (ra, dec, half-size in pixels) indicates that you want to grab a ROI around the given RA,Dec. "roiradecbox" = (ra0, ra1, dec0, dec1) indicates that you want to grab a ROI containing the given RA,Dec ranges. "invvarAtCentr" -- get a scalar constant inverse-variance "invvarAtCenterImage" -- get a scalar constant inverse-variance but still make an image out of it. Returns: (tractor.Image, dict) dict contains useful details like: 'sky' 'skysig' ''' from astrometry.sdss import band_index origpsf = psf if psf.startswith('bright-'): psf = psf[7:] brightpsf = True print('Setting bright PSF handling') else: brightpsf = False valid_psf = ['dg', 'kl-gm', 'kl-pix'] if psf not in valid_psf: raise RuntimeError('PSF must be in ' + str(valid_psf)) if sdss is None: from astrometry.sdss import DR8 sdss = DR8(curl=curl) bandnum = band_index(bandname) for ft in ['psField', 'fpM']: fn = sdss.retrieve(ft, run, camcol, field, bandname) fn = sdss.retrieve('frame', run, camcol, field, bandname) # http://data.sdss3.org/datamodel/files/BOSS_PHOTOOBJ/frames/RERUN/RUN/CAMCOL/frame.html frame = sdss.readFrame(run, camcol, field, bandname, filename=fn) #image = frame.getImage().astype(np.float32) #(H,W) = image.shape H,W = frame.getImageShape() info = dict() hdr = frame.getHeader() tai = hdr.get('TAI') stripe = hdr.get('STRIPE') strip = hdr.get('STRIP') obj = hdr.get('OBJECT') info.update(tai=tai, stripe=stripe, strip=strip, object=obj, hdr=hdr) astrans = frame.getAsTrans() wcs = SdssWcs(astrans) #print('Created SDSS Wcs:', wcs) #print('(x,y) = 1,1 -> RA,Dec', wcs.pixelToPosition(1,1)) X = interpret_roi(wcs, (H,W), roi=roi, roiradecsize=roiradecsize, roiradecbox=roiradecbox) if X is None: return None,None roi,hasroi = X info.update(roi=roi) x0,x1,y0,y1 = roi # Mysterious half-pixel shift. asTrans pixel coordinates? wcs.setX0Y0(x0 + 0.5, y0 + 0.5) if nanomaggies: photocal = LinearPhotoCal(1., band=bandname) else: photocal = SdssNanomaggiesPhotoCal(bandname) sky = 0. skyobj = ConstantSky(sky) calibvec = frame.getCalibVec() invvarAtCenter = invvarAtCenter or invvarAtCenterImage psfield = sdss.readPsField(run, camcol, field) iva = dict(ignoreSourceFlux=invvarIgnoresSourceFlux) if invvarAtCenter: if hasroi: iva.update(constantSkyAt=((x0+x1)/2., (y0+y1)/2.)) else: iva.update(constantSkyAt=(W/2., H/2.)) invvar = frame.getInvvar(psfield, bandnum, **iva) invvar = invvar.astype(np.float32) if not invvarAtCenter: assert(invvar.shape == (H,W)) # Could get this from photoField instead # http://data.sdss3.org/datamodel/files/BOSS_PHOTOOBJ/RERUN/RUN/photoField.html gain = psfield.getGain(bandnum) darkvar = psfield.getDarkVariance(bandnum) meansky = np.mean(frame.sky) meancalib = np.mean(calibvec) skysig = sqrt((meansky / gain) + darkvar) * meancalib info.update(sky=sky, skysig=skysig) zr = np.array(zrange)*skysig + sky info.update(zr=zr) # http://data.sdss3.org/datamodel/files/PHOTO_REDUX/RERUN/RUN/objcs/CAMCOL/fpM.html fpM = sdss.readFpM(run, camcol, field, bandname) if not hasroi: image = frame.getImage() else: roislice = (slice(y0,y1), slice(x0,x1)) image = frame.getImageSlice(roislice).astype(np.float32) if invvarAtCenterImage: invvar = invvar + np.zeros(image.shape, np.float32) elif invvarAtCenter: pass else: invvar = invvar[roislice].copy() H,W = image.shape if (not invvarAtCenter) or invvarAtCenterImage: for plane in [ 'INTERP', 'SATUR', 'CR', 'GHOST' ]: fpM.setMaskedPixels(plane, invvar, 0, roi=roi) dgpsf = psfield.getDoubleGaussian(bandnum, normalize=True) info.update(dgpsf=dgpsf) if psf == 'kl-pix': # Pixelized KL-PSF klpsf = psfield.getPsfAtPoints(bandnum, x0+W/2, y0+H/2) # Trim symmetric zeros sh,sw = klpsf.shape while True: if (np.all(klpsf[0,:] == 0.) and np.all(klpsf[:,0] == 0.) and np.all(klpsf[-1,:] == 0.) and np.all(klpsf[:,-1] == 0.)): klpsf = klpsf[1:-1, 1:-1] else: break mypsf = PixelizedPSF(klpsf) elif psf == 'kl-gm': from emfit import em_fit_2d from fitpsf import em_init_params # Create Gaussian mixture model PSF approximation. klpsf = psfield.getPsfAtPoints(bandnum, x0+W/2, y0+H/2) S = klpsf.shape[0] # number of Gaussian components K = 3 w,mu,sig = em_init_params(K, None, None, None) II = klpsf.copy() II /= II.sum() # HIDEOUS HACK II = np.maximum(II, 0) #print('Multi-Gaussian PSF fit...') xm,ym = -(S/2), -(S/2) if savepsfimg is not None: plt.clf() plt.imshow(II, interpolation='nearest', origin='lower') plt.title('PSF image to fit with EM') plt.savefig(savepsfimg) res = em_fit_2d(II, xm, ym, w, mu, sig) #print('em_fit_2d result:', res) if res == 0: # print('w,mu,sig', w,mu,sig) mypsf = GaussianMixturePSF(w, mu, sig) mypsf.computeRadius() else: # Failed! Return 'dg' model instead? print('PSF model fit', psf, 'failed! Returning DG model instead') psf = 'dg' if psf == 'dg': print('Creating double-Gaussian PSF approximation') (a,s1, b,s2) = dgpsf mypsf = NCircularGaussianPSF([s1, s2], [a, b]) if brightpsf: print('Wrapping PSF in SdssBrightPSF') (a1,s1, a2,s2, a3,sigmap,beta) = psfield.getPowerLaw(bandnum) mypsf = SdssBrightPSF(mypsf, a1,s1,a2,s2,a3,sigmap,beta) print('PSF:', mypsf) timg = Image(data=image, invvar=invvar, psf=mypsf, wcs=wcs, sky=skyobj, photocal=photocal, name=('SDSS (r/c/f/b=%i/%i/%i/%s)' % (run, camcol, field, bandname)), time=TAITime(tai), **imargs) timg.zr = zr return timg,info
def _get_tractor_image_dr8(run, camcol, field, bandname, sdss=None, roi=None, psf='kl-gm', roiradecsize=None, roiradecbox=None, savepsfimg=None, curl=False, nanomaggies=False, zrange=[-3, 10], invvarIgnoresSourceFlux=False, invvarAtCenter=False, invvarAtCenterImage=False, imargs={}): # retry_retrieve=True, ''' Creates a tractor.Image given an SDSS field identifier. If not None, roi = (x0, x1, y0, y1) defines a region-of-interest in the image, in zero-indexed pixel coordinates. x1,y1 are NON-inclusive; roi=(0,100,0,100) will yield a 100 x 100 image. psf can be: "dg" for double-Gaussian "kl-gm" for SDSS KL-decomposition approximated as a Gaussian mixture "bright-*", "*" one of the above PSFs, with special handling at the bright end. "roiradecsize" = (ra, dec, half-size in pixels) indicates that you want to grab a ROI around the given RA,Dec. "roiradecbox" = (ra0, ra1, dec0, dec1) indicates that you want to grab a ROI containing the given RA,Dec ranges. "invvarAtCentr" -- get a scalar constant inverse-variance "invvarAtCenterImage" -- get a scalar constant inverse-variance but still make an image out of it. Returns: (tractor.Image, dict) dict contains useful details like: 'sky' 'skysig' ''' from astrometry.sdss import band_index origpsf = psf if psf.startswith('bright-'): psf = psf[7:] brightpsf = True print('Setting bright PSF handling') else: brightpsf = False valid_psf = ['dg', 'kl-gm', 'kl-pix'] if psf not in valid_psf: raise RuntimeError('PSF must be in ' + str(valid_psf)) if sdss is None: from astrometry.sdss import DR8 sdss = DR8(curl=curl) bandnum = band_index(bandname) for ft in ['psField', 'fpM']: fn = sdss.retrieve(ft, run, camcol, field, bandname) fn = sdss.retrieve('frame', run, camcol, field, bandname) # http://data.sdss3.org/datamodel/files/BOSS_PHOTOOBJ/frames/RERUN/RUN/CAMCOL/frame.html frame = sdss.readFrame(run, camcol, field, bandname, filename=fn) #image = frame.getImage().astype(np.float32) #(H,W) = image.shape H, W = frame.getImageShape() info = dict() hdr = frame.getHeader() tai = hdr.get('TAI') stripe = hdr.get('STRIPE') strip = hdr.get('STRIP') obj = hdr.get('OBJECT') info.update(tai=tai, stripe=stripe, strip=strip, object=obj, hdr=hdr) astrans = frame.getAsTrans() wcs = SdssWcs(astrans) #print('Created SDSS Wcs:', wcs) #print('(x,y) = 1,1 -> RA,Dec', wcs.pixelToPosition(1,1)) X = interpret_roi(wcs, (H, W), roi=roi, roiradecsize=roiradecsize, roiradecbox=roiradecbox) if X is None: return None, None roi, hasroi = X info.update(roi=roi) x0, x1, y0, y1 = roi # Mysterious half-pixel shift. asTrans pixel coordinates? wcs.setX0Y0(x0 + 0.5, y0 + 0.5) if nanomaggies: photocal = LinearPhotoCal(1., band=bandname) else: photocal = SdssNanomaggiesPhotoCal(bandname) sky = 0. skyobj = ConstantSky(sky) calibvec = frame.getCalibVec() invvarAtCenter = invvarAtCenter or invvarAtCenterImage psfield = sdss.readPsField(run, camcol, field) iva = dict(ignoreSourceFlux=invvarIgnoresSourceFlux) if invvarAtCenter: if hasroi: iva.update(constantSkyAt=((x0 + x1) / 2., (y0 + y1) / 2.)) else: iva.update(constantSkyAt=(W / 2., H / 2.)) invvar = frame.getInvvar(psfield, bandnum, **iva) invvar = invvar.astype(np.float32) if not invvarAtCenter: assert (invvar.shape == (H, W)) # Could get this from photoField instead # http://data.sdss3.org/datamodel/files/BOSS_PHOTOOBJ/RERUN/RUN/photoField.html gain = psfield.getGain(bandnum) darkvar = psfield.getDarkVariance(bandnum) meansky = np.mean(frame.sky) meancalib = np.mean(calibvec) skysig = sqrt((meansky / gain) + darkvar) * meancalib info.update(sky=sky, skysig=skysig) zr = np.array(zrange) * skysig + sky info.update(zr=zr) # http://data.sdss3.org/datamodel/files/PHOTO_REDUX/RERUN/RUN/objcs/CAMCOL/fpM.html fpM = sdss.readFpM(run, camcol, field, bandname) if not hasroi: image = frame.getImage() else: roislice = (slice(y0, y1), slice(x0, x1)) image = frame.getImageSlice(roislice).astype(np.float32) if invvarAtCenterImage: invvar = invvar + np.zeros(image.shape, np.float32) elif invvarAtCenter: pass else: invvar = invvar[roislice].copy() H, W = image.shape if (not invvarAtCenter) or invvarAtCenterImage: for plane in ['INTERP', 'SATUR', 'CR', 'GHOST']: fpM.setMaskedPixels(plane, invvar, 0, roi=roi) dgpsf = psfield.getDoubleGaussian(bandnum, normalize=True) info.update(dgpsf=dgpsf) if psf == 'kl-pix': # Pixelized KL-PSF klpsf = psfield.getPsfAtPoints(bandnum, x0 + W / 2, y0 + H / 2) # Trim symmetric zeros sh, sw = klpsf.shape while True: if (np.all(klpsf[0, :] == 0.) and np.all(klpsf[:, 0] == 0.) and np.all(klpsf[-1, :] == 0.) and np.all(klpsf[:, -1] == 0.)): klpsf = klpsf[1:-1, 1:-1] else: break mypsf = PixelizedPSF(klpsf) elif psf == 'kl-gm': from emfit import em_fit_2d from fitpsf import em_init_params # Create Gaussian mixture model PSF approximation. klpsf = psfield.getPsfAtPoints(bandnum, x0 + W / 2, y0 + H / 2) S = klpsf.shape[0] # number of Gaussian components K = 3 w, mu, sig = em_init_params(K, None, None, None) II = klpsf.copy() II /= II.sum() # HIDEOUS HACK II = np.maximum(II, 0) #print('Multi-Gaussian PSF fit...') xm, ym = -(S / 2), -(S / 2) if savepsfimg is not None: plt.clf() plt.imshow(II, interpolation='nearest', origin='lower') plt.title('PSF image to fit with EM') plt.savefig(savepsfimg) res = em_fit_2d(II, xm, ym, w, mu, sig) #print('em_fit_2d result:', res) if res == 0: # print('w,mu,sig', w,mu,sig) mypsf = GaussianMixturePSF(w, mu, sig) mypsf.computeRadius() else: # Failed! Return 'dg' model instead? print('PSF model fit', psf, 'failed! Returning DG model instead') psf = 'dg' if psf == 'dg': print('Creating double-Gaussian PSF approximation') (a, s1, b, s2) = dgpsf mypsf = NCircularGaussianPSF([s1, s2], [a, b]) if brightpsf: print('Wrapping PSF in SdssBrightPSF') (a1, s1, a2, s2, a3, sigmap, beta) = psfield.getPowerLaw(bandnum) mypsf = SdssBrightPSF(mypsf, a1, s1, a2, s2, a3, sigmap, beta) print('PSF:', mypsf) timg = Image(data=image, invvar=invvar, psf=mypsf, wcs=wcs, sky=skyobj, photocal=photocal, name=('SDSS (r/c/f/b=%i/%i/%i/%s)' % (run, camcol, field, bandname)), time=TAITime(tai), **imargs) timg.zr = zr return timg, info