Пример #1
0
    def _project_reference_hdu(self, name_hdr, muse_hdu=None):
        """Project the reference image onto the MUSE field
        """
        if self.use_montage:
            # The original way. Sometimes this introduces an offset
            hdu_repr = montage.reproject_hdu(self.reference_hdu,
                                             header=name_hdr,
                                             exact_size=True)
        else:
            # The mpdaf way
            if muse_hdu is not None:

                wcs_ref = WCS(hdr=self.reference_hdu.header)
                ima_ref = Image(data=self.reference_hdu.data, wcs=wcs_ref)

                wcs_muse = WCS(hdr=muse_hdu.header)
                ima_muse = Image(data=muse_hdu.data, wcs=wcs_muse)

                ima_ref = ima_ref.align_with_image(ima_muse)

                hdu_repr = ima_ref.get_data_hdu()

            else:
                hdu_repre = None
                print(
                    "Warning: provide target HDU when not using montage to reproject"
                )

        return hdu_repr
Пример #2
0
    def test_get(self):
        """WCS class: testing getters"""
        wcs = WCS(crval=(0, 0), shape=(5, 6), crpix=(1, 1))
        assert_array_equal(wcs.get_step(), [1.0, 1.0])
        assert_array_equal(wcs.get_start(), [0.0, 0.0])
        assert_array_equal(wcs.get_end(), [4.0, 5.0])
        assert_array_equal(wcs.get_range(), [0.0, 0.0, 4.0, 5.0])
        assert_array_equal(wcs.get_center(), [2.0, 2.5])

        wcs2 = WCS(crval=(0, 0), shape=(5, 6))
        assert_array_equal(wcs2.get_step(), [1.0, 1.0])
        assert_array_equal(wcs2.get_start(), [-2.0, -2.5])
        assert_array_equal(wcs2.get_end(), [2.0, 2.5])
        assert_array_equal(wcs2.get_center(), [0, 0])

        wcs2.set_step([0.5, 2.5])
        assert_array_equal(wcs2.get_step(), [0.5, 2.5])

        assert wcs2.get_crval1() == 0.0
        assert wcs2.get_crval2() == 0.0

        wcs2.set_crval1(-2, unit=2 * u.pix)
        assert wcs2.get_crval1(unit=2 * u.pix) == -2.0
        wcs2.set_crval2(-2, unit=2 * u.pix)
        assert wcs2.get_crval2(unit=2 * u.pix) == -2.0
Пример #3
0
 def test_from_hdr(self):
     """WCS class: testing constructor """
     h = fits.getheader(get_data_file('obj', 'a370II.fits'))
     wcs = WCS(h)
     h2 = wcs.to_header()
     wcs2 = WCS(h2)
     wcs2.naxis1 = wcs.naxis1
     wcs2.naxis2 = wcs.naxis2
     assert wcs.isEqual(wcs2)
Пример #4
0
def test_gauss(fwhm, flux, factor, weight, fit_back, cont, center,
               pos_min, pos_max):
    """Image class: testing Gaussian fit"""
    params = dict(fwhm=(2, 1), rot=60, cont=2.0, flux=5.)
    wcs = WCS(cdelt=(0.2, 0.3), crval=(8.5, 12), shape=(40, 30))
    ima = gauss_image(wcs=wcs, factor=factor, unit_center=u.pix,
                      unit_fwhm=u.pix, **params)
    ima._var = np.ones_like(ima._data)
    gauss = ima.gauss_fit(fit_back=fit_back, cont=cont, verbose=True,
                          center=center, pos_min=pos_min, pos_max=pos_max,
                          factor=factor, fwhm=fwhm, flux=flux, weight=weight,
                          unit_center=None, unit_fwhm=None, circular=False,
                          full_output=True, maxiter=200)
    assert isinstance(gauss.ima, Image)
    if factor == 1:
        assert_array_almost_equal(gauss.center, (19.5, 14.5))
    else:
        # FIXME: This must be fixed, when factor=2 center is wrong
        assert_array_almost_equal(gauss.center, (19.25, 14.25))

    for param, value in params.items():
        if np.isscalar(value):
            assert_almost_equal(getattr(gauss, param), value)
        else:
            assert_array_almost_equal(getattr(gauss, param), value)
Пример #5
0
def test_convolve():
    """Image class: testing discrete convolution method."""

    shape = (12, 25)
    wcs = WCS(cdelt=(1.0, 1.0), crval=(0.0, 0.0), shape=shape)
    data = np.zeros(shape)
    data[7, 5] = 1.0
    mask = np.zeros(shape, dtype=bool)
    mask[5, 3] = True
    ima = Image(wcs=wcs, data=data, mask=mask, copy=False)

    # Create a symmetric convolution kernel with an even number of elements
    # along one dimension and and odd number along the other dimension.
    # Make the kernel symmetric around (shape-1)//2. This requires that
    # the final column be all zeros.
    kern = np.array([[0.1, 0.25, 0.1, 0.0],
                     [0.25, 0.50, 0.25, 0.0],
                     [0.1, 0.25, 0.1, 0.0]])

    # The image should consist of a copy of the convolution kernel, centered
    # such that pixels (kern.shape-1)//2 is at pixel 7,5 of data.
    expected_data = np.ma.array(data=np.zeros(shape), mask=mask)
    expected_data.data[6:9, 4:8] = kern

    res = ima.convolve(kern)
    assert_masked_allclose(res.data, expected_data)

    res = ima.convolve(Image(data=kern))
    assert_masked_allclose(res.data, expected_data)

    res = ima.fftconvolve(kern)
    assert_masked_allclose(res.data, expected_data, atol=1e-15)
Пример #6
0
def test_moffat(fwhm, flux, fit_n, n, fit_back, cont, center, pos_min,
                pos_max):
    """Image class: testing Moffat fit"""
    params = dict(flux=12.3,
                  fwhm=(2.8, 2.1),
                  n=1.6,
                  cont=8.24,
                  center=(50., 50.),
                  rot=30)
    ima = moffat_image(wcs=WCS(cdelt=(1., 1.), crval=(0, 0)),
                       shape=(101, 101),
                       unit_center=u.pix,
                       unit_fwhm=u.pix,
                       **params)

    moffat = ima.moffat_fit(fit_back=fit_back,
                            cont=cont,
                            verbose=True,
                            unit_center=None,
                            unit_fwhm=None,
                            full_output=True,
                            center=center,
                            pos_min=pos_min,
                            pos_max=pos_max,
                            fit_n=fit_n,
                            n=n,
                            circular=False,
                            fwhm=fwhm,
                            flux=flux)
    assert isinstance(moffat.ima, Image)
    for param, value in params.items():
        if np.isscalar(value):
            assert_almost_equal(getattr(moffat, param), value, 2)
        else:
            assert_array_almost_equal(getattr(moffat, param), value, 2)
