Esempio n. 1
0
File: cube.py Progetto: heh15/Arp240
def convolve_projection(proj, newbeam, res_tol=0.0, min_coverage=0.8,
                        append_raw=False, verbose=False,
                        suppress_error=False):
    """
    Convolve a 2D image to a specified beam.

    Very similar to `convolve_cube()`, but this function deals with
    2D images (i.e., projections) rather than 3D cubes.

    Parameters
    ----------
    proj : ~spectral_cube.Projection object
        Input 2D image
    newbeam : radio_beam.Beam object
        Target beam to convolve to
    res_tol : float, optional
        Tolerance on the difference between input/output resolution
        By default, a convolution is performed on the input image
        whenever its native resolution is different from (sharper than)
        the target resolution. Use this keyword to specify a tolerance
        on resolution, within which no convolution will be performed.
        For example, res_tol=0.1 will allow a 10% tolerance.
    min_coverage : float or None, optional
        When the convolution meets NaN values or edges, the output is
        calculated based on beam-weighted average. This keyword specifies
        the minimum beam covering fraction of valid (np.finite) values.
        All pixels with less beam covering fraction will be assigned NaNs.
        Default is 80% beam covering fraction (min_coverage=0.8).
        If the user would rather use the interpolation strategy in
        `astropy.convolution.convolve_fft`, set this keyword to None.
        Note that the NaN pixels will be kept as NaN.
    append_raw : bool, optional
        Whether to append the raw convolved image and weight image
        Default is not to append.
    verbose : bool, optional
        Whether to print the detailed processing information in terminal
        Default is to not print.
    suppress_error : bool, optional
        Whether to suppress the error when convolution is unsuccessful
        Default is to not suppress.

    Returns
    -------
    outproj : Projection object or tuple
        Convolved 2D image (when append_raw=False), or a tuple
        comprising 3 images (when append_raw=True)
    """

    from functools import partial
    from astropy.convolution import convolve_fft

    if min_coverage is None:
        # Skip coverage check and preserve NaN values.
        # This uses the default interpolation strategy
        # implemented in 'astropy.convolution.convolve_fft'
        convolve_func = partial(convolve_fft, preserve_nan=True,
                                allow_huge=True, quiet=~verbose)
    else:
        # Do coverage check to determine the mask on the output
        convolve_func = partial(convolve_fft, nan_treatment='fill',
                                boundary='fill', fill_value=0.,
                                allow_huge=True, quiet=~verbose)

    if (res_tol > 0) and (newbeam.major != newbeam.minor):
        raise ValueError("You cannot specify a non-zero resolution "
                         "torelance if the target beam is not round")

    tol = newbeam.major * np.array([1-res_tol, 1+res_tol])
    if ((tol[0] < proj.beam.major < tol[1]) and
        (tol[0] < proj.beam.minor < tol[1])):
        if verbose:
            print("Native resolution within tolerance - "
                  "Copying original image...")
        my_append_raw = False
        newproj = proj.copy()
    else:
        if verbose:
            print("Convolving image...")
        try:
            convproj = proj.convolve_to(newbeam,
                                        convolve=convolve_func)
            if min_coverage is not None:
                # divide the raw convolved image by the weight image
                my_append_raw = True
                wtproj = Projection(
                    np.isfinite(proj.data).astype('float'),
                    wcs=proj.wcs, beam=proj.beam)
                wtproj = wtproj.convolve_to(newbeam,
                                            convolve=convolve_func)
                newproj = convproj / wtproj.hdu.data
                # mask all pixels w/ weight smaller than min_coverage
                threshold = min_coverage * u.dimensionless_unscaled
                newproj[wtproj < threshold] = np.nan
            else:
                my_append_raw = False
                newproj = convproj
        except ValueError as err:
            if suppress_error:
                return
            else:
                raise ValueError(
                    "Unsuccessful convolution: {}\nOld: {}\nNew: {}"
                    "".format(err, proj.beam, newbeam))

    if append_raw and my_append_raw:
        return newproj, convproj, wtproj
    else:
        return newproj
