def _combine_calib_images( images: List[Image], bias: Optional[Image] = None, normalize: bool = False, method: str = "average" ) -> Image: """Combine a list of given images. Args: images: List of images to combine. bias: If given, subtract from images before combining them. normalize: If True, images are normalized to median of 1 before and after combining them. method: Method for combining images. """ import ccdproc # get CCDData objects data = [image.to_ccddata() for image in images] # subtract bias? if bias is not None: bias_data = bias.to_ccddata() data = [ccdproc.subtract_bias(d, bias_data) for d in data] # normalize? if normalize: data = [d.divide(np.median(d.data), handle_meta="first_found") for d in data] # combine image combined = ccdproc.combine( data, method=method, sigma_clip=True, sigma_clip_low_thresh=5, sigma_clip_high_thresh=5, mem_limit=350e6, unit="adu", combine_uncertainty_function=np.ma.std, ) # normalize? if normalize: combined = combined.divide(np.median(combined.data), handle_meta="first_found") # to Image and copy header image = Image.from_ccddata(combined) # add history for i, src in enumerate(images, 1): basename = src.header["FNAME"].replace(".fits.fz", "").replace(".fits", "") image.header["L1AVG%03d" % i] = (basename, "Image used for average") image.header["RLEVEL"] = (1, "Reduction level") # finished return image
async def __call__(self, image: Image) -> Image: """Calibrate an image. Args: image: Image to calibrate. Returns: Calibrated image. """ import ccdproc # get calibration masters try: bias = await self._find_master(image, ImageType.BIAS) dark = await self._find_master(image, ImageType.DARK) flat = await self._find_master(image, ImageType.SKYFLAT) except ValueError as e: log.error("Could not find calibration frames: " + str(e)) return image # calibrate image c = await asyncio.get_running_loop().run_in_executor( None, partial( ccdproc.ccd_process, image.to_ccddata(), oscan=image.header["BIASSEC"] if "BIASSEC" in image.header else None, trim=image.header["TRIMSEC"] if "TRIMSEC" in image.header else None, error=True, master_bias=bias.to_ccddata() if bias is not None else None, dark_frame=dark.to_ccddata() if dark is not None else None, master_flat=flat.to_ccddata() if flat is not None else None, bad_pixel_mask=None, gain=image.header["DET-GAIN"] * u.electron / u.adu, readnoise=image.header["DET-RON"] * u.electron, dark_exposure=dark.header["EXPTIME"] * u.second if dark is not None else None, data_exposure=image.header["EXPTIME"] * u.second, dark_scale=True, gain_corrected=False, ), ) # to image calibrated = Image.from_ccddata(c) calibrated.header["BUNIT"] = ("electron", "Unit of pixel values") # set raw filename if "ORIGNAME" in image.header: calibrated.header["L1RAW"] = image.header["ORIGNAME"].replace( ".fits", "") # add calibration frames if bias is not None: calibrated.header["L1BIAS"] = ( bias.header["FNAME"].replace(".fits.fz", "").replace(".fits", ""), "Name of BIAS frame", ) if dark is not None: calibrated.header["L1DARK"] = ( dark.header["FNAME"].replace(".fits.fz", "").replace(".fits", ""), "Name of DARK frame", ) if flat is not None: calibrated.header["L1FLAT"] = ( flat.header["FNAME"].replace(".fits.fz", "").replace(".fits", ""), "Name of FLAT frame", ) # set RLEVEL calibrated.header["RLEVEL"] = (1, "Reduction level") # finished return calibrated