# Place to put flatfield-related utility functions
import warnings
import numpy as np
from scipy import interpolate
from scipy.ndimage import median_filter
from scipy.ndimage import gaussian_filter as gauss
from scipy import ndimage
from scipy.signal import convolve2d
from scipy.ndimage import zoom
import astropy.io.fits as fits
from astropy.convolution import convolve_fft
from astropy.utils.exceptions import AstropyUserWarning
import photutils.centroids as centr
from photutils.aperture import CircularAperture
from photutils.detection import DAOStarFinder
import corgidrp.data as data
import corgidrp.mocks as mocks
[docs]
def create_flatfield(flat_dataset):
"""
Turn this dataset of image frames that were taken for performing the flat calibration and
to make one master flat image
this is currently a placeholder, until the final flat fielding calibration package is completed.
Args:
flat_dataset (corgidrp.data.Dataset): a dataset of Image frames (L2a-level)
Returns:
flat_field (corgidrp.data.FlatField): a master flat for flat calibration
"""
combined_frame = np.nanmean(flat_dataset.all_data, axis=0)
flat_field = data.FlatField(combined_frame, pri_hdr=flat_dataset[0].pri_hdr.copy(),
ext_hdr=flat_dataset[0].ext_hdr.copy(), input_dataset=flat_dataset)
#determine the standard error of the mean: stddev/sqrt(n_frames)
flat_field.err = np.nanstd(flat_dataset.all_data, axis=0)/np.sqrt(len(flat_dataset))
flat_field.err=flat_field.err.reshape((1,)+flat_field.err.shape) # Get it into the right dimension
# Make sure FlatField products have consistent headers, including synthetic ones-flats
header_defaults = {
# Image header (HDU 1)
"FRMSEL01": -999.0,
"FRMSEL02": False,
"FRMSEL03": -999.0,
"FRMSEL04": -999.0,
"FRMSEL05": -999.0,
"FRMSEL06": -999.0,
"FWC_EM_E": -999.0,
"FWC_PP_E": -999.0,
"SAT_DN": -999.0,
"RN": -999.0,
"RN_ERR": -999.0,
"KGAIN_ER": -999.0,
"DESMEAR": False,
"NUM_FR": -999,
}
for k, v in header_defaults.items():
if k not in flat_field.ext_hdr:
flat_field.ext_hdr[k] = v
# ERR headers
if getattr(flat_field, "err_hdr", None) is not None:
if "BUNIT" not in flat_field.err_hdr:
flat_field.err_hdr["BUNIT"] = "photoelectron"
for k in ("KGAIN_ER", "RN_ERR", "DESMEAR"):
if k in flat_field.ext_hdr and k not in flat_field.err_hdr:
flat_field.err_hdr[k] = flat_field.ext_hdr[k]
if "LAYER_1" not in flat_field.err_hdr:
flat_field.err_hdr["LAYER_1"] = "combined_error"
return flat_field
[docs]
def raster_kernel(width, image, hard=True):
"""
Convolution kernel to create flat field circular raster pattern
Args:
width (float): radius of circular raster in pixels
image (np.array): 2-D image to specify the full size of the kernel needed
hard (bool): if true, use hard edge kernel, otherwise, use Gaussian tapered kernel
Returns:
np.array: smoothing kernel value at each pixel
"""
kernel_width = width
im = image
# define coordinate grid
x = np.arange(0,im.shape[1] + 1) - im.shape[1]/2 - 1
y = np.arange(0,im.shape[0] + 1) - im.shape[0]/2 - 1
xx,yy = np.meshgrid(x, y)
rr = np.sqrt(xx**2 + yy**2)
rr_hard = np.sqrt(xx**2 + yy**2)
# Define the convolution kernel
if hard == True:
rr_hard[rr_hard<=kernel_width/2] = 1
rr_hard[rr_hard>kernel_width/2] = 0
kernel = rr_hard # option 1: hard-edge kernel
else:
kernel = np.exp(-rr**2/(2*kernel_width**2)) # option 2: Gaussian kernel
return kernel
[docs]
def flatfield_residuals(dataset, N=None):
"""Turn this dataset of image frames of neptune or uranus and create matched filters and estimate residuals after
dividing from matched filters
Args:
dataset (corgidrp.data.Dataset): dataset of cropped neptune or uranus image frames
N (int): number of images to be grouped. defaults to 3 for both Neptune and uranus. (If we use different number of dithers for neptune and uranus, option is provided to change N)
Returns:
matched_residuals_dataset (corgidrp.data.Dataset) : dataset of neptune or uranus images divided by matched filter
"""
raster_images = np.array(dataset.all_data)
# M is the number of images in each group used to calculate a filter.
M = len(raster_images) // N
images_split = np.array(np.split(np.array(raster_images),N))
matched_filters = np.array([np.nanmedian(np.stack(images_split[i],2),2) for i in np.arange(0,len(images_split))])
matched_filters_smooth = [gauss(matched_filters[i],3) for i in range(len(matched_filters))]
#Estimate the initial uncertainty (standard error of the mean/median) for each filter group.
sigma_matched_filters = np.array([np.nanstd(images_split[i], axis=0) / np.sqrt(M) for i in np.arange(N)])
# Apply the same smoothing (Gauss(sigma=3)) to the error maps as was applied to the filters.
sigma_matched_filters_smooth = [gauss(sigma_matched_filters[i], 3) for i in range(N)]
planet_matched_residuals=[]; planet_matched_residuals_err=[]; matched_residuals=[];matched_residuals_dataset=[]
for j in range(len(dataset)):
n_images=int(np.floor(j//(len(raster_images)//len(matched_filters_smooth))))
planet_image = dataset[j].data
planet_image_err = dataset[j].err
prihdr=dataset[j].pri_hdr
exthdr=dataset[j].ext_hdr
planet_matched_filter= matched_filters_smooth[n_images]
#calculate the residual
planet_residual = planet_image / planet_matched_filter
planet_matched_residuals.append(planet_residual)
#error propagation for division (Z = X/Y) ---
# Sigma_Z = |Z| * sqrt( (Sigma_X/X)^2 + (Sigma_Y/Y)^2 )
planet_matched_filter_err = sigma_matched_filters_smooth[n_images]
relative_variance_image = (planet_image_err / np.maximum(planet_image, 1e-6))**2
#relative variance of the filter: (Sigma_Y/Y)^2
relative_variance_filter = (planet_matched_filter_err / np.maximum(planet_matched_filter, 1e-6))**2
#final Error Calculation
planet_residual_error = planet_residual * np.sqrt(relative_variance_image + relative_variance_filter)
planet_matched_residuals_err.append(planet_residual_error)
matched_residuals_frames = mocks.Image(planet_residual, pri_hdr = prihdr, ext_hdr = exthdr, err = planet_residual_error)
matched_residuals.append(matched_residuals_frames)
matched_residuals_dataset=data.Dataset(matched_residuals)
return(matched_residuals_dataset)
[docs]
def combine_flatfield_rasters(resi_images_dataset,cent=None,planet=None,band=None,im_size=420,rad_mask=None, planet_rad=None, n_pix=165, n_pad=302,image_center_x=512,image_center_y=512):
"""combine the dataset of residual image frames of neptune or uranus and create flat field
and associated error
Args:
resi_images_dataset (corgidrp.data.Dataset): dataset of residual images of uranus or Neptune
cent (np.array): centroid of residual images of uranus or Neptune
planet (str): name of the planet neptune or uranus
band (str): band of the observation band1 or band4
im_size (int): x-dimension of the planet image (in pixels= 420 for the HST images)
rad_mask (float): radius in pixels used for creating a mask for band (band 1 =1.25, band 4=1.75)
planet_rad (int): radius of the planet in pixels (planet_rad=54 for neptune, planet_rad=65)
n_pix (int): Number of pixels in radius covering the Roman CGI imaging FOV defaults to 165 pixels
n_pad (int): Number of pixels padded with '1s' to generate the image size 1024X1024 pixels around imaging FOV (defaults to 302 pixels)
image_center_x (int): x coordinate of the center pixel of the final image
image_center_y (int): y coordinate of the center pixel of the final image
Returns:
full_residuals (np.array): flat field created from uranus or neptune images
err_residuals (np.array): Error in the flatfield estimated using the ideal flat field
cent_n (np.array): centroid of the combined image
"""
n = im_size
full_residuals = np.zeros((n,n))
p_flat=full_residuals.copy()
err_residuals= np.zeros((n,n))
if planet_rad is None:
if planet.lower() == 'neptune':
planet_rad = 50
elif planet.lower() == 'uranus':
planet_rad = 65
if rad_mask is None:
if band[0] == "1":
rad_mask = 1.25
elif band[0] == "4":
rad_mask = 1.75
aperture = CircularAperture((np.ceil(rad_mask), np.ceil(rad_mask)), r=rad_mask)
mask= aperture.to_mask().data
rad = planet_rad
for i in range(len(resi_images_dataset)):
nx = np.arange(0,resi_images_dataset[i].data.shape[1])
ny = np.arange(0,resi_images_dataset[i].data.shape[0])
nxx,nyy = np.meshgrid(nx,ny)
nrr = np.sqrt((nxx-rad-5)**2 + (nyy-rad-5)**2)
nrr_copy = nrr.copy();
nrr_err_copy=nrr.copy()
nrr_copy[nrr<rad] = resi_images_dataset[i].data[nrr<rad]
nrr_err_copy[nrr<rad] = resi_images_dataset[i].err[0][nrr<rad]
nrr_copy[nrr>=rad] = None
nrr_err_copy[nrr>=rad] = None
ymin = int(cent[i][0])
ymax = int(cent[i][1])
xmin = int(cent[i][2])
xmax = int(cent[i][3])
bool_innotzero = np.logical_and(nrr<rad,full_residuals[ymin:ymax,xmin:xmax]!=0)
bool_iniszero = np.logical_and(nrr<rad,full_residuals[ymin:ymax,xmin:xmax]==0)
bool_outisnotzero = np.logical_and(nrr>=rad,full_residuals[ymin:ymax,xmin:xmax]!=0)
bool_innotzero_err = np.logical_and(nrr<rad,err_residuals[ymin:ymax,xmin:xmax]!=0)
bool_iniszero_err = np.logical_and(nrr<rad,err_residuals[ymin:ymax,xmin:xmax]==0)
bool_outisnotzero_err = np.logical_and(nrr>=rad,err_residuals[ymin:ymax,xmin:xmax]!=0)
full_residuals[ymin:ymax,xmin:xmax][bool_innotzero] = (nrr_copy[bool_innotzero] + full_residuals[ymin:ymax,xmin:xmax][bool_innotzero])/2
full_residuals[ymin:ymax,xmin:xmax][bool_iniszero] = nrr_copy[bool_iniszero]
full_residuals[ymin:ymax,xmin:xmax][bool_outisnotzero] = full_residuals[ymin:ymax,xmin:xmax][bool_outisnotzero]
err_residuals[ymin:ymax,xmin:xmax][bool_innotzero_err] = (nrr_err_copy[bool_innotzero_err] + err_residuals[ymin:ymax,xmin:xmax][bool_innotzero_err])/2
err_residuals[ymin:ymax,xmin:xmax][bool_iniszero_err] = nrr_err_copy[bool_iniszero_err]
err_residuals[ymin:ymax,xmin:xmax][bool_outisnotzero_err] = err_residuals[ymin:ymax,xmin:xmax][bool_outisnotzero_err]
full_residuals_resel = ndimage.convolve(full_residuals,mask)
full_residuals[full_residuals==0] = None
resid_mask = ~np.isnan(full_residuals)
cent_rmask=centr.centroid_com(resid_mask)
p_flat=np.roll(full_residuals, (image_center_x-int(cent_rmask[1]),image_center_y-int(cent_rmask[0])), axis=(0,1))
p_flat_err=np.roll(err_residuals, (image_center_x-int(cent_rmask[1]),image_center_y-int(cent_rmask[0])), axis=(0,1))
nx = np.arange(0,full_residuals_resel.shape[1])
ny = np.arange(0,full_residuals_resel.shape[0])
nxx,nyy = np.meshgrid(ny,nx)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=AstropyUserWarning)
cent_n=centr.centroid_com(p_flat)
nrr = np.sqrt((nxx-cent_n[0])**2 + (nyy-cent_n[1])**2)
p_flat[nrr>n_pix//2]= 1
p_flat_err[nrr>n_pix//2]=0
full_residuals=np.pad(p_flat, ((n_pad,n_pad),(n_pad,n_pad)), mode='constant',constant_values=(1))
err_residuals=np.pad(p_flat_err, ((n_pad,n_pad),(n_pad,n_pad)), mode='constant',constant_values=(0))
return (full_residuals,err_residuals,cent_n)
[docs]
def create_onsky_flatfield(dataset, planet=None,band=None,up_radius=55,im_size=None,N=1,rad_mask=None, planet_rad=None, n_pix=44, n_pad=None, sky_annulus_rin=2, sky_annulus_rout=4,image_center_x=512,image_center_y=512):
"""Turn this dataset of image frames of uranus or neptune raster scannned that were taken for performing the flat calibration and create one master flat image.
The input image frames are L2b image frames that have been dark subtracted, divided by k-gain, divided by EM gain, desmeared.
Args:
dataset (corgidrp.data.Dataset): a dataset of image frames of uranus or neptune that are raster scanned (L2a-level)
planet (str): neptune or uranus
band (str): 1 or 4
up_radius (int): Number of pixels on either side of centroided planet images (=55 pixels for Neptune and uranus)
im_size (int): x-dimension of the input image (in pixels; default is size of input dataset; = 420 for the HST images)
N (int): Number of images to be combined for creating a matched filter (defaults to 1, may not work for N>1 right now)
rad_mask (float): radius in pixels used for creating a mask for band (band1=1.25, band4=1.75)
planet_rad (int): radius of the planet in pixels (planet_rad=50 for neptune, planet_rad=65)
n_pix (int): Number of pixels in radius covering the Roman CGI imaging FOV (defaults to 44 pix for Band1 HLC; 165 pixels for full shaped pupil FOV).
n_pad (int): Number of pixels padded with '1s' to generate the image size 1024X1024 pixels around imaging FOV (defaults to None; rest of the FOV to reach 1024)
sky_annulus_rin (float): Inner radius of annulus to use for sky subtraction. In units of planet_rad.
If both sky_annulus_rin and sky_annulus_rout = None, skips sky subtraciton.
sky_annulus_rout (float): Outer radius of annulus to use for sky subtraction. In units of planet_rad.
image_center_x (int): x coordinate of the center pixel of the final image
image_center_y (int): y coordinate of the center pixel of the final image
Returns:
data.FlatField (corgidrp.data.FlatField): a master flat for flat calibration using on sky images of planet in band specified
"""
if im_size is None:
# assume square images
im_size = dataset[0].data.shape[0]
if n_pad is None:
n_pad = 1024 - im_size
if n_pad < 0:
n_pad = 0
if planet is None:
planet=dataset[0].pri_hdr['TARGET']
if band is None:
band=dataset[0].ext_hdr['CFAMNAME']
if planet_rad is None:
if planet.lower() =='neptune':
planet_rad = 50
elif planet.lower() == 'uranus':
planet_rad = 65
if rad_mask is None:
if band[0] == "1":
rad_mask = 1.25
elif band[0] == "4":
rad_mask = 1.75
raster_frames=[]; cent=[]; act_cents=[]; frames=[];
for j in range(len(dataset)):
planet_image=dataset[j].data
planet_image_err=dataset[j].err[0]
prihdr=dataset[j].pri_hdr
exthdr=dataset[j].ext_hdr
image_size=np.shape(planet_image)
centroid = centr.centroid_com(planet_image)
centroid[np.isnan(centroid)]=0
act_cents.append((centroid[1],centroid[0]))
xc =int( centroid[0])
yc = int(centroid[1])
# sky subtraction if needed
if sky_annulus_rin is not None and sky_annulus_rout is not None:
ycoords, xcoords = np.indices(planet_image.shape)
dist_from_planet = np.sqrt((xcoords - xc)**2 + (ycoords - yc)**2)
sky_annulus = np.where((dist_from_planet >= sky_annulus_rin*planet_rad) & (dist_from_planet < sky_annulus_rout*planet_rad))
planet_image -= np.nanmedian(planet_image[sky_annulus])
# cropping the raster scanned images
raster_images=planet_image[yc-up_radius:yc+up_radius,xc-up_radius:xc+up_radius]
raster_images_err=planet_image_err[yc-up_radius:yc+up_radius,xc-up_radius:xc+up_radius]
#centroid of the cropped images
cent.append((yc-up_radius,yc+up_radius,xc-up_radius,xc+up_radius))
r_frames= mocks.Image(raster_images, pri_hdr = prihdr, ext_hdr = exthdr, err = raster_images_err)
raster_frames.append(r_frames)
raster_images_dataset=data.Dataset(raster_frames)
resi_images_dataset=flatfield_residuals(raster_images_dataset,N=N)
raster_com=combine_flatfield_rasters(resi_images_dataset,planet=planet,band=band,cent=cent, im_size=im_size, rad_mask=rad_mask,planet_rad=planet_rad,n_pix=n_pix, n_pad=n_pad,image_center_x=image_center_x,image_center_y=image_center_y)
onskyflat=raster_com[0]
onsky_flatfield = data.FlatField(onskyflat, pri_hdr=prihdr, ext_hdr=exthdr, input_dataset=dataset)
onsky_flatfield.err=raster_com[1]
return(onsky_flatfield)
[docs]
def create_onsky_pol_flatfield(dataset, planet=None,band=None,up_radius=55,im_size=None,N=1,rad_mask=None, planet_rad=None, n_pix=None, observing_mode='NFOV', n_pad=None, fwhm_guess=20,sky_annulus_rin=2, sky_annulus_rout=4,plate_scale=0.0218,image_center_x=512,image_center_y=512,separation_diameter_arcsec=7.5,alignment_angle_WP1=0,alignment_angle_WP2=45,dpamname=None):
"""Turn this dataset of image frames of uranus or neptune raster scannned that were taken for performing the flat calibration and create one master flat image.
The input image frames are L2b image frames that have been dark subtracted, divided by k-gain, divided by EM gain, desmeared.
Args:
dataset (corgidrp.data.Dataset): a dataset of image frames that are raster scanned (L2a-level)
planet (str): neptune or uranus
band (str): 1 or 4
up_radius (int): Number of pixels on either side of centroided planet images (=55 pixels for Neptune and uranus)
im_size (int): x-dimension of the input image (in pixels; default is size of input dataset; = 420 for the HST images)
N (int): Number of images to be combined for creating a matched filter (defaults to 1, may not work for N>1 right now)
rad_mask (float): radius in pixels used for creating a mask for band (band1=1.25, band4=1.75)
planet_rad (int): radius of the planet in pixels (planet_rad=50 for neptune, planet_rad=65)
n_pix (int): Number of pixels in radius covering the Roman CGI imaging FOV (defaults to 44 pix for Band1 HLC; 165 pixels for full shaped pupil FOV).
observing_mode (string): observing mode of the coronagraph
n_pad (int): Number of pixels padded with '1s' to generate the image size 1024X1024 pixels around imaging FOV (defaults to None; rest of the FOV to reach 1024)
fwhm_guess (int):FWHM guess for the planet image which is downsampled by a factor of 8
sky_annulus_rin (float): Inner radius of annulus to use for sky subtraction. In units of planet_rad.
If both sky_annulus_rin and sky_annulus_rout = None, skips sky subtraciton.
sky_annulus_rout (float): Outer radius of annulus to use for sky subtraction. In units of planet_rad.
plate_scale (float): platescale estimated from calibration (0.0218)
image_center_x (int): x coordinate of the center pixel of the final image with two orthogonal pol components
image_center_y (int): y coordinate of the center pixel of the final image with two orthogonal pol components
separation_diameter_arcsec (float): separation between the two orthogonal pol components in arcsecs
alignment_angle_WP1 (int): wollaston prism angle for Pol0 - 0
alignment_angle_WP2 (int): wollaston prism angle for Pol45- 45
dpamname (str): wollaston prism POL0 or POL45. Defaults to DPAMNAME in the header.
Returns:
data.FlatField (corgidrp.data.FlatField): a master flat corresponding to Pol0 or Pol 45 for flat calibration using on sky images of planet in band specified
"""
if im_size is None:
# assume square images
im_size = dataset[0].data.shape[0]
n=im_size
if n_pad is None:
n_pad = 1024 - im_size
if n_pad < 0:
n_pad = 0
if planet is None:
planet=dataset[0].pri_hdr['TARGET']
if band is None:
band=dataset[0].ext_hdr['CFAMNAME']
if dpamname is None:
dpamname=dataset[0].ext_hdr['DPAMNAME']
if planet_rad is None:
if planet.lower() =='neptune':
planet_rad = 50
elif planet.lower() == 'uranus':
planet_rad = 65
if rad_mask is None:
if band[0] == "1":
rad_mask = 1.25
elif band[0] == "4":
rad_mask = 1.75
if n_pix is None:
if observing_mode=='NFOV':
n_pix=44
elif observing_mode=='WFOV':
n_pix=174
# the planet images with two pol components are downsampled by a factor of 8 for finding the centroids using daostarfinder
raster_frames_pol1=[];raster_frames_pol2=[]; cent_pol1=[]; cent_pol2=[]; act_cents_1=[]; act_cents_2=[];
for j in range(len(dataset)):
planet_image=dataset[j].data
planet_image_err=dataset[j].err[0]
prihdr=dataset[j].pri_hdr
exthdr=dataset[j].ext_hdr
planet_image_downsampled = zoom(planet_image, 1/8)
threshold_value = np.max(planet_image)
daofind = DAOStarFinder(fwhm=fwhm_guess, threshold=threshold_value, min_separation=10.0)
sources = daofind(planet_image_downsampled)
if sources is not None:
x_centroids = sources['xcentroid']*8
y_centroids = sources['ycentroid']*8
pol1_x = int(x_centroids[0])
pol1_y = int(y_centroids[0])
pol2_x = int(x_centroids[1])
pol2_y = int(y_centroids[1])
x_centroids[np.isnan(x_centroids)]=0
y_centroids[np.isnan(y_centroids)]=0
act_cents_1.append((pol1_x,pol1_y))
act_cents_2.append((pol2_x,pol2_y))
# sky subtraction if needed
xc=np.mean([pol1_x,pol2_x])
yc=np.mean([pol1_y,pol2_y])
if sky_annulus_rin is not None and sky_annulus_rout is not None:
ycoords, xcoords = np.indices(planet_image.shape)
dist_from_planet = np.sqrt((xcoords - xc)**2 + (ycoords - yc)**2)
sky_annulus = np.where((dist_from_planet >= sky_annulus_rin*planet_rad) & (dist_from_planet < sky_annulus_rout*planet_rad))
planet_image -= np.nanmedian(planet_image[sky_annulus])
# cropping the raster scanned images
raster_images_pol1=planet_image[pol1_y-up_radius:pol1_y+up_radius,pol1_x-up_radius:pol1_x+up_radius]
raster_images_pol2=planet_image[pol2_y-up_radius:pol2_y+up_radius,pol2_x-up_radius:pol2_x+up_radius]
raster_images_pol1_err=planet_image_err[pol1_y-up_radius:pol1_y+up_radius,pol1_x-up_radius:pol1_x+up_radius]
raster_images_pol2_err=planet_image_err[pol2_y-up_radius:pol2_y+up_radius,pol2_x-up_radius:pol2_x+up_radius]
#centroid of the cropped images
cent_pol1.append((pol1_y-up_radius,pol1_y+up_radius,pol1_x-up_radius,pol1_x+up_radius))
cent_pol2.append((pol2_y-up_radius,pol2_y+up_radius,pol2_x-up_radius,pol2_x+up_radius))
r_frames_pol1 = mocks.Image(raster_images_pol1, pri_hdr = prihdr, ext_hdr = exthdr, err = raster_images_pol1_err)
r_frames_pol2 = mocks.Image(raster_images_pol2, pri_hdr = prihdr, ext_hdr = exthdr, err = raster_images_pol2_err)
raster_frames_pol1.append(r_frames_pol1)
raster_frames_pol2.append(r_frames_pol2)
raster_images_pol1_dataset=data.Dataset(raster_frames_pol1)
raster_images_pol2_dataset=data.Dataset(raster_frames_pol2)
resi_images_pol1_dataset=flatfield_residuals(raster_images_pol1_dataset,N=N)
resi_images_pol2_dataset=flatfield_residuals(raster_images_pol2_dataset,N=N)
combined_rasters_pol1=combine_flatfield_rasters(resi_images_pol1_dataset,planet=planet,band=band,cent=cent_pol1, im_size=im_size, rad_mask=rad_mask,planet_rad=planet_rad,n_pix=n_pix, n_pad=n_pad,image_center_x=512,image_center_y=512)
combined_rasters_pol2=combine_flatfield_rasters(resi_images_pol2_dataset,planet=planet,band=band,cent=cent_pol2, im_size=im_size, rad_mask=rad_mask,planet_rad=planet_rad,n_pix=n_pix, n_pad=n_pad,image_center_x=512,image_center_y=512)
if dpamname == 'POL0':
#place image according to specified angle
angle_rad = (alignment_angle_WP1 * np.pi) / 180
else:
angle_rad = (alignment_angle_WP2 * np.pi) / 180
displacement_x = int(round((separation_diameter_arcsec * np.cos(angle_rad)) / (2 * plate_scale)))
displacement_y = int(round((separation_diameter_arcsec * np.sin(angle_rad)) / (2 * plate_scale)))
center_left = (image_center_x - displacement_x, image_center_y + displacement_y)
center_right = (image_center_x + displacement_x, image_center_y - displacement_y)
image_radius = n_pix // 2
start_left = (center_left[0] - image_radius, center_left[1] - image_radius)
start_right = (center_right[0] - image_radius, center_right[1] - image_radius)
cent_n_pol1=combined_rasters_pol1[2]
cent_n_pol2=combined_rasters_pol2[2]
onsky_polflat=np.ones((n,n))
onsky_polflat_error=np.zeros((n,n))
onsky_polflat[start_left[1]:start_left[1]+n_pix, start_left[0]:start_left[0]+n_pix]=combined_rasters_pol1[0][int(cent_n_pol1[1]) - n_pix//2:int(cent_n_pol1[1]) + n_pix//2,int(cent_n_pol1[0]) - n_pix//2:int(cent_n_pol1[0]) + n_pix//2]
onsky_polflat[start_right[1]:start_right[1]+n_pix, start_right[0]:start_right[0]+n_pix]=combined_rasters_pol2[0][int(cent_n_pol2[1])- n_pix//2:int(cent_n_pol2[1]) + n_pix//2,int(cent_n_pol2[0]) - n_pix//2:int(cent_n_pol2[0]) + n_pix//2]
onsky_polflat_error[start_left[1]:start_left[1]+n_pix, start_left[0]:start_left[0]+n_pix]=combined_rasters_pol1[1][int(cent_n_pol1[1]) - n_pix//2:int(cent_n_pol1[1]) + n_pix//2,int(cent_n_pol1[0]) - n_pix//2:int(cent_n_pol1[0]) + n_pix//2]
onsky_polflat_error[start_right[1]:start_right[1]+n_pix, start_right[0]:start_right[0]+n_pix]=combined_rasters_pol2[1][int(cent_n_pol2[1]) - n_pix//2:int(cent_n_pol2[1]) + n_pix//2,int(cent_n_pol2[0]) - n_pix//2:int(cent_n_pol2[0]) + n_pix//2]
onsky_pol_flatfield = data.FlatField(onsky_polflat, pri_hdr=prihdr, ext_hdr=exthdr, input_dataset=dataset)
onsky_pol_flatfield.err=onsky_polflat_error
return(onsky_pol_flatfield)