Esempio n. 2
0
def convolve_image(inimage,
                   newbeam,
                   mode='dataimage',
                   res_tol=0.0,
                   min_coverage=0.8,
                   nan_treatment='fill',
                   boundary='fill',
                   fill_value=0.,
                   append_raw=False,
                   verbose=False,
                   suppress_error=False):
    """
    Convolve a 2D image or an rms noise image to a specified beam.

    This function is similar to `convolve_cube()`, but it deals with
    2D images (i.e., projections) rather than 3D cubes.

    Parameters
    ----------
    inimage : FITS HDU object or ~spectral_cube.Projection object
        Input 2D image
    newbeam : radio_beam.Beam object
        Target beam to convolve to
    mode : {'dataimage', 'noiseimage'}, optional
        Whether the input image is a data image or an rms noise image.
        In the former case, a direct convolution is performed;
        in the latter case, the convolution attempts to mimic the
        error propagation process to the specified lower resolution.
        (Default: 'dataimage')
    res_tol : float, optional
        Tolerance on the difference between input/output resolution
        By default, a convolution is performed on the input image
        when its native resolution is different from (sharper than)
        the target resolution. Use this keyword to specify a tolerance
        on resolution, within which no convolution will be performed.
        For example, res_tol=0.1 will allow a 10% tolerance.
    min_coverage : float or None, optional
        This keyword specifies a minimum beam covering fraction of
        valid pixels for convolution (Default: 0.8).
        Locations with a beam covering fraction less than this value
        will be overwritten to "NaN" in the convolved cube.
        If the user would rather use the ``preserve_nan`` mode in
        `astropy.convolution.convolve_fft`, set this keyword to None.
    nan_treatment: {'interpolate', 'fill'}, optional
        To be passed to `astropy.convolution.convolve_fft`.
        (Default: 'fill')
    boundary: {'fill', 'wrap'}, optional
        To be passed to `astropy.convolution.convolve_fft`.
        (Default: 'fill')
    fill_value : float, optional
        To be passed to `astropy.convolution.convolve_fft`.
        (Default: 0)
    append_raw : bool, optional
        Whether to append the raw convolved image and weight image.
        Default is not to append.
    verbose : bool, optional
        Whether to print the detailed processing log in terminal.
        Default is to not print.
    suppress_error : bool, optional
        Whether to suppress the error message when convolution is
        unsuccessful. Default is to not suppress.

    Returns
    -------
    outimage : FITS HDU objects or Projection objects
        Convolved 2D images (when append_raw=False), or a 3-tuple
        including a masked verson, an unmaked version, and a coverage
        fraction map (when append_raw=True).
        The output will be the same type of objects as the input.
    """

    if isinstance(inimage, Projection):
        proj = inimage
    elif isinstance(inimage, (fits.PrimaryHDU, fits.ImageHDU)):
        proj = Projection.from_hdu(inimage)
    else:
        raise ValueError("`inimage` needs to be either a FITS HDU object "
                         "or a spectral_cube.Projection object")

    if (res_tol > 0) and (newbeam.major != newbeam.minor):
        raise ValueError("Cannot handle a non-zero resolution torelance "
                         "when the target beam is not round")

    if min_coverage is None:
        # Skip coverage check and preserve NaN values.
        # This uses the default 'preserve_nan' scheme
        # implemented in 'astropy.convolution.convolve_fft'
        convolve_func = partial(convolve_fft,
                                fill_value=fill_value,
                                nan_treatment=nan_treatment,
                                boundary=boundary,
                                preserve_nan=True,
                                allow_huge=True)
    else:
        # Do coverage check to determine the mask on the output
        convolve_func = partial(convolve_fft,
                                fill_value=fill_value,
                                nan_treatment=nan_treatment,
                                boundary=boundary,
                                allow_huge=True)
    convolve_func_w = partial(convolve_fft,
                              fill_value=0.,
                              boundary='fill',
                              allow_huge=True)

    tol = newbeam.major * np.array([1 - res_tol, 1 + res_tol])
    if ((tol[0] < proj.beam.major < tol[1])
            and (tol[0] < proj.beam.minor < tol[1])):
        if verbose:
            print("Native resolution within tolerance - "
                  "Copying original image...")
        my_append_raw = False
        convproj = wtproj = None
        newproj = proj.copy()
    else:
        if verbose:
            print("Deconvolving beam...")
        try:
            beamdiff = newbeam.deconvolve(proj.beam)
        except ValueError as err:
            if suppress_error:
                if verbose:
                    print("Unsuccessful beam deconvolution: "
                          "{}\nOld: {}\nNew: {}"
                          "".format(err, proj.beam, newbeam))
                    print("Exiting...")
                return
            else:
                raise ValueError("Unsuccessful beam deconvolution: "
                                 "{}\nOld: {}\nNew: {}"
                                 "".format(err, proj.beam, newbeam))
        if verbose:
            print("Convolving image...")
        if mode == 'dataimage':
            # do convolution
            convproj = proj.convolve_to(newbeam, convolve=convolve_func)
            if min_coverage is not None:
                my_append_raw = True
                wtproj = Projection(np.isfinite(proj.data).astype('float'),
                                    wcs=proj.wcs,
                                    beam=proj.beam)
                wtproj = wtproj.convolve_to(newbeam, convolve=convolve_func_w)
                # divide the raw convolved image by the weight image
                # to correct for filling fraction
                newproj = convproj / wtproj.hdu.data
                # mask all pixels w/ weight smaller than min_coverage
                threshold = min_coverage * u.dimensionless_unscaled
                newproj[wtproj < threshold] = np.nan
            else:
                my_append_raw = False
                newproj = convproj
                wtproj = None
        elif mode == 'noiseimage':
            # Empirically derive a noise image at the lower resolution
            # Step 1: square the high resolution noise image
            projsq = proj**2
            # Step 2: convolve the squared noise image with a kernel
            #         that is sqrt(2) times narrower than the one
            #         used for data image convolution (this is because
            #         the Gaussian weight needs to be squared in
            #         error propagation)
            beamdiff_small = Beam(major=beamdiff.major / np.sqrt(2),
                                  minor=beamdiff.minor / np.sqrt(2),
                                  pa=beamdiff.pa)
            newbeam_small = proj.beam.convolve(beamdiff_small)
            convprojsq = projsq.convolve_to(newbeam_small,
                                            convolve=convolve_func)
            if min_coverage is not None:
                my_append_raw = True
                wtproj = Projection(np.isfinite(proj.data).astype('float'),
                                    wcs=proj.wcs,
                                    beam=proj.beam)
                # divide the raw convolved image by the weight image
                # to correct for filling fraction
                wtproj_d = wtproj.convolve_to(newbeam_small,
                                              convolve=convolve_func_w)
                newprojsq = convprojsq / wtproj_d.hdu.data
                # mask all pixels w/ weight smaller than min_coverage
                # (here I force the masking of the noise image to be
                #  consistent with that of the data image)
                wtproj = wtproj.convolve_to(newbeam, convolve=convolve_func_w)
                threshold = min_coverage * u.dimensionless_unscaled
                newprojsq[wtproj < threshold] = np.nan
            else:
                my_append_raw = False
                newprojsq = convprojsq
                wtproj = None
            # Step 3: find the sqrt of the convolved noise image
            convproj = np.sqrt(convprojsq)
            newproj = np.sqrt(newprojsq)
            # Step 4: apply a multiplicative factor, which accounts
            #         for the decrease in rms noise due to averaging
            convproj = (convproj *
                        np.sqrt(proj.beam.sr / newbeam.sr).to('').value)
            newproj = (newproj *
                       np.sqrt(proj.beam.sr / newbeam.sr).to('').value)
        else:
            raise ValueError("Invalid `mode` value: {}".format(mode))

    if isinstance(inimage, Projection):
        if append_raw and my_append_raw:
            return newproj, convproj, wtproj
        else:
            return newproj
    elif isinstance(inimage, (fits.PrimaryHDU, fits.ImageHDU)):
        if append_raw and my_append_raw:
            return newproj.hdu, convproj.hdu, wtproj.hdu
        else:
            return newproj.hdu