Image registration

In some cases a calibration image of a field containing a lot of stars obtained with SITELLE in the same filter and during the same run as the science data can be used to compute a distortion model that can be then used directly to enhance the astrometrical calibration of the science cube which may have much less stars. The distortion model is then kept as is and only the basic registration parameters are recomputed (see Martin el al. 2017, http://adsabs.harvard.edu/abs/2017arXiv170701366M)

[2]:
import pylab as pl
import orb.image
import logging
# useful to get logging output. orb and orcs use the logging package which is automatically set to INFO level when a cube is loaded.
# But, as we are not loading a cube this time and only working on images, you must do it manually.
logging.getLogger().setLevel(logging.INFO)

Initial calibration

target_ra and target_dec are the celestial coordinates at the center of the image.

note: this process can be done in two steps. For the first step we are only concerned with the calibration of the center of the image. During the second step a distortion model is computed over the whole field of view.

[ ]:
im = orb.image.Image('M1-71_SN3.merged.InterferogramMerger.deep_frame.fits', instrument='sitelle')
# in case any wrong wcs parameters were stored in the header, it is safe to reset the wcs to get a fresh start before the registration.
im.reset_wcs(294.1120729, 19.70678597)
im.register()
[4]:
# the HDF5 format is the native format for orb and orcs and you are sure to keep all the data with it.
# But for an image you can, in principle, safely use the fits format.
im.writeto('M1-71.deep_frame.wcs.init.hdf5')
im.to_fits('M1-71.deep_frame.wcs.init.fits')
INFO:root:Data written as M1-71.deep_frame.wcs.init.fits in 0.05 s
[2]:
# now we will check the results of the calibration by loading the positions of the stars in the field from the gaia catalog
# before plotting them on the image
im = orb.image.Image('M1-71.deep_frame.wcs.init.hdf5')
sl = im.get_stars_from_catalog(max_stars=1000)
INFO:root:Sending query to VizieR server (catalog: gaia2)
INFO:root:Looking for stars at RA: 294.113293 DEC: 19.706693
INFO:root:1000 stars recorded in the given field
INFO:root:Magnitude min: 9.5522, max:17.1995
[4]:
sl # the catalog position ra, dec as well as their positions in pixels are stroed in a pandas.DataFrame object
[4]:
x y ra dec
0 1264.454377 286.145713 294.091757 19.639255
1 1724.392602 1250.186154 294.045982 19.725027
2 419.186355 1268.322106 294.170612 19.728998
3 902.256353 1206.209789 294.124590 19.722561
4 1696.768721 562.242566 294.049953 19.663274
... ... ... ... ...
654 1471.934583 2031.989426 294.068588 19.795722
655 1948.153033 1610.327001 294.023907 19.756971
656 1561.509786 1794.601911 294.060488 19.774233
657 2003.091878 1433.274841 294.019005 19.740964
658 1237.617490 1228.616455 294.092516 19.723973

659 rows × 4 columns

[5]:
# You can see that the registration is correct near the center of the field but without a proper distortion model, the position in the corners can be very bad.
im.imshow(cmap='gray', wcs=False)
pl.scatter(sl.x, sl.y, marker='+', color='red')
[5]:
<matplotlib.collections.PathCollection at 0x7f7d4595f950>
_images/script_example_image_registration_7_1.png

Final calibration

The obtained wcs is used as an input for the final calibration. Now the whole field of view can be considered.

A few passes may be necessary to get a good enough astrometrical precision in the corners. But don’t try to get something perfect here (above all with cubes obtained before the optical upgrade of 2020 - http://www.cfht.hawaii.edu/en/news/SITELLEUpgrade/), the distortion is very important and capturing the stars in the corners may worsen the astrometrical precision in the center.

[53]:
im = orb.image.Image('M1-71.deep_frame.wcs.init.hdf5')
wcs, dxmap, dymap = im.register(skip_registration=True, compute_distortion=True, return_error_maps=True, max_radius_coeff=2, sip_order=4, max_stars_detect=200)
im.writeto('M1-71.deep_frame.wcs.sip1.hdf5')
INFO:root:Computing WCS
INFO:root:initial wcs
INFO:root:WCS Transformation

This transformation has 2 pixel and 2 world dimensions

Array shape (Numpy order): (2064, 2048)

Pixel Dim  Data size  Bounds
        0       2048  None
        1       2064  None

World Dim  Physical Type  Units
        0  pos.eq.ra      deg
        1  pos.eq.dec     deg

Correlation between pixel and world axes:

           Pixel Dim
World Dim    0    1
        0  yes  yes
        1  yes  yes
INFO:root:initial sip: None
INFO:root:Sending query to VizieR server (catalog: gaia2)
INFO:root:Looking for stars at RA: 294.113293 DEC: 19.706693
INFO:root:14059 stars recorded in the given field
INFO:root:Magnitude min: 9.5522, max:21.0
INFO:root:5753 stars detected
INFO:root:star list reduced to 200 stars
INFO:root:Detected stars FWHM : 3.22(0.11) pixels, 1.04(0.04) arc-seconds
INFO:root:Computing SIP coefficients
INFO:root:sip before sip fit (A matrix)
INFO:root:no sip
INFO:root:sip computed with 4000 stars
INFO:root:3745 stars fitted
INFO:root:Optimized average radius for direct transformation (in pixel) 3.3457407284985066
INFO:root:Optimized average radius for reverse transformation (in pixel) 6.971630626817465e-09
INFO:root:sip after sip fit (A matrix)
INFO:root:[[-2.33140073e-09  1.05467159e-04  3.69628840e-09 -6.76514129e-10
   7.69110989e-13]
 [ 1.58698975e-09  2.38309823e-09 -1.68439216e-09 -1.68141704e-12
   0.00000000e+00]
 [ 1.09299987e-06  1.41609573e-09 -3.67666881e-12  0.00000000e+00
   0.00000000e+00]
 [-4.70860701e-09  1.27235285e-12  0.00000000e+00  0.00000000e+00
   0.00000000e+00]
 [-6.70326658e-13  0.00000000e+00  0.00000000e+00  0.00000000e+00
   0.00000000e+00]]
INFO:root:Computing distortion maps
INFO:root:distortion maps computed with 4000 stars
INFO:root:distortion maps computed with 2409 good stars
/home/thomas/Astro/Python/ORB/Orb/orb/ext/zern.py:300: FutureWarning: `rcond` parameter will change to the default of machine precision times ``max(M, N)`` where M and N are the input matrix dimensions.
To use the future default and silence this warning we advise to pass `rcond=None`, to keep using the old, explicitly pass `rcond=-1`.
  wf_zern_vec = np.linalg.lstsq((zern_basismat[:, grid_vec] * weight).T, wf_w.ravel())[0]
INFO:root:Standard deviation of the residual: 0.6869370369065136
INFO:root:Median relative error (err/val)): 33.70%
INFO:root:Standard deviation of the residual: 0.6838007029545435
INFO:root:Median relative error (err/val)): 30.54%
INFO:root:computing full sized dxdymaps (error maps)
INFO:root:Computing astrometrical precision
INFO:root:Astrometrical precision [in arcsec]: 0.140 [+/-0.075] computed over 3042 stars
INFO:root:corrected WCS computed
INFO:root:internal WCS updated (to update the file on disk use self.writeto function)

Check the results

[55]:
im = orb.image.Image('M1-71.deep_frame.wcs.sip1.hdf5')
sl = im.get_stars_from_catalog(max_stars=1000)
im.imshow(cmap='gray', wcs=True)
pl.scatter(sl.x, sl.y, marker='+', color='red')
pl.grid()
INFO:root:Sending query to VizieR server (catalog: gaia2)
INFO:root:Looking for stars at RA: 294.113336 DEC: 19.706637
INFO:root:1000 stars recorded in the given field
INFO:root:Magnitude min: 9.5522, max:17.1968
_images/script_example_image_registration_11_1.png
[ ]: