def convert_polimage_to_stokes(im: Image):
    """Convert a polarisation image to stokes (complex)
    
    """
    assert isinstance(im, Image)
    assert im.data.dtype == 'complex'
    
    if im.polarisation_frame == PolarisationFrame('linear'):
        cimarr = convert_linear_to_stokes(im.data)
        return create_image_from_array(cimarr, im.wcs, PolarisationFrame('stokesIQUV'))
    elif im.polarisation_frame == PolarisationFrame('circular'):
        cimarr = convert_circular_to_stokes(im.data)
        return create_image_from_array(cimarr, im.wcs, PolarisationFrame('stokesIQUV'))
    else:
        raise ValueError("Cannot convert %s to stokes" % (im.polarisation_frame.type))
def convert_convolutionfunction_to_image(cf):
    """ Convert ConvolutionFunction to an image
    
    :param cf:
    :return:
    """
    return create_image_from_array(cf.data, cf.grid_wcs, cf.polarisation_frame)
예제 #3
0
def grid_visibility_to_griddata_fast(vis, griddata, cf, gcf):
    """Grid Visibility onto a GridData

    :param vis: Visibility to be gridded
    :param griddata: GridData
    :param kwargs:
    :return: GridData
    """
    nchan, npol, nz, ny, nx = griddata.shape
    sumwt = numpy.zeros([nchan, npol])
    pu_grid, pu_offset, pv_grid, pv_offset, pwg_grid, pwg_fraction, pwc_grid, pwc_fraction, pfreq_grid = \
        convolution_mapping(vis, griddata, cf)
    _, _, _, _, _, gv, gu = cf.shape
    coords = zip(vis.vis, vis.weight, pfreq_grid, pu_grid, pv_grid, pwg_grid)
    griddata.data[...] = 0.0
    
    for v, vwt, chan, xx, yy, zzg in coords:
        griddata.data[chan, :, zzg, yy, xx] += v * vwt
        sumwt[chan, :] += vwt
    
    projected = numpy.sum(griddata.data, axis=2)
    im_data = numpy.real(fft(projected)) * gcf.data
    im = create_image_from_array(im_data, griddata.projection_wcs, griddata.polarisation_frame)
    
    return im, sumwt
def image_channel_iter(im: Image, subimages=1) -> collections.Iterable:
    """Create a image_channel_iter generator, returning images

    The WCS is adjusted appropriately for each raster element. Hence this is a coordinate-aware
    way to iterate through an image.

    Provided we don't break reference semantics, memory should be conserved

    To update the image in place:
        for r in raster(im, facets=2)::
            r.data[...] = numpy.sqrt(r.data[...])

    :param im: Image
    :param subimages: Number of subimages
    """
    
    nchan, npol, ny, nx = im.shape
    
    assert subimages <= nchan, "More subimages %d than channels %d" % (subimages, nchan)
    step = nchan // subimages
    channels = numpy.array(range(0, nchan, step), dtype='int')
    assert len(channels) == subimages, "subimages %d does not match length of channels %d" % (subimages, len(channels))
    
    for i, channel in enumerate(channels):
        if i + 1 < len(channels):
            channel_max = channels[i + 1]
        else:
            channel_max = nchan
        
        # Adjust WCS
        wcs = im.wcs.deepcopy()
        wcs.wcs.crpix[3] -= channel
        
        # Yield image from slice (reference!)
        yield create_image_from_array(im.data[channel:channel_max, ...], wcs, im.polarisation_frame)
예제 #5
0
def create_box_convolutionfunction(im, oversampling=1, support=1):
    """ Fill a box car function into a ConvolutionFunction

    Also returns the griddata correction function as an image

    :param im: Image template
    :param oversampling: Oversampling of the convolution function in uv space
    :return: griddata correction Image, griddata kernel as ConvolutionFunction
    """
    assert isinstance(im, Image)
    cf = create_convolutionfunction_from_image(im, oversampling=1, support=4)

    nchan, npol, _, _ = im.shape

    cf.data[...] = 0.0 + 0.0j
    cf.data[..., 2, 2] = 1.0 + 0.0j

    # Now calculate the griddata correction function as an image with the same coordinates as the image
    # which is necessary so that the correction function can be applied directly to the image
    nchan, npol, ny, nx = im.data.shape
    nu = numpy.abs(coordinates(nx))

    gcf1d = numpy.sinc(nu)
    gcf = numpy.outer(gcf1d, gcf1d)
    gcf = 1.0 / gcf

    gcf_data = numpy.zeros_like(im.data)
    gcf_data[...] = gcf[numpy.newaxis, numpy.newaxis, ...]
    gcf_image = create_image_from_array(gcf_data, cf.projection_wcs,
                                        im.polarisation_frame)

    return gcf_image, cf
def convert_stokes_to_polimage(im: Image, polarisation_frame: PolarisationFrame):
    """Convert a stokes image to polarisation_frame

    """
    
    assert isinstance(im, Image)
    assert isinstance(polarisation_frame, PolarisationFrame)
    
    if polarisation_frame == PolarisationFrame('linear'):
        cimarr = convert_stokes_to_linear(im.data)
        return create_image_from_array(cimarr, im.wcs, polarisation_frame)
    elif polarisation_frame == PolarisationFrame('circular'):
        cimarr = convert_stokes_to_circular(im.data)
        return create_image_from_array(cimarr, im.wcs, polarisation_frame)
    else:
        raise ValueError("Cannot convert stokes to %s" % (polarisation_frame.type))
def convert_griddata_to_image(gd):
    """ Convert griddata to an image
    
    :param gd:
    :return:
    """
    return create_image_from_array(gd.data, gd.grid_wcs, gd.polarisation_frame)
예제 #8
0
 def setUp(self):
     from data_models.parameters import arl_path
     self.dir = arl_path('test_results')
     self.lowcore = create_named_configuration('LOWBD2-CORE')
     self.times = (numpy.pi / (12.0)) * numpy.linspace(-3.0, 3.0, 7)
     self.frequency = numpy.array([1e8])
     self.channel_bandwidth = numpy.array([1e6])
     self.phasecentre = SkyCoord(ra=+180.0 * u.deg, dec=-60.0 * u.deg, frame='icrs', equinox='J2000')
     self.vis = create_visibility(self.lowcore, self.times, self.frequency,
                                  channel_bandwidth=self.channel_bandwidth,
                                  phasecentre=self.phasecentre, weight=1.0,
                                  polarisation_frame=PolarisationFrame('stokesI'), zerow=True)
     self.vis.data['vis'] *= 0.0
     
     # Create model
     self.test_model = create_test_image(cellsize=0.001, phasecentre=self.vis.phasecentre,
                                         frequency=self.frequency)
     self.vis = predict_2d(self.vis, self.test_model)
     assert numpy.max(numpy.abs(self.vis.vis)) > 0.0
     self.model = create_image_from_visibility(self.vis, npixel=512, cellsize=0.001,
                                               polarisation_frame=PolarisationFrame('stokesI'))
     self.dirty, sumwt = invert_2d(self.vis, self.model)
     self.psf, sumwt = invert_2d(self.vis, self.model, dopsf=True)
     window = numpy.zeros(shape=self.model.shape, dtype=numpy.bool)
     window[..., 129:384, 129:384] = True
     self.innerquarter = create_image_from_array(window, self.model.wcs, polarisation_frame=PolarisationFrame(
         'stokesI'))
예제 #9
0
    def setUp(self):
        from data_models.parameters import arl_path
        self.dir = arl_path('test_results')
        self.niter = 1000
        self.lowcore = create_named_configuration('LOWBD2-CORE')
        self.nchan = 5
        self.times = (numpy.pi / 12.0) * numpy.linspace(-3.0, 3.0, 7)
        self.frequency = numpy.linspace(0.9e8, 1.1e8, self.nchan)
        self.channel_bandwidth = numpy.array(
            self.nchan * [self.frequency[1] - self.frequency[0]])
        self.phasecentre = SkyCoord(ra=+0.0 * u.deg,
                                    dec=-45.0 * u.deg,
                                    frame='icrs',
                                    equinox='J2000')
        self.vis = create_visibility(
            self.lowcore,
            self.times,
            self.frequency,
            self.channel_bandwidth,
            phasecentre=self.phasecentre,
            weight=1.0,
            polarisation_frame=PolarisationFrame('stokesI'),
            zerow=True)
        self.vis.data['vis'] *= 0.0

        # Create model
        self.test_model = create_low_test_image_from_gleam(
            npixel=512,
            cellsize=0.001,
            phasecentre=self.vis.phasecentre,
            frequency=self.frequency,
            channel_bandwidth=self.channel_bandwidth,
            flux_limit=1.0)
        beam = create_low_test_beam(self.test_model)
        export_image_to_fits(beam,
                             "%s/test_deconvolve_mmclean_beam.fits" % self.dir)
        self.test_model.data *= beam.data
        export_image_to_fits(
            self.test_model,
            "%s/test_deconvolve_mmclean_model.fits" % self.dir)
        self.vis = predict_2d(self.vis, self.test_model)
        assert numpy.max(numpy.abs(self.vis.vis)) > 0.0
        self.model = create_image_from_visibility(
            self.vis,
            npixel=512,
            cellsize=0.001,
            polarisation_frame=PolarisationFrame('stokesI'))
        self.dirty, sumwt = invert_2d(self.vis, self.model)
        self.psf, sumwt = invert_2d(self.vis, self.model, dopsf=True)
        export_image_to_fits(
            self.dirty, "%s/test_deconvolve_mmclean-dirty.fits" % self.dir)
        export_image_to_fits(self.psf,
                             "%s/test_deconvolve_mmclean-psf.fits" % self.dir)
        window = numpy.ones(shape=self.model.shape, dtype=numpy.bool)
        window[..., 129:384, 129:384] = True
        self.innerquarter = create_image_from_array(
            window,
            self.model.wcs,
            polarisation_frame=PolarisationFrame('stokesI'))
def reproject_image(im: Image, newwcs: WCS, shape=None) -> (Image, Image):
    """ Re-project an image to a new coordinate system
    
    Currently uses the reproject python package. This seems to have some features do be careful using this method.
    For timeslice imaging I had to use griddata.


    :param im: Image to be reprojected
    :param newwcs: New WCS
    :param shape:
    :return: Reprojected Image, Footprint Image
    """
    
    assert isinstance(im, Image), im
    rep, foot = reproject_interp((im.data, im.wcs), newwcs, shape, order='bicubic',
                                 independent_celestial_slices=True)
    return create_image_from_array(rep, newwcs, im.polarisation_frame), create_image_from_array(foot, newwcs,
                                                                                                im.polarisation_frame)
예제 #11
0
def image_voronoi_iter(im: Image, components: Skycomponent) -> collections.Iterable:
    """Iterate through Voronoi decomposition, returning a generator yielding fullsize images

    :param im: Image
    :param components: Components to define Voronoi decomposition
    """
    if len(components) == 1:
        mask = numpy.ones(im.data.shape)
        yield create_image_from_array(mask, wcs=im.wcs,
                                      polarisation_frame=im.polarisation_frame)
    else:
        vor, vertex_array = voronoi_decomposition(im, components)
        
        nregions = numpy.max(vertex_array) + 1
        for region in range(nregions):
            mask = numpy.zeros(im.data.shape)
            mask[(vertex_array == region)[numpy.newaxis, numpy.newaxis,...]] = 1.0
            yield create_image_from_array(mask, wcs=im.wcs,
                                          polarisation_frame=im.polarisation_frame)
