Example #1
0
    def cutout_psf_single(x, y, image, mask, kernel_size, kernel_init):
        """

        :param x: x-coordinate of point source
        :param y: y-coordinate of point source
        :param image: image (i.e. data - all models subtracted, except a single point source)
        :param mask: mask of pixels in the image not to be considered in the PSF estimate (being replaced by kernel_init)
        :param kernel_size: width in pixel of the kernel
        :param kernel_init: initial guess of kernel (pixels that are masked are replaced by those values)
        :return: estimate of the PSF based on the image and position of the point source
        """
        # cutout the star
        x_int = int(round(x))
        y_int = int(round(y))
        star_cutout = kernel_util.cutout_source(x_int,
                                                y_int,
                                                image,
                                                kernel_size + 2,
                                                shift=False)
        # cutout the mask
        mask_cutout = kernel_util.cutout_source(x_int,
                                                y_int,
                                                mask,
                                                kernel_size + 2,
                                                shift=False)
        # enlarge the initial PSF kernel to the new cutout size
        kernel_enlarged = np.zeros((kernel_size + 2, kernel_size + 2))
        kernel_enlarged[1:-1, 1:-1] = kernel_init
        # shift the initial kernel to the shift of the star
        shift_x = x_int - x
        shift_y = y_int - y
        kernel_shifted = ndimage.shift(kernel_enlarged,
                                       shift=[-shift_y, -shift_x],
                                       order=1)
        # compute normalization of masked and unmasked region of the shifted kernel
        # norm_masked = np.sum(kernel_shifted[mask_i == 0])
        norm_unmasked = np.sum(kernel_shifted[mask_cutout == 1])
        # normalize star within the unmasked region to the norm of the initial kernel of the same region
        star_cutout /= np.sum(star_cutout[mask_cutout == 1]) * norm_unmasked
        # replace mask with shifted initial kernel (+2 size)
        star_cutout[mask_cutout == 0] = kernel_shifted[mask_cutout == 0]
        star_cutout[star_cutout < 0] = 0
        # de-shift kernel
        kernel_deshifted = kernel_util.de_shift_kernel(
            star_cutout,
            shift_x,
            shift_y,
            iterations=20,
            fractional_step_size=0.1)
        # re-size kernel
        kernel_deshifted = image_util.cut_edges(kernel_deshifted, kernel_size)
        # re-normalize kernel again
        kernel_deshifted = kernel_util.kernel_norm(kernel_deshifted)
        return kernel_deshifted
Example #2
0
    def error_map_estimate(self, kernel, star_cutout_list, amp, x_pos, y_pos):
        """
        provides a psf_error_map based on the goodness of fit of the given PSF kernel on the point source cutouts,
        their estimated amplitudes and positions

        :param kernel: PSF kernel
        :param star_cutout_list: list of 2d arrays of cutouts of the point sources with all other model components subtracted
        :param amp: list of amplitudes of the estimated PSF kernel
        :param x_pos: pixel position (in original data unit, not in cutout) of the point sources (same order as amp and star cutouts)
        :param y_pos: pixel position (in original data unit, not in cutout) of the point sources (same order as amp and star cutouts)
        :return: relative uncertainty in the psf model (in quadrature) per pixel based on residuals achieved in the image
        """
        error_map_list = np.zeros(
            (len(star_cutout_list), len(kernel), len(kernel)))
        for i, star in enumerate(star_cutout_list):
            x, y, amp_i = x_pos[i], y_pos[i], amp[i]
            # shift kernel
            x_int = int(round(x))
            y_int = int(round(y))
            shift_x = x_int - x
            shift_y = y_int - y
            kernel_shifted = interp.shift(kernel, [-shift_y, -shift_x],
                                          order=1)
            # multiply kernel with amplitude
            model = kernel_shifted * amp_i
            # compute residuals
            residual = np.abs(star - model)
            # subtract background and Poisson noise residuals
            C_D_cutout = kernel_util.cutout_source(
                x_int,
                y_int,
                self._image_model_class.Data.C_D,
                len(star),
                shift=False)
            mask = kernel_util.cutout_source(
                x_int,
                y_int,
                self._image_model_class.likelihood_mask,
                len(star),
                shift=False)
            residual -= np.sqrt(C_D_cutout)
            residual[residual < 0] = 0
            # estimate relative error per star
            residual /= amp_i
            error_map_list[i, :, :] = residual**2 * mask
        # take median absolute error for each pixel
        error_map = np.median(error_map_list, axis=0)
        error_map[kernel > 0] /= kernel[kernel > 0]**2
        return error_map
