def _fits2sl(fits_sect): pyth_slice = {} for k, sects in fits_sect.items(): pyth_slice[k] = [] for sect in sects: pyth_slice[k].append(fitsxy2py(sect)) return pyth_slice
def vertical_correct(ccd, fitting_sections=None, method='median', sigclip_kw=dict(sigma=2, maxiters=5), dtype='float32', return_pattern=False, update_header=True, verbose=False): ''' Correct vertical strip patterns. Paramters --------- ccd : CCDData, HDU object, HDUList, or ndarray. The CCD to subtract the vertical pattern. fitting_sections : list of two str, optional. The sections to be used for the vertical pattern estimation. This must be identical to the usual FITS section (i.e., that used in SAO ds9 or IRAF, 1-indexing and last-index-inclusive), not in python. **Give it in the order of ``[<upper>, <lower>]`` in FITS y-coordinate.** method : str, optional. One of ``['med', 'avg', 'median', 'average', 'mean']``. sigma, maxiters : float and int, optional A sigma-clipping will be done to remove hot pixels and cosmic rays for estimating the vertical pattern. To turn sigma-clipping off, set ``maxiters=0``. sigclip_kw : dict, optional The keyword arguments for the sigma clipping. dtype : str, dtype, optional The data type to be returned. return_pattern : bool, optional. If `True`, the subtracted pattern will also be returned. Default is `False`. update_header : bool, optional. Whether to update the header if there is any. Return ------ ''' _t = Time.now() data, hdr = _parse_data_header(ccd) if fitting_sections is None: fitting_sections = VERTICALSECTS elif len(fitting_sections) != 2: raise ValueError("fitting_sections must have two elements.") if method in ['median', 'med']: methodstr = 'taking median' fun = np.median idx = 1 elif method in ['average', 'avg', 'mean']: methodstr = 'taking average' fun = np.mean idx = 0 else: raise ValueError( "method not understood; it must be one of [med, avg, median, average, mean]." ) parts = [] # The two horizontal box areas from raw data strips = [] # The estimated patterns (1-D) for sect in fitting_sections: parts.append(data[fitsxy2py(sect)]) try: if sigclip_kw["maxiters"] == 0: clipstr = "no clipping" for part in parts: strips.append(fun(part, axis=0)) except KeyError: pass # The user wants to use default value of maxiters in astropy. clipstr = f"sigma-clipping in astropy (v {astropy.__version__})" if sigclip_kw: clipstr += f", given {sigclip_kw}." else: clipstr += "." for part in parts: clip = sigma_clipped_stats(part, axis=0, **sigclip_kw) strips.append(clip[idx]) ny, nx = data.shape vpattern = np.repeat(strips, ny / 2, axis=0) vsub = data - vpattern.astype(dtype) if update_header and hdr is not None: # add as history add_to_header( hdr, 'h', verbose=verbose, t_ref=_t, s=f"Vertical pattern subtracted using {fitting_sections} by {methodstr} with {clipstr}" ) try: nccd = CCDData(data=vsub, header=hdr) except ValueError: nccd = CCDData(data=vsub, header=hdr, unit='adu') nccd.data = nccd.data.astype(dtype) if return_pattern: return nccd, vpattern return nccd
def fit_fourier(data, freqs, mask=None, filt=None, apply_crrej_mask=False, apply_sigclip_mask=True, fitting_y_sections=None, subtract_x_sections=["[520:900]"], npool=5): """Fit Fourier series along column.""" if mask is None: _mask = np.zeros(data.shape).astype(bool) else: _mask = mask.copy() if apply_crrej_mask: if filt is None: raise ValueError("filt must be given if apply_crrej_mask is True.") mask_cr = cr_reject_nic(data, filt=filt, verbose=False).mask _mask = _mask | mask_cr if fitting_y_sections is None: try: fitting_y_sections = FOURIERSECTS[filt] except KeyError: fitting_y_sections = ["[10:245]", "[850:1010]"] elif isinstance(fitting_y_sections, str): fitting_y_sections = [fitting_y_sections] if isinstance(subtract_x_sections, str): subtract_x_sections = [subtract_x_sections] noslice = slice(None, None, None) subsls = [(noslice, fitsxy2py(s)[0]) for s in subtract_x_sections] fitsls = [(fitsxy2py(s)[0], noslice) for s in fitting_y_sections] fitmask = np.ones(data.shape).astype(bool) # initialize with masking True submask = np.ones(data.shape).astype(bool) # initialize with masking True for fitsl in fitsls: fitmask[fitsl] = False for subsl in subsls: submask[subsl] = False _mask = _mask | fitmask | submask if apply_sigclip_mask: _data = np.ma.array(data, mask=mask) mask_sc = sigma_clip(_data, axis=0, sigma=3, maxiters=5).mask _mask = _mask | mask_sc ny, nx = data.shape yy, xx = np.mgrid[:ny, :nx] pool = Pool(npool) args = [ list(yy.T), # x to eval (= y_index of data, 0~1024) list(data.T), # y for fit (pixel value) list(_mask.T), # mask for fit [freqs] * nx, np.arange(nx) # x_index of data ] res = np.array(pool.starmap(_fitter, np.array(args).T)) pool.close() res = res[np.argsort(res[:, 0])] # sort by index popts = np.array(res[:, 1]) pattern = np.stack(res[:, 2], axis=1) return pattern, popts, _mask
pyth_slice[k] = [] for sect in sects: pyth_slice[k].append(fitsxy2py(sect)) return pyth_slice def OBJSLICES(right_half=False): return _fits2sl(OBJSECTS(right_half)) FOURIERSLICES = _fits2sl(FOURIERSECTS) NICSLICES = {} VERTICALSLICES = [] for k, sect in NICSECTS.items(): NICSLICES[k] = fitsxy2py(sect) for sect in VERTICALSECTS: VERTICALSLICES.append(fitsxy2py(sect)) FOURIPEAKSLICE = fitsxy2py(FOURIPEAKSECT) NHAO_LOCATION = dict(lon=134.3356, lat=35.0253, elevation=0.449) def infer_filter(ccd, filt=None, verbose=True): if filt is None: try: filt = ccd.header["FILTER"] if verbose:
for k, sects in fits_sect.items(): pyth_slice[k] = [] for sect in sects: pyth_slice[k].append(fitsxy2py(sect)) return pyth_slice def OBJSLICES(right_half=False): return _fits2sl(OBJSECTS(right_half)) NICSLICES = {} VERTICALSLICES = [] for k, sect in NICSECTS.items(): NICSLICES[k] = fitsxy2py(sect) for sect in VERTICALSECTS: VERTICALSLICES.append(fitsxy2py(sect)) NHAO_LOCATION = dict(lon=134.3356, lat=35.0253, elevation=0.449) def infer_filter(ccd, filt=None, verbose=True): if filt is None: try: filt = ccd.header["FILTER"] if verbose: print(f"Assuming filter is '{filt}' from header.") except (KeyError, AttributeError): raise TypeError("Filter cannot be inferred from the given ccd.")