def fit_pps(hdu=None, img=None, header=None, plot=True, figure=pylab.figure(1), return_profile=False, return_gaussian_profile=False, verbose=False): if hdu is not None: img = hdu[0].data header = hdu[0].header img[img != img] = 0 asperpix = -header['CD1_1'] * 3600.0 xx, yy = numpy.indices(img.shape) # Try to fit, make sure fit is successful fitloop = 1 while fitloop: if fitloop == 1: # this never works for no reason at all fitpars = gaussfitter.gaussfit( img, params=[ 0, img.max(), img.shape[0] / 2, img.shape[1] / 2, 2, 2, 0 ], minpars=[0, img.max() / 50.0, 0, 0, 1, 1, 0], maxpars=[0, 0, 0, 0, 6, 6, 360], limitedmin=[False, True, False, False, True, True, True], limitedmax=[False, False, False, False, True, True, True], fixed=[1, 0, 0, 0, 0, 0, 0]) elif fitloop == 2: if verbose: print "Fitloop 1 failed: ", fitpars, ". Trying fitloop2" fitpars = gaussfitter.gaussfit( img, params=[0, img.max(), 0, 0, 2, 2, 0], minpars=[0, img.max() / 10.0, 0, 0, 1, 1, 0], maxpars=[0, 0, 0, 0, 6, 6, 360], limitedmin=[False, True, False, False, True, True, True], limitedmax=[False, False, False, False, True, True, True], fixed=[1, 0, 0, 0, 0, 0, 0], usemoment=[0, 0, 1, 1, 0, 0, 0]) elif fitloop == 3: if verbose: print "Fitloop 2 failed: ", fitpars, ". Trying fitloop3" fitpars = gaussfitter.gaussfit( img, minpars=[0, img.max() / 10.0, 0, 0, 1, 1, 0], maxpars=[0, 0, 0, 0, 6, 6, 360], limitedmin=[False, True, False, False, True, True, True], limitedmax=[False, False, False, False, True, True, True]) elif fitloop == 4: if verbose: print "Fitloop 3 failed: ", fitpars, ". Trying fitloop4" fitpars = gaussfitter.gaussfit(img) else: fitpars = numpy.array(gaussfitter.moments(img, 0, 1, 1)) print "Using the parameters you specified: ", fitpars fitloop = -1 gaussim = gaussfitter.twodgaussian(fitpars)(xx, yy) wxy = fitpars[4:6] * asperpix * numpy.sqrt(8 * numpy.log(2)) mean_fwhm = wxy.sum() / 2.0 if mean_fwhm > 70: fitloop += 1 else: # there were other conditions before... if 1: if verbose: print "Success. Fitpars: ", fitpars fitloop = 0 else: fitloop += 1 wcs = pywcs.WCS(header) glon, glat = wcs.wcs_pix2sky(fitpars[2:3], fitpars[3:4], 0) if plot: ff = aplpy.FITSFigure(hdu, figure=figure) ff.show_grayscale() ff.show_circles([glon], [glat], [1. / 60.], edgecolor='r') ff.show_circles([glon], [glat], [2. / 60.], edgecolor='b') if return_gaussian_profile: return azimuthalAverage(gaussim, center=fitpars[2:4], returnradii=True) elif return_profile: return azimuthalAverage(img, center=fitpars[2:4], returnradii=True) else: return glon, glat
def cross_correlation_shifts(image1, image2, errim1=None, errim2=None, maxoff=None, verbose=False, gaussfit=False, return_error=False, zeromean=True, **kwargs): """ Use cross-correlation and a 2nd order taylor expansion to measure the offset between two images Given two images, calculate the amount image2 is offset from image1 to sub-pixel accuracy using 2nd order taylor expansion. Parameters ---------- image1: np.ndarray The reference image image2: np.ndarray The offset image. Must have the same shape as image1 errim1: np.ndarray [optional] The pixel-by-pixel error on the reference image errim2: np.ndarray [optional] The pixel-by-pixel error on the offset image. maxoff: int Maximum allowed offset (in pixels). Useful for low s/n images that you know are reasonably well-aligned, but might find incorrect offsets due to edge noise zeromean : bool Subtract the mean from each image before performing cross-correlation? verbose: bool Print out extra messages? gaussfit : bool Use a Gaussian fitter to fit the peak of the cross-correlation? return_error: bool Return an estimate of the error on the shifts. WARNING: I still don't understand how to make these agree with simulations. The analytic estimate comes from http://adsabs.harvard.edu/abs/2003MNRAS.342.1291Z At high signal-to-noise, the analytic version overestimates the error by a factor of about 1.8, while the gaussian version overestimates error by about 1.15. At low s/n, they both UNDERestimate the error. The transition zone occurs at a *total* S/N ~ 1000 (i.e., the total signal in the map divided by the standard deviation of the map - it depends on how many pixels have signal) **kwargs are passed to correlate2d, which in turn passes them to convolve. The available options include image padding for speed and ignoring NaNs. References ---------- From http://solarmuri.ssl.berkeley.edu/~welsch/public/software/cross_cor_taylor.pro Examples -------- >>> import numpy as np >>> im1 = np.zeros([10,10]) >>> im2 = np.zeros([10,10]) >>> im1[4,3] = 1 >>> im2[5,5] = 1 >>> import image_registration >>> yoff,xoff = image_registration.cross_correlation_shifts(im1,im2) >>> im1_aligned_to_im2 = np.roll(np.roll(im1,int(yoff),1),int(xoff),0) >>> assert (im1_aligned_to_im2-im2).sum() == 0 """ if zeromean: image1 = image1 - (image1[image1 == image1].mean()) image2 = image2 - (image2[image2 == image2].mean()) image1 = np.nan_to_num(image1) image2 = np.nan_to_num(image2) quiet = kwargs.pop('quiet') if 'quiet' in kwargs else not verbose ccorr = (correlate2d(image1, image2, quiet=quiet, **kwargs) / image1.size) # allow for NaNs set by convolve (i.e., ignored pixels) ccorr[ccorr != ccorr] = 0 if ccorr.shape != image1.shape: raise ValueError( "Cross-correlation image must have same shape as input images. This can only be violated if you pass a strange kwarg to correlate2d." ) ylen, xlen = image1.shape xcen = xlen / 2 - (1 - xlen % 2) ycen = ylen / 2 - (1 - ylen % 2) if ccorr.max() == 0: warnings.warn("WARNING: No signal found! Offset is defaulting to 0,0") return 0, 0 if maxoff is not None: if verbose: print("Limiting maximum offset to %i" % maxoff) subccorr = ccorr[ycen - maxoff:ycen + maxoff + 1, xcen - maxoff:xcen + maxoff + 1] ymax, xmax = np.unravel_index(subccorr.argmax(), subccorr.shape) xmax = xmax + xcen - maxoff ymax = ymax + ycen - maxoff else: ymax, xmax = np.unravel_index(ccorr.argmax(), ccorr.shape) subccorr = ccorr if return_error: #if errim1 is None: # errim1 = np.ones(ccorr.shape) * image1[image1==image1].std() #if errim2 is None: # errim2 = np.ones(ccorr.shape) * image2[image2==image2].std() #eccorr =( (correlate2d(errim1**2, image2**2,quiet=quiet,**kwargs)+ #correlate2d(errim2**2, image1**2,quiet=quiet,**kwargs))**0.5 # / image1.size) eccorr = ( (correlate2d( (image1 * 0.15)**2, image2**2, quiet=quiet, **kwargs) + correlate2d( (image2 * 0.15)**2, image1**2, quiet=quiet, **kwargs))**0.5 / image1.size) if maxoff is not None: subeccorr = eccorr[ycen - maxoff:ycen + maxoff + 1, xcen - maxoff:xcen + maxoff + 1] else: subeccorr = eccorr if gaussfit: try: from agpy import gaussfitter except ImportError: raise ImportError( "Couldn't import agpy.gaussfitter; try using cross_correlation_shifts with gaussfit=False" ) if return_error: pars, epars = gaussfitter.gaussfit(subccorr, err=subeccorr, return_all=True) exshift = epars[2] eyshift = epars[3] else: pars, epars = gaussfitter.gaussfit(subccorr, return_all=True) xshift = maxoff - pars[2] if maxoff is not None else xcen - pars[2] yshift = maxoff - pars[3] if maxoff is not None else ycen - pars[3] if verbose: print("Gaussian fit pars: ", xshift, yshift, epars[2], epars[3], pars[4], pars[5], epars[4], epars[5]) else: xshift_int = xmax - xcen yshift_int = ymax - ycen local_values = ccorr[ymax - 1:ymax + 2, xmax - 1:xmax + 2] d1y, d1x = np.gradient(local_values) d2y, d2x, dxy = second_derivative(local_values) fx, fy, fxx, fyy, fxy = d1x[1, 1], d1y[1, 1], d2x[1, 1], d2y[1, 1], dxy[1, 1] shiftsubx = (fyy * fx - fy * fxy) / (fxy**2 - fxx * fyy) shiftsuby = (fxx * fy - fx * fxy) / (fxy**2 - fxx * fyy) xshift = -(xshift_int + shiftsubx) yshift = -(yshift_int + shiftsuby) # http://adsabs.harvard.edu/abs/2003MNRAS.342.1291Z # Zucker error if return_error: #acorr1 = (correlate2d(image1,image1,quiet=quiet,**kwargs) / image1.size) #acorr2 = (correlate2d(image2,image2,quiet=quiet,**kwargs) / image2.size) #ccorrn = ccorr / eccorr**2 / ccorr.size #/ (errim1.mean()*errim2.mean()) #/ eccorr**2 normalization = 1. / ((image1**2).sum() / image1.size) / ( (image2**2).sum() / image2.size) ccorrn = ccorr * normalization exshift = (np.abs( -1 * ccorrn.size * fxx * normalization / ccorrn[ymax, xmax] * (ccorrn[ymax, xmax]**2 / (1 - ccorrn[ymax, xmax]**2)))**-0.5) eyshift = (np.abs( -1 * ccorrn.size * fyy * normalization / ccorrn[ymax, xmax] * (ccorrn[ymax, xmax]**2 / (1 - ccorrn[ymax, xmax]**2)))**-0.5) if np.isnan(exshift): raise ValueError("Error: NAN error!") if return_error: return xshift, yshift, exshift, eyshift else: return xshift, yshift
ax2.set_xlim(0, 100) ax2.set_ylim(0, zzplot[ddplot < 100].max() * 1.1) pylab.draw() print >> outf, file, wxarr, wyarr, cx, cy, peak, frac20, frac40, frac60, flux20, flux40, flux60, fluxnb20, fluxnb40, fluxnb60, fluxr20, fluxr40, fluxr60, pflux, meandc, stddc pylab.savefig(file.replace('fits', 'png')) print "Completed loop for file %s in %0.1fs" % (file, time.time() - t0) outf.close() if sample in ("notmars", "dec2011notmars"): PSF = numpy.median(imstack, axis=2)[100:200, 100:200] gf, gg = gaussfitter.gaussfit(PSF, returnfitimage=True, limitedmin=[1, 1, 0, 0, 0, 0, 0, 0]) fitsfile[0].data = PSF fitsfile[0].header['CRVAL1'] = 0 fitsfile[0].header['CRVAL2'] = 0 fitsfile[0].header['CRPIX1'] = 0 fitsfile[0].header['CRPIX2'] = 0 fitsfile[0].header['CD1_1'] = -0.002 fitsfile[0].header['CD2_2'] = 0.002 fitsfile[0].header.update('OBJECT', "Uranus & Neptune") fitsfile[0].header['BMAJ'] = gf[4] * numpy.sqrt( 8 * numpy.log(2)) * 7.2 / 3600.0 fitsfile[0].header['BMIN'] = gf[5] * numpy.sqrt( 8 * numpy.log(2)) * 7.2 / 3600.0 for key in [
wxarr, wyarr = numpy.zeros(len(filelist)), numpy.zeros(len(filelist)) frac20, frac40, frac60 = numpy.zeros(len(filelist)), numpy.zeros( len(filelist)), numpy.zeros(len(filelist)) for jj, file in enumerate(filelist): fitsfile = pyfits.open(file) img = fitsfile[0].data header = fitsfile[0].header img[img != img] = 0 xx, yy = numpy.indices(img.shape) fitpars = gaussfitter.gaussfit( img, params=[img.max(), img.shape[0] / 2, img.shape[1] / 2, 2, 2, 0], minpars=[0, 0, 0, 0, 1, 1, 0], vheight=0) gaussim = gaussfitter.twodgaussian(fitpars)(xx, yy) asperpix = -header['CD1_1'] * 3600.0 cy, cx = fitpars[2:4] wxy = fitpars[4:6] * asperpix * numpy.sqrt(8 * numpy.log(2)) wxarr[jj], wyarr[jj] = max(wxy), min(wxy) rr = (numpy.sqrt((xx - cx)**2 + (yy - cy)**2)) rrs = numpy.argsort(rr.flat) zz = img.flat[rrs] zzg = gaussim.flat[rrs] #dd = numpy.arange(rr.min(),rr.max())
def cross_correlation_shifts(image1, image2, errim1=None, errim2=None, maxoff=None, verbose=False, gaussfit=False, return_error=False, zeromean=True, **kwargs): """ Use cross-correlation and a 2nd order taylor expansion to measure the offset between two images Given two images, calculate the amount image2 is offset from image1 to sub-pixel accuracy using 2nd order taylor expansion. Parameters ---------- image1: np.ndarray The reference image image2: np.ndarray The offset image. Must have the same shape as image1 errim1: np.ndarray [optional] The pixel-by-pixel error on the reference image errim2: np.ndarray [optional] The pixel-by-pixel error on the offset image. maxoff: int Maximum allowed offset (in pixels). Useful for low s/n images that you know are reasonably well-aligned, but might find incorrect offsets due to edge noise zeromean : bool Subtract the mean from each image before performing cross-correlation? verbose: bool Print out extra messages? gaussfit : bool Use a Gaussian fitter to fit the peak of the cross-correlation? return_error: bool Return an estimate of the error on the shifts. WARNING: I still don't understand how to make these agree with simulations. The analytic estimate comes from http://adsabs.harvard.edu/abs/2003MNRAS.342.1291Z At high signal-to-noise, the analytic version overestimates the error by a factor of about 1.8, while the gaussian version overestimates error by about 1.15. At low s/n, they both UNDERestimate the error. The transition zone occurs at a *total* S/N ~ 1000 (i.e., the total signal in the map divided by the standard deviation of the map - it depends on how many pixels have signal) **kwargs are passed to correlate2d, which in turn passes them to convolve. The available options include image padding for speed and ignoring NaNs. References ---------- From http://solarmuri.ssl.berkeley.edu/~welsch/public/software/cross_cor_taylor.pro Examples -------- >>> import numpy as np >>> im1 = np.zeros([10,10]) >>> im2 = np.zeros([10,10]) >>> im1[4,3] = 1 >>> im2[5,5] = 1 >>> import image_registration >>> yoff,xoff = image_registration.cross_correlation_shifts(im1,im2) >>> im1_aligned_to_im2 = np.roll(np.roll(im1,int(yoff),1),int(xoff),0) >>> assert (im1_aligned_to_im2-im2).sum() == 0 """ if not image1.shape == image2.shape: raise ValueError("Images must have same shape.") if zeromean: image1 = image1 - (image1[image1==image1].mean()) image2 = image2 - (image2[image2==image2].mean()) image1 = np.nan_to_num(image1) image2 = np.nan_to_num(image2) quiet = kwargs.pop('quiet') if 'quiet' in kwargs else not verbose ccorr = (correlate2d(image1,image2,quiet=quiet,**kwargs) / image1.size) # allow for NaNs set by convolve (i.e., ignored pixels) ccorr[ccorr!=ccorr] = 0 if ccorr.shape != image1.shape: raise ValueError("Cross-correlation image must have same shape as input images. This can only be violated if you pass a strange kwarg to correlate2d.") ylen,xlen = image1.shape xcen = xlen/2-(1-xlen%2) ycen = ylen/2-(1-ylen%2) if ccorr.max() == 0: warnings.warn("WARNING: No signal found! Offset is defaulting to 0,0") return 0,0 if maxoff is not None: if verbose: print("Limiting maximum offset to %i" % maxoff) subccorr = ccorr[ycen-maxoff:ycen+maxoff+1,xcen-maxoff:xcen+maxoff+1] ymax,xmax = np.unravel_index(subccorr.argmax(), subccorr.shape) xmax = xmax+xcen-maxoff ymax = ymax+ycen-maxoff else: ymax,xmax = np.unravel_index(ccorr.argmax(), ccorr.shape) subccorr = ccorr if return_error: if errim1 is None: errim1 = np.ones(ccorr.shape) * image1[image1==image1].std() if errim2 is None: errim2 = np.ones(ccorr.shape) * image2[image2==image2].std() eccorr =( (correlate2d(errim1**2, image2**2,quiet=quiet,**kwargs)+ correlate2d(errim2**2, image1**2,quiet=quiet,**kwargs))**0.5 / image1.size) if maxoff is not None: subeccorr = eccorr[ycen-maxoff:ycen+maxoff+1,xcen-maxoff:xcen+maxoff+1] else: subeccorr = eccorr if gaussfit: try: from agpy import gaussfitter except ImportError: raise ImportError("Couldn't import agpy.gaussfitter; try using cross_correlation_shifts with gaussfit=False") if return_error: pars,epars = gaussfitter.gaussfit(subccorr,err=subeccorr,return_all=True) exshift = epars[2] eyshift = epars[3] else: pars,epars = gaussfitter.gaussfit(subccorr,return_all=True) xshift = maxoff - pars[2] if maxoff is not None else xcen - pars[2] yshift = maxoff - pars[3] if maxoff is not None else ycen - pars[3] if verbose: print("Gaussian fit pars: ",xshift,yshift,epars[2],epars[3],pars[4],pars[5],epars[4],epars[5]) else: xshift_int = xmax-xcen yshift_int = ymax-ycen local_values = ccorr[ymax-1:ymax+2,xmax-1:xmax+2] d1y,d1x = np.gradient(local_values) d2y,d2x,dxy = second_derivative(local_values) fx,fy,fxx,fyy,fxy = d1x[1,1],d1y[1,1],d2x[1,1],d2y[1,1],dxy[1,1] shiftsubx=(fyy*fx-fy*fxy)/(fxy**2-fxx*fyy) shiftsuby=(fxx*fy-fx*fxy)/(fxy**2-fxx*fyy) xshift = -(xshift_int+shiftsubx) yshift = -(yshift_int+shiftsuby) # http://adsabs.harvard.edu/abs/2003MNRAS.342.1291Z # Zucker error if return_error: #acorr1 = (correlate2d(image1,image1,quiet=quiet,**kwargs) / image1.size) #acorr2 = (correlate2d(image2,image2,quiet=quiet,**kwargs) / image2.size) #ccorrn = ccorr / eccorr**2 / ccorr.size #/ (errim1.mean()*errim2.mean()) #/ eccorr**2 normalization = 1. / ((image1**2).sum()/image1.size) / ((image2**2).sum()/image2.size) ccorrn = ccorr * normalization exshift = (np.abs(-1 * ccorrn.size * fxx*normalization/ccorrn[ymax,xmax] * (ccorrn[ymax,xmax]**2/(1-ccorrn[ymax,xmax]**2)))**-0.5) eyshift = (np.abs(-1 * ccorrn.size * fyy*normalization/ccorrn[ymax,xmax] * (ccorrn[ymax,xmax]**2/(1-ccorrn[ymax,xmax]**2)))**-0.5) if np.isnan(exshift): raise ValueError("Error: NAN error!") if return_error: return xshift,yshift,exshift,eyshift else: return xshift,yshift