Example #3
0
    def cutout_psf(self, ra_image, dec_image, x, y, image_list, kernelsize, kernel_init, block_center_neighbour=0):
        """

        :param x_:
        :param y_:
        :param image_list: list of images (i.e. data - all models subtracted, except a single point source)
        :param kernelsize:
        :return:
        """
        mask = self._image_model_class.likelihood_mask
        ra_grid, dec_grid = self._image_model_class.Data.pixel_coordinates
        ra_grid = util.image2array(ra_grid)
        dec_grid = util.image2array(dec_grid)
        radius = block_center_neighbour

        kernel_list = []
        star_cutout_list = []
        for l in range(len(x)):
            mask_point_source = self.mask_point_source(ra_image, dec_image, ra_grid, dec_grid, radius, i=l)
            mask_i = mask * mask_point_source
            kernel_deshifted = self.cutout_psf_single(x[l], y[l], image_list[l], mask_i, kernelsize, kernel_init)
            kernel_list.append(kernel_deshifted)
            x_int = int(round(x[l]))
            y_int = int(round(y[l]))
            star_cutout = kernel_util.cutout_source(x_int, y_int, image_list[l], kernelsize, shift=False)
            star_cutout_list.append(star_cutout)
        return kernel_list, star_cutout_list
Example #4
0
    def error_map_estimate(self, kernel, star_cutout_list, amp, x_pos, y_pos, error_map_radius=None):
        """
        provides a psf_error_map based on the goodness of fit of the given PSF kernel on the point source cutouts,
        their estimated amplitudes and positions

        :param kernel: PSF kernel
        :param star_cutout_list: list of 2d arrays of cutouts of the point sources with all other model components subtracted
        :param amp: list of amplitudes of the estimated PSF kernel
        :param x_pos: pixel position (in original data unit, not in cutout) of the point sources (same order as amp and star cutouts)
        :param y_pos: pixel position (in original data unit, not in cutout) of the point sources (same order as amp and star cutouts)
        :param error_map_radius: float, radius (in arc seconds) of the outermost error in the PSF estimate (e.g. to avoid double counting of overlapping PSF erros)
        :return: relative uncertainty in the psf model (in quadrature) per pixel based on residuals achieved in the image
        """
        error_map_list = np.zeros((len(star_cutout_list), len(kernel), len(kernel)))
        for i, star in enumerate(star_cutout_list):
            x, y, amp_i = x_pos[i], y_pos[i], amp[i]
            # shift kernel
            x_int = int(round(x))
            y_int = int(round(y))
            shift_x = x_int - x
            shift_y = y_int - y
            kernel_shifted = interp.shift(kernel, [-shift_y, -shift_x], order=1)
            # multiply kernel with amplitude
            model = kernel_shifted * amp_i
            # compute residuals
            residual = np.abs(star - model)
            # subtract background and Poisson noise residuals
            C_D_cutout = kernel_util.cutout_source(x_int, y_int, self._image_model_class.Data.C_D, len(star), shift=False)
            mask = kernel_util.cutout_source(x_int, y_int, self._image_model_class.likelihood_mask, len(star), shift=False)
            residual -= np.sqrt(C_D_cutout)
            residual[residual < 0] = 0
            # estimate relative error per star
            residual /= amp_i
            error_map_list[i, :, :] = residual**2*mask
        # take median absolute error for each pixel
        error_map = np.median(error_map_list, axis=0)
        error_map[kernel > 0] /= kernel[kernel > 0]**2
        error_map = np.nan_to_num(error_map)
        error_map[error_map > 1] = 1  # cap on error to be the same

        # mask the error map outside a certain radius (can avoid double counting of errors when map is overlapping
        if error_map_radius is not None:
            pixel_scale = self._image_model_class.Data.pixel_width
            x_grid, y_grid = util.make_grid(numPix=len(error_map), deltapix=pixel_scale)
            mask = mask_util.mask_azimuthal(x_grid, y_grid, center_x=0, center_y=0, r=error_map_radius)
            error_map *= util.array2image(mask)
        return error_map