예제 #12
0
def create_pswf_convolutionfunction(im, oversampling=8, support=6):
    """ Fill an Anti-Aliasing filter into a ConvolutionFunction

    Fill the Prolate Spheroidal Wave Function into a GriData with the specified oversampling. Only the inner
    non-zero part is retained

    Also returns the griddata correction function as an image

    :param im: Image template
    :param oversampling: Oversampling of the convolution function in uv space
    :return: griddata correction Image, griddata kernel as ConvolutionFunction
    """
    assert isinstance(im, Image)
    # Calculate the convolution kernel. We oversample in u,v space by the factor oversampling
    cf = create_convolutionfunction_from_image(im,
                                               oversampling=oversampling,
                                               support=support)

    kernel = numpy.zeros([oversampling, support])
    for grid in range(support):
        for subsample in range(oversampling):
            nu = ((grid - support // 2) -
                  (subsample - oversampling // 2) / oversampling)
            kernel[subsample, grid] = grdsf([nu / (support // 2)])[1]

    kernel /= numpy.sum(numpy.real(kernel[oversampling // 2, :]))

    nchan, npol, _, _ = im.shape

    cf.data = numpy.zeros(
        [nchan, npol, 1, oversampling, oversampling, support,
         support]).astype('complex')
    for y in range(oversampling):
        for x in range(oversampling):
            cf.data[:, :, 0, y,
                    x, :, :] = numpy.outer(kernel[y, :],
                                           kernel[x, :])[numpy.newaxis,
                                                         numpy.newaxis, ...]
    norm = numpy.sum(numpy.real(cf.data[0, 0, 0, 0, 0, :, :]))
    cf.data /= norm

    # Now calculate the griddata correction function as an image with the same coordinates as the image
    # which is necessary so that the correction function can be applied directly to the image
    nchan, npol, ny, nx = im.data.shape
    nu = numpy.abs(2.0 * coordinates(nx))
    gcf1d = grdsf(nu)[0]
    gcf = numpy.outer(gcf1d, gcf1d)
    gcf[gcf > 0.0] = gcf.max() / gcf[gcf > 0.0]

    gcf_data = numpy.zeros_like(im.data)
    gcf_data[...] = gcf[numpy.newaxis, numpy.newaxis, ...]
    gcf_image = create_image_from_array(gcf_data, cf.projection_wcs,
                                        im.polarisation_frame)

    return gcf_image, cf
def fft_griddata_to_image(griddata, gcf, imaginary=False):
    """ FFT griddata after applying gcf

    :param griddata:
    :param gcf: Grid correction image
    :return:
    """
   
    projected = numpy.sum(griddata.data, axis=2)
    _, _, ny, nx = projected.data.shape
    
    im_data = ifft(projected) * gcf.data * float(nx) * float(ny)
    
    im_real = create_image_from_array(im_data.real, griddata.projection_wcs, griddata.polarisation_frame)
    
    if imaginary:
        im_imag = create_image_from_array(im_data.imag, griddata.projection_wcs, griddata.polarisation_frame)
        return im_real, im_imag
    else:
        return im_real
def calculate_image_frequency_moments(im: Image,
                                      reference_frequency=None,
                                      nmoments=3) -> Image:
    """Calculate frequency weighted moments
    
    Weights are ((freq-reference_frequency)/reference_frequency)**moment
    
    Note that the spectral axis is replaced by a MOMENT axis.
    
    For example, to find the moments and then reconstruct from just the moments::
    
        moment_cube = calculate_image_frequency_moments(model_multichannel, nmoments=5)
        reconstructed_cube = calculate_image_from_frequency_moments(model_multichannel, moment_cube)

    :param im: Image cube
    :param reference_frequency: Reference frequency (default None uses average)
    :param nmoments: Number of moments to calculate
    :return: Moments image
    """
    assert isinstance(im, Image)
    nchan, npol, ny, nx = im.shape
    channels = numpy.arange(nchan)
    with warnings.catch_warnings():
        warnings.simplefilter('ignore', FITSFixedWarning)
        freq = im.wcs.sub(['spectral']).wcs_pix2world(channels, 0)[0]

    assert nmoments <= nchan, "Number of moments %d cannot exceed the number of channels %d" % (
        nmoments, nchan)

    if reference_frequency is None:
        reference_frequency = numpy.average(freq)
    log.debug(
        "calculate_image_frequency_moments: Reference frequency = %.3f (MHz)" %
        (reference_frequency / 1e6))

    moment_data = numpy.zeros([nmoments, npol, ny, nx])

    for moment in range(nmoments):
        for chan in range(nchan):
            weight = numpy.power(
                (freq[chan] - reference_frequency) / reference_frequency,
                moment)
            moment_data[moment, ...] += im.data[chan, ...] * weight

    moment_wcs = copy.deepcopy(im.wcs)
    moment_wcs.wcs.ctype[3] = 'MOMENT'
    moment_wcs.wcs.crval[3] = 0.0
    moment_wcs.wcs.crpix[3] = 1.0
    moment_wcs.wcs.cdelt[3] = 1.0
    moment_wcs.wcs.cunit[3] = ''

    return create_image_from_array(moment_data, moment_wcs,
                                   im.polarisation_frame)
예제 #15
0
 def test_create_image_from_array(self):
     m31model_by_array = create_image_from_array(
         self.m31image.data, self.m31image.wcs,
         self.m31image.polarisation_frame)
     add_image(self.m31image, m31model_by_array)
     add_image(self.m31image, m31model_by_array, docheckwcs=True)
     assert m31model_by_array.shape == self.m31image.shape
     log.debug(
         export_image_to_fits(self.m31image,
                              fitsfile='%s/test_model.fits' % (self.dir)))
     log.debug(qa_image(m31model_by_array,
                        context='test_create_from_image'))
def convert_hdf_to_image(f):
    """ Convert HDF root to an Image

    :param f:
    :return:
    """
    assert f.attrs['ARL_data_model'] == "Image", "Not an Image"
    data = numpy.array(f['data'])
    polarisation_frame = PolarisationFrame(f.attrs['polarisation_frame'])
    wcs = WCS(f.attrs['wcs'])
    im = create_image_from_array(data, wcs=wcs,
                                 polarisation_frame=polarisation_frame)
    return im
def convert_hdf_to_image(f):
    """ Convert HDF root to an Image

    :param f:
    :return:
    """
    if 'ARL_data_model' in f.attrs.keys() and f.attrs['ARL_data_model'] == "Image":
        data = numpy.array(f['data'])
        polarisation_frame = PolarisationFrame(f.attrs['polarisation_frame'])
        wcs = WCS(f.attrs['wcs'])
        im = create_image_from_array(data, wcs=wcs,
                                 polarisation_frame=polarisation_frame)
        return im
    else:
        return None
def fft_griddata_to_image(gd: GridData):
    """ FFT GridData to an Image, transform WCS as well

    :param gd: GridData model
    :return: Image
    """
    assert len(gd.shape) == 5
    assert gd.grid_wcs.wcs.ctype[0] == 'UU'
    assert gd.grid_wcs.wcs.ctype[1] == 'VV'
    wcs = gd.projection_wcs.deepcopy()
    assert gd.shape[2] == 1, "Multiple z planes not supported"
    image_data = fft(gd.data[:, :, 0, ...].astype('complex'))
    return create_image_from_array(image_data,
                                   wcs=wcs,
                                   polarisation_frame=gd.polarisation_frame)
def add_image(im1: Image, im2: Image, docheckwcs=False) -> Image:
    """ Add two images
    
    :param docheckwcs:
    :param im1:
    :param im2:
    :return: Image
    """
    assert isinstance(im1, Image), im1
    assert isinstance(im2, Image), im2
    if docheckwcs:
        checkwcs(im1.wcs, im2.wcs)
    
    assert im1.polarisation_frame == im2.polarisation_frame
    
    return create_image_from_array(im1.data + im2.data, im1.wcs, im1.polarisation_frame)
def create_low_test_beam(model: Image) -> Image:
    """Create a test power beam for LOW using an image from OSKAR

    :param model: Template image
    :return: Image
    """
    
    beam = import_image_from_fits(arl_path('data/models/SKA1_LOW_beam.fits'))
    
    # Scale the image cellsize to account for the different in frequencies. Eventually we will want to
    # use a frequency cube
    log.info("create_low_test_beam: primary beam is defined at %.3f MHz" % (beam.wcs.wcs.crval[2] * 1e-6))
    
    nchan, npol, ny, nx = model.shape
    
    # We need to interpolate each frequency channel separately. The beam is assumed to just scale with
    # frequency.
    
    reprojected_beam = create_empty_image_like(model)
    
    for chan in range(nchan):
        
        model2dwcs = model.wcs.sub(2).deepcopy()
        model2dshape = [model.shape[2], model.shape[3]]
        beam2dwcs = beam.wcs.sub(2).deepcopy()
        
        # The frequency axis is the second to last in the beam
        frequency = model.wcs.sub(['spectral']).wcs_pix2world([chan], 0)[0]
        fscale = beam.wcs.wcs.crval[2] / frequency
        
        beam2dwcs.wcs.cdelt = fscale * beam.wcs.sub(2).wcs.cdelt
        beam2dwcs.wcs.crpix = beam.wcs.sub(2).wcs.crpix
        beam2dwcs.wcs.crval = model.wcs.sub(2).wcs.crval
        beam2dwcs.wcs.ctype = model.wcs.sub(2).wcs.ctype
        model2dwcs.wcs.crpix = [model.shape[2] // 2 + 1, model.shape[3] // 2 + 1]
        
        beam2d = create_image_from_array(beam.data[0, 0, :, :], beam2dwcs, model.polarisation_frame)
        reprojected_beam2d, footprint = reproject_image(beam2d, model2dwcs, shape=model2dshape)
        assert numpy.max(footprint.data) > 0.0, "No overlap between beam and model"
        
        reprojected_beam2d.data *= reprojected_beam2d.data
        reprojected_beam2d.data[footprint.data <= 0.0] = 0.0
        for pol in range(npol):
            reprojected_beam.data[chan, pol, :, :] = reprojected_beam2d.data[:, :]
    
    return reprojected_beam
예제 #21
0
def invert_2d(vis: Visibility, im: Image, dopsf: bool = False, normalize: bool = True, **kwargs) \
        -> (Image, numpy.ndarray):
    """ Invert using 2D convolution function, including w projection optionally

    Use the image im as a template. Do PSF in a separate call.

    This is at the bottom of the layering i.e. all transforms are eventually expressed in terms
    of this function. . Any shifting needed is performed here.

    :param vis: Visibility to be inverted
    :param im: image template (not changed)
    :param dopsf: Make the psf instead of the dirty image
    :param normalize: Normalize by the sum of weights (True)
    :return: resulting image

    """
    if not isinstance(vis, Visibility):
        svis = coalesce_visibility(vis, **kwargs)
    else:
        svis = copy_visibility(vis)

    if dopsf:
        svis.data['vis'] = numpy.ones_like(svis.data['vis'])

    svis = shift_vis_to_image(svis, im, tangent=True, inverse=False)

    nchan, npol, ny, nx = im.data.shape

    padding = {}
    if get_parameter(kwargs, "padding", False):
        padding = {'padding': get_parameter(kwargs, "padding", False)}
    spectral_mode, vfrequencymap = get_frequency_map(svis, im)
    polarisation_mode, vpolarisationmap = get_polarisation_map(svis, im)
    uvw_mode, shape, padding, vuvwmap = get_uvw_map(svis, im, **padding)
    kernel_name, gcf, vkernellist = get_kernel_list(svis, im, **kwargs)

    # Optionally pad to control aliasing
    imgridpad = numpy.zeros(
        [nchan, npol,
         int(round(padding * ny)),
         int(round(padding * nx))],
        dtype='complex')
    imgridpad, sumwt = convolutional_grid(vkernellist, imgridpad,
                                          svis.data['vis'],
                                          svis.data['imaging_weight'], vuvwmap,
                                          vfrequencymap)

    # Fourier transform the padded grid to image, multiply by the gridding correction
    # function, and extract the unpadded inner part.

    # Normalise weights for consistency with transform
    sumwt /= float(padding * int(round(padding * nx)) * ny)

    imaginary = get_parameter(kwargs, "imaginary", False)
    if imaginary:
        log.debug("invert_2d: retaining imaginary part of dirty image")
        result = extract_mid(ifft(imgridpad) * gcf, npixel=nx)
        resultreal = create_image_from_array(result.real, im.wcs,
                                             im.polarisation_frame)
        resultimag = create_image_from_array(result.imag, im.wcs,
                                             im.polarisation_frame)
        if normalize:
            resultreal = normalize_sumwt(resultreal, sumwt)
            resultimag = normalize_sumwt(resultimag, sumwt)
        return resultreal, sumwt, resultimag
    else:
        result = extract_mid(numpy.real(ifft(imgridpad)) * gcf, npixel=nx)
        resultimage = create_image_from_array(result, im.wcs,
                                              im.polarisation_frame)
        if normalize:
            resultimage = normalize_sumwt(resultimage, sumwt)
        return resultimage, sumwt
def image_raster_iter(im: Image, facets=1, overlap=0, taper='flat', make_flat=False) -> collections.Iterable:
    """Create an image_raster_iter generator, returning images, optionally with overlaps

    The WCS is adjusted appropriately for each raster element. Hence this is a coordinate-aware
    way to iterate through an image.

    Provided we don't break reference semantics, memory should be conserved. However make_flat
    creates a new set of images and thus reference semantics dont hold.

    To update the image in place:
        for r in raster(im, facets=2)::
            r.data[...] = numpy.sqrt(r.data[...])
            
    If the overlap is greater than zero, we choose to keep all images the same size so the
    other ring of facets are ignored. So if facets=4 and overlap > 0 then the iterator returns
    (facets-2)**2 = 4 images.
    
    A taper is applied in the overlap regions. None implies a constant value, linear is a ramp, and
    quadratic is parabolic at the ends.

    :param im: Image
    :param facets: Number of image partitions on each axis (2)
    :param overlap: overlap in pixels
    :param taper: method of tapering at the edges: 'flat' or 'linear' or 'quadratic' or 'tukey'
    :param make_flat: Make the flat images
    """
    nchan, npol, ny, nx = im.shape
    assert facets <= ny, "Cannot have more raster elements than pixels"
    assert facets <= nx, "Cannot have more raster elements than pixels"
    
    assert facets >=1, "Facets cannot be zero or less"
    assert overlap >= 0, "Overlap must be zero or greater"
    
    if facets == 1:
        yield im
    else:
        
        assert overlap < (nx // facets), "Overlap in facets is too large"
        assert overlap < (ny // facets), "Overlap in facets is too large"

        # Step between facets
        sx = nx // facets + overlap
        sy = ny // facets + overlap
    
        # Size of facet
        dx = sx + overlap
        dy = sy + overlap

        # Step between facets
        sx = nx // facets + overlap
        sy = ny // facets + overlap

        # Size of facet
        dx = nx // facets + 2 * overlap
        dy = nx // facets + 2 * overlap

        def taper_linear():
            t = numpy.ones(dx)
            ramp = numpy.arange(0, overlap).astype(float) / float(overlap)
            
            t[:overlap] = ramp
            t[(dx - overlap):dx] = 1.0 - ramp
            result = numpy.outer(t, t)
            
            return result

        def taper_quadratic():
            t = numpy.ones(dx)
            ramp = numpy.arange(0, overlap).astype(float) / float(overlap)
            
            quadratic_ramp = numpy.ones(overlap)
            quadratic_ramp[0:overlap // 2] = 2.0 * ramp[0:overlap // 2] ** 2
            quadratic_ramp[overlap // 2:] = 1 - 2.0 * ramp[overlap // 2:0:-1] ** 2
            
            t[:overlap] = quadratic_ramp
            t[(dx - overlap):dx] = 1.0 - quadratic_ramp
            
            result = numpy.outer(t, t)
            return result

        def taper_tukey():

            xs = numpy.arange(dx) / float(dx)
            r = 2 * overlap / dx
            t = [tukey_filter(x, r) for x in xs]
    
            result = numpy.outer(t, t)
            return result

        i = 0
        for fy in range(facets):
            y = ny // 2 + sy * (fy - facets // 2) - overlap // 2
            for fx in range(facets):
                x = nx // 2 + sx * (fx - facets // 2) - overlap // 2
                if (x >= 0) and (x + dx) <= nx and (y >= 0) and (y + dy) <= ny:
                    # Adjust WCS
                    wcs = im.wcs.deepcopy()
                    wcs.wcs.crpix[0] -= x
                    wcs.wcs.crpix[1] -= y
                    # yield image from slice (reference!)
                    subim = create_image_from_array(im.data[..., y:y + dy, x:x + dx], wcs, im.polarisation_frame)
                    if overlap > 0 and make_flat:
                        flat = create_empty_image_like(subim)
                        if taper == 'linear':
                            flat.data[..., :, :] = taper_linear()
                        elif taper == 'quadratic':
                            flat.data[..., :, :] = taper_quadratic()
                        elif taper == 'tukey':
                            flat.data[..., :, :] = taper_tukey()
                        else:
                            flat.data[...] = 1.0
                        yield flat
                    else:
                        yield subim
                    i += 1
def create_image_from_visibility(vis: Union[BlockVisibility, Visibility], **kwargs) -> Image:
    """Make an empty image from params and Visibility
    
    This makes an empty, template image consistent with the visibility, allowing optional overriding of select
    parameters. This is a convenience function and does not transform the visibilities.

    :param vis:
    :param phasecentre: Phasecentre (Skycoord)
    :param channel_bandwidth: Channel width (Hz)
    :param cellsize: Cellsize (radians)
    :param npixel: Number of pixels on each axis (512)
    :param frame: Coordinate frame for WCS (ICRS)
    :param equinox: Equinox for WCS (2000.0)
    :param nchan: Number of image channels (Default is 1 -> MFS)
    :return: image
    """
    assert isinstance(vis, Visibility) or isinstance(vis, BlockVisibility), \
        "vis is not a Visibility or a BlockVisibility: %r" % (vis)
    
    log.debug("create_image_from_visibility: Parsing parameters to get definition of WCS")
    
    imagecentre = get_parameter(kwargs, "imagecentre", vis.phasecentre)
    phasecentre = get_parameter(kwargs, "phasecentre", vis.phasecentre)
    
    # Spectral processing options
    ufrequency = numpy.unique(vis.frequency)
    vnchan = len(ufrequency)
    
    frequency = get_parameter(kwargs, "frequency", vis.frequency)
    inchan = get_parameter(kwargs, "nchan", vnchan)
    reffrequency = frequency[0] * units.Hz
    channel_bandwidth = get_parameter(kwargs, "channel_bandwidth", 0.99999999999 * vis.channel_bandwidth[0]) * units.Hz
    
    if (inchan == vnchan) and vnchan > 1:
        log.debug(
            "create_image_from_visibility: Defining %d channel Image at %s, starting frequency %s, and bandwidth %s"
            % (inchan, imagecentre, reffrequency, channel_bandwidth))
    elif (inchan == 1) and vnchan > 1:
        assert numpy.abs(channel_bandwidth.value) > 0.0, "Channel width must be non-zero for mfs mode"
        log.debug("create_image_from_visibility: Defining single channel MFS Image at %s, starting frequency %s, "
                 "and bandwidth %s"
                 % (imagecentre, reffrequency, channel_bandwidth))
    elif inchan > 1 and vnchan > 1:
        assert numpy.abs(channel_bandwidth.value) > 0.0, "Channel width must be non-zero for mfs mode"
        log.debug("create_image_from_visibility: Defining multi-channel MFS Image at %s, starting frequency %s, "
                 "and bandwidth %s"
                 % (imagecentre, reffrequency, channel_bandwidth))
    elif (inchan == 1) and (vnchan == 1):
        assert numpy.abs(channel_bandwidth.value) > 0.0, "Channel width must be non-zero for mfs mode"
        log.debug("create_image_from_visibility: Defining single channel Image at %s, starting frequency %s, "
                 "and bandwidth %s"
                 % (imagecentre, reffrequency, channel_bandwidth))
    else:
        raise ValueError("create_image_from_visibility: unknown spectral mode ")
    
    # Image sampling options
    npixel = get_parameter(kwargs, "npixel", 512)
    uvmax = numpy.max((numpy.abs(vis.data['uvw'][..., 0:1])))
    if isinstance(vis, BlockVisibility):
        uvmax *= numpy.max(frequency) / constants.c.to('m s^-1').value
    log.debug("create_image_from_visibility: uvmax = %f wavelengths" % uvmax)
    criticalcellsize = 1.0 / (uvmax * 2.0)
    log.debug("create_image_from_visibility: Critical cellsize = %f radians, %f degrees" % (
        criticalcellsize, criticalcellsize * 180.0 / numpy.pi))
    cellsize = get_parameter(kwargs, "cellsize", 0.5 * criticalcellsize)
    log.debug("create_image_from_visibility: Cellsize          = %g radians, %g degrees" % (cellsize,
                                                                                           cellsize * 180.0 / numpy.pi))
    override_cellsize = get_parameter(kwargs, "override_cellsize", True)
    if override_cellsize and cellsize > criticalcellsize:
        log.debug("create_image_from_visibility: Resetting cellsize %g radians to criticalcellsize %g radians" % (
            cellsize, criticalcellsize))
        cellsize = criticalcellsize
    pol_frame = get_parameter(kwargs, "polarisation_frame", PolarisationFrame("stokesI"))
    inpol = pol_frame.npol
    
    # Now we can define the WCS, which is a convenient place to hold the info above
    # Beware of python indexing order! wcs and the array have opposite ordering
    shape = [inchan, inpol, npixel, npixel]
    log.debug("create_image_from_visibility: image shape is %s" % str(shape))
    w = wcs.WCS(naxis=4)
    # The negation in the longitude is needed by definition of RA, DEC
    w.wcs.cdelt = [-cellsize * 180.0 / numpy.pi, cellsize * 180.0 / numpy.pi, 1.0, channel_bandwidth.to(units.Hz).value]
    # The numpy definition of the phase centre of an FFT is n // 2 (0 - rel) so that's what we use for
    # the reference pixel. We have to use 0 rel everywhere.
    w.wcs.crpix = [npixel // 2 + 1, npixel // 2 + 1, 1.0, 1.0]
    w.wcs.ctype = ["RA---SIN", "DEC--SIN", 'STOKES', 'FREQ']
    w.wcs.crval = [phasecentre.ra.deg, phasecentre.dec.deg, 1.0, reffrequency.to(units.Hz).value]
    w.naxis = 4
    
    w.wcs.radesys = get_parameter(kwargs, 'frame', 'ICRS')
    w.wcs.equinox = get_parameter(kwargs, 'equinox', 2000.0)
    
    return create_image_from_array(numpy.zeros(shape), wcs=w, polarisation_frame=pol_frame)
def create_low_test_image_from_gleam(npixel=512, polarisation_frame=PolarisationFrame("stokesI"), cellsize=0.000015,
                                     frequency=numpy.array([1e8]), channel_bandwidth=numpy.array([1e6]),
                                     phasecentre=None, kind='cubic', applybeam=False, flux_limit=0.1,
                                     radius=None, insert_method='Nearest') -> Image:
    """Create LOW test image from the GLEAM survey

    Stokes I is estimated from a cubic spline fit to the measured fluxes. The polarised flux is always zero.
    
    See http://www.mwatelescope.org/science/gleam-survey The catalog is available from Vizier.
    
    VIII/100   GaLactic and Extragalactic All-sky MWA survey  (Hurley-Walker+, 2016)

    GaLactic and Extragalactic All-sky Murchison Wide Field Array (GLEAM) survey. I: A low-frequency extragalactic
    catalogue. Hurley-Walker N., et al., Mon. Not. R. Astron. Soc., 464, 1146-1167 (2017), 2017MNRAS.464.1146H

    :param npixel: Number of pixels
    :param polarisation_frame: Polarisation frame (default PolarisationFrame("stokesI"))
    :param cellsize: cellsize in radians
    :param frequency:
    :param channel_bandwidth: Channel width (Hz)
    :param phasecentre: phasecentre (SkyCoord)
    :param kind: Kind of interpolation (see scipy.interpolate.interp1d) Default: linear
    :return: Image
    
    """
    
    if phasecentre is None:
        phasecentre = SkyCoord(ra=+15.0 * u.deg, dec=-35.0 * u.deg, frame='icrs', equinox='J2000')
    
    if radius is None:
        radius = npixel * cellsize / numpy.sqrt(2.0)
    
    sc = create_low_test_skycomponents_from_gleam(flux_limit=flux_limit, polarisation_frame=polarisation_frame,
                                                  frequency=frequency, phasecentre=phasecentre,
                                                  kind=kind, radius=radius)
    if polarisation_frame is None:
        polarisation_frame = PolarisationFrame("stokesI")
    
    npol = polarisation_frame.npol
    nchan = len(frequency)
    shape = [nchan, npol, npixel, npixel]
    w = WCS(naxis=4)
    # The negation in the longitude is needed by definition of RA, DEC
    w.wcs.cdelt = [-cellsize * 180.0 / numpy.pi, cellsize * 180.0 / numpy.pi, 1.0, channel_bandwidth[0]]
    w.wcs.crpix = [npixel // 2 + 1, npixel // 2 + 1, 1.0, 1.0]
    w.wcs.ctype = ["RA---SIN", "DEC--SIN", 'STOKES', 'FREQ']
    w.wcs.crval = [phasecentre.ra.deg, phasecentre.dec.deg, 1.0, frequency[0]]
    w.naxis = 4
    w.wcs.radesys = 'ICRS'
    w.wcs.equinox = 2000.0
    
    model = create_image_from_array(numpy.zeros(shape), w, polarisation_frame=polarisation_frame)
    
    model = insert_skycomponent(model, sc, insert_method=insert_method)
    if applybeam:
        beam = create_low_test_beam(model)
        model.data[...] *= beam.data[...]
    
    log.info(qa_image(model, context='create_low_test_image_from_gleam'))
    
    return model
예제 #25
0
def create_low_test_skymodel_from_gleam(npixel=512,
                                        polarisation_frame=PolarisationFrame(
                                            "stokesI"),
                                        cellsize=0.000015,
                                        frequency=numpy.array([1e8]),
                                        channel_bandwidth=numpy.array([1e6]),
                                        phasecentre=None,
                                        kind='cubic',
                                        applybeam=True,
                                        flux_limit=0.1,
                                        flux_max=numpy.inf,
                                        flux_threshold=1.0,
                                        insert_method='Nearest',
                                        telescope='LOW') -> SkyModel:
    """Create LOW test skymodel from the GLEAM survey

    Stokes I is estimated from a cubic spline fit to the measured fluxes. The polarised flux is always zero.

    See http://www.mwatelescope.org/science/gleam-survey The catalog is available from Vizier.

    VIII/100   GaLactic and Extragalactic All-sky MWA survey  (Hurley-Walker+, 2016)

    GaLactic and Extragalactic All-sky Murchison Wide Field Array (GLEAM) survey. I: A low-frequency extragalactic
    catalogue. Hurley-Walker N., et al., Mon. Not. R. Astron. Soc., 464, 1146-1167 (2017), 2017MNRAS.464.1146H

    :param telescope:
    :param npixel: Number of pixels
    :param polarisation_frame: Polarisation frame (default PolarisationFrame("stokesI"))
    :param cellsize: cellsize in radians
    :param frequency:
    :param channel_bandwidth: Channel width (Hz)
    :param phasecentre: phasecentre (SkyCoord)
    :param kind: Kind of interpolation (see scipy.interpolate.interp1d) Default: cubic
    :param applybeam: Apply the primary beam?
    :param flux_limit: Weakest component
    :param flux_max: Maximum strength component to be included in components
    :param flux_threshold: Split between components (brighter) and image (weaker)
    :param insert_method: Nearest | PSWF | Lanczos
    :return:
    :return: SkyModel

    """

    if phasecentre is None:
        phasecentre = SkyCoord(ra=+15.0 * u.deg,
                               dec=-35.0 * u.deg,
                               frame='icrs',
                               equinox='J2000')

    radius = npixel * cellsize

    sc = create_low_test_skycomponents_from_gleam(
        flux_limit=flux_limit,
        polarisation_frame=polarisation_frame,
        frequency=frequency,
        phasecentre=phasecentre,
        kind=kind,
        radius=radius)

    sc = filter_skycomponents_by_flux(sc, flux_max=flux_max)
    if polarisation_frame is None:
        polarisation_frame = PolarisationFrame("stokesI")

    npol = polarisation_frame.npol
    nchan = len(frequency)
    shape = [nchan, npol, npixel, npixel]
    w = WCS(naxis=4)
    # The negation in the longitude is needed by definition of RA, DEC
    w.wcs.cdelt = [
        -cellsize * 180.0 / numpy.pi, cellsize * 180.0 / numpy.pi, 1.0,
        channel_bandwidth[0]
    ]
    w.wcs.crpix = [npixel // 2 + 1, npixel // 2 + 1, 1.0, 1.0]
    w.wcs.ctype = ["RA---SIN", "DEC--SIN", 'STOKES', 'FREQ']
    w.wcs.crval = [phasecentre.ra.deg, phasecentre.dec.deg, 1.0, frequency[0]]
    w.naxis = 4
    w.wcs.radesys = 'ICRS'
    w.wcs.equinox = 2000.0

    model = create_image_from_array(numpy.zeros(shape),
                                    w,
                                    polarisation_frame=polarisation_frame)

    if applybeam:
        beam = create_pb(model, telescope=telescope)
        sc = apply_beam_to_skycomponent(sc, beam)

    weaksc = filter_skycomponents_by_flux(sc, flux_max=flux_threshold)
    brightsc = filter_skycomponents_by_flux(sc, flux_min=flux_threshold)
    model = insert_skycomponent(model, weaksc, insert_method=insert_method)

    log.info(
        'create_low_test_skymodel_from_gleam: %d bright sources above flux threshold %.3f, %d weak sources below '
        % (len(brightsc), flux_threshold, len(weaksc)))

    return SkyModel(components=brightsc,
                    image=model,
                    mask=None,
                    gaintable=None)
def deconvolve_cube(dirty: Image,
                    psf: Image,
                    prefix='',
                    **kwargs) -> (Image, Image):
    """ Clean using a variety of algorithms
    
    Functions that clean a dirty image using a point spread function. The algorithms available are:
    
    hogbom: Hogbom CLEAN See: Hogbom CLEAN A&A Suppl, 15, 417, (1974)
    
    msclean: MultiScale CLEAN See: Cornwell, T.J., Multiscale CLEAN (IEEE Journal of Selected Topics in Sig Proc,
    2008 vol. 2 pp. 793-801)

    mfsmsclean, msmfsclean, mmclean: MultiScale Multi-Frequency See: U. Rau and T. J. Cornwell,
    “A multi-scale multi-frequency deconvolution algorithm for synthesis imaging in radio interferometry,” A&A 532,
    A71 (2011).
    
    For example::
    
        comp, residual = deconvolve_cube(dirty, psf, niter=1000, gain=0.7, algorithm='msclean',
                                         scales=[0, 3, 10, 30], threshold=0.01)
                                         
    For the MFS clean, the psf must have number of channels >= 2 * nmoment
    
    :param dirty: Image dirty image
    :param psf: Image Point Spread Function
    :param window_shape: Window image (Bool) - clean where True
    :param mask: Window in the form of an image, overrides woindow_shape
    :param algorithm: Cleaning algorithm: 'msclean'|'hogbom'|'mfsmsclean'
    :param gain: loop gain (float) 0.7
    :param threshold: Clean threshold (0.0)
    :param fractional_threshold: Fractional threshold (0.01)
    :param scales: Scales (in pixels) for multiscale ([0, 3, 10, 30])
    :param nmoment: Number of frequency moments (default 3)
    :param findpeak: Method of finding peak in mfsclean: 'Algorithm1'|'ASKAPSoft'|'CASA'|'ARL', Default is ARL.
    :return: componentimage, residual
    
    """

    assert isinstance(dirty, Image), dirty
    assert isinstance(psf, Image), psf

    window_shape = get_parameter(kwargs, 'window_shape', None)
    if window_shape == 'quarter':
        log.info("deconvolve_cube %s: window is inner quarter" % prefix)
        qx = dirty.shape[3] // 4
        qy = dirty.shape[2] // 4
        window = numpy.zeros_like(dirty.data)
        window[..., (qy + 1):3 * qy, (qx + 1):3 * qx] = 1.0
        log.info(
            'deconvolve_cube %s: Cleaning inner quarter of each sky plane' %
            prefix)
    elif window_shape == 'no_edge':
        edge = get_parameter(kwargs, 'window_edge', 16)
        nx = dirty.shape[3]
        ny = dirty.shape[2]
        window = numpy.zeros_like(dirty.data)
        window[..., (edge + 1):(ny - edge), (edge + 1):(nx - edge)] = 1.0
        log.info(
            'deconvolve_cube %s: Window omits %d-pixel edge of each sky plane'
            % (prefix, edge))
    elif window_shape is None:
        log.info("deconvolve_cube %s: Cleaning entire image" % prefix)
        window = None
    else:
        raise ValueError("Window shape %s is not recognized" % window_shape)

    mask = get_parameter(kwargs, 'mask', None)
    if isinstance(mask, Image):
        if window is not None:
            log.warning(
                'deconvolve_cube %s: Overriding window_shape with mask image' %
                (prefix))
        window = mask.data

    psf_support = get_parameter(kwargs, 'psf_support',
                                max(dirty.shape[2] // 2, dirty.shape[3] // 2))
    if (psf_support <= psf.shape[2] // 2) and (
        (psf_support <= psf.shape[3] // 2)):
        centre = [psf.shape[2] // 2, psf.shape[3] // 2]
        psf.data = psf.data[..., (centre[0] - psf_support):(centre[0] +
                                                            psf_support),
                            (centre[1] - psf_support):(centre[1] +
                                                       psf_support)]
        log.info('deconvolve_cube %s: PSF support = +/- %d pixels' %
                 (prefix, psf_support))
        log.info('deconvolve_cube %s: PSF shape %s' %
                 (prefix, str(psf.data.shape)))

    algorithm = get_parameter(kwargs, 'algorithm', 'msclean')

    if algorithm == 'msclean':
        log.info(
            "deconvolve_cube %s: Multi-scale clean of each polarisation and channel separately"
            % prefix)
        gain = get_parameter(kwargs, 'gain', 0.7)
        assert 0.0 < gain < 2.0, "Loop gain must be between 0 and 2"
        thresh = get_parameter(kwargs, 'threshold', 0.0)
        assert thresh >= 0.0
        niter = get_parameter(kwargs, 'niter', 100)
        assert niter > 0
        scales = get_parameter(kwargs, 'scales', [0, 3, 10, 30])
        fracthresh = get_parameter(kwargs, 'fractional_threshold', 0.01)
        assert 0.0 < fracthresh < 1.0

        comp_array = numpy.zeros_like(dirty.data)
        residual_array = numpy.zeros_like(dirty.data)
        for channel in range(dirty.data.shape[0]):
            for pol in range(dirty.data.shape[1]):
                if psf.data[channel, pol, :, :].max():
                    log.info(
                        "deconvolve_cube %s: Processing pol %d, channel %d" %
                        (prefix, pol, channel))
                    if window is None:
                        comp_array[channel, pol, :, :], residual_array[channel, pol, :, :] = \
                            msclean(dirty.data[channel, pol, :, :], psf.data[channel, pol, :, :],
                                    None, gain, thresh, niter, scales, fracthresh, prefix)
                    else:
                        comp_array[channel, pol, :, :], residual_array[channel, pol, :, :] = \
                            msclean(dirty.data[channel, pol, :, :], psf.data[channel, pol, :, :],
                                    window[channel, pol, :, :], gain, thresh, niter, scales, fracthresh,
                                    prefix)
                else:
                    log.info(
                        "deconvolve_cube %s: Skipping pol %d, channel %d" %
                        (prefix, pol, channel))

        comp_image = create_image_from_array(comp_array, dirty.wcs,
                                             dirty.polarisation_frame)
        residual_image = create_image_from_array(residual_array, dirty.wcs,
                                                 dirty.polarisation_frame)

    elif algorithm == 'msmfsclean' or algorithm == 'mfsmsclean' or algorithm == 'mmclean':
        findpeak = get_parameter(kwargs, "findpeak", 'ARL')

        log.info(
            "deconvolve_cube %s: Multi-scale multi-frequency clean of each polarisation separately"
            % prefix)
        nmoment = get_parameter(kwargs, "nmoment", 3)
        assert nmoment >= 1, "Number of frequency moments must be greater than or equal to one"
        nchan = dirty.shape[0]
        assert nchan > 2 * (nmoment -
                            1), "Require nchan %d > 2 * (nmoment %d - 1)" % (
                                nchan, 2 * (nmoment - 1))
        dirty_taylor = calculate_image_frequency_moments(dirty,
                                                         nmoment=nmoment)
        if nmoment > 1:
            psf_taylor = calculate_image_frequency_moments(psf,
                                                           nmoment=2 * nmoment)
        else:
            psf_taylor = calculate_image_frequency_moments(psf, nmoment=1)
        psf_peak = numpy.max(psf_taylor.data)
        dirty_taylor.data /= psf_peak
        psf_taylor.data /= psf_peak
        log.info("deconvolve_cube %s: Shape of Dirty moments image %s" %
                 (prefix, str(dirty_taylor.shape)))
        log.info("deconvolve_cube %s: Shape of PSF moments image %s" %
                 (prefix, str(psf_taylor.shape)))
        gain = get_parameter(kwargs, 'gain', 0.7)
        assert 0.0 < gain < 2.0, "Loop gain must be between 0 and 2"
        thresh = get_parameter(kwargs, 'threshold', 0.0)
        assert thresh >= 0.0
        niter = get_parameter(kwargs, 'niter', 100)
        assert niter > 0
        scales = get_parameter(kwargs, 'scales', [0, 3, 10, 30])
        fracthresh = get_parameter(kwargs, 'fractional_threshold', 0.1)
        assert 0.0 < fracthresh < 1.0

        comp_array = numpy.zeros(dirty_taylor.data.shape)
        residual_array = numpy.zeros(dirty_taylor.data.shape)
        for pol in range(dirty_taylor.data.shape[1]):
            if psf_taylor.data[0, pol, :, :].max():
                log.info("deconvolve_cube %s: Processing pol %d" %
                         (prefix, pol))
                if window is None:
                    comp_array[:, pol, :, :], residual_array[:, pol, :, :] = \
                        msmfsclean(dirty_taylor.data[:, pol, :, :], psf_taylor.data[:, pol, :, :],
                                   None, gain, thresh, niter, scales, fracthresh, findpeak, prefix)
                else:
                    log.info(
                        'deconvolve_cube %s: Clean window has %d valid pixels'
                        % (prefix, int(numpy.sum(window[0, pol]))))
                    comp_array[:, pol, :, :], residual_array[:, pol, :, :] = \
                        msmfsclean(dirty_taylor.data[:, pol, :, :], psf_taylor.data[:, pol, :, :],
                                   window[0, pol, :, :], gain, thresh, niter, scales, fracthresh,
                                   findpeak, prefix)
            else:
                log.info("deconvolve_cube %s: Skipping pol %d" % (prefix, pol))

        comp_image = create_image_from_array(comp_array, dirty_taylor.wcs,
                                             dirty.polarisation_frame)
        residual_image = create_image_from_array(residual_array,
                                                 dirty_taylor.wcs,
                                                 dirty.polarisation_frame)

        return_moments = get_parameter(kwargs, "return_moments", False)
        if not return_moments:
            log.info("deconvolve_cube %s: calculating spectral cubes" % prefix)
            comp_image = calculate_image_from_frequency_moments(
                dirty, comp_image)
            residual_image = calculate_image_from_frequency_moments(
                dirty, residual_image)
        else:
            log.info("deconvolve_cube %s: constructed moment cubes" % prefix)

    elif algorithm == 'hogbom':
        log.info(
            "deconvolve_cube %s: Hogbom clean of each polarisation and channel separately"
            % prefix)
        gain = get_parameter(kwargs, 'gain', 0.7)
        assert 0.0 < gain < 2.0, "Loop gain must be between 0 and 2"
        thresh = get_parameter(kwargs, 'threshold', 0.0)
        assert thresh >= 0.0
        niter = get_parameter(kwargs, 'niter', 100)
        assert niter > 0
        fracthresh = get_parameter(kwargs, 'fractional_threshold', 0.1)
        assert 0.0 < fracthresh < 1.0

        comp_array = numpy.zeros(dirty.data.shape)
        residual_array = numpy.zeros(dirty.data.shape)
        for channel in range(dirty.data.shape[0]):
            for pol in range(dirty.data.shape[1]):
                if psf.data[channel, pol, :, :].max():
                    log.info(
                        "deconvolve_cube %s: Processing pol %d, channel %d" %
                        (prefix, pol, channel))
                    if window is None:
                        comp_array[channel, pol, :, :], residual_array[channel, pol, :, :] = \
                            hogbom(dirty.data[channel, pol, :, :], psf.data[channel, pol, :, :],
                                   None, gain, thresh, niter, fracthresh, prefix)
                    else:
                        comp_array[channel, pol, :, :], residual_array[channel, pol, :, :] = \
                            hogbom(dirty.data[channel, pol, :, :], psf.data[channel, pol, :, :],
                                   window[channel, pol, :, :], gain, thresh, niter, fracthresh, prefix)
                else:
                    log.info(
                        "deconvolve_cube %s: Skipping pol %d, channel %d" %
                        (prefix, pol, channel))

        comp_image = create_image_from_array(comp_array, dirty.wcs,
                                             dirty.polarisation_frame)
        residual_image = create_image_from_array(residual_array, dirty.wcs,
                                                 dirty.polarisation_frame)
    elif algorithm == 'hogbom-complex':
        log.info(
            "deconvolve_cube_complex: Hogbom-complex clean of each polarisation and channel separately"
        )
        gain = get_parameter(kwargs, 'gain', 0.7)
        assert 0.0 < gain < 2.0, "Loop gain must be between 0 and 2"
        thresh = get_parameter(kwargs, 'threshold', 0.0)
        assert thresh >= 0.0
        niter = get_parameter(kwargs, 'niter', 100)
        assert niter > 0
        fracthresh = get_parameter(kwargs, 'fractional_threshold', 0.1)
        assert 0.0 <= fracthresh < 1.0

        comp_array = numpy.zeros(dirty.data.shape)
        residual_array = numpy.zeros(dirty.data.shape)
        for channel in range(dirty.data.shape[0]):
            for pol in range(dirty.data.shape[1]):
                if pol == 0 or pol == 3:
                    if psf.data[channel, pol, :, :].max():
                        log.info(
                            "deconvolve_cube_complex: Processing pol %d, channel %d"
                            % (pol, channel))
                        if window is None:
                            comp_array[channel, pol, :, :], residual_array[channel, pol, :, :] = \
                                hogbom(dirty.data[channel, pol, :, :], psf.data[channel, pol, :, :],
                                       None, gain, thresh, niter, fracthresh)
                        else:
                            comp_array[channel, pol, :, :], residual_array[channel, pol, :, :] = \
                                hogbom(dirty.data[channel, pol, :, :], psf.data[channel, pol, :, :],
                                       window[channel, pol, :, :], gain, thresh, niter, fracthresh)
                    else:
                        log.info(
                            "deconvolve_cube_complex: Skipping pol %d, channel %d"
                            % (pol, channel))
                if pol == 1:
                    if psf.data[channel, 1:2, :, :].max():
                        log.info(
                            "deconvolve_cube_complex: Processing pol 1 and 2, channel %d"
                            % (channel))
                        if window is None:
                            comp_array[channel, 1, :, :], comp_array[
                                channel, 2, :, :], residual_array[
                                    channel, 1, :, :], residual_array[
                                        channel, 2, :, :] = hogbom_complex(
                                            dirty.data[channel, 1, :, :],
                                            dirty.data[channel, 2, :, :],
                                            psf.data[channel, 1, :, :],
                                            psf.data[channel, 2, :, :], None,
                                            gain, thresh, niter, fracthresh)
                        else:
                            comp_array[channel, 1, :, :], comp_array[
                                channel, 2, :, :], residual_array[
                                    channel, 1, :, :], residual_array[
                                        channel, 2, :, :] = hogbom_complex(
                                            dirty.data[channel, 1, :, :],
                                            dirty.data[channel, 2, :, :],
                                            psf.data[channel, 1, :, :],
                                            psf.data[channel, 2, :, :],
                                            window[channel, pol, :, :], gain,
                                            thresh, niter, fracthresh)
                    else:
                        log.info(
                            "deconvolve_cube_complex: Skipping pol 1 and 2, channel %d"
                            % (channel))
                if pol == 2:
                    continue

        comp_image = create_image_from_array(
            comp_array,
            dirty.wcs,
            polarisation_frame=PolarisationFrame('stokesIQUV'))
        residual_image = create_image_from_array(
            residual_array,
            dirty.wcs,
            polarisation_frame=PolarisationFrame('stokesIQUV'))

    else:
        raise ValueError('deconvolve_cube %s: Unknown algorithm %s' %
                         (prefix, algorithm))

    return comp_image, residual_image
def create_test_image_from_s3(npixel=16384, polarisation_frame=PolarisationFrame("stokesI"), cellsize=0.000015,
                              frequency=numpy.array([1e8]), channel_bandwidth=numpy.array([1e6]),
                              phasecentre=None, fov=20, flux_limit=1e-3) -> Image:
    """Create LOW test image from S3

    The input catalog was generated at http://s-cubed.physics.ox.ac.uk/s3_sex using the following query::
        Database: s3_sex
        SQL: select * from Galaxies where (pow(10,itot_151)*1000 > 1.0) and (right_ascension between -5 and 5) and (declination between -5 and 5);;

    Number of rows returned: 29966

    For frequencies < 610MHz, there are three tables to use::

        data/models/S3_151MHz_10deg.csv, use fov=10
        data/models/S3_151MHz_20deg.csv, use fov=20
        data/models/S3_151MHz_40deg.csv, use fov=40
        
    For frequencies > 610MHz, there are three tables:
    
        data/models/S3_1400MHz_1mJy_10deg.csv, use flux_limit>= 1e-3
        data/models/S3_1400MHz_100uJy_10deg.csv, use flux_limit < 1e-3
        data/models/S3_1400MHz_1mJy_18deg.csv, use flux_limit>= 1e-3
        data/models/S3_1400MHz_100uJy_18deg.csv, use flux_limit < 1e-3

    The component spectral index is calculated from the 610MHz and 151MHz or 1400MHz and 610MHz, and then calculated
    for the specified frequencies.

    If polarisation_frame is not stokesI then the image will a polarised axis but the values will be zero.

    :param npixel: Number of pixels
    :param polarisation_frame: Polarisation frame (default PolarisationFrame("stokesI"))
    :param cellsize: cellsize in radians
    :param frequency:
    :param channel_bandwidth: Channel width (Hz)
    :param phasecentre: phasecentre (SkyCoord)
    :param fov: fov 10 | 20 | 40
    :param flux_limit: Minimum flux (Jy)
    :return: Image
    """
    
    ras = []
    decs = []
    fluxes = []
    
    if phasecentre is None:
        phasecentre = SkyCoord(ra=+180.0 * u.deg, dec=-60.0 * u.deg, frame='icrs', equinox='J2000')
    
    if polarisation_frame is None:
        polarisation_frame = PolarisationFrame("stokesI")
    
    npol = polarisation_frame.npol
    
    nchan = len(frequency)
    
    shape = [nchan, npol, npixel, npixel]
    w = WCS(naxis=4)
    # The negation in the longitude is needed by definition of RA, DEC
    w.wcs.cdelt = [-cellsize * 180.0 / numpy.pi, cellsize * 180.0 / numpy.pi, 1.0, channel_bandwidth[0]]
    w.wcs.crpix = [npixel // 2 + 1, npixel // 2 + 1, 1.0, 1.0]
    w.wcs.ctype = ["RA---SIN", "DEC--SIN", 'STOKES', 'FREQ']
    w.wcs.crval = [phasecentre.ra.deg, phasecentre.dec.deg, 1.0, frequency[0]]
    w.naxis = 4
    
    w.wcs.radesys = 'ICRS'
    w.wcs.equinox = 2000.0
    
    model = create_image_from_array(numpy.zeros(shape), w, polarisation_frame=polarisation_frame)
    
    if numpy.max(frequency) > 6.1E8:
        if fov > 10:
            fovstr = '18'
        else:
            fovstr = '10'
        if flux_limit >= 1e-3:
            csvfilename = arl_path('data/models/S3_1400MHz_1mJy_%sdeg.csv' % fovstr)
        else:
            csvfilename = arl_path('data/models/S3_1400MHz_100uJy_%sdeg.csv' % fovstr)
        log.info('create_test_image_from_s3: Reading S3 sources from %s ' % csvfilename)
    else:
        assert fov in [10, 20, 40], "Field of view invalid: use one of %s" % ([10, 20, 40])
        csvfilename = arl_path('data/models/S3_151MHz_%ddeg.csv' % (fov))
        log.info('create_test_image_from_s3: Reading S3 sources from %s ' % csvfilename)

    with open(csvfilename) as csvfile:
        readCSV = csv.reader(csvfile, delimiter=',')
        r = 0
        for row in readCSV:
            # Skip first row
            if r > 0:
                ra = float(row[4]) + phasecentre.ra.deg
                dec = float(row[5]) + phasecentre.dec.deg
                if numpy.max(frequency) > 6.1E9:
                    alpha = (float(row[11]) - float(row[10])) / numpy.log10(1400.0 / 610.0)
                    flux = numpy.power(10, float(row[10])) * numpy.power(frequency / 1.4e9, alpha)
                else:
                    alpha = (float(row[10]) - float(row[9])) / numpy.log10(610.0 / 151.0)
                    flux = numpy.power(10, float(row[9])) * numpy.power(frequency / 1.51e8, alpha)
                if flux.any() > flux_limit:
                    ras.append(ra)
                    decs.append(dec)
                    fluxes.append(flux)
            r += 1
    
    csvfile.close()

    assert len(fluxes) > 0, "No sources found above flux limit %s" % flux_limit

    log.info('create_test_image_from_s3: %d sources read' % (len(fluxes)))

    p = w.sub(2).wcs_world2pix(numpy.array(ras), numpy.array(decs), 1)
    total_flux = numpy.sum(fluxes)
    fluxes = numpy.array(fluxes)
    ip = numpy.round(p).astype('int')
    ok = numpy.where((0 <= ip[0, :]) & (npixel > ip[0, :]) & (0 <= ip[1, :]) & (npixel > ip[1, :]))[0]
    ps = ip[:, ok]
    fluxes = fluxes[ok]
    actual_flux = numpy.sum(fluxes)
    
    log.info('create_test_image_from_s3: %d sources inside the image' % (ps.shape[1]))
    
    log.info('create_test_image_from_s3: average channel flux in S3 model = %.3f, actual average channel flux in '
             'image = %.3f' % (total_flux / float(nchan), actual_flux / float(nchan)))
    for chan in range(nchan):
        for iflux, flux in enumerate(fluxes):
            model.data[chan, 0, ps[1, iflux], ps[0, iflux]] = flux[chan]
    
    return model