Пример #7
0
def test_segment():
    wcs = WCS(cdelt=(0.2, 0.2), crval=(8.5, 12), shape=(100, 100))
    ima = gauss_image(wcs=wcs, fwhm=(2, 2), cont=2.0,
                      unit_center=u.pix, unit_fwhm=u.pix, flux=10, peak=True)
    subimages = ima.segment(minpts=20, background=2.1, median=(5, 5))
    assert len(subimages) == 1
    assert subimages[0].shape == (45, 45)
Пример #8
0
 def test_rot(self):
     """WCS class: testing constructor 2 """
     h = fits.getheader(get_data_file('obj', 'IMAGE-HDFS-1.34.fits'), ext=1)
     wcs = WCS(h)
     assert wcs.get_rot() == 0
     wcs.rotate(90)
     assert wcs.get_rot() == 90
Пример #9
0
 def test_coordTransform(self):
     """WCS class: testing coordinates transformations"""
     wcs = WCS(crval=(0, 0), shape=(5, 6))
     pixcrd = [[0, 0], [2, 3], [3, 2]]
     pixsky = wcs.pix2sky(pixcrd)
     pixcrd2 = wcs.sky2pix(pixsky)
     assert_array_equal(pixcrd, pixcrd2)
Пример #10
0
def test_dtype():
    """Image class: testing dtype."""
    wcs = WCS(cdelt=(0.2, 0.3), crval=(8.5, 12), shape=(40, 30), deg=True)
    data = np.zeros((40, 30))
    data[19, 14] = 1
    ima = Image(wcs=wcs, data=data, dtype=int)
    ima2 = ima.fftconvolve_gauss(center=None,
                                 flux=1.,
                                 fwhm=(20000., 10000.),
                                 peak=False,
                                 rot=60.,
                                 factor=1,
                                 unit_center=u.deg,
                                 unit_fwhm=u.arcsec)

    g = ima2.gauss_fit(verbose=False)
    assert_almost_equal(g.fwhm[0], 20000, 2)
    assert_almost_equal(g.fwhm[1], 10000, 2)
    assert_almost_equal(g.center[0], 8.5)
    assert_almost_equal(g.center[1], 12)
    assert_equal(ima2.dtype, np.float64)

    ima3 = ima2.resample(newdim=(32, 24),
                         newstart=None,
                         newstep=ima2.get_step(unit=u.arcsec) * 0.8)
    assert_equal(ima3.dtype, np.float64)
Пример #11
0
def test_fftconvolve():
    """Image class: testing FFT convolution method."""
    wcs = WCS(cdelt=(0.2, 0.3), crval=(8.5, 12), shape=(40, 30), deg=True)
    data = np.zeros((40, 30))
    data[19, 14] = 1
    ima = Image(wcs=wcs, data=data)
    ima2 = ima.fftconvolve_gauss(center=None,
                                 flux=1.,
                                 fwhm=(20000., 10000.),
                                 peak=False,
                                 rot=60.,
                                 factor=1,
                                 unit_center=u.deg,
                                 unit_fwhm=u.arcsec)

    g = ima2.gauss_fit(verbose=False)
    assert_almost_equal(g.fwhm[0], 20000, 2)
    assert_almost_equal(g.fwhm[1], 10000, 2)
    assert_almost_equal(g.center[0], 8.5)
    assert_almost_equal(g.center[1], 12)
    ima2 = ima.fftconvolve_moffat(center=None,
                                  flux=1.,
                                  a=10000,
                                  q=1,
                                  n=2,
                                  peak=False,
                                  rot=60.,
                                  factor=1,
                                  unit_center=u.deg,
                                  unit_a=u.arcsec)
    m = ima2.moffat_fit(verbose=False)
    assert_almost_equal(m.center[0], 8.5)
    assert_almost_equal(m.center[1], 12)
Пример #12
0
def test_ee():
    """Image class: testing ensquared energy."""
    wcs = WCS()
    data = np.ones(shape=(6, 5)) * 2
    image1 = Image(data=data, wcs=wcs)
    image1.mask_region((2, 2), (1.5, 1.5),
                       inside=False,
                       unit_center=None,
                       unit_radius=None)

    assert image1.ee() == 9 * 2
    assert image1.ee(frac=True) == 1.0
    ee = image1.ee(center=(2, 2), unit_center=None, radius=1, unit_radius=None)
    assert ee == 4 * 2

    r, eer = image1.eer_curve(center=(2, 2),
                              unit_center=None,
                              unit_radius=None,
                              cont=0)
    assert r[1] == 1.0
    assert eer[1] == 1.0

    size = image1.ee_size(center=(2, 2),
                          unit_center=None,
                          unit_size=None,
                          cont=0)
    assert_almost_equal(size[0], 1.775)
Пример #13
0
 def test_from_hdr2(self):
     """WCS class: testing constructor 2 """
     with fits.open(get_data_file('sdetect', 'a478hst-cutout.fits')) as h:
         frame, equinox = determine_refframe(h[0].header)
         wcs = WCS(h[1].header, frame=frame, equinox=equinox)
     assert wcs.wcs.wcs.equinox == 2000.0
     assert wcs.wcs.wcs.radesys == 'FK5'
     assert wcs.is_deg()
Пример #14
0
    def test_init(self):
        wcs = WCS()
        assert wcs.naxis1 == 0
        assert wcs.naxis2 == 0

        wcs.naxis1 = 10
        assert wcs.naxis1 == 10
        assert wcs.naxis2 == 0
Пример #15
0
def test_rebin():
    """Image class: testing rebin methods."""
    wcs = WCS(crval=(0, 0))
    data = np.arange(30).reshape(6, 5)
    image1 = Image(data=data, wcs=wcs, var=np.ones(data.shape) * 0.5)
    image1.mask_region((2, 2), (1.5, 1.5),
                       inside=False,
                       unit_center=None,
                       unit_radius=None)

    # The test data array looks as follows:
    #
    # ---- ---- ---- ---- ----
    # ----  6.0  7.0  8.0 ----
    # ---- 11.0 12.0 13.0 ----
    # ---- 16.0 17.0 18.0 ----
    # ---- ---- ---- ---- ----
    # ---- ---- ---- ---- ----
    #
    # Where ---- signifies a masked value.
    #
    # After reducing both dimensions by a factor of 2, we should
    # get a data array of the following 6 means of 4 pixels each:
    #
    #  ---- ---- => 6/1         ---- ---- => (7+8)/2
    #  ----  6.0                 7.0  8.0
    #
    #  ---- 11.0 => (11+16)/2   12.0 13.0 => (12+13+17+18)/4
    #  ---- 16.0                17.0 18.0
    #
    #  ---- ---- => ----        ---- ---- => ----
    #  ---- ----                ---- ----

    expected = np.ma.array(data=[[6.0, 7.5], [13.5, 15], [0.0, 0.0]],
                           mask=[[False, False], [False, False], [True, True]])
    image2 = image1.rebin(2)
    assert_masked_allclose(image2.data, expected)

    image2 = image1.rebin(factor=(2, 2))
    assert_masked_allclose(image2.data, expected)

    # The variances of the original pixels were all 0.5, so taking the
    # mean of N of these should give the mean a variance of 0.5/N.
    # Given the number of pixels averaged in each of the above means,
    # we thus expect the variance array to look as follows.

    expected = np.ma.array(data=[[0.5, 0.25], [0.25, 0.125], [0.0, 0.0]],
                           mask=[[False, False], [False, False], [True, True]])
    assert_masked_allclose(image2.var, expected)

    # Check the WCS information.

    start = image2.get_start()
    assert start[0] == 0.5
    assert start[1] == 0.5