Example #5
0
    def test_raise(self):
        with self.assertRaises(ValueError):
            kernel = np.zeros((2, 2))
            kernel_util.center_kernel(kernel, iterations=1)

        with self.assertRaises(ValueError):
            kernel_super = np.ones((9, 9))
            kernel_util.split_kernel(kernel_super, supersampling_kernel_size=2, supersampling_factor=3)
        with self.assertRaises(ValueError):
            kernel_util.split_kernel(kernel_super, supersampling_kernel_size=3, supersampling_factor=0)
        with self.assertRaises(ValueError):
            image = np.ones((10, 10))
            kernel_util.cutout_source(x_pos=3, y_pos=2, image=image, kernelsize=2)
        with self.assertRaises(ValueError):
            kernel_util.fwhm_kernel(kernel=np.ones((4, 4)))
        with self.assertRaises(ValueError):
            kernel_util.fwhm_kernel(kernel=np.ones((5, 5)))
Example #6
0
def test_cutout_source2():
    grid2d = np.zeros((20, 20))
    grid2d[7:9, 7:9] = 1
    kernel = kernel_util.cutout_source(x_pos=7.5,
                                       y_pos=7.5,
                                       image=grid2d,
                                       kernelsize=5,
                                       shift=False)
    assert kernel[2, 2] == 1
Example #7
0
def test_cutout_source_border():
    kernel_size = 7
    image = np.zeros((10, 10))
    kernel = np.zeros((kernel_size, kernel_size))
    kernel[2, 2] = 1
    shift_x = +0.1
    shift_y = 0
    x_c, y_c = 2, 5
    x_pos = x_c + shift_x
    y_pos = y_c + shift_y
    #kernel_shifted = interp.shift(kernel, [shift_y, shift_x], order=1)
    image = image_util.add_layer2image(image, x_pos, y_pos, kernel, order=1)
    kernel_new = kernel_util.cutout_source(x_pos=x_pos, y_pos=y_pos, image=image, kernelsize=kernel_size)
    nx_new, ny_new = np.shape(kernel_new)
    print(kernel_new)
    assert nx_new == kernel_size
    assert ny_new == kernel_size
    npt.assert_almost_equal(kernel_new[2, 2], kernel[2, 2], decimal=2)
Example #8
0
def test_cutout_source():
    """
    test whether a shifted psf can be reproduced sufficiently well
    :return:
    """
    kernel_size = 5
    image = np.zeros((10, 10))
    kernel = np.zeros((kernel_size, kernel_size))
    kernel[2, 2] = 1
    shift_x = 0.5
    shift_y = 0
    x_c, y_c = 5, 5
    x_pos = x_c + shift_x
    y_pos = y_c + shift_y
    #kernel_shifted = interp.shift(kernel, [shift_y, shift_x], order=1)
    image = image_util.add_layer2image(image, x_pos, y_pos, kernel, order=1)
    print(image)
    kernel_new = kernel_util.cutout_source(x_pos=x_pos, y_pos=y_pos, image=image, kernelsize=kernel_size)
    npt.assert_almost_equal(kernel_new[2, 2], kernel[2, 2], decimal=2)
Example #9
0
    def point_like_source_cutouts(x_pos, y_pos, image_list, cutout_size):
        """
        cutouts of point-like objects

        :param x_pos: list of image positions in pixel units
        :param y_pos: list of image position in pixel units
        :param image_list: list of 2d numpy arrays with cleaned images, with all contaminating sources removed except
         the point-like object to be cut out.
        :param cutout_size: odd integer, size of cutout.
        :return: list of cutouts
        """

        star_cutout_list = []
        for l in range(len(x_pos)):
            x_int = int(round(x_pos[l]))
            y_int = int(round(y_pos[l]))
            star_cutout = kernel_util.cutout_source(x_int,
                                                    y_int,
                                                    image_list[l],
                                                    cutout_size,
                                                    shift=False)
            star_cutout_list.append(star_cutout)
        return star_cutout_list
