-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial commit for aperture photometry #1
Changes from 1 commit
8c2313f
e6845a9
11e8991
c2b1582
94ceb85
e8ddb6a
dbd9fc4
a3c6d5a
d215374
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import logistics |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import wcs |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
from skimage.registration import phase_cross_correlation | ||
import numpy as np | ||
from astropy.coordinates import SkyCoord | ||
import scipy.spatial as sspa | ||
from skimage.measure import ransac | ||
from skimage import transform as tf | ||
from astropy.wcs import WCS,utils | ||
|
||
import microlensing_photometry.logistics.GaiaTools.GaiaCatalog as GC | ||
|
||
def find_images_shifts(reference,image,image_fraction =0.25, upsample_factor=1): | ||
""" | ||
Estimate the shifts between two images. Generally a good idea to do a fraction in the middle, | ||
where the effect of rotation is minimal. | ||
|
||
Parameters | ||
---------- | ||
reference : an image acting as the reference | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parameter types should be specified in each case - for example, are the images being passed here as arrays? File paths? |
||
image : the image we want to align | ||
image_fraction : the fraction of image around the center we want to analyze | ||
upsample_factor : the degree of upsampling, if one wants subpixel accuracy | ||
|
||
|
||
Returns | ||
------- | ||
shiftx : the shift in pixels in the x direction | ||
shifty : the shift in pixels in the y direction | ||
""" | ||
|
||
leny, lenx = (np.array(image.shape) * image_fraction).astype(int) | ||
starty,startx = (np.array(image.shape)*0.5-[leny/2,lenx/2]).astype(int) | ||
|
||
subref = reference.astype(float)[starty:starty+leny,startx:startx+lenx] | ||
subimage = image.astype(float)[starty:starty+leny,startx:startx+lenx] | ||
shifts, errors, phasediff = phase_cross_correlation(subref,subimage, | ||
normalization=None, | ||
upsample_factor=upsample_factor) | ||
|
||
shifty,shiftx = shifts | ||
|
||
return shiftx,shifty | ||
|
||
|
||
def refine_image_wcs(image, stars_image, image_wcs, gaia_catalog, star_limit = 1000): | ||
""" | ||
Refine the WCS of an image with Gaia catalog | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above, the comment needs to be more specific to be informative. How is the WCS refined? |
||
|
||
Parameters | ||
---------- | ||
image : an array with the image | ||
stars_image : the x,y positions of stars in the image | ||
image_wcs : the original astropy WCS solution | ||
gaia_catalog : the entire gaia catalog | ||
|
||
|
||
Returns | ||
------- | ||
new_wcs : an updated astropy WCS object | ||
""" | ||
mo_image,skycoords,star_pix = GC.build_Gaia_image(gaia_catalog, image_wcs, image_shape=image.shape) | ||
|
||
shiftx, shifty = find_images_shifts(mo_image, image, image_fraction=0.25, upsample_factor=1) | ||
|
||
dists = sspa.distance.cdist(stars_image[:star_limit], | ||
np.c_[star_pix[0][:star_limit] - shiftx, star_pix[1][:star_limit] - shifty]) | ||
|
||
mask = dists < 20 | ||
lines, cols = np.where(mask) | ||
|
||
pts1 = np.c_[star_pix[0], star_pix[1]][:star_limit][cols] | ||
pts2 = np.c_[stars_image[:,0], stars_image[:,1]][:star_limit][lines] | ||
model_robust, inliers = ransac((pts2, pts1), tf.AffineTransform, min_samples=10, residual_threshold=3, | ||
max_trials=300) | ||
|
||
new_wcs = utils.fit_wcs_from_points(pts2[:star_limit][inliers].T, skycoords[cols][inliers]) | ||
|
||
|
||
### might be valuable some looping here | ||
|
||
# Refining??? | ||
# dists2 = distance.cdist(np.c_[sources['xcentroid'],sources['ycentroid']][:500],projected2[:500]) | ||
# mask2 = dists2<1 | ||
# lines2,cols2 = np.where(mask2) | ||
# pts12 = np.c_[star_pix[0],star_pix[1]][:500][cols2] | ||
# pts22 = np.c_[sources['xcentroid'],sources['ycentroid']][:500][lines2] | ||
|
||
# model_robust2, inliers2 = ransac(( pts22,pts12), tf. AffineTransform,min_samples=10, residual_threshold=1, max_trials=300) | ||
# projected22 = model_robust2.inverse(pts12) | ||
# projected222 = model_robust2.inverse(np.c_[star_pix[0],star_pix[1]]) | ||
|
||
# print(shifts) | ||
|
||
return new_wcs |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
from astroquery.gaia import Gaia | ||
import astropy.units as u | ||
from astropy.coordinates import SkyCoord | ||
import numpy as np | ||
|
||
def collect_Gaia_catalog(ra,dec,radius=15,row_limit = 10000,catalog_name='Gaia_catalog.dat', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The old pipeline's vizier query module should be ported over to the new pipeline, and this function should be integrated with it - there is existing code for querying the Gaia catalog, which provides more robust failover handling in the event of servers being unreachable. |
||
catalog_path='./'): | ||
""" | ||
Collect the Gaia catalog for the field ra,dec,radius. | ||
|
||
Parameters | ||
---------- | ||
ra : right ascenscion in degree | ||
dec : right ascenscion in degree | ||
radius : radius in arcmin | ||
row_limit : the maximum number of stars (default:10000) | ||
catalog_name : the name of the catalog to save/load | ||
catalog_path : the path where to find the catalog | ||
|
||
Returns | ||
------- | ||
gaia_catalog : astropy.Table, sorted by magnitude (brighter to fainter) | ||
""" | ||
|
||
try: | ||
|
||
from astropy.table import Table | ||
|
||
gaia_catalog = Table.read(catalog_name, format='ascii') | ||
|
||
except: | ||
|
||
Gaia.ROW_LIMIT = row_limit | ||
coord = SkyCoord(ra=ra, dec=dec, unit=(u.degree, u.degree), frame='icrs') | ||
|
||
radius = u.Quantity(radius /60., u.deg) | ||
|
||
gaia_catalog = Gaia.query_object_async(coordinate=coord,radius = radius) | ||
gaia_catalog = gaia_catalog[ gaia_catalog['phot_g_mean_mag'].argsort(),] | ||
gaia_catalog.write(catalog_name, format='ascii') | ||
|
||
|
||
return gaia_catalog | ||
|
||
def build_Gaia_image(gaia_catalog,image_wcs, image_shape=(4096,4096), star_limit = 1000): | ||
""" | ||
Construct a model image of the Gaia catalog, useful for estimating intial X,Y shifts. | ||
|
||
Parameters | ||
---------- | ||
gaia_catalog : an astropy table containing the Gaia catalog | ||
image_wcs : an astropy wcs object to convert ra,dec to x,y | ||
image_shape : the shape of the image to build | ||
star_limit : limit to the X brightest star to simulate | ||
|
||
Returns | ||
------- | ||
gaia_image : a model image of the Gaia catalog | ||
""" | ||
|
||
coords = SkyCoord(ra=gaia_catalog['ra'].data[:star_limit], | ||
dec=gaia_catalog['dec'].data[:star_limit], | ||
unit=(u.degree, u.degree), frame='icrs') | ||
|
||
fluxes = 10**((22-gaia_catalog['phot_g_mean_mag'].data)/2.5) | ||
|
||
star_pix = image_wcs.world_to_pixel(coords) | ||
XX, YY = np.indices((13, 13)) | ||
model_gaussian = Gaussian2d(1, 6, 6, 2, 2, XX, YY) | ||
|
||
model_image = np.zeros(image_shape) | ||
|
||
for ind in range(len(star_pix[0][:star_limit])): | ||
try: | ||
model_image[star_pix[1].astype(int)[ind] - 6:star_pix[1].astype(int)[ind] + 7, | ||
star_pix[0].astype(int)[ind] - 6:star_pix[0].astype(int)[ind] + 7] += model_gaussian*fluxes[ind] | ||
except: | ||
#skip borders | ||
pass | ||
|
||
return model_image,coords,star_pix | ||
|
||
def Gaussian2d(intensity,x_center,y_center,width_x,width_y,X_star,Y_star): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is very general and should go into a separate module so that it can be reused more cleanly. |
||
""" | ||
A 2d Gaussian model, to ingest stars in image model | ||
|
||
Parameters | ||
---------- | ||
intensity : the intensity of the gaussian | ||
x_center : the gaussian center in x direction | ||
y_center : the gaussian center in y direction | ||
width_x : the gaussian sigma x | ||
width_y : the gaussian sigma y | ||
X_star : 2D array containing the X value | ||
Y_star : 2D array containing the Y value | ||
|
||
Returns | ||
------- | ||
model : return the 2D gaussiam | ||
""" | ||
model = intensity * np.exp( | ||
-(((X_star -x_center) / width_x) ** 2 + \ | ||
((Y_star - y_center) / width_y) ** 2) / 2) | ||
|
||
return model |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import GaiaCatalog |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import GaiaTools |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from . import aperture_photometry | ||
from . import photometric_scale_factor |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
from photutils.aperture import aperture_photometry | ||
from photutils.aperture import CircularAnnulus, CircularAperture | ||
from photutils.aperture import ApertureStats | ||
from astropy.io import fits | ||
from astropy.wcs import WCS | ||
import astropy.units as u | ||
from astropy.coordinates import SkyCoord | ||
import numpy as np | ||
|
||
from microlensing_photometry.astrometry import wcs as lcowcs | ||
|
||
class AperturePhotometryAnalyst(object): | ||
""" | ||
Class of worker to perform WCS refinement and aperture photometry for one image | ||
|
||
Attributes | ||
---------- | ||
image_path : a path+name to the data | ||
gaia_catalog : the Gaia catalog of the field | ||
|
||
|
||
Returns | ||
------- | ||
new_wcs : an updated astropy WCS object | ||
""" | ||
def __init__(self,image_path,gaia_catalog): | ||
|
||
self.image_path = image_path | ||
self.image_layers = fits.open(self.image_path) | ||
self.gaia_catalog = gaia_catalog | ||
|
||
### To fix with config files | ||
self.image_data = self.image_layers[0].data | ||
self.image_errors = self.image_layers[3].data | ||
self.image_original_wcs = WCS(self.image_layers[0].header) | ||
|
||
self.process_image() | ||
|
||
def process_image(self): | ||
|
||
self.find_star_catalog() | ||
self.refine_wcs() | ||
self.run_aperture_photometry() | ||
|
||
def find_star_catalog(self): | ||
|
||
if self.image_layers[1].header['EXTNAME']=='CAT': | ||
#BANZAI image | ||
self.star_catalog = np.c_[self.image_layers[1].data['x'], | ||
self.image_layers[1].data['y']] | ||
else: | ||
### run starfinder | ||
pass | ||
|
||
def refine_wcs(self): | ||
|
||
wcs2 = lcowcs.refine_image_wcs(self.image_data , self.star_catalog, | ||
self.image_original_wcs, self.gaia_catalog, | ||
star_limit = 1000) | ||
|
||
self.image_new_wcs = wcs2 | ||
|
||
def run_aperture_photometry(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have a class method and a function with a name collision - this should be clarified. |
||
|
||
skycoord = SkyCoord(ra=self.gaia_catalog['ra'], dec=self.gaia_catalog['dec'], unit=(u.degree, u.degree)) | ||
xx, yy = self.image_new_wcs.world_to_pixel(skycoord) | ||
positions = np.c_[xx,yy] | ||
|
||
phot_table = run_aperture_photometry(self.image_data, self.image_errors, positions, 3) | ||
exptime = self.image_layers[0].header['EXPTIME'] | ||
|
||
phot_table['aperture_sum'] /= exptime | ||
phot_table['aperture_sum_err'] /= exptime | ||
|
||
self.aperture_photometry_table = phot_table | ||
|
||
def run_aperture_photometry(image, error, positions,radius): | ||
""" | ||
Aperture photometry on a image | ||
|
||
Parameters | ||
---------- | ||
image : the image data | ||
error : the error data (2D) | ||
positions: [X,Y] positions of the stars to place aperture | ||
radius: aperture radius | ||
|
||
Returns | ||
------- | ||
phot_table : the photometric table | ||
""" | ||
aperture = CircularAperture(positions, r=radius) | ||
annulus_aperture = CircularAnnulus(positions, r_in=radius+3, r_out=radius+5) | ||
|
||
|
||
aperstats = ApertureStats(image, annulus_aperture) | ||
|
||
bkg_mean = aperstats.mean | ||
|
||
phot_table = aperture_photometry(image, aperture, error=error) | ||
total_bkg = aperture.area * bkg_mean | ||
|
||
phot_table['aperture_sum'] -= total_bkg | ||
|
||
return phot_table |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import numpy as np | ||
|
||
|
||
def photometric_scale_factor_from_lightcurves(lcs): | ||
""" | ||
Estimate the 16,50,84 percentiles of the photometric scale factor | ||
|
||
Parameters | ||
---------- | ||
lcs : an array containing all the lightcurves | ||
|
||
Returns | ||
------- | ||
pscales : the photometric scale factors | ||
""" | ||
pscales = np.nanpercentile(lcs/np.nanmedian(lcs,axis=1)[:,None],[16,50,84],axis=0) | ||
|
||
return pscales |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,3 +14,4 @@ fsspec = "2024.10.0" | |
[build-system] | ||
requires = ["poetry-core"] | ||
build-backend = "poetry.core.masonry.api" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment needs to be a little more specific - I guess you mean a fraction of the overall image field of view.