Пример #16
0
def test_peak_detection_and_fwhm():
    shape = (101, 101)
    fwhm = (5, 5)
    wcs = WCS(cdelt=(1., 1.), crval=(8.5, 12), shape=shape)
    ima = gauss_image(wcs=wcs, fwhm=fwhm, cont=2.0,
                      unit_center=u.pix, unit_fwhm=u.pix, flux=10, peak=True)

    peaks = ima.peak_detection(5, 2)
    assert peaks.shape == (1, 2)
    assert_allclose(peaks[0], (np.array(shape) - 1) / 2.0)
    assert_allclose(ima.fwhm(unit_radius=None), fwhm, rtol=0.1)
Пример #17
0
def test_background(a370II):
    """Image class: testing background value"""
    wcs = WCS()
    data = np.ones(shape=(6, 5)) * 2
    image1 = Image(data=data, wcs=wcs)
    (background, std) = image1.background()
    assert background == 2
    assert std == 0
    (background, std) = a370II[1647:1732, 618:690].background()
    # compare with IRAF results
    assert (background - std < 1989) & (background + std > 1989)
Пример #18
0
def test_arithmetric():
    """Spectrum class: testing arithmetic functions"""
    wave = WaveCoord(crpix=2.0, cdelt=3.0, crval=0.5, cunit=u.nm)
    spectrum1 = Spectrum(data=np.array([0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
                         wave=wave)
    spectrum2 = spectrum1 > 6  # [-,-,-,-,-,-,-,7,8,9]
    # +
    spectrum3 = spectrum1 + spectrum2
    assert spectrum3.data.data[3] == 3
    assert spectrum3.data.data[8] == 16
    spectrum3 = 4.2 + spectrum1
    assert spectrum3.data.data[3] == 3 + 4.2
    # -
    spectrum3 = spectrum1 - spectrum2
    assert spectrum3.data.data[3] == 3
    assert spectrum3.data.data[8] == 0
    spectrum3 = spectrum1 - 4.2
    assert spectrum3.data.data[8] == 8 - 4.2
    # *
    spectrum3 = spectrum1 * spectrum2
    assert spectrum3.data.data[8] == 64
    spectrum3 = 4.2 * spectrum1
    assert spectrum3.data.data[9] == 9 * 4.2
    # /
    spectrum3 = spectrum1 / spectrum2
    # divide functions that have a validity domain returns the masked constant
    # whenever the input is masked or falls outside the validity domain.
    assert spectrum3.data.data[8] == 1
    spectrum3 = 1.0 / (4.2 / spectrum1)
    assert spectrum3.data.data[5] == 5 / 4.2

    # with cube
    wcs = WCS()
    cube1 = Cube(data=np.ones(shape=(10, 6, 5)), wave=wave, wcs=wcs)
    cube2 = spectrum1 + cube1
    sp1data = spectrum1.data[:, np.newaxis, np.newaxis]
    assert_array_almost_equal(cube2.data, sp1data + cube1.data)

    cube2 = spectrum1 - cube1
    assert_array_almost_equal(cube2.data, sp1data - cube1.data)

    cube2 = spectrum1 * cube1
    assert_array_almost_equal(cube2.data, sp1data * cube1.data)

    cube2 = spectrum1 / cube1
    assert_array_almost_equal(cube2.data, sp1data / cube1.data)

    # spectrum * image
    data = np.ones(shape=(6, 5)) * 2
    image1 = Image(data=data, wcs=wcs)
    cube2 = spectrum1 * image1
    assert_array_almost_equal(cube2.data,
                              sp1data * image1.data[np.newaxis, :, :])
Пример #19
0
def test_peak(a370II):
    """Image class: testing peak research"""
    wcs = WCS()
    data = np.ones(shape=(6, 5)) * 2
    image1 = Image(data=data, wcs=wcs)
    image1.data[2, 3] = 8
    p = image1.peak()
    assert p['p'] == 2
    assert p['q'] == 3
    p = a370II.peak(center=(790, 875), radius=20, plot=False, unit_center=None,
                    unit_radius=None)
    assert_almost_equal(p['p'], 793.1, 1)
    assert_almost_equal(p['q'], 875.9, 1)
Пример #20
0
def make_empty_cube(radec_center, pixscale, nside, wave_coord, wcs=None):
    """Makes a new datacube with different WCS and 2*nside+1
    pixels per side

    Parameters
    ----------
    radec_center : SkyCoord
        Coordinate of the central spaxel
    pixscale : Angle
        Pixel scale of the cube
    nside : int
        Number of pixels at each side of the central one
    wave_coord : mpdaf.obj.WaveCoord
        The WaveCoord object of the new datacube
    wcs : mpdaf.obj.WCS ; optional
        If given, it will overwrite radec_center, pixscale, nside

    Returns
    -------
     cube : mpdaf.obj.Cube
        Cube object filled with zeros

    """
    if wcs is None:
        ntot = 2 * nside + 1  # total side always odd
        ny, nx = ntot, ntot
        crpix = (nside + 1, nside + 1)
        crval = radec_center[1].to('deg').value, radec_center[0].to(
            'deg').value
        cdelt = pixscale.to('deg').value
        dy, dx = cdelt, -1 * cdelt  # dx is negative for convention East goes to negative x's
        # cd_matrix = np.zeros((2,2))
        # cd_matrix[0,0] = cdelt
        # cd_matrix[1,1] = cdelt
        deg_bool = True
        shape = (ny, nx)
        wcs_new = WCS(crpix=crpix,
                      crval=crval,
                      cdelt=(dy, dx),
                      deg=deg_bool,
                      shape=shape)
    else:
        wcs_new = wcs
        nx = wcs.naxis1
        ny = wcs.naxis2

    nw = wave_coord.shape  #
    data = np.zeros((nw, ny, nx))
    cube = Cube(wcs=wcs_new, data=data, var=data, wave=wave_coord)
    return cube
Пример #21
0
    def test_coord(self):
        """WCS class: testing coord"""
        wcs = WCS(shape=(4, 5),
                  crval=(-23.0, 5.0),
                  cdelt=(0.2 / 3600., -0.2 / 3600.),
                  deg=True)

        p, q = wcs.coord(spaxel=True, relative=True)
        assert p.mean() == 0
        assert q.mean() == 0
        assert p.shape == (4, 5)
        assert q.shape == (4, 5)

        p, q = wcs.coord(spaxel=True, relative=True, reshape=True)
        assert p.shape == (4, )
        assert q.shape == (5, )

        p, q = wcs.coord(spaxel=True, relative=True, center=(5.2, 6.3))
        assert p.min() == -5.2
        assert q.min() == -6.3
        assert p.shape == (4, 5)
        assert q.shape == (4, 5)

        dec, ra = wcs.coord()
        assert_allclose(ra.min(), 4.99987929, rtol=1e-6)
        assert_allclose(dec.min(), -23.000083333, rtol=1e-6)

        dec, ra = wcs.coord(relative=True, unit='arcsec')
        assert_allclose(ra.min(), -0.4345444, rtol=1e-6)
        assert_allclose(dec.min(), -0.3000001, rtol=1e-6)

        r, theta = wcs.coord(polar=True, unit='arcsec')
        assert_allclose(r.max(), 0.5280425, rtol=1e-6)
        assert_allclose(theta.mean(), 0.314159265, rtol=1e-6)

        mask = np.empty(shape=(4, 5), dtype=bool)
        mask[:, :] = False
        mask[0, 0] = True
        p, q = wcs.coord(spaxel=True, mask=mask)
        assert p.shape == (19, )
        assert q.shape == (19, )
Пример #22
0
    def test_coordTransform2(self):
        """WCS class: testing transformations with a more complete header."""

        w = pywcs.WCS(naxis=2)
        w.wcs.crpix = [167.401033093, 163.017401336]
        w.wcs.cd = np.array([[-5.5555555555555003e-05, 0],
                             [0, 5.5555555555555003e-05]])
        w.wcs.crval = [338.23092027, -60.56375796]
        w.wcs.ctype = ["RA---TAN", "DEC--TAN"]

        wcs = WCS()
        wcs.wcs = w

        pix = np.array([[108.41, 81.34]])
        pix2 = pix[:, [1, 0]]
        pixint = pix2.astype(int)
        ref = np.array([[338.2375, -60.5682]])
        ref2 = ref[:, [1, 0]]
        sky = wcs.pix2sky(pix2)

        assert_allclose(wcs.wcs.wcs_pix2world(pix, 0), ref, rtol=1e-4)
        assert_allclose(sky, ref2, rtol=1e-4)
        assert_allclose(wcs.sky2pix(wcs.pix2sky(pix2)), pix2)
        assert_allclose(wcs.sky2pix(sky, nearest=True), pixint)
Пример #23
0
def test_mask(cube):
    """Cube class: testing mask functionalities"""
    # A region of half-width=1 and half-height=1 should have a size of
    # 2x2 pixels. A 2x2 region of pixels has a center at the shared
    # corner of the 4 pixels, and the closest corner to the requested
    # center of 2.1,1.8 is 2.5,1.5, so we expect the square of unmasked pixels
    # to be pixels 2,3 along the Y axis, and pixels 1,2 along the X axis.
    cube.mask_region((2.1, 1.8), (1, 1), lmin=2, lmax=5, inside=True,
                     unit_center=None, unit_radius=None, unit_wave=None)

    # The expected mask for the images between lmin and lmax.
    expected_mask = np.array([[False, False, False, False, False],
                              [False, False, False, False, False],
                              [False, True, True, False, False],
                              [False, True, True, False, False],
                              [False, False, False, False, False],
                              [False, False, False, False, False]], dtype=bool)
    assert_array_equal(np.any(cube.mask[:2, :, :], axis=0),
                       np.zeros(cube.shape[1:]))
    assert_array_equal(np.all(cube._mask[2:5, :, :], axis=0), expected_mask)
    assert_array_equal(np.any(cube.mask[5:, :, :], axis=0),
                       np.zeros(cube.shape[1:]))

    cube.unmask()

    # Do the same experiment, but this time mask pixels outside the region
    # instead of within the region.
    cube.mask_region((2.1, 1.8), (1, 1), lmin=2, lmax=5, inside=False,
                     unit_center=None, unit_radius=None, unit_wave=None)
    assert_array_equal(np.any(cube.mask[:2, :, :], axis=0),
                       np.ones(cube.shape[1:]))
    assert_array_equal(np.all(cube._mask[2:5, :, :], axis=0), ~expected_mask)
    assert_array_equal(np.any(cube.mask[5:, :, :], axis=0),
                       np.ones(cube.shape[1:]))
    cube.unmask()

    # Do the same experiment, but this time with the center and size
    # of the region specified using the equivalent world coordinates.
    wcs = WCS(deg=True)
    wave = WaveCoord(cunit=u.angstrom)
    cube = Cube(data=cube.data, wave=wave, wcs=wcs, copy=False)
    cube.mask_region(wcs.pix2sky([2.1, 1.8]), (3600, 3600), lmin=2, lmax=5,
                     inside=False)
    assert_array_equal(np.any(cube.mask[:2, :, :], axis=0),
                       np.ones(cube.shape[1:]))
    assert_array_equal(np.all(cube._mask[2:5, :, :], axis=0), ~expected_mask)
    assert_array_equal(np.any(cube.mask[5:, :, :], axis=0),
                       np.ones(cube.shape[1:]))
    cube.unmask()

    # Mask around a region of half-width and half-height 1.1 pixels,
    # specified in arcseconds, centered close to pixel 2.4,3.8. This
    # ideally corresponds to a region of 2.2x2.2 pixels. The closest
    # possible size is 2x2 pixels. A region of 2x2 pixels has its
    # center at the shared corner of these 4 pixels, and the nearest
    # corner to the desired central index of (2.4,3.8) is (2.5,3.5).
    # So all of the image should be masked, except for a 2x2 area of
    # pixel indexes 2,3 along the Y axis and pixel indexes 3,4 along
    # the X axis.
    cube.mask_region(wcs.pix2sky([2.4, 3.8]), 1.1 * 3600.0, inside=False,
                     lmin=2, lmax=5)

    # The expected mask for the images between lmin and lmax.
    expected_mask = np.array([[True, True, True, True, True],
                              [True, True, True, True, True],
                              [True, True, True, False, False],
                              [True, True, True, False, False],
                              [True, True, True, True, True],
                              [True, True, True, True, True]], dtype=bool)
    assert_array_equal(np.any(cube.mask[:2, :, :], axis=0),
                       np.ones(cube.shape[1:]))
    assert_array_equal(np.all(cube._mask[2:5, :, :], axis=0), expected_mask)
    assert_array_equal(np.any(cube.mask[5:, :, :], axis=0),
                       np.ones(cube.shape[1:]))
    cube.unmask()

    # Mask outside an elliptical region centered at pixel 3.5,3.5.
    # The boolean expected_mask array given below was a verified
    # output of mask_ellipse() for the specified ellipse parameters.
    cube = generate_cube(shape=(10, 8, 8), wcs=wcs, var=None)
    cube.mask_ellipse([3.5, 3.5], (2.5, 3.5), 45.0, unit_radius=None,
                      unit_center=None, inside=False, lmin=2, lmax=5)
    expected_mask = np.array([
        [True, True, True, True, True, True, True, True],
        [True, True, True, False, False, False, True, True],
        [True, True, False, False, False, False, False, True],
        [True, False, False, False, False, False, False, True],
        [True, False, False, False, False, False, False, True],
        [True, False, False, False, False, False, True, True],
        [True, True, False, False, False, True, True, True],
        [True, True, True, True, True, True, True, True]],
        dtype=bool)
    assert_array_equal(np.any(cube.mask[:2, :, :], axis=0),
                       np.ones(cube.shape[1:]))
    assert_array_equal(np.all(cube._mask[2:5, :, :], axis=0), expected_mask)
    assert_array_equal(np.any(cube.mask[5:, :, :], axis=0),
                       np.ones(cube.shape[1:]))

    # Check that we can select the same mask via the output of np.where()
    # passed to mask_selection().
    ksel = np.where(cube.data.mask)
    cube.unmask()
    cube.mask_selection(ksel)
    assert_array_equal(np.any(cube.mask[:2, :, :], axis=0),
                       np.ones(cube.shape[1:]))
    assert_array_equal(np.all(cube._mask[2:5, :, :], axis=0), expected_mask)
    assert_array_equal(np.any(cube.mask[5:, :, :], axis=0),
                       np.ones(cube.shape[1:]))

    # The cube was generated without any variance information.
    # Check that mask_variance() raises an error due to this.
    with pytest.raises(ValueError):
        cube.mask_variance(0.1)

    # Add an array of variances to the cube and check that mask_variance()
    # masks all pixels that have variances greater than 0.1.
    cube.unmask()
    cube.var = np.random.randn(*cube.shape)
    mask = cube.var > 0.1
    cube.mask_variance(0.1)
    assert_array_equal(cube.data.mask, mask)
Пример #24
0
def _generate_test_data(data=2.3,
                        var=1.0,
                        mask=None,
                        shape=None,
                        unit=u.ct,
                        uwave=u.angstrom,
                        wcs=None,
                        wave=None,
                        copy=True,
                        ndim=None,
                        crpix=2.0,
                        cdelt=3.0,
                        crval=0.5):

    # Determine a shape for the data and var arrays. This is either a
    # specified shape, the shape of a specified data or var array, or
    # the default shape.

    if shape is None:
        if isinstance(data, np.ndarray):
            shape = data.shape
            ndim = data.ndim
        elif isinstance(var, np.ndarray):
            shape = var.shape
            ndim = var.ndim
        elif isinstance(mask, np.ndarray):
            shape = mask.shape
            ndim = mask.ndim
        elif ndim is not None:
            if ndim == 1:
                shape = DEFAULT_SHAPE[0]
            elif ndim == 2:
                shape = DEFAULT_SHAPE[1:]
            elif ndim == 3:
                shape = DEFAULT_SHAPE
        else:
            raise ValueError('Missing shape/ndim specification')

    if np.isscalar(shape):
        shape = (shape, )

    if len(shape) != ndim:
        raise ValueError('shape does not match the number of dimensions')

    # Convert the data and var arguments to ndarray's
    if data is None:
        if ndim == 1:
            # Special case for spectra ...
            data = np.arange(shape[0], dtype=float)
            data[0] = 0.5
    elif np.isscalar(data):
        data = data * np.ones(shape, dtype=type(data))
    elif data is not None:
        data = np.array(data, copy=copy)
        assert shape == data.shape

    if np.isscalar(var):
        var = var * np.ones(shape, dtype=type(var))
    elif var is not None:
        var = np.array(var, copy=copy)
        assert shape == var.shape

    if mask is None:
        mask = False

    if not np.isscalar(mask):
        mask = np.array(mask, copy=copy, dtype=bool)
        assert shape == mask.shape

    # Substitute default world-coordinates where not specified.
    if ndim == 2:
        wcs = wcs or WCS(crval=(0, 0), crpix=1.0, shape=shape)
    elif ndim == 3:
        wcs = wcs or WCS(crval=(0, 0), crpix=1.0, shape=shape[1:])

    # Substitute default wavelength-coordinates where not specified.
    if ndim in (1, 3):
        wave = wave or WaveCoord(
            crpix=crpix, cdelt=cdelt, crval=crval, shape=shape[0], cunit=uwave)
        if wave.shape is None:
            wave.shape = shape[0]

    if ndim == 1:
        cls = Spectrum
    elif ndim == 2:
        cls = Image
    elif ndim == 3:
        cls = Cube

    return cls(data=data,
               var=var,
               mask=mask,
               wave=wave,
               wcs=wcs,
               unit=unit,
               copy=copy,
               dtype=None)
Пример #25
0
#cosmological parameters to calculate the distance
z = 0.73379
scale = 7.28
cosmo = FlatLambdaCDM(H0=70, Om0=0.3)
dist = cosmo.luminosity_distance(z)

#galaxy imputs
galPA = 55
galcen = SkyCoord(ra=237.52059628552735 * u.degree,
                  dec=-78.188149277705151 * u.degree,
                  frame='icrs')

datacube = Cube('nor_cut_cube.fits')
head = datacube.primary_header
wcs1 = WCS(head)
wave1 = WaveCoord(cdelt=0.1, crval=4825.12, cunit=u.angstrom)


def residual(p,
             datacube=datacube,
             pos=None,
             specwidth=spectralpixel,
             galcen=galcen,
             galPA=galPA):
    par = p.valuesdict()
    incli = par['incli']
    col_denst = par['col_dens']
    h = par['height']
    bes = par['dop_param']
    v_max = par['vel_max']
Пример #26
0
def test_get_image():
    """Cube class: testing get_image method"""
    shape = (2000, 6, 5)
    wave = WaveCoord(crpix=1, cdelt=3.0, crval=2000, cunit=u.angstrom,
                     shape=shape[0])
    wcs = WCS(crval=(0, 0))
    data = np.ones(shape=shape) * 2
    cube1 = Cube(data=data, wave=wave, wcs=wcs)

    # Add a gaussian shaped spectral line at image pixel 2,2.
    cube1[:, 2, 2].add_gaussian(5000, 1200, 20)

    # Specify the range of wavelengths to be combined and the corresponding
    # slice along the wavelength axis. The wavelength range is chosen to
    # include all wavelengths affected by add_gaussian().
    lrange = (4800, 5200)
    lslice = slice(np.rint(cube1.wave.pixel(lrange[0])).astype(int),
                   np.rint(cube1.wave.pixel(lrange[1])).astype(int) + 1)

    # Get an image that is the mean of all images in the above wavelength
    # range, minus a background image estimated from outside this range,
    # where all image values are 2.0.
    ima = cube1.get_image(wave=lrange, method='mean', subtract_off=True)

    # In the cube, all spectral pixels of image pixel 0,0 were 2.0,
    # so the mean over the desired wavelength range, minus the mean
    # over background ranges either side of this should be zero.
    assert ima[0, 0] == 0

    # Spectral pixels of image pixel 2,2 in the original cube were all
    # 2.0 everywhere except where the gaussian was added to 2.0. Hence
    # pixel 2,2 of the mean image should be the mean of the gaussian
    # minus the average 2.0 background.
    assert_almost_equal(ima[2, 2], cube1[lslice, 2, 2].mean()[0] - 2, 3)

    # Get another mean image, but this time without subtracting off a
    # background.  Image pixel 0,0 should have a mean of 2.0, and
    # pixel 2,2 should equal the mean of the gaussian added to 2.0
    ima = cube1.get_image(wave=lrange, method='mean', subtract_off=False)
    assert ima[0, 0] == 2
    assert_almost_equal(ima[2, 2], cube1[lslice, 2, 2].mean()[0], 3)

    # For this test, perform a sum over the chosen wavelength range,
    # and subtract off a background image taken from wavelength
    # regions above and below the wavelength range. Pixel 0,0 of the
    # summed image should be zero, since both the summed wavelength
    # range and the background image wavelength ranges have the same
    # pixel values, and the background sum is scaled to have the same
    # units as the output image. Check the background subtraction of
    # pixel 2,2 using an equal number of pixels that were not affected
    # by the addition of the gaussian.
    ima = cube1.get_image(wave=lrange, method='sum', subtract_off=True)
    assert ima[0, 0] == 0
    assert_almost_equal(ima[2, 2], cube1[lslice, 2, 2].sum()[0] -
                        cube1[lslice, 0, 0].sum()[0], 3)
    
    # repeat with median filter method
    ima = cube1.get_image(wave=lrange, method='sum', subtract_off=True, median_filter=200)
    assert ima[0, 0] == 0
    assert_almost_equal(ima[2, 2], cube1[lslice, 2, 2].sum()[0] -
                        cube1[lslice, 0, 0].sum()[0], 3)    

    # Finally, perform a sum of the chosen wavelength range without
    # subtracting a background image. This is easy to test by doing
    # equivalent sums through the cube over the chosen wavelength range.
    ima = cube1.get_image(wave=lrange, method='sum', subtract_off=False)
    assert ima[0, 0] == cube1[lslice, 0, 0].sum()[0]
    assert_almost_equal(ima[2, 2], cube1[lslice, 2, 2].sum()[0])
Пример #27
0
    def __init__(self,shape=(41,101,101),lmbda=20,noise=None, \
        spectraSourcesLmbda=None,spectraSourcesWidth=5,listCenter=None, \
        listRadius=None,link=None,intens=0.2,rho=0,variation=0,noiseType='student',\
        decrease='True',noiseCube=None,df_stu=5,randomShape=False,seed=None,steps=50,dilate_factor=5):
        """
        Param:
        Param:
        Param:
        """

        self.shape = shape
        self.lmbda = lmbda
        self.data = np.zeros(shape)
        self.listCenter = listCenter
        self.center = listCenter[0]
        self.listRadius = listRadius
        self.intens = intens
        self.link = link
        if randomShape == False:
            self.maskSources, self.label = buildMaskSources(
                self.listCenter, self.listRadius, shape, decrease)
        else:
            self.maskSources, self.label = buildRandomMaskSources(
                shape,
                self.listCenter,
                self.listRadius,
                seed=seed,
                steps=steps,
                dilate_factor=dilate_factor)
            np.random.seed()
        self.spectraSources = []
        self.spectraSourcesLmbda = spectraSourcesLmbda
        self.spectraSourcesWidth = spectraSourcesWidth
        self.maskAll = self.maskSources > 0

        #create "filament link" between point sources
        if link is not None:
            for k in link:
                self.linkGal2(k[0], k[1], intens)

        if spectraSourcesLmbda is not None:
            for k in range(len(spectraSourcesLmbda)):
                self.spectraSources.append(
                    createSpectra(spectraSourcesLmbda[k], shape[0], width=5))
                for i in range(shape[1]):
                    for j in range(shape[2]):
                        if self.label[i, j] == k + 1:

                            if variation != 0:
                                if decrease == True:
                                    spectra = createSpectra(
                                        spectraSourcesLmbda[k] +
                                        np.random.randint(variation),
                                        shape[0],
                                        width=5)
                                else:
                                    spectra = createSpectra(
                                        spectraSourcesLmbda[k] + 1 / 2. *
                                        np.sqrt((listCenter[k][0] - i)**2 +
                                                (listCenter[k][1] - j)**2),
                                        shape[0],
                                        width=5)
                            else:
                                spectra = self.spectraSources[k]
                            self.data[:, i,
                                      j] = spectra * self.maskSources[i, j]

        self.noise = noise
        self.rho = rho

        if self.noise != None:
            if noiseType == 'student':
                self.dataClean = self.data.copy()
                self.noiseCube = sst.t.rvs(df_stu,
                                           loc=0,
                                           scale=self.noise,
                                           size=shape[0] * shape[1] *
                                           shape[2]).reshape(shape)
                if rho != 0:
                    ker = np.array([[0.05, 0.1, 0.05], [0.1, 0.4, 0.1],
                                    [0.05, 0.1, 0.05]])
                    self.noiseCube = ssl.fftconvolve(self.noiseCube,
                                                     ker[np.newaxis, :],
                                                     mode='same')
                self.data = self.data + self.noiseCube
                self.var = np.ones((shape)) * self.noise**2
            else:
                self.dataClean = self.data.copy()
                if rho == 0:  #no correlation
                    self.noiseCube = np.random.normal(
                        scale=self.noise,
                        size=shape[0] * shape[1] * shape[2]).reshape(shape)
                    self.data = self.data + self.noiseCube
                else:
                    if rho == 1:  #generate spatially correlated noise
                        #self.createCorrNoiseCube()
                        ker = np.array([[0.05, 0.1, 0.05], [0.1, 0.4, 0.1],
                                        [0.05, 0.1, 0.05]])
                    elif rho == 2:
                        ker = np.array([[0.00, 0.1, 0.00], [0.1, 0.6, 0.1],
                                        [0.00, 0.1, 0.00]])
                    elif rho == 3:
                        ker = np.array([[0.02, 0.02, 0.02, 0.02, 0.02],
                                        [0.02, 0.05, 0.05, 0.05, 0.02],
                                        [0.02, 0.05, 0.28, 0.05, 0.02],
                                        [0.02, 0.05, 0.05, 0.05, 0.02],
                                        [0.02, 0.02, 0.02, 0.02, 0.02]])
                    elif rho == 4:
                        ker = generateMoffatIm(shape=(15, 15),
                                               alpha=3.1,
                                               beta=2.8,
                                               center=(7, 7),
                                               dim=None)
                    self.noiseCube = np.random.normal(
                        scale=self.noise,
                        size=shape[0] * shape[1] * shape[2]).reshape(shape)
                    self.noiseCube = ssl.fftconvolve(self.noiseCube,
                                                     ker[np.newaxis, :],
                                                     mode='same')
                    self.data = self.data + self.noiseCube
                self.var = np.ones((shape)) * self.noise**2
        elif noiseCube is not None:
            self.dataClean = self.data.copy()
            self.noiseCube = noiseCube
            self.data = self.data + self.noiseCube
            self.var = np.ones((shape)) * np.var(noiseCube)

        else:
            self.dataClean = self.data
            self.var = np.ones((shape))

        cubeClean = Cube(data=self.dataClean, wcs=WCS(), wave=WaveCoord())
        cubeNoisy = Cube(data=self.data,
                         var=self.var,
                         wcs=WCS(),
                         wave=WaveCoord())
        ra, dec = listCenter[0]
        self.src = Source.from_data(4000,
                                    ra + 1,
                                    dec + 1,
                                    origin='Simulated',
                                    cubes={
                                        'TRUTH_CUBE': cubeClean,
                                        'MUSE_CUBE': cubeNoisy
                                    })
        self.src.add_line(['LBDA_OBS', 'LINE'], [lmbda, "LYALPHA"])
Пример #28
0
def test_mask():
    """Image class: testing mask functionalities"""
    wcs = WCS()
    data = np.ones(shape=(6, 5)) * 2

    # A region of half-width=1 and half-height=1 should have a size of
    # 2x2 pixels. A 2x2 region of pixels has a center at the shared
    # corner of the 4 pixels, and the closest corner to the requested
    # center of 2.1,1.8 is 2.5,1.5, so we expect the square of unmasked pixels
    # to be pixels 2,3 along the Y axis, and pixels 1,2 along the X axis.
    image1 = Image(data=data, wcs=wcs)
    image1.mask_region((2.1, 1.8), (1, 1), inside=False, unit_center=None,
                       unit_radius=None)
    expected_mask = np.array([[1, 1, 1, 1, 1],
                              [1, 1, 1, 1, 1],
                              [1, 0, 0, 1, 1],
                              [1, 0, 0, 1, 1],
                              [1, 1, 1, 1, 1],
                              [1, 1, 1, 1, 1]], dtype=bool)
    assert_array_equal(image1._mask, expected_mask)

    # Test that inside=True gives the opposite result
    image1.unmask()
    image1.mask_region((2.1, 1.8), (1, 1), inside=True, unit_center=None,
                       unit_radius=None)
    assert_array_equal(image1._mask, ~expected_mask)

    # And test with a rotation, 90° so should give the same result
    image1.unmask()
    image1.mask_region((2.1, 1.8), (1, 1), inside=True, unit_center=None,
                       unit_radius=None, posangle=90)
    assert_array_equal(image1._mask, ~expected_mask)

    # Try exactly the same experiment as the above, except that the center
    # and size of the region are specified in world-coordinates instead of
    # pixels.
    wcs = WCS(deg=True)
    image1 = Image(data=data, wcs=wcs)
    image1.mask_region(wcs.pix2sky([2.1, 1.8]), (3600, 3600), inside=False)
    assert_array_equal(image1._mask, expected_mask)

    # And same with a rotation
    image1.unmask()
    image1.mask_region(wcs.pix2sky([2.1, 1.8]), (3600, 3600), inside=True,
                       posangle=90)
    assert_array_equal(image1._mask, ~expected_mask)

    # Mask around a region of half-width and half-height 1.1 pixels,
    # specified in arcseconds, centered close to pixel 2.4,3.8. This
    # ideally corresponds to a region of 2.2x2.2 pixels. The closest
    # possible size is 2x2 pixels. A region of 2x2 pixels has its
    # center at the shared corner of these 4 pixels, and the nearest
    # corner to the desired central index of (2.4,3.8) is (2.5,3.5).
    # So all of the image should be masked, except for a 2x2 area of
    # pixel indexes 2,3 along the Y axis and pixel indexes 3,4 along
    # the X axis.
    image1.unmask()
    image1.mask_region(wcs.pix2sky([2.4, 3.8]), 1.1 * 3600.0, inside=False)
    expected_mask = np.array([[1, 1, 1, 1, 1],
                              [1, 1, 1, 1, 1],
                              [1, 1, 1, 0, 0],
                              [1, 1, 1, 0, 0],
                              [1, 1, 1, 1, 1],
                              [1, 1, 1, 1, 1]], dtype=bool)
    assert_array_equal(image1._mask, expected_mask)

    # Mask outside an elliptical region centered at pixel 3.5,3.5.
    # The boolean expected_mask array given below was a verified
    # output of mask_ellipse() for the specified ellipse parameters.
    data = np.ones(shape=(8, 8))
    image1 = Image(data=data, wcs=wcs)
    image1.mask_ellipse([3.5, 3.5], (2.5, 3.5), 45.0, unit_radius=None,
                        unit_center=None, inside=False)
    expected_mask = np.array([
        [1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 0, 0, 0, 1, 1],
        [1, 1, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 1, 1],
        [1, 1, 0, 0, 0, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1]],
        dtype=bool)
    assert_array_equal(image1._mask, expected_mask)

    # Use np.where to select the masked pixels and check that mask_selection()
    # then reproduces the same mask.
    ksel = np.where(image1.data.mask)
    image1.unmask()
    image1.mask_selection(ksel)
    assert_array_equal(image1._mask, expected_mask)

    # Check inside=True
    image1.unmask()
    image1.mask_ellipse([3.5, 3.5], (2.5, 3.5), 45.0, unit_radius=None,
                        unit_center=None, inside=True)
    assert_array_equal(image1._mask, ~expected_mask)
Пример #29
0
specwidth = [14, 36]

s_n_mask = np.load('out_r4_3.npy')
all_pixels = []
for i in range(len(s_n_mask)):
    posi = [s_n_mask[i][0], s_n_mask[i][1]]
    all_pixels.append(posi)

all_pixels = np.asarray(all_pixels)

datacube = Cube('nor_cut_cube.fits')

xlen = len(datacube[0, :, 0].data)
ylen = len(datacube[0, 0, :].data[0])

wcs1 = WCS(crval=0, cdelt=0.2)
MyData = np.ones((xlen, ylen))
ima = Image(data=MyData, wcs=wcs1)

for i in range(xlen):
    for j in range(ylen):
        par = params.valuesdict()
        incli = par['incli']
        col_denst = par['col_dens']
        h = par['height']
        bes = par['dop_param']
        v_max = par['vel_max']
        h_vt = par['h_v']

        csize = par['csize']
        r_0t = par['r_0']
Пример #30
0
def test_rebin():
    """Cube class: testing rebin methods"""

    # Create spectral and spatial world coordinates that make each
    # pixel equal its index.

    wcs = WCS(crval=(0, 0), crpix=(1, 1), cdelt=(1.0, 1.0))
    wave = WaveCoord(crval=0.0, crpix=1.0, cdelt=1.0)

    # Create a cube with even valued dimensions, filled with ones.
    data = ma.ones((4, 6, 8))       # Start with all pixels 1.0
    data.reshape(4 * 6 * 8)[::2] = 0.0   # Set every second pixel to 0.0
    data.mask = data < -1            # Unmask all pixels.
    cube1 = generate_cube(data=data.data, mask=data.mask, wcs=wcs, wave=wave)

    # Rebin each of the axes such as to leave only 2 pixels along each
    # dimension.
    factor = (2, 3, 4)
    cube2 = cube1.rebin(factor=factor)

    # Compute the expected output cube, given that the input cube is a
    # repeating pattern of 1,0, and we divided the x-axis by a
    # multiple of 2, the output pixels should all be 0.5.
    expected = ma.ones((2, 2, 2)) * 0.5
    expected.mask = expected < 0  # All pixels unmasked.
    assert_masked_allclose(cube2.data, expected)

    # Do the same experiment but with the zero valued pixels all masked.
    data = ma.ones((4, 6, 8))       # Start with all pixels 1.0
    data.reshape(4 * 6 * 8)[::2] = 0.0   # Set every second pixel to 0.0
    data.mask = data < 0.1           # Mask the pixels that are 0.0
    cube1 = generate_cube(data=data.data, mask=data.mask, wcs=wcs, wave=wave)

    # Rebin each of the axes such as to leave only 2 pixels along each
    # dimension.
    factor = np.array([2, 3, 4])
    cube2 = cube1.rebin(factor=factor)

    # Compute the expected output cube. The zero valued pixels are all
    # masked, leaving just pixels with values of 1, so the mean that is
    # recorded in each output pixel should be 1.0.
    expected = ma.ones((2, 2, 2)) * 1.0
    expected.mask = expected < 0  # All output pixels should be unmasked.
    assert_masked_allclose(cube2.data, expected)

    # Check that the world coordinates are correct.  We averaged
    # factor[] pixels whose coordinates were equal to their pixel
    # indexes, so the coordinates of the first pixel of the rebinned
    # cube should be the mean of the first factor indexes along each
    # dimension. The sum from k=0 to factor-1 is
    # ((factor-1)*factor)/2, and dividing this by the number of pixels
    # gives (factor-1)/2.
    assert_allclose(np.asarray(cube2.get_start()), (factor - 1) / 2.0)

    # Create a cube that has a larger number of pixels along the
    # y and x axes of the images, so that we can divide those axes
    # by a number whose remainder is large enough to test selection
    # of the truncated part of the cube.
    shape = np.array([4, 17, 15])
    data = ma.ones(shape)                # Start with all pixels 1.0
    data.reshape(shape.prod())[::2] = 0.0   # Set every second pixel to 0.0
    data.mask = data < -1                   # Unmask all pixels.
    cube1 = generate_cube(data=data.data, mask=data.mask, wcs=wcs, wave=wave)

    # Choose the rebinning factors such that there is a significant
    # remainder after dividing the final two dimensions of the cube by
    # the specified factor. We don't do this for the first axis because
    # we want the interleaved pattern of 0s and 1s to remain.
    factor = np.array([2, 7, 9])
    cube2 = cube1.rebin(factor=factor, margin='origin')

    # Compute the expected output cube. Given that the input cube is a
    # repeating pattern of 1,0, and we divided the x-axis by a
    # multiple of 2, the output pixels should all be 0.5.
    expected_shape = cube1.shape // factor
    expected = ma.ones(expected_shape) * 0.5
    expected.mask = expected < 0  # All pixels unmasked.
    assert_masked_allclose(cube2.data, expected)

    # We chose a margin value of 'origin', so that the outer corner of
    # pixel 0,0,0 of the input cube would also be the outer corner of
    # pixel 0,0,0 of the rebinned cube. The output world coordinates
    # can be calculated as described for the previous test.
    assert_allclose(np.asarray(cube2.get_start()), (factor - 1) / 2.0)

    # Do the same test, but with margin='center'.
    cube2 = cube1.rebin(factor=factor, margin='center')

    # Compute the expected output cube. The values should be the
    # same as the previous test.
    expected_shape = cube1.shape // factor
    expected = ma.ones(expected_shape) * 0.5
    expected.mask = expected < 0  # All pixels unmasked.
    assert_masked_allclose(cube2.data, expected)

    # We chose a margin value of 'center'. We need to know which
    # pixels should have contributed to pixel 0,0,0. First calculate
    # how many pixels remain after dividing the shape by the reduction
    # factors. This is the number of extra pixels that should have been
    # discarded. Divide this by 2 to determine the number of pixels that
    # are removed from the start of each axis.
    cut = np.mod(shape, factor).astype(int) // 2

    # The world coordinates of the input cube were equal to its pixel
    # indexes, and the world coordinates of a pixel of the output cube
    # is thus the mean of the indexes of the pixels that were combined
    # to make that pixel. In this case, we combine pixels
    # data[cut:cut+factor] along each axis. This can be calculated as
    # the sum of pixel indexes from 0 to cut+factor, minus the sum of
    # pixel indexes from 0 to cut, with the result divided by the number
    # of pixels that were averaged. Again we make use of the series sum,
    # sum[n=0..N] = (n*(n-1))/2.
    tmp = cut + factor
    assert_allclose(np.asarray(cube2.get_start()),
                    ((tmp * (tmp - 1)) / 2.0 -
                     (cut * (cut - 1)) / 2.0) / factor)