Example #10
0
    def error_map_estimate_new(self,
                               psf_kernel,
                               psf_kernel_list,
                               ra_image,
                               dec_image,
                               point_amp,
                               supersampling_factor,
                               error_map_radius=None):
        """
        relative uncertainty in the psf model (in quadrature) per pixel based on residuals achieved in the image

        :param psf_kernel: PSF kernel (super-sampled)
        :param psf_kernel_list: list of individual best PSF kernel estimates
        :param ra_image: image positions in angles
        :param dec_image: image positions in angles
        :param point_amp: image amplitude
        :param supersampling_factor: super-sampling factor
        :param error_map_radius: radius (in angle) to cut the error map
        :return: psf error map such that square of the uncertainty gets boosted by error_map * (psf * amp)**2
        """
        kernel_low = kernel_util.degrade_kernel(psf_kernel,
                                                supersampling_factor)
        error_map_list = np.zeros(
            (len(psf_kernel_list), len(kernel_low), len(kernel_low)))
        x_pos, y_pos = self._image_model_class.Data.map_coord2pix(
            ra_image, dec_image)

        for i, psf_kernel_i in enumerate(psf_kernel_list):
            kernel_low_i = kernel_util.degrade_kernel(psf_kernel_i,
                                                      supersampling_factor)

            x, y, amp_i = x_pos[i], y_pos[i], point_amp[i]
            x_int = int(round(x))
            y_int = int(round(y))

            C_D_cutout = kernel_util.cutout_source(
                x_int,
                y_int,
                self._image_model_class.Data.C_D,
                len(kernel_low_i),
                shift=False)
            residuals_i = np.abs(kernel_low - kernel_low_i)
            residuals_i -= np.sqrt(C_D_cutout) / amp_i
            residuals_i[residuals_i < 0] = 0
            error_map_list[i, :, :] = residuals_i**2

        error_map = np.median(error_map_list, axis=0)
        error_map[kernel_low > 0] /= kernel_low[kernel_low > 0]**2
        error_map = np.nan_to_num(error_map)
        error_map[error_map > 1] = 1  # cap on error to be the same

        # mask the error map outside a certain radius (can avoid double counting of errors when map is overlapping
        if error_map_radius is not None:
            pixel_scale = self._image_model_class.Data.pixel_width
            x_grid, y_grid = util.make_grid(numPix=len(error_map),
                                            deltapix=pixel_scale)
            mask = mask_util.mask_azimuthal(x_grid,
                                            y_grid,
                                            center_x=0,
                                            center_y=0,
                                            r=error_map_radius)
            error_map *= util.array2image(mask)
        return error_map
Example #11
0
    def psf_estimate_individual(self, ra_image, dec_image, point_amp,
                                residuals, cutout_size, kernel_guess,
                                supersampling_factor, block_center_neighbour):
        """

        :param ra_image: list; position in angular units of the image
        :param dec_image: list; position in angular units of the image
        :param point_amp: list of model amplitudes of point sources
        :param residuals: data - model
        :param cutout_size: pixel size of cutout around single star/quasar to be considered for the psf reconstruction
        :param kernel_guess: initial guess of super-sampled PSF
        :param supersampling_factor: int, super-sampling factor
        :param block_center_neighbour:
        :return: list of best-guess PSF's for each star based on the residual patterns
        """
        mask = self._image_model_class.likelihood_mask
        ra_grid, dec_grid = self._image_model_class.Data.pixel_coordinates
        ra_grid = util.image2array(ra_grid)
        dec_grid = util.image2array(dec_grid)
        radius = block_center_neighbour
        x_, y_ = self._image_model_class.Data.map_coord2pix(
            ra_image, dec_image)

        kernel_list = []
        for l in range(len(ra_image)):
            mask_point_source = self.mask_point_source(ra_image,
                                                       dec_image,
                                                       ra_grid,
                                                       dec_grid,
                                                       radius,
                                                       i=l)
            mask_i = mask * mask_point_source

            # cutout residuals
            x_int = int(round(x_[l]))
            y_int = int(round(y_[l]))
            residual_cutout = kernel_util.cutout_source(x_int,
                                                        y_int,
                                                        residuals,
                                                        cutout_size + 2,
                                                        shift=False)
            # cutout the mask
            mask_cutout = kernel_util.cutout_source(x_int,
                                                    y_int,
                                                    mask_i,
                                                    cutout_size + 2,
                                                    shift=False)
            # apply mask
            residual_cutout_mask = residual_cutout * mask_cutout
            # re-scale residuals with point source brightness
            residual_cutout_mask /= point_amp[l]
            # enlarge residuals by super-sampling factor
            residual_cutout_mask = residual_cutout_mask.repeat(
                supersampling_factor, axis=0).repeat(supersampling_factor,
                                                     axis=1)

            # inverse shift residuals
            shift_x = (x_int - x_[l]) * supersampling_factor
            shift_y = (y_int - y_[l]) * supersampling_factor
            # for odd number super-sampling
            if supersampling_factor % 2 == 1:
                residuals_shifted = ndimage.shift(residual_cutout_mask,
                                                  shift=[shift_y, shift_x],
                                                  order=1)

            else:
                # for even number super-sampling half a super-sampled pixel offset needs to be performed
                residuals_shifted = ndimage.shift(
                    residual_cutout_mask,
                    shift=[shift_y - 0.5, shift_x - 0.5],
                    order=1)
                # and the last column and row need to be removed
                residuals_shifted = residuals_shifted[:-1, :-1]

            # re-size shift residuals
            psf_size = len(kernel_guess)
            residuals_shifted = image_util.cut_edges(residuals_shifted,
                                                     psf_size)

            # normalize residuals
            correction = residuals_shifted - np.mean(residuals_shifted)
            # correct old PSF with inverse shifted residuals
            kernel_new = kernel_guess + correction
            kernel_list.append(kernel_new)
        return kernel_list
