Skip to content

Commit 3817202

Browse files
authored
fix bug: aperture estimation, position correction (#174)
1 parent ac5e68d commit 3817202

File tree

1 file changed

+99
-26
lines changed

1 file changed

+99
-26
lines changed

iop4lib/instruments/instrument.py

Lines changed: 99 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -520,10 +520,17 @@ def compute_aperture_photometry(cls, redf, aperpix, r_in, r_out):
520520
from photutils.utils import calc_total_error
521521
from astropy.stats import SigmaClip
522522

523-
bkg_box_size = redf.mdata.shape[0]//10
523+
img = redf.mdata
524+
525+
# centroid_sources and fit_fwhm need bkg-substracted data
524526

527+
# opt 1) estimate bkg using more complex algs
528+
bkg_box_size = redf.mdata.shape[0]//10
525529
bkg = get_bkg(redf.mdata, filter_size=1, box_size=bkg_box_size)
526-
img = redf.mdata
530+
median = bkg.background_median
531+
532+
# opt 2) estimate bkg using simple sigma clip
533+
# mean, median, std = sigma_clipped_stats(img, sigma=3.0)
527534

528535
error = calc_total_error(img, bkg.background_rms, cls.gain_e_adu)
529536

@@ -544,17 +551,31 @@ def compute_aperture_photometry(cls, redf, aperpix, r_in, r_out):
544551
logger.warning(f"{redf}: ({pairs}) image of {astrosource.name} is too close to the border, skipping aperture photometry.")
545552
continue
546553

554+
# # correct position using centroid
555+
# # choose a box size that is somewhat larger than the aperture
556+
# # in case of pairs, cap box size so that it is somewhat smaller than the distance between pairs
557+
558+
# box_size = math.ceil(1.6 * aperpix)//2 * 2 + 1
559+
560+
# if redf.has_pairs:
561+
# box_size_max = math.ceil(np.linalg.norm(Instrument.by_name(redf.instrument).disp_sign_mean))//2 * 2 - 1
562+
# box_size = min(box_size, box_size_max)
563+
547564
# correct position using centroid
548-
# choose a box size that is somewhat larger than the aperture
549-
# in case of pairs, cap box size that is somewhat smaller than the distance between pairs
565+
# choose a box size around 12.0 arcsec (0.4'' is excellent seeing)
566+
# in case of pairs, cap box size so that it is somewhat smaller than the distance between pairs
550567

551-
box_size = math.ceil(1.6 * aperpix)//2 * 2 + 1
568+
arcsec_px = redf.pixscale.to(u.arcsec/u.pix).value
569+
570+
box_size = math.ceil( ( 12 / arcsec_px ) ) // 2 * 2 + 1
571+
572+
logger.debug(f"{box_size=}")
552573

553574
if redf.has_pairs:
554-
_box_size_max = math.ceil(np.linalg.norm(Instrument.by_name(redf.instrument).disp_sign_mean))//2 * 2 - 1
555-
box_size = min(box_size, _box_size_max)
575+
box_size_max = math.ceil(np.linalg.norm(Instrument.by_name(redf.instrument).disp_sign_mean))//2 * 2 - 1
576+
box_size = min(box_size, box_size_max)
556577

557-
centroid_px_pos = centroid_sources(img, xpos=wcs_px_pos[0], ypos=wcs_px_pos[1], box_size=box_size, centroid_func=centroid_2dg)
578+
centroid_px_pos = centroid_sources(img-median, xpos=wcs_px_pos[0], ypos=wcs_px_pos[1], box_size=box_size, centroid_func=centroid_2dg)
558579
centroid_px_pos = (centroid_px_pos[0][0], centroid_px_pos[1][0])
559580

560581
# check that the centroid position is within the borders of the image
@@ -580,14 +601,16 @@ def compute_aperture_photometry(cls, redf, aperpix, r_in, r_out):
580601
flux_counts = ap_stats.sum - annulus_stats.mean*ap_stats.sum_aper_area.value # TODO: check if i should use mean!
581602
flux_counts_err = ap_stats.sum_err
582603

583-
apres = AperPhotResult.create(reducedfit=redf,
584-
astrosource=astrosource,
585-
aperpix=aperpix,
586-
r_in=r_in, r_out=r_out,
587-
x_px=centroid_px_pos[0], y_px=centroid_px_pos[1],
588-
pairs=pairs,
589-
bkg_flux_counts=bkg_flux_counts, bkg_flux_counts_err=bkg_flux_counts_err,
590-
flux_counts=flux_counts, flux_counts_err=flux_counts_err)
604+
apres = AperPhotResult.create(
605+
reducedfit=redf,
606+
astrosource=astrosource,
607+
aperpix=aperpix,
608+
r_in=r_in, r_out=r_out,
609+
x_px=centroid_px_pos[0], y_px=centroid_px_pos[1],
610+
pairs=pairs,
611+
bkg_flux_counts=bkg_flux_counts, bkg_flux_counts_err=bkg_flux_counts_err,
612+
flux_counts=flux_counts, flux_counts_err=flux_counts_err,
613+
)
591614

592615
apres_L.append(apres)
593616

@@ -709,8 +732,10 @@ def estimate_common_apertures(cls, reducedfits, reductionmethod=None, fit_boxsiz
709732
It fits the target source profile in the fields and returns some multiples of the fwhm which are used as the aperture and as the inner and outer radius of the annulus for local bkg estimation).
710733
"""
711734

712-
from iop4lib.utils import fit_gaussian
713-
from iop4lib.db import AstroSource
735+
from photutils.psf import fit_fwhm
736+
from iop4lib.utils.sourcedetection import get_bkg
737+
738+
# select the target source from the list of files (usually, there is only one)
714739

715740
astrosource_S = set.union(*[set(redf.sources_in_field.all()) for redf in reducedfits])
716741
target_L = [astrosource for astrosource in astrosource_S if not astrosource.is_calibrator]
@@ -723,30 +748,78 @@ def estimate_common_apertures(cls, reducedfits, reductionmethod=None, fit_boxsiz
723748
target = astrosource_S.pop()
724749
else:
725750
return np.nan, np.nan, np.nan, {'mean_fwhm':np.nan, 'sigma':np.nan}
751+
752+
logger.debug(f"{target=}")
753+
754+
# Iterate over each file, get the fwhm of the source
726755

727756
fwhm_L = list()
728757

729758
for redf in reducedfits:
759+
730760
try:
731-
gaussian = fit_gaussian(px_start=redf.wcs.world_to_pixel(target.coord), redf=redf)
732-
fwhm = (2*np.sqrt(2*math.log(2))) * np.sqrt(gaussian[0].x_stddev.value**2+gaussian[0].y_stddev.value**2)
733-
if not (fwhm_min < fwhm < fwhm_max):
734-
logger.warning(f"ReducedFit {redf.id} {target.name}: fwhm = {fwhm} px, skipping this reduced fit")
735-
continue
736-
logger.debug(f"ReducedFit {redf.id} [{target.name}] Gaussian FWHM: {fwhm:.1f} px")
737-
fwhm_L.append(fwhm)
761+
762+
img = redf.mdata
763+
764+
# centroid_sources and fit_fwhm need bkg-substracted data
765+
766+
# opt 1) estimate bkg using more complex algs
767+
bkg_box_size = redf.mdata.shape[0]//10
768+
bkg = get_bkg(redf.mdata, filter_size=1, box_size=bkg_box_size)
769+
median = bkg.background_median
770+
771+
# opt 2) estimate bkg using simple sigma clip
772+
# mean, median, std = sigma_clipped_stats(img, sigma=3.0)
773+
774+
for pairs, wcs in (('O', redf.wcs1), ('E', redf.wcs2)) if redf.has_pairs else (('O',redf.wcs),):
775+
776+
wcs_px_pos = target.coord.to_pixel(wcs)
777+
778+
# correct position using centroid
779+
# choose a box size around 12.0 arcsec (0.4'' is excellent seeing)
780+
# in case of pairs, cap box size so that it is somewhat smaller than the distance between pairs
781+
782+
arcsec_px = redf.pixscale.to(u.arcsec/u.pix).value
783+
784+
box_size = math.ceil( ( 12 / arcsec_px ) ) // 2 * 2 + 1
785+
786+
logger.debug(f"{box_size=}")
787+
788+
if redf.has_pairs:
789+
box_size_max = math.ceil(np.linalg.norm(Instrument.by_name(redf.instrument).disp_sign_mean))//2 * 2 - 1
790+
box_size = min(box_size, box_size_max)
791+
792+
centroid_px_pos = centroid_sources(img-median, xpos=wcs_px_pos[0], ypos=wcs_px_pos[1], box_size=box_size, centroid_func=centroid_2dg)
793+
centroid_px_pos = (centroid_px_pos[0][0], centroid_px_pos[1][0])
794+
795+
# log the difference between the WCS and the centroid
796+
wcs_diff = np.sqrt((centroid_px_pos[0] - wcs_px_pos[0])**2 + (centroid_px_pos[1] - wcs_px_pos[1])**2)
797+
798+
logger.debug(f"ReducedFit {redf.id}: {target.name} {pairs}: WCS centroid distance = {wcs_diff:.1f} px")
799+
800+
fwhm = fit_fwhm(img-median, xypos=centroid_px_pos, fit_shape=7)[0]
801+
802+
if not (fwhm_min < fwhm < fwhm_max):
803+
logger.warning(f"ReducedFit {redf.id} {target.name}: fwhm = {fwhm} px, skipping this reduced fit")
804+
continue
805+
806+
logger.debug(f"ReducedFit {redf.id} [{target.name}] Gaussian FWHM: {fwhm} px")
807+
808+
fwhm_L.append(fwhm)
809+
738810
except Exception as e:
739811
logger.warning(f"ReducedFit {redf.id} {target.name}: error in gaussian fit, skipping this reduced fit ({e})")
740812
continue
741813

742814
if len(fwhm_L) > 0:
743815
mean_fwhm = np.mean(fwhm_L)
744816
else:
745-
logger.error(f"Could not find an appropriate aperture for Reduced Fits {[redf.id for redf in reducedfits]}, using standard fwhm of 3.5px")
817+
logger.error(f"Could not find an appropriate aperture for Reduced Fits {[redf.id for redf in reducedfits]}, using standard fwhm of {fwhm_default} px")
746818
mean_fwhm = fwhm_default
747819

748820
sigma = mean_fwhm / (2*np.sqrt(2*math.log(2)))
749821

822+
750823
return 3.0*sigma, 5.0*sigma, 9.0*sigma, {'mean_fwhm':mean_fwhm, 'sigma':sigma}
751824

752825

0 commit comments

Comments
 (0)