This section provides working examples to exercise Mosaic and MosaicAD class and their functions.
Start your favorite Python shell
importing modules
from astrodata import AstroData
# The directory mosaicAD.py and gemMosaicFunction.py modules
# will probably change when the code goes into production.
from gempy.adlibrary.mosaicAD import MosaicAD
# This is a user function available for your use,
# it supports GMOS and GSAOI data
from gempy.gemini.gemMosaicFunction import gemini_mosaic_function
Use AstroData to open a FITS file
ad = AstroData('S20100113S0110vardq.fits')
With MosaicAD we instantiate an object using a user written function gemini_mosaic_function. The default image extension name is ‘SCI’. Click here to see an example of a user_function.
mo = MosaicAD(ad,mosaic_ad_function=gemini_mosaic_function)
Use mosaic_image_data method. The output mosaic_array is a numpy array of the same datatype as the input image array in the ad object. The blocks array are corrected (transformed) for shift, rotation and magnification with respect to the reference block.
mosaic_array = mo.mosaic_image_data()
This example uses the as_astrodata method to create mosaics with merged associated tables, provided that your input file has BINTABLE extensions. The default action is to act on all the extensions in the input AstroData object but you can specify a given extname as in the example. Please see the documentation for the available parameters in this method.
from astrodata import AstroData
# The directory for mosaicAD.py and gemMosaicFunction.py
# will probably change when the code goes into production.
from gempy.adlibrary.mosaicAD import MosaicAD
# This is a user function available for your use,
# it supports GMOS and GSAOI data
from gempy.gemini.gemMosaicFunction import gemini_mosaic_function
ad = AstroData(file)
# Create a mosaicAD object using the input ad
# and the default mosaic function.
mo = MosaicAD(ad, gemini_mosaic_function)
# Now run the method to mosaic and associate the
# input ad extensions.
outad = mo.as_astrodata()
# Print the content of the resulting ad object.
print outad.info()
# DO THE SAME BUT USE ONLY THE 'VAR' extension.
# If the input tables are associated with 'VAR', they
# will be merged.
# NOTICE that running 'as_astrodata' with the 'DQ' extension
# is VERY slow since it will transform 8 DQ planes separately.
outad_var = mo.as_astrodata(extname='VAR')
# Print the content of the resulting ad object.
print outad.info()
A tile array is a mosaic array without the correction for shift, rotation and magnification. Here we used the mosaic_image_data method with the default extension name ‘SCI’. By using the extname parameter you can change the extname to get tile from. Notice the parameter tile must be True.
from astrodata import AstroData
# The directory for mosaicAD.py and gemMosaicFunction.py
# will probably change when the code goes into production.
from gempy.adlibrary.mosaicAD import MosaicAD
# This is a user function available for your use,
# it supports GMOS and GSAOI data
from gempy.gemini.gemMosaicFunction import gemini_mosaic_function
ad = AstroData(file)
# Create a mosaicAD object using the input ad
# and the gemini mosaic function (assuming that file
# points to a GMOS or GSAOI FITS file).
mo = MosaicAD(ad, gemini_mosaic_function)
# The default value for extname is 'SCI'
sci_tile = mo.mosaic_image_data(tile=True)
# Print the shape of the resulting tile.
print 'Tile shape:',sci_tile.shape
# Use 'as_astrodata' to get an AstroData object as output.
# Notice the use of extname since the default action is
# to work on all extensions.
outad = mo.as_astrodata(tile=True, extname='SCI')
# Print the content of the resulting ad object.
print outad.info()
A mosaic consists of one or more blocks, e.g. for GMOS 3-amp mode a mosaic has 3 blocks; for a 6-amps mode still the mosaic has 3 blocks but each block has 2-amps. The blocks’ layout is represented with a tuple of the form (column, row) (zero-based). Use the extname parameter from mosaic_image_data to select which extname to get the block from.
from astrodata import AstroData
# The directory for mosaicAD.py and gemMosaicFunction.py
# will probably change when the code goes into production.
from gempy.adlibrary.mosaicAD import MosaicAD
# This is a user function available for your use,
# it supports GMOS and GSAOI data
from gempy.gemini.gemMosaicFunction import gemini_mosaic_function
ad = AstroData(file)
mo = MosaicAD(ad, gemini_mosaic_function)
# Now use the mosaic_image_data method to generate
# an output ndarray by using the parameter block and
# its value set to a tuple (col,row) (0-based) of the block
# you want returned. For GMOS the block values are
# (0,0), (1,0), (2,0). The extension name is the
# default 'SCI'.
block_array = mo.mosaic_image_data(block=(1,0),extname='VAR')
# Get the shape: (height, width) in pixels.
print block_array.shape
A user function is necessary to instantiate a MosaicAD object. If you have an arbitrary FITS file then this one would probably work depending whether the input FITS file have the keywords DETSEC, CCDSEC and DATASEC.
from astrodata import AstroData
from gempy.gemini_metadata_utils import sectionStrToIntList
# The directory for mosaic.py will probably
# change when the code goes into production.
from gempy.library.mosaic import MosaicData, MosaicGeometry
def my_input_function(file,extname=None):
"""
ad: Input AstroData object.
SUMMARY:
1) Read image extensions 'SCI' from the ad.
Append each extension to a *data_list* list.
2) Read header keywords DETSEC and CCDSEC from the same
extension as in 1) and form two lists with the keyword
values. Turn these values to zero-based tuples of the
form (x1,x2,y1,y2). The DETSEC list is named 'amp_mosaic_coord'
and the CCDSEC list is named 'amp_block_coord'.
If you don't have these keywords use other means to
determine 'amp_mosaic_coord' and 'amp_block_coord'.
Make a 'coords' dictionary with 'amp_mosaic_coord' and 'amp_block_coord' keys.
So we would have:
coords = {'amp_mosaic_coord': detsec_list, 'amp_block_coord': ccdsec_list}
3) Instantiate a MosaicData object with the above lists.
4) Set 'blocksize' to (nx,ny). nx is width and ny is the
height -in pixels of the block containing the data_list
elements.
5) Set 'mosaic_grid'. (nblocks_x,nblocks_y), where nblocks_x
is the number of blocks in the x_direction and nblockcs_y
is the number of rows. This is the mosaic layout.
RETURN: (mosaic_data, mosaic_geometry)
"""
# TO THE READER: The basic of the AstroData assumptions if that EXTNAME
# is the same for all IMAGE extension of a certain type
# and the EXTVER value is different for all these extname's.
# Example: GMOS have EXTNAME 'SCI' and EXTVER have values
# 1 for FITS extension 1, 2 for FITS extension 2, etc.
# get data_list from the ad object
ad = AstroData(file)
data_list = [hdu.data for hdu in ad['SCI']]
# Get DETSEC
amps_mosaic_coord = ad.detector_section(extname='SCI').as_list()
# Get CCDSEC. These can be equal for all extensions. The as_list() method
# will remove duplicates, so let's try something else.
amps_block_coord = (
[sectionStrToIntList(hdu.header['CCDSEC']) for hdu in ad['SCI']] )
# Form the coords dictionary
coords = {'amps_mosaic_coord': amps_mosaic_coord,
'amps_block_coord': amps_block_coord}
# Mosaic Data object
md = MosaicData(data_list,coords)
# Important: blocksize tuple is (blocksize_x, blocksize_y). Just to
# keep the external representation in (x,y) order rather than
# python's (y,x).
# For simplicity make the blocksize the same as the input
# data shape
(sz_y, sz_x) = data_list[0].shape
blocksize = (sz_y, sz_x)
mosaic_grid = (2,2)
# MosaicGeometry. We have a 'transformation' dictionary which
# allows us to correct for rotation in this case.
geo_dict = {
'mosaic_grid': mosaic_grid,
'blocksize': blocksize,
'ref_block': (0,0), # 0-based
'transformation': { # shift and magnification will
# have default values
'rotation': (0, 5.0, 4.5, 5.3), # Rotation in degrees
# for each block.
},
}
mg = MosaicGeometry(geo_dict)
# Return require objects
#
return md,mg
A user function is necessary to instantiate a MosaicAD object. If you have an arbitrary FITS file then this one would probably work depending whether the input FITS file have the keywords DETSEC, CCDSEC and DATASEC.
import pyfits as pf
from gempy.gemini_metadata_utils import sectionStrToIntList
# The directory for mosaic.py will probably
# change when the code goes into production.
from gempy.library.mosaic import MosaicData, MosaicGeometry
def my_input_function(file,extname=None):
"""
SUMMARY:
1) Read image extensions 'SCI' from the hdulist.
Append each extension to a *data_list* list.
If the FITS file already have extension names other than
'SCI' will try something else.
2) Read header keywords DETSEC and CCDSEC from the same
extension as in 1) and form two lists with the keyword
values. Turn these values to zero-based tuples of the
form (x1,x2,y1,y2). The DETSEC list is named 'amp_mosaic_coord'
and the CCDSEC list is named 'amp_block_coord'.
If you don't have these keywords use other means to
determine 'amp_mosaic_coord' and 'amp_block_coord'.
Make a 'coords' dictionary with 'amp_mosaic_coord' and 'amp_block_coord' keys.
So we would have:
coords = {'amp_mosaic_coord': detsec_list, 'amp_block_coord': ccdsec_list}
3) Instantiate a MosaicData object with the above lists.
4) Set 'blocksize' to (nx,ny). nx is width and ny is the
height -in pixels of the block containing the data_list
elements.
5) Set 'mosaic_grid'. (nblocks_x,nblocks_y), where nblocks_x
is the number of blocks in the x_direction and nblockcs_y
is the number of rows. This is the mosaic layout.
RETURN: (mosaic_data, mosaic_geometry)
"""
fits = pf.open(file)
data_list = [hdu.data for hdu in fits[1:]]
amps_mosaic_coord = (
[sectionStrToIntList(hdu.header['DETSEC']) for hdu in fits[1:]] )
amps_block_coord = (
[sectionStrToIntList(hdu.header['CCDSEC']) for hdu in fits[1:]] )
# Form the coords dictionary
coords = {'amps_mosaic_coord': amps_mosaic_coord,
'amps_block_coord': amps_block_coord}
# Mosaic Data object
md = MosaicData(data_list,coords)
# Important: blocksize tuple is (blocksize_x, blocksize_y). Just to
# keep the external representation in (x,y) order rather than
# python's (y,x).
# For simplicity make the blocksize the same as the input
# data shape
(sz_y, sz_x) = data_list[0].shape
blocksize = (sz_y, sz_x)
mosaic_grid = (2,2)
# MosaicGeometry. We have a 'transformation' dictionary which
# allows us to correct for rotation in this case.
geo_dict = {
'mosaic_grid':mosaic_grid,
'blocksize':blocksize,
'ref_block': (0,0), # 0-based
'transformation': { # shift and magnification will
# have default values
'rotation': (0, 5.0, 4.5, 5.3), # Rotation in degrees
# for each block.
},
}
mg = MosaicGeometry(geo_dict)
# Return require objects
#
return md,mg
In order to create a mosaic we need at least a MosaicData object to be used as input to the Mosaic class initialize function. Let’s make a list of numpy arrays and a dictionary of the arrays locations in the mosaic.
The location of the data arrays is set with a dictionary of the corner coordinates containing ‘amp_mosaic_coord’ and ‘amp_block_coord’ keys, where ‘amp_mosaic_coord’ is a list tuples (x1,x2,y1,y2). (x1,y1) is the lower left, (x2,y2) is the right top corner with respect to the origin (0,0) at the lower left corner of the mosaic to be created. The ‘amp_block_coord’ is a list of tuples (x1,x2,y1,y2) describing the corners of each data array element but with origin as the lower left corner of each block. A block is defined as a subsection of the mosaic containing one or more data arrays; e.g. a detector array data having two readouts (amplifiers).
import numpy
# The directory for mosaic.py will probably
# change when the code goes into production.
from gempy.library.mosaic import MosaicData
# Make 4 data arrays of size nx:1024, ny:2048
data = numpy.linspace(0.,1000.,1024*2048).reshape(2048,1024)
data_list = [data*(-1)**k for k in numpy.arange(4)]
# Image section are: (x1,x2,y1,y2)
coords = {'amp_mosaic_coord': [(0, 1024, 0, 2048), (1024, 2048, 0, 2048),
(2048, 3072, 0, 2048), (3072, 4096, 0, 2048)],
'amp_block_coord': [(0, 1024, 0, 2048), (0, 1024, 0, 2048),
(0, 1024, 0, 2048), (0, 1024, 0, 2048)]
}
# Now instantiate the MosaicData object
data_object = MosaicData(data_list, coords)
Each data block might need to be corrected for shift, rotation and magnification. In this example we have four data blocks and the ‘geo_dict’ contains values for these parameters . There are 4 tuples for shift, one for each data block. The first tuple correspond to the values for the reference block ref_block with values shift:(0,0) to not shift, rotation: 0.0 to not rotate and magnification:1.0 to not magnify; all the rest of the list are values with respect to the reference block.
from gempy.library.mosaic import MosaicGeometry
geo_dict = {
'transformation': {
'shift':[(0,0), (43.60, -1.24),
(0.02, 41.10), (43.42, 41.72)], # List of (x,y) shift in pixel
'rotation': (0.0, -1.033606,
0.582767, 0.769542), # List of degrees, counterwise
# w/r to the x_axis
'magnification': (1., 1.0013,
1.0052, 1.0159), # List of magnification
},
# Gap dictionary:
# block: (x_gap,y_gap) in pixel
'gaps': {(0,0):(0,0), (1,0):(36,0), (2,0):(36,0), (3,0):(36,0)},
'blocksize': (1024,2048), # (npix_x, npix_y)
'mosaic_grid': (4,1), # Number of blocks in x and number of rows.
'ref_block': (0,0), # Reference block (column,row) 0-based.
'interpolator': 'linear', # Interpolator
}
# Now instantiate the MosaicGeometry object
geometry_object = MosaicGeometry(geo_dict)
Use the data_object from Example 6 and geometry_object from Example 7 to instantiate a Mosaic object. We print the shape of the output mosaic and display it -using ds9. Make sure you have ds9 up and running.
from numdisplay import display
# The directory for mosaic.py will probably
# change when the code goes into production.
from gempy.library.mosaic import Mosaic
# Go to Example 6 and create the data_object, go to
# example 7 and create the geometry_object.
mo = Mosaic(data_object, geometry_object)
# Now produce a mosaic with the layout given by 'amp_mosaic_coord' and 'amp_block_coord'
# from 'data_object' attribute.
mosaic_array = mo.mosaic_image_data()
print mosaic_array.shape
# display
display(mosaic_array,frame=1)
The Mosaic class method mosaic_image_data generates mask of the same shape as the output mosaic and with pixel value 0 for image data and 1 for no-data values in the output mosaic. No-data values are gaps areas and those produced by transformation when the image is shifted and/or rotated.
#
# display the mask for the mosaic in the previous example.
display(mo.mask,frame=2,z1=0,z2=1.5)
Using the data_object and geometry_object from Examples 6 and 7 create a Mosaic object, then transform the block (0,1) (the top left block).
The purpose of this example is to show the usage of the Mosaic method ‘transform’.
import numpy as np
from gempy.library.mosaic import Mosaic, MosaicGeometry, MosaicData
from numdisplay import display
geo_dict = {
'transformation': { # these tuples correspond to blocks:
# (0,0) (1,0) (0,1) (1,1)
'shift': [(0.,0.), (-10,20), (-10,20), (0,0)],
'rotation': (0.0, 0.0, 45.0, 45.0),
'magnification': (1.0, 1.0, 1.0, 0.5),
},
'interpolator': 'linear',
# gaps block: (x_gap,y_gap) (pixels)
'gaps': {(0,0):(0,0), (1,0):(20,0), (0,1):(0,30), (1,1):(20,30)}
'blocksize': (200,300), # number of pixels in x and in y.
'ref_block': (0,0), # 0-base reference block
'mosaic_grid': (2,2) # number of blocks in x and in y
}
mosaic_geometry = MosaicGeometry(geo_dict)
# Make a rectangle (200,300) (wide,high).
data = np.ones((300,200),dtype=np.float32)
data = data*20 # make the background 20
# Make a four elements data_list (mosaic_grid).
# The blocks layout in the mosaic is:
# (0,0), (1,0), (0,1), (1,1)
data_list = [data,data,data,data]
# Inside each block, make a small box 50x50
# starting at (50,50) with value 100
for k in range(4):
data_list[k][50:101,50:101] = 100.
# Mark the block borders with value 400
data_list[k][:,0] =400
data_list[k][:,199]=400
data_list[k][0,:] =400
data_list[k][299,:]=400
# Now create the MosaicData object
mosaic_data = MosaicData(data_list)
# With these two objects we instantiate a Mosaic object
mo = Mosaic(mosaic_data, mosaic_geometry)
# Let take the block corresponding to the location (0,1) within the
# mosaic and transform. The values used are: shift: (-10,-20) in (x,y),
# rotation: 45 degrees about the center and magnification: 1.0
# (no magnification)
trans_data = mo.transform(mo.data_list[2],(0,1))
# ---- Now display both blocks to visually see the difference
# between original and transformed blocks.
# display input data
display(mo.data_list[2],frame=1)
# display transform data
display(trans_data,frame=2)
When transforming a block, a default interpolation function is used (linear). The available functions are: ‘nearest’, ‘linear’, and ‘spline’ with order (0-5).
The purpose of this example is to illustrate the effects on a transformed block when resetting the interpolation function.
The method to reset the interpolation function is:
mo.set_transformation_function(function_name, order)
Create 2 ndarrays list and mark a strip of the 2nd ndarray with a higher value. Set the Geometry dictionary with ‘rotate’ this ndarray by 5 degrees. Now we create the mosaic with default interpolation function and again with the ‘spline’ function of order 5. We plot a column from each image.
import numpy as np
from gempy.library.mosaic import Mosaic,MosaicGeometry, MosaicData
from matplotlib import pyplot as pl
geo_dict = {
'transformation': { # shift and magnification will
# have default values
'rotation': (0.0, 5.),
},
'blocksize': (100,100),
'mosaic_grid': (2,1)
}
# With this dictionary create a MoaicGeometry object
geometry_object = MosaicGeometry(geo_dict)
# Make an ndarray
data = np.zeros((100,100),dtype=np.float32)
# put a stripe of 5 rows with value 5
data[45:50,:] = 5
# Make an 2x1 array with this rectangle.
data_list = [data,data]
# Create a MosaicData object
data_object = MosaicData(data_list)
# With these two objects we instantiate a Mosaic object
mo = Mosaic(data_object, geometry_object)
# Finally make the mosaic
mosaic_linear = mo.mosaic_image_data()
# Now reset the interpolator function the spline or order 5.
mo.set_interpolator('spline',spline_order=5)
# Create the mosaic
mosaic_spline = mo.mosaic_image_data()
# Now plot across the stripes
pl.plot(mosaic_linear[:,140])
pl.plot(mosaic_spline[:,140])
# The difference between the 2 plots is the edge effect at the
# low and high stripe corners plot due to interpolation.