Example #12
0
    def cutout_psf(self, x, y, image_list, kernelsize, mask, mask_point_source_list, kernel_init, symmetry=1):
        """

        :param x_:
        :param y_:
        :param image_list: list of images (i.e. data - all models subtracted, except a single point source)
        :param kernelsize:
        :return:
        """
        n = len(x) * symmetry
        angle = 360. / symmetry
        kernel_list = np.zeros((n, kernelsize, kernelsize))
        i = 0
        for l in range(len(x)):
            # cutout the star
            x_, y_ = x[l], y[l]
            x_int = int(round(x_))
            y_int = int(round(y_))
            star_cutout = kernel_util.cutout_source(x_int, y_int, image_list[l], kernelsize + 2, shift=False)
            # cutout the mask
            mask_i = mask * mask_point_source_list[l]
            mask_cutout = kernel_util.cutout_source(x_int, y_int, mask_i, kernelsize + 2, shift=False)
            # enlarge the initial PSF kernel to the new cutout size
            kernel_enlarged = np.zeros((kernelsize+2, kernelsize+2))
            kernel_enlarged[1:-1, 1:-1] = kernel_init
            # shift the initial kernel to the shift of the star
            shift_x = x_int - x_
            shift_y = y_int - y_
            kernel_shifted = interp.shift(kernel_enlarged, [-shift_y, -shift_x], order=1)
            # compute normalization of masked and unmasked region of the shifted kernel
            # norm_masked = np.sum(kernel_shifted[mask_i == 0])
            norm_unmaksed = np.sum(kernel_shifted[mask_cutout == 1])
            # normalize star within the unmasked region to the norm of the initial kernel of the same region
            star_cutout /= np.sum(star_cutout[mask_cutout == 1]) * norm_unmaksed
            # replace mask with shifted initial kernel (+2 size)
            star_cutout[mask_cutout == 0] = kernel_shifted[mask_cutout == 0]
            star_cutout[star_cutout < 0] = 0
            # de-shift kernel
            kernel_deshifted = kernel_util.de_shift_kernel(star_cutout, shift_x, shift_y)
            # re-size kernel
            kernel_deshifted = image_util.cut_edges(kernel_deshifted, kernelsize)

            # re-normalize kernel again
            kernel_deshifted = kernel_util.kernel_norm(kernel_deshifted)
            """

            kernel_shifted = kernel_util.cutout_source(x_[l], y_[l], image_list[l],
                                                       kernelsize + 2)  # don't de-shift it here
            mask_i = mask * mask_point_source_list[l]
            mask_cutout = kernel_util.cutout_source(int(round(x_[l])), int(round(x_[l])), mask_i, kernelsize + 2,
                                                    shift=False)
            kernel_shifted[kernel_shifted < 0] = 0
            kernel_shifted *= mask_cutout
            kernel_init = kernel_util.kernel_norm(kernel_init)
            mask_cutout = image_util.cut_edges(mask_cutout, kernelsize)
            kernel_shifted = image_util.cut_edges(kernel_shifted, kernelsize)
            kernel_norm = np.sum(kernel_init[mask_cutout == 1])
            kernel_shifted = kernel_util.kernel_norm(kernel_shifted)
            kernel_shifted *= kernel_norm
            kernel_shifted[mask_cutout == 0] = kernel_init[mask_cutout == 0]
            #kernel_shifted[mask_cutout == 1] /= (np.sum(kernel_init[mask_cutout == 1]) * np.sum(kernel_shifted[mask_cutout == 1]))
            """
            for k in range(symmetry):
                kernel_rotated = image_util.rotateImage(kernel_deshifted, angle * k)
                kernel_norm = kernel_util.kernel_norm(kernel_rotated)
                try:
                    kernel_list[i, :, :] = kernel_norm
                except:
                    raise ValueError("cutout kernel has not the same shape as the PSF."
                                     " This is probably because the cutout of the psf hits the boarder of the data."
                                     "Use a smaller PSF or a larger data frame for the modelling.")
                i += 1
        return kernel_list