Source code for pyreduce.clipnflip

# -*- coding: utf-8 -*-
"""
Module that
    - Clips remove pre- and overscan regions
    - Flips orients the image so that orders are roughly horizontal
"""

import logging

import numpy as np

logger = logging.getLogger(__name__)


[docs]def clipnflip( image, header, xrange=None, yrange=None, orientation=None, transpose=None ): """ Process an image and associated FITS header already in memory as follows: 1. Trim image to desired subregion: newimage = image(xlo:xhi,ylo:yhi) 2. Transform to standard orientation (red at top, orders run left to right) Parameters ----------- image : array[nrow, ncol] raw image to be processed. header : fits.header, dict FITS header of the image xrange : tuple(int, int), optional column - range to keep in the image (default: data from header/instrument) yrange : tuple(int, int), optional row - range to keep in the image (default: data from header/instrument) orientation : int, optional number of counterclockwise 90 degrees rotation to apply to the image (default: data from header/instrument) transpose : bool, optional if True the image will be transposed before rotation (default: data from header/instrument) Returns ------- image : array[yrange, xrange] clipped and flipped image Raises ------- NotImplementedError nonlinear images are not supported yet """ # This part depends on how many amplifiers were used for the readout n_amp = header.get("e_ampl", 1) image = np.asarray(image) if n_amp > 1: # pragma: no cover raise NotImplementedError xlo = np.array(header["e_xlo*"].values()) xhi = np.array(header["e_xhi*"].values()) ylo = np.array(header["e_ylo*"].values()) yhi = np.array(header["e_yhi*"].values()) # Make sure trim region is a subset of actual image. sz = image.shape if ( np.any(xlo < 0) | np.any(xlo >= sz[1]) | np.any(ylo < 0) | np.any(ylo >= sz[0]) | np.any(xhi < 0) | np.any(xhi > sz[1]) | np.any(yhi < 0) | np.any(yhi > sz[0]) ): raise ValueError("Error specifying trim region") linear = header.get("e_linear", False) if not linear: # TODO raise NotImplementedError("only linear for now") # pref = header["e_prefmo"] # image = call_function("nonlinear_" + pref, image, header) # i = np.where(header["e_linear"] >= 0) # if len(i) > 0: # header = header[0 : i - 1 + 1] + header[i + 1 :] # header["e_linear"] = ("t", "image corrected of non-linearity") # ii = np.where(header["e_gain*"] >= 0) # if len(ii) > 0: # for i in np.arange(0, len(ii) - 1 + 1, 1): # k = ii[i] # header = [header[0 : k - 1 + 1], header[k + 1 :]] # header["e_gain"] = (1, "image was converted to e-") # Trim image to leave only the subimage containing valid image data. # For two amplifiers we assume a single vertical or horizontal gap. # With four amplifiers we can have a cross. if n_amp == 2: # TODO this needs testing if xlo[0] == xlo[1]: xsize = xhi[0] - xlo[0] ysize = yhi[0] - ylo[0] + yhi[1] - ylo[1] timage = np.empty((xsize, ysize), dtype=image.dtype) ysize = yhi[0] - ylo[0] timage[:ysize, :xsize] = image[xlo[0]] timage[ysize:, :xsize] = image[xlo[1]] elif ylo[0] == ylo[1]: xsize = xhi[0] - xlo[0] + xhi[1] - xlo[1] ysize = yhi[0] - ylo[0] timage = np.empty((xsize, ysize), dtype=image.dtype) xsize = xhi[0] - xlo[0] timage[:ysize, :xsize] = image[xlo[0]] timage[:ysize, xsize:] = image[xlo[1]] else: raise Exception( "The two ccd sections are aligned neither in x nor in y" ) elif n_amp == 4: raise NotImplementedError("4-amplifier section is not implemented yet") else: xlo, xhi = xrange if xrange is not None else (header["e_xlo"], header["e_xhi"]) ylo, yhi = yrange if yrange is not None else (header["e_ylo"], header["e_yhi"]) # Make sure trim region is a subset of actual image. sz = image.shape[-2:] if not (0 <= xlo < xhi <= sz[1] and 0 <= ylo < yhi <= sz[0]): raise IndexError( "Image Clipping Indices are not within the image (or in inverse order)" ) # Trim image to leave only the subimage containing valid image data. timage = image[..., ylo:yhi, xlo:xhi] # trimmed image # Flip image (if necessary) to achieve standard image orientation. orientation = orientation if orientation is not None else header.get("e_orient", 0) # As per the old IDL definition, transpose is true for orientations larger than 4 transpose = ( transpose if transpose is not None else header.get("e_transpose", (orientation % 8) >= 4) ) if transpose: timage = np.transpose(timage, axes=(-1, -2)) timage = np.rot90(timage, -1 * orientation, axes=(-2, -1)) # TODO just sum up groups and stuff? while timage.ndim > 2: timage = np.sum(timage, axis=0) return timage