def from_scarlet(self, scarlet_model_dir, pixel_scale=HSC_pixel_scale): from galsim import Image, InterpolatedImage from galsim.interpolant import Lanczos with open(scarlet_model_dir, 'rb') as f: blend, info, mask = dill.load(f) f.close() assert ''.join(blend.observations[0].channels) == self.channels # Crop the mask new_weights = blend.observations[0].weights x1, y1 = blend.sources[0].bbox.origin[1:] x2 = x1 + blend.sources[0].bbox.shape[1:][0] y2 = y1 + blend.sources[0].bbox.shape[1:][1] mask = mask.astype(bool)[x1:x2, y1:y2] mask += np.sum((new_weights[:, x1:x2, y1:y2] == 0), axis=0).astype(bool) mockgal_img = blend.sources[0].get_model() * np.repeat( ~mask[np.newaxis, :, :], len(self.channels), axis=0) gal_image = np.empty_like(self.bkg.images) for i in range(len(mockgal_img)): mockgal = InterpolatedImage(Image(mockgal_img[i], dtype=float), scale=pixel_scale, x_interpolant=Lanczos(3)) gal_image[i] = mockgal.drawImage(scale=pixel_scale, nx=self.bkg.images.shape[2], ny=self.bkg.images.shape[1]).array # Generate variance map ra, dec = self.bkg.wcs.wcs_pix2world(self.bkg.images.shape[2] / 2, self.bkg.images.shape[1] / 2, 0) info = { 'ra': ra, 'dec': dec, 'scarlet_model': scarlet_model_dir, 'model_type': 'scarlet_lsbg_wvlt_0.5' } mock_model = Data(images=gal_image, variances=None, masks=None, channels=self.channels, wcs=None, weights=None, psfs=self.bkg.psfs, info=info) # Finished!!! self.model = mock_model # model only has `images`, `channels`, `psfs`, and `info`! self.set_mock() # mock has other things, including modified variances.
def resize_mask(self, f, method='iraf', order=5, cval=0.0): '''Zoom/Resize the mask of Celestial object. f > 1 means the mask will be resampled (finer)! f < 1 means the mask will be degraded. Parameters: f (float): the positive factor of zoom. If 0 < f < 1, the mask will be resized to smaller one. method (str): interpolation method. Use 'lanczos' or 'spline' or 'iraf'. order (int): the order Lanczos interpolation (>0). cval (scalar): value to fill the edges. Default is NaN. Returns: shift_image: ndarray. ''' if method == 'lanczos': try: # try to import galsim from galsim import degrees, Angle from galsim.interpolant import Lanczos from galsim import Image, InterpolatedImage from galsim.fitswcs import AstropyWCS except: raise ImportError( '# Import `galsim` failed! Please check if `galsim` is installed!' ) assert (order > 0) and isinstance( order, int), 'order of ' + method + ' must be positive interger.' galimg = InterpolatedImage(Image(self.mask, dtype=float), scale=self.pixel_scale, x_interpolant=Lanczos(order)) ny, nx = self.mask.shape result = galimg.drawImage( scale=self.pixel_scale / f, nx=round(nx * f), ny=round(ny * f)) #, wcs=AstropyWCS(self.wcs)) self.wcs = self._resize_wcs(self.image, self.wcs, f) self._image = result.array self.shape = self.mask.shape self._wcs_header_merge() self.pixel_scale /= f return result.array elif method == 'iraf': self.save_to_fits('./_temp.fits', 'mask') if f > 1: magnify('./_temp.fits', './_resize_temp.fits', f, f) else: blkavg('./_temp.fits', './_resize_temp.fits', 1 / f, 1 / f, option='sum') hdu = fits.open('./_resize_temp.fits') self.mask = hdu[0].data self.shape = hdu[0].data.shape self.header = hdu[0].header self.wcs = wcs.WCS(self.header) self.pixel_scale /= f hdu.close() imdelete('./*temp.fits') else: raise ValueError( "# Not supported interpolation method. Use 'lanczos' or 'iraf'." )
def shift_mask(self, dx, dy, method='iraf', order=5, cval=0.0): '''Shift the mask of Celestial object. Parameters: dx, dy (float): shift distance (in pixel) along x (horizontal) and y (vertical). Note that elements in one row has the same y but different x. Example: dx = 2 is to shift the image "RIGHT", dy = 3 is to shift the image "UP". method (str): interpolation method. Use 'lanczos' or 'spline' or 'iraf' order (int): the order of spline interpolation (within 0-5) or Lanczos interpolation (>0). cval (scalar): value to fill the edges. Default is NaN. Returns: shift_mask: ndarray. ''' ny, nx = self.mask.shape if abs(dx) > nx or abs(ny) > ny: raise ValueError('# Shift distance is beyond the image size.') if method == 'lanczos': try: # try to import galsim from galsim import degrees, Angle from galsim.interpolant import Lanczos from galsim import Image, InterpolatedImage from galsim.fitswcs import AstropyWCS except: raise ImportError( '# Import `galsim` failed! Please check if `galsim` is installed!' ) # Begin shift assert (order > 0) and isinstance( order, int), 'order of ' + method + ' must be positive interger.' galimg = InterpolatedImage(Image(self.mask, dtype=float), scale=self.pixel_scale, x_interpolant=Lanczos(order)) galimg = galimg.shift(dx=dx * self.pixel_scale, dy=dy * self.pixel_scale) result = galimg.drawImage(scale=self.pixel_scale, nx=nx, ny=ny) #, wcs=AstropyWCS(self.wcs)) self._mask = result.array # Change the WCS of image hdr = copy.deepcopy(self.header) hdr['CRPIX1'] += dx hdr['CRPIX2'] += dy self.header = hdr self.wcs = wcs.WCS(hdr) self._wcs_header_merge() return result.array elif method == 'iraf': self.save_to_fits('./_temp.fits', 'mask') imshift('./_temp.fits', './_shift_temp.fits', dx, dy, interp_type='poly3', boundary_type='constant') hdu = fits.open('./_shift_temp.fits') self.mask = hdu[0].data self.shape = hdu[0].data.shape self.header = hdu[0].header self.wcs = wcs.WCS(self.header) hdu.close() imdelete('./*temp.fits') else: raise ValueError( "# Not supported interpolation method. Use 'lanczos' or 'iraf'." )
def outlier_rejection(star_list, pos_list, mask_list, ccd_list, SNR_list=None, RA_list=None, DEC_list=None, shape_std_max=5., print_fun=None): r"""Outlier star rejection method. Notes ----- It is based on the measurements from Galsim's HSM adaptive moments. The method calculates the 2nd order moments from the stars in the exposure. If there are stars that have an aberrant value in one of the stats, e1, e2 or R2 we discard the star. An aberrant value is defined as a value that is more than ``shape_std_max`` sigmas away from the mean. It inputs all the lists that will be used as a method and returns the same lists without the stars that where considered as outliers. ``print_fun`` is a function that prints details about the stars being removed. """ # Define the printing function. Could be printing on a log file. if print_fun is None: def print_fun(msg): print(msg) # Reject outliers all_stars = np.concatenate(star_list, axis=2) all_stars = utils.reg_format(np.copy(all_stars)) all_masks = np.concatenate(mask_list, axis=2) all_masks = utils.reg_format(np.copy(all_masks)) # hsm thinks 0 means good badpix_masks = np.rint(np.abs(all_masks - 1)) star_moms = [ hsm.FindAdaptiveMom(Image(star), badpix=Image(bp), strict=False) for star, bp in zip(all_stars, badpix_masks) ] star_shapes = np.array([[ moms.observed_shape.g1, moms.observed_shape.g2, 2. * moms.moments_sigma**2, int(bool(moms.error_message)) ] for moms in star_moms]) # Outlier rejection based on e1, e2 and R2 R2_thresh = shape_std_max * np.std(star_shapes[:, 2]) + \ np.mean(star_shapes[:, 2]) R2_bad_stars = (abs(star_shapes[:, 2]) > R2_thresh) e2_thresh = shape_std_max * np.std(star_shapes[:, 1]) + \ np.mean(star_shapes[:, 1]) e2_bad_stars = (abs(star_shapes[:, 1]) > e2_thresh) e1_thresh = shape_std_max * np.std(star_shapes[:, 0]) + \ np.mean(star_shapes[:, 0]) e1_bad_stars = (abs(star_shapes[:, 0]) > e1_thresh) bad_stars = np.logical_or(e1_bad_stars, e2_bad_stars) bad_stars = np.logical_or(bad_stars, R2_bad_stars) bad_stars_idx = np.nonzero(bad_stars)[0] print_fun(bad_stars_idx) print_fun(bad_stars_idx.shape) # Create masks erase_masks = [ np.zeros(star_list[i].shape[2], dtype=bool) for i in range(len(star_list)) ] if bad_stars_idx.size > 0: # We have to erase the outliers # Create the reference ids (to match the global # array to the list of arrays) idx_ref = np.zeros((len(all_stars), 3), dtype=int) glob_id, star_id, ccd_id = 0, 0, 0 for stars in star_list: star_id = 0 for _star in utils.reg_format(stars): idx_ref[glob_id, 0] = glob_id idx_ref[glob_id, 1] = star_id idx_ref[glob_id, 2] = ccd_id glob_id += 1 star_id += 1 ccd_id += 1 # select outlier stars for bad_id in bad_stars_idx: print_fun('Outlier: Glob_id=%d , star_id=%d , ccd_id=%d' % (idx_ref[bad_id, 0], idx_ref[bad_id, 1], idx_ref[bad_id, 2])) erase_masks[idx_ref[bad_id, 2]][idx_ref[bad_id, 1]] = True for it_star in range(len(star_list)): mask = ~erase_masks[it_star] # erase elements and overwrite star_list[it_star] = star_list[it_star][:, :, mask] mask_list[it_star] = mask_list[it_star][:, :, mask] pos_list[it_star] = pos_list[it_star][mask, :] if SNR_list is not None: SNR_list[it_star] = SNR_list[it_star][mask] if RA_list is not None: RA_list[it_star] = RA_list[it_star][mask] DEC_list[it_star] = DEC_list[it_star][mask] return star_list, pos_list, mask_list, ccd_list, SNR_list, RA_list,\ DEC_list, erase_masks
def resize_mask(self, f, method='lanczos', order=5, cval=0.0): '''Zoom/Resize the mask of Stack object. Cautious: don't use ['bicubic', 'nearest', 'cubic', 'bilinear'] methods! They don't conserve the total flux! Parameters: f (float): the positive factor of zoom. If 0 < f < 1, the image will be resized to smaller one. method (str): interpolation method. Use 'lanczos' or 'spline'. order (int): the order of spline interpolation (within 0-5) or Lanczos interpolation (>0). cval (scalar): value to fill the edges. Default is NaN. Returns: shift_image: ndarray. ''' if method == 'lanczos': try: # try to import galsim from galsim import degrees, Angle from galsim.interpolant import Lanczos from galsim import Image, InterpolatedImage from galsim.fitswcs import AstropyWCS except: raise ImportError( '# Import `galsim` failed! Please check if `galsim` is installed!' ) assert (order > 0) and isinstance( order, int), 'order of ' + method + ' must be positive interger.' galimg = InterpolatedImage(Image(self.mask, dtype=float), scale=0.168, x_interpolant=Lanczos(order)) #galimg = galimg.magnify(f) ny, nx = self.mask.shape result = galimg.drawImage( scale=0.168 / f, nx=round(nx * f), ny=round(ny * f)) #, wcs=AstropyWCS(self.wcs)) self.wcs = self._resize_wcs(self.mask, self.wcs, f) self._mask = result.array self.shape = self.mask.shape return result.array elif method == 'spline': from scipy.ndimage import zoom assert 0 < order <= 5 and isinstance( order, int), 'order of ' + method + ' must be within 0-5.' result = zoom(self.mask, float(f), order=order, mode='constant', cval=cval) self._mask = result self.wcs = self._resize_wcs(self.mask, self.wcs, f) self.shape = self.mask.shape return result elif method in ['bicubic', 'nearest', 'cubic', 'bilinear']: raise Warning( "Cautious! Don't use ['bicubic', 'nearest', 'cubic', 'bilinear'] methods! They don't conserve the total flux!" ) try: from scipy.misc import imresize except: raise ImportError( '# Import `scipy.misc.imresize` failed! This function may no longer be included in scipy!' ) result = imresize(self.mask, float(f), interp=method) self._mask = result.astype(float) self.wcs = self._resize_wcs(self.mask, self.wcs, f) self.shape = self.mask.shape return result.astype(float) else: raise ValueError( "# Not supported interpolation method. Use 'lanczos' or 'spline'." )
def shift_mask(self, dx, dy, method='lanczos', order=5, cval=0.0): '''Shift the mask of Stack object. Parameters: dx, dy (float): shift distance (in pixel) along x (horizontal) and y (vertical). Note that elements in one row has the same y but different x. Example: dx = 2 is to shift the image "RIGHT", dy = 3 is to shift the image "UP". method (str): interpolation method. Use 'lanczos' or 'spline'. order (int): the order of spline interpolation (within 0-5) or Lanczos interpolation (>0). cval (scalar): value to fill the edges. Default is NaN. Returns: shift_mask: ndarray. ''' if not hasattr(self, 'mask'): raise AttributeError("This `Stack` object doesn't have `mask`!") ny, nx = self.image.shape if abs(dx) > nx or abs(ny) > ny: raise ValueError('# Shift distance is beyond the image size.') if method == 'lanczos': try: # try to import galsim from galsim import degrees, Angle from galsim.interpolant import Lanczos from galsim import Image, InterpolatedImage from galsim.fitswcs import AstropyWCS except: raise ImportError( '# Import `galsim` failed! Please check if `galsim` is installed!' ) # Begin shift assert (order > 0) and isinstance( order, int), 'order of ' + method + ' must be positive interger.' galimg = InterpolatedImage(Image(self.mask, dtype=float), scale=0.168, x_interpolant=Lanczos(order)) galimg = galimg.shift(dx=dx * 0.168, dy=dy * 0.168) result = galimg.drawImage(scale=0.168, nx=nx, ny=ny) #, wcs=AstropyWCS(self.wcs)) self._mask = (result.array > 0.5).astype(float) # Change the WCS of image hdr = copy.deepcopy(self.header) hdr['CRPIX1'] += dx hdr['CRPIX2'] += dy self.header = hdr self.wcs = wcs.WCS(hdr) return result.array elif method == 'spline': from scipy.ndimage.interpolation import shift assert 0 < order <= 5 and isinstance( order, int), 'order of ' + method + ' must be within 0-5.' result = shift(self.mask, [dy, dx], order=order, mode='constant', cval=cval) self._mask = (result > 0.5).astype(float) # Change the WCS of image hdr = copy.deepcopy(self.header) hdr['CRPIX1'] += dx hdr['CRPIX2'] += dy self.header = hdr self.wcs = wcs.WCS(hdr) return result else: raise ValueError( "# Not supported interpolation method. Use 'lanczos' or 'spline'." )
def rotate_mask(self, angle, method='lanczos', order=5, reshape=False, cval=0.0): '''Rotate the mask of Stack object. Parameters: angle (float): rotation angle in degress, counterclockwise. method (str): interpolation method. Use 'lanczos', 'spline', 'cubic', 'bicubic', 'nearest' or 'bilinear'. order (int): the order of spline interpolation (within 0-5) or Lanczos interpolation (>0). reshape (bool): if True, the output shape is adapted so that the rorated image is contained completely in the output array. cval (scalar): value to fill the edges. Default is NaN. Returns: rotate_image: ndarray. ''' angle = angle % 360 if not hasattr(self, 'mask'): raise AttributeError("This `Stack` object doesn't have `mask`!") else: if method == 'lanczos': try: from galsim import degrees, Angle from galsim.interpolant import Lanczos from galsim import Image, InterpolatedImage from galsim.fitswcs import AstropyWCS except: raise ImportError( '# Import `galsim` failed! Please check if `galsim` is installed!' ) # Begin rotation assert (order > 0) and isinstance( order, int), 'order of ' + method + ' must be positive interger.' galimg = InterpolatedImage(Image(self.mask, dtype=float), scale=0.168, x_interpolant=Lanczos(order)) galimg = galimg.rotate(Angle(angle, unit=degrees)) ny, nx = self.image.shape result = galimg.drawImage(scale=0.168, nx=nx, ny=ny) #, wcs=AstropyWCS(self.wcs)) self._mask = (result.array > 0.5).astype(float) self.wcs = self._rotate_wcs(self.mask, self.wcs, angle) return result.array elif method == 'spline': from scipy.ndimage.interpolation import rotate as rt assert 0 < order <= 5 and isinstance( order, int), 'order of ' + method + ' must be within 0-5.' result = rt(self.mask, -angle, order=order, mode='constant', cval=cval, reshape=reshape) self._mask = (result > 0.5).astype(float) self.wcs = self._rotate_wcs(self.mask, self.wcs, angle) return result elif method in ['bicubic', 'nearest', 'cubic', 'bilinear']: raise Warning( "Cautious! Don't use ['bicubic', 'nearest', 'cubic', 'bilinear'] methods! They don't conserve the total flux!" ) try: from scipy.misc import imrotate except: raise ImportError( '# Import `scipy.misc.imrotate` failed! This function may no longer be included in scipy!' ) result = imrotate(self.mask, -angle, interp=method) self._mask = (result > 0.5).astype(float) self.wcs = self._rotate_wcs(self.mask, self.wcs, angle) return result else: raise ValueError( "# Not supported interpolation method. Use 'lanczos', 'spline', 'cubic', \ 'bicubic', 'nearest', 'bilinear'.")
def resize_mask(self, f, method='cubic', order=5, cval=0.0): ''' Zoom/Resize the mask of Celestial object. f > 1 means the mask will be resampled (finer)! f < 1 means the mask will be degraded. Parameters: f (float): the positive factor of zoom. If 0 < f < 1, the mask will be resized to smaller one. method (str): interpolation method. Use 'lanczos', 'cubic', 'quintic' or 'iraf'. First three methods require ``GalSim`` installed. Other methods are now consistent with "iraf" results. order (int): the order Lanczos interpolation (>0). cval (float): value to fill the edges. Default is 0. Returns: resize_mask (ndarray): resized image. The "mask" attribute of ``Celestial`` class will also be changed accordingly. ''' if not hasattr(self, 'mask'): raise ValueError("This object doesn't have mask yet!") if method == 'lanczos' or method == 'cubic' or method == 'quintic': try: # try to import galsim from galsim import degrees, Angle from galsim.interpolant import Lanczos from galsim import Image, InterpolatedImage from galsim.fitswcs import AstropyWCS except: raise ImportError( '# Import `galsim` failed! Please check if `galsim` is installed!' ) assert (order > 0) and isinstance( order, int), 'order of ' + method + ' must be positive interger.' if method == 'lanczos': galimg = InterpolatedImage(Image(self.mask, dtype=float), scale=self.pixel_scale, x_interpolant=Lanczos(order)) else: galimg = InterpolatedImage(Image(self.mask, dtype=float), scale=self.pixel_scale, x_interpolant=method) ny, nx = self.mask.shape if f > 1: result = galimg.drawImage(scale=self.pixel_scale / f, nx=int((nx - 1) * f + 1), ny=int((ny - 1) * f + 1)) self.header = self._resize_header_wcs(self.mask, f) self.header['CRPIX1'] += (1 - f * 1) self.header['CRPIX2'] += (1 - f * 1) self._mask = result.array self.shape = self.mask.shape self.header['NAXIS1'] = result.array.shape[1] self.header['NAXIS2'] = result.array.shape[0] self.pixel_scale /= f self.wcs = wcs.WCS(self.header) #### Cautious! The following block could be wrong! #### ## Probably you'll need extra shift of image dshift = 2 * (1 - f * 1) % 0.5 self.shift_mask(dshift, dshift, method='spline') # We don't want to shift wcs. self.header['CRPIX1'] -= dshift self.header['CRPIX2'] -= dshift self.wcs = wcs.WCS(self.header) #### Cautious! The above block could be wrong! #### else: from math import ceil b = round(1 / f) nxout = ceil(nx / b) nyout = ceil(ny / b) result = galimg.drawImage(scale=self.pixel_scale * b, nx=nxout, ny=nyout) self.header = self._resize_header_wcs(self.mask, f) self.header['CRPIX1'] += 0.5 - 1 / b / 2 self.header['CRPIX2'] += 0.5 - 1 / b / 2 self._mask = result.array self.shape = self.image.shape self.header['NAXIS1'] = result.array.shape[1] self.header['NAXIS2'] = result.array.shape[0] self.pixel_scale *= b self.wcs = wcs.WCS(self.header) #### Cautious! The following block could be wrong! #### ## Probably you'll need extra shift of image dshift = 0.5 - 1 / b / 2 self.shift_image(-dshift, -dshift, method='spline') # We don't want to shift wcs. self.header['CRPIX1'] -= dshift self.header['CRPIX2'] -= dshift self.wcs = wcs.WCS(self.header) #### Cautious! The above block could be wrong! #### return self.mask elif method == 'iraf': self.save_to_fits('./_temp.fits', 'mask') if f > 1: magnify('./_temp.fits', './_resize_temp.fits', f, f) else: blkavg('./_temp.fits', './_resize_temp.fits', round(1 / f), round(1 / f), option='sum') hdu = fits.open('./_resize_temp.fits') self.mask = hdu[0].data self.shape = hdu[0].data.shape self.header = hdu[0].header self.wcs = wcs.WCS(self.header) self.pixel_scale /= f hdu.close() imdelete('./*temp.fits') return self.mask else: raise ValueError( "# Not supported interpolation method. Use 'lanczos', 'spline' or 'iraf'." )
def shift_mask(self, dx, dy, method='spline', order=5, cval=0.0): '''Shift the mask of Celestial object. Parameters: dx (float): shift distance (in pixel) along x (horizontal). Note that elements in one row has the same y but different x. Example: dx = 2 is to shift the mask "RIGHT" (as seen in DS9), dy = 3 is to shift the image "UP". dy (float): shift distance (in pixel) along y (vertical). Note that elements in one row has the same y but different x. Example: dx = 2 is to shift the mask "RIGHT" (as seen in DS9), dy = 3 is to shift the image "UP". method (str): interpolation method. Use 'spline', lanczos' or 'iraf'. If using 'iraf', default interpolation is 'poly3. 'Lanczos' requires ``GalSim`` installed. order (int): the order of Spline or Lanczos interpolation (>0). cval (float): value to fill the edges. Default is 0. Returns: shift_mask (ndarray): shifted mask. The "mask" attribute of ``Celestial`` class will also be changed accordingly. ''' ny, nx = self.mask.shape if abs(dx) > nx or abs(ny) > ny: raise ValueError('# Shift distance is beyond the image size.') if method == 'lanczos' or method == 'cubic' or method == 'quintic': try: # try to import galsim from galsim import degrees, Angle from galsim.interpolant import Lanczos from galsim import Image, InterpolatedImage from galsim.fitswcs import AstropyWCS except: raise ImportError( '# Import ``galsim`` failed! Please check if ``galsim`` is installed!' ) # Begin shift assert (order > 0) and isinstance( order, int), 'order of ' + method + ' must be positive interger.' if method == 'lanczos': galimg = InterpolatedImage(Image(self.image, dtype=float), scale=self.pixel_scale, x_interpolant=Lanczos(order)) else: galimg = InterpolatedImage(Image(self.image, dtype=float), scale=self.pixel_scale, x_interpolant=method) galimg = galimg.shift(dx=dx * self.pixel_scale, dy=dy * self.pixel_scale) result = galimg.drawImage(scale=self.pixel_scale, nx=nx, ny=ny) #, wcs=AstropyWCS(self.wcs)) self._mask = result.array # Change the WCS of image hdr = copy.deepcopy(self.header) hdr['CRPIX1'] += dx hdr['CRPIX2'] += dy self.header = hdr self.wcs = wcs.WCS(self.header) return result.array elif method == 'iraf': self.save_to_fits('./_temp.fits', 'mask') imshift('./_temp.fits', './_shift_temp.fits', dx, dy, interp_type='poly3', boundary_type='constant') hdu = fits.open('./_shift_temp.fits') self.mask = hdu[0].data self.shape = hdu[0].data.shape self.header = hdu[0].header self.wcs = wcs.WCS(self.header) hdu.close() imdelete('./*temp.fits') return self.mask elif method == 'spline': from scipy.ndimage.interpolation import shift assert 0 < order <= 5 and isinstance( order, int), 'order of ' + method + ' must be within 0-5.' result = shift(self.mask, [dy, dx], order=order, mode='constant', cval=cval) self._mask = result # Change the WCS of image hdr = copy.deepcopy(self.header) hdr['CRPIX1'] += dx hdr['CRPIX2'] += dy self.header = hdr self.wcs = wcs.WCS(self.header) return result else: raise ValueError( "# Not supported interpolation method. Use 'lanczos' or 'iraf'." )
def resize_image(self, f, method='cubic', order=3, cval=0.0): ''' Zoom/Resize the image of Celestial object. f > 1 means the image will be resampled (finer)! f < 1 means the image will be degraded. Parameters: f (float): the positive factor of zoom. If 0 < f < 1, the image will be resized to smaller one. method (str): interpolation method. Use 'spline', 'iraf', or 'lanczos', 'cubic', 'quintic'. We recommend using 'spline' or 'iraf. The last three methods require ``GalSim`` installed. Other methods are now consistent with "iraf" results. order (int): the order Lanczos interpolation (>0). cval (float): value to fill the edges. Default is 0. Returns: resize_image (ndarray): resized image. The "image" attribute of ``Celestial`` class will also be changed accordingly. ''' if method == 'lanczos' or method == 'cubic' or method == 'quintic': try: # try to import galsim from galsim import degrees, Angle from galsim.interpolant import Lanczos from galsim import Image, InterpolatedImage from galsim.fitswcs import AstropyWCS except: raise ImportError( '# Import `galsim` failed! Please check if `galsim` is installed!' ) assert (order > 0) and isinstance( order, int), 'order of ' + method + ' must be positive interger.' if method == 'lanczos': galimg = InterpolatedImage(Image(self.image, dtype=float), scale=self.pixel_scale, x_interpolant=Lanczos(order)) else: galimg = InterpolatedImage(Image(self.image, dtype=float), scale=self.pixel_scale, x_interpolant=method) ny, nx = self.image.shape if f > 1: result = galimg.drawImage(scale=self.pixel_scale / f, nx=int((nx - 1) * f + 1), ny=int((ny - 1) * f + 1)) self.header = self._resize_header_wcs(f) self.header['CRPIX1'] += (1 - f * 1) self.header['CRPIX2'] += (1 - f * 1) self._image = result.array self.shape = self.image.shape self.header['NAXIS1'] = result.array.shape[1] self.header['NAXIS2'] = result.array.shape[0] self.pixel_scale /= f self.wcs = wcs.WCS(self.header) #### Cautious! The following block could be wrong! #### ## Probably you'll need extra shift of image dshift = 2 * (1 - f * 1) % 0.5 self.shift_image(dshift, dshift, method='spline') # We don't want to shift wcs. self.header['CRPIX1'] -= dshift self.header['CRPIX2'] -= dshift self.wcs = wcs.WCS(self.header) #### Cautious! The above block could be wrong! #### else: from math import ceil b = round(1 / f) nxout = ceil(nx / b) nyout = ceil(ny / b) result = galimg.drawImage(scale=self.pixel_scale * b, nx=nxout, ny=nyout) self.header = self._resize_header_wcs(f) self.header['CRPIX1'] += 0.5 - 1 / b / 2 self.header['CRPIX2'] += 0.5 - 1 / b / 2 self._image = result.array self.shape = self.image.shape self.header['NAXIS1'] = result.array.shape[1] self.header['NAXIS2'] = result.array.shape[0] self.pixel_scale *= b self.wcs = wcs.WCS(self.header) #### Cautious! The following block could be wrong! #### ## Probably you'll need extra shift of image dshift = 0.5 - 1 / b / 2 self.shift_image(-dshift, -dshift, method='spline') # We don't want to shift wcs. self.header['CRPIX1'] -= dshift self.header['CRPIX2'] -= dshift self.wcs = wcs.WCS(self.header) #### Cautious! The above block could be wrong! #### return self.image elif method == 'iraf': self.save_to_fits('./_temp.fits', 'image') if f > 1: magnify('./_temp.fits', './_resize_temp.fits', f, f) else: blkavg('./_temp.fits', './_resize_temp.fits', round(1 / f), round(1 / f), option='sum') try: hdu = fits.open('./_resize_temp.fits') except Exception as e: raise ValueError( 'Interpolation using IRAF filed with error "{}". \n Please try another interpolation method!' .format(e)) self.image = hdu[0].data self.shape = hdu[0].data.shape self.header = hdu[0].header #### Remove redundant PC keywords ### for i in self.header['PC*'].keys(): del self.header[i] ##################################### self.wcs = wcs.WCS(self.header) self.pixel_scale /= f hdu.close() imdelete('./*temp.fits') return self.image elif method == 'spline': ny, nx = self.image.shape if f > 1: from scipy import ndimage assert 0 < order <= 5 and isinstance( order, int), 'order of ' + method + ' must be within 0-5.' nx_zoomed = (nx - 1) * f + 1 f_eff = nx_zoomed / nx result = ndimage.zoom(self.image, f_eff, order=order) result *= 1 / (f_eff**2 ) # Multiplying by this factor to conserve flux self.header = self._resize_header_wcs(f) #self.header['CRPIX1'] += (1 - f * 1) #self.header['CRPIX2'] += (1 - f * 1) self._image = result self.shape = self.image.shape self.header['NAXIS1'] = result.shape[1] self.header['NAXIS2'] = result.shape[0] self.pixel_scale /= f self.wcs = wcs.WCS(self.header) #### Cautious! The following block could be wrong! #### ## Probably you'll need extra shift of image dshift = 2 * (1 - f * 1) % 0.5 self.shift_image(dshift, dshift, method='spline') # We don't want to shift wcs. self.header['CRPIX1'] -= dshift self.header['CRPIX2'] -= dshift self.wcs = wcs.WCS(self.header) #### Cautious! The above block could be wrong! #### else: b = round(1 / f) ny_bin = int(ny / b) nx_bin = int(nx / b) shape = (ny_bin, b, nx_bin, b) x_crop = int(nx_bin * b) y_crop = int(ny_bin * b) result = self.image[0:y_crop, 0:x_crop].reshape(shape).sum(3).sum(1) self.header = self._resize_header_wcs(f) self.header['CRPIX1'] += 0.5 - 1 / b / 2 self.header['CRPIX2'] += 0.5 - 1 / b / 2 self._image = result self.shape = self.image.shape self.header['NAXIS1'] = result.shape[1] self.header['NAXIS2'] = result.shape[0] self.pixel_scale *= b self.wcs = wcs.WCS(self.header) else: raise ValueError( "# Not supported interpolation method. Use 'lanczos', 'spline' or 'iraf'." )
def gen_mock_lsbg(self, galaxy, zp=HSC_zeropoint, pixel_scale=HSC_pixel_scale, verbose=True): ''' Generate mock low surface brightness galaxies. ''' import galsim from galsim import Angle, Image, InterpolatedImage, degrees from galsim.fitswcs import AstropyWCS from galsim.interpolant import Lanczos big_fft_params = galsim.GSParams(maximum_fft_size=20000) if not isinstance(galaxy['comp'], list): galaxy['comp'] = list(galaxy['comp']) if len(galaxy['comp']) == 1: galaxy['flux_fraction'] = [1.0] # print some information if verbose: print('# Generating mock galaxy.') print(' - Total components: ', len(galaxy['comp'])) print(' - Types: ', [c['model'].__name__ for c in galaxy['comp']]) print(' - Flux fraction: ', galaxy['flux_fraction']) # Empty canvas field = np.empty_like(self.bkg.images[0]) model_images = np.empty_like(self.bkg.images) # Calculate RA, DEC of the mock galaxy y_cen = self.bkg.images.shape[2] / 2 x_cen = self.bkg.images.shape[1] / 2 galaxy['ra'], galaxy['dec'] = self.bkg.wcs.wcs_pix2world( x_cen, y_cen, 0) # Calculate flux based on i-band mag and SED i_band_loc = np.argwhere(np.array(list(self.channels)) == 'i')[0][ 0] # location of i-band in `channels` seds = np.array([c['sed'] for c in galaxy['comp']]) # Normalize SED w.r.t i-band seds /= seds[:, i_band_loc][:, np.newaxis] tot_sed = np.sum(seds * np.array(galaxy['flux_fraction'])[:, np.newaxis], axis=0) for i, band in enumerate(self.channels): galaxy[f'{band}mag'] = -2.5 * np.log10(tot_sed[i]) + galaxy['imag'] if verbose: print(f' - Magnitude in {self.channels}: ', [round(galaxy[f'{band}mag'], 1) for band in self.channels]) #### Star generating mock galaxy in each band #### for i, band in enumerate(self.channels): # griz # Random number seed # This random number seed should be fixed across bands!!! rng = galsim.BaseDeviate(23333) # Sky background level sky_SB = 29 # mag/arcsec^2 sky_level = 10**((zp - sky_SB) / 2.5) # counts / arcsec^2 # Define the PSF interp_psf = InterpolatedImage(Image(self.bkg.psfs[i] / self.bkg.psfs[i].sum(), dtype=float), scale=pixel_scale, x_interpolant=Lanczos(3)) # Total flux for all components tot_flux = 10**((zp - galaxy[f'{band}mag']) / 2.5) gal_list = [] for k, comp in enumerate(galaxy['comp']): # Define the galaxy gal = comp['model'](**comp['model_params'], gsparams=big_fft_params) gal_shape = galsim.Shear( **comp['shear_params']) # Shear the galaxy gal = gal.shear(gal_shape) if 'shift' in comp.keys(): # Shift the center gal = gal.shift(comp['shift']) # Add star forming knots if 'n_knots' in comp.keys() and comp['n_knots'] > 0: if not 'knots_frac' in comp.keys(): raise KeyError( '`knots_frac` must be provided to generate star forming knots!' ) else: if 'knots_sed' in comp.keys(): knot_frac = comp['knots_frac'] * \ (comp['knots_sed'] / np.sum(comp['knots_sed']))[i] else: knot_frac = comp['knots_frac'] * 0.25 # flat SED knots = galsim.RandomKnots( comp['n_knots'], half_light_radius=comp['model_params'] ['half_light_radius'], flux=knot_frac, rng=rng) gal = galsim.Add([gal, knots]) gal = gal.withFlux(tot_flux * galaxy['flux_fraction'][k]) # Get Flux gal_list.append(gal) # Adding all components together gal = galsim.Add(gal_list) # Convolve galaxy with PSF final = galsim.Convolve([gal, interp_psf]) # Draw the image with a particular pixel scale. gal_image = final.drawImage(scale=pixel_scale, nx=field.shape[1], ny=field.shape[0]) # Add noise sky_sigma = hsc_sky[f'{band}'] / 3.631 * \ 10**((zp - 22.5) / 2.5) * pixel_scale**2 noise = galsim.GaussianNoise(rng, sigma=sky_sigma) # gal_image.addNoise(noise) # Generate mock image model_img = gal_image.array model_images[i] = model_img # Generate variance map mock_model = Data(images=model_images, variances=None, masks=None, channels=self.channels, wcs=None, weights=None, psfs=self.bkg.psfs, info=galaxy) # Finished!!! self.model = mock_model # model only has `images`, `channels`, `psfs`, and `info`! self.set_mock() # mock has other things, including modified variances.