Пример #1
0
def test_align_wcs_1im_no_ref(mock_fits_wcs):
    xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]])
    imcat = Table(xy, names=('x', 'y'))
    tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat})

    with pytest.raises(ValueError) as e:
        align_wcs(tpwcs, refcat=None, fitgeom='shift', match=None)
    assert e.value.args[0] == "Too few input images (or groups of images)."
Пример #2
0
def test_align_wcs_tpwcs_refcat_must_have_catalog(mock_fits_wcs):
    xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]])
    imcat = Table(xy, names=('x', 'y'))
    tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat})
    reftpwcs = FITSWCS(mock_fits_wcs)

    with pytest.raises(ValueError) as e:
        align_wcs(tpwcs, refcat=reftpwcs)
    assert (e.value.args[0] == "Reference 'TPWCS' must contain a catalog.")
Пример #3
0
def test_align_wcs_unknown_fitgeom(mock_fits_wcs):
    xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]])
    imcat = Table(xy, names=('x', 'y'))
    tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat})

    with pytest.raises(ValueError) as e:
        align_wcs(tpwcs, fitgeom='unknown')
    assert (e.value.args[0] == "Unsupported 'fitgeom'. Valid values are: "
            "'shift', 'rshift', 'rscale', or 'general'")
Пример #4
0
def test_align_wcs_wrong_refcat_type(mock_fits_wcs):
    xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]])
    imcat = Table(xy, names=('x', 'y'))
    tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat})

    with pytest.raises(TypeError) as e:
        align_wcs(tpwcs, refcat=xy, fitgeom='shift', match=None)
    assert (e.value.args[0] == "Unsupported 'refcat' type. Supported 'refcat' "
            "types are 'tweakwcs.tpwcs.TPWCS' and 'astropy.table.Table'")
Пример #5
0
def test_align_wcs_no_radec_in_refcat(mock_fits_wcs):
    xy = np.array([[1, 0], [2, 3], [3, 1], [4, 5]])
    imcat = Table(xy, names=('x', 'y'))
    refcat = Table(xy, names=('x', 'y'))
    tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat})

    with pytest.raises(KeyError) as e:
        align_wcs(tpwcs, refcat, fitgeom='shift', match=None)
    assert (e.value.args[0] == "Reference catalogs *must* contain *both* 'RA' "
            "and 'DEC' columns.")
Пример #6
0
def test_fit_wcs_empty_cat(empty_refcat, empty_imcat, mock_fits_wcs):
    tpwcs = FITSWCS(
        mock_fits_wcs,
        meta={'catalog': Table([[], []], names=('x', 'y')), 'group_id': 1}
    )

    with pytest.raises(ValueError) as e:
        align_wcs([tpwcs, tpwcs, tpwcs])
    assert e.value.args[0] == ("Too few input images (or groups of images) "
                               "with non-empty catalogs.")
Пример #7
0
def test_fit_drop_empty(mock_fits_wcs):
    t0 = Table([[], []], names=('x', 'y'))
    t1 = Table([[1], [3]], names=('x', 'y'))
    wcscats = [
        FITSWCS(
            copy.deepcopy(mock_fits_wcs),
            meta={'catalog': t0.copy(), 'group_id': 1}
        ),
        FITSWCS(
            copy.deepcopy(mock_fits_wcs),
            meta={'catalog': t1.copy(), 'group_id': 2}
        ),
        FITSWCS(
            copy.deepcopy(mock_fits_wcs),
            meta={'catalog': t0.copy(), 'group_id': 2}
        ),
        FITSWCS(
            copy.deepcopy(mock_fits_wcs),
            meta={'catalog': t0.copy(), 'group_id': 3}
        ),
        FITSWCS(
            copy.deepcopy(mock_fits_wcs),
            meta={'catalog': t0.copy(), 'group_id': 3}
        ),
        FITSWCS(
            copy.deepcopy(mock_fits_wcs),
            meta={'catalog': t1.copy(), 'group_id': 4}
        ),
        FITSWCS(
            copy.deepcopy(mock_fits_wcs),
            meta={'catalog': t1.copy(), 'group_id': 4}
        )
    ]

    align_wcs(wcscats, fitgeom='shift')

    status = [w.meta.get('fit_info')['status'] for w in wcscats]

    assert status[0] == 'FAILED: empty source catalog'
    assert status[3] == 'FAILED: empty source catalog'
    assert status[4] == 'FAILED: empty source catalog'

    if status[1] == 'SUCCESS':
        assert status[2] == 'SUCCESS'
        assert status[5] == 'REFERENCE'
        assert status[6] == 'REFERENCE'

    elif status[1] == 'REFERENCE':
        assert status[2] == 'REFERENCE'
        assert status[5] == 'SUCCESS'
        assert status[6] == 'SUCCESS'

    else:
        assert False
Пример #8
0
def test_align_wcs_refcat_from_imcat(mock_fits_wcs, enforce):
    shift = (12, -34)
    rot = (15, 17)
    scale = (1.0123, 0.9876)

    crpix = mock_fits_wcs.wcs.crpix - 1
    xy = 1024 * np.random.random((100, 2))
    m = build_fit_matrix(rot, scale)
    xyr = np.dot(xy - crpix, m) + crpix + shift
    imcat = Table(xy, names=('x', 'y'))
    refcat = Table(xyr, names=('x', 'y'))
    tpwcs1 = FITSWCS(mock_fits_wcs, meta={'catalog': imcat})
    tpwcs2 = copy.deepcopy(tpwcs1)
    reftpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': refcat})

    input_catalogs = [reftpwcs, tpwcs1, tpwcs2]
    status = align_wcs(
        input_catalogs, refcat=None, fitgeom='general',
        match=None, enforce_user_order=enforce, expand_refcat=True
    )
    assert status

    for cat in input_catalogs:
        if cat.meta['fit_info']['status'] == 'REFERENCE':
            if enforce:
                assert cat is reftpwcs
            continue
        assert cat.meta['fit_info']['status'] == 'SUCCESS'
Пример #9
0
def test_align_wcs_simple_ref_image_general(shift, rot, scale, fitgeom,
                                            weighted, mock_fits_wcs):
    xy = 1024 * np.random.random((100, 2))
    if weighted:
        w = np.ones((100, 1))
        xy = np.hstack((xy, w))
        names = ('x', 'y', 'weight')
    else:
        names = ('x', 'y')
    m = build_fit_matrix(rot, scale)
    xyr = np.dot(xy[:, :2], m.T) + shift
    imcat = Table(xy, names=names)
    radec = mock_fits_wcs.wcs_pix2world(xyr, 0)
    if weighted:
        radec = np.hstack((radec, w))
        names = ('RA', 'DEC', 'weight')
    else:
        names = ('RA', 'DEC')
    refcat = Table(radec, names=names)
    tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat})
    status = align_wcs(tpwcs, refcat, fitgeom=fitgeom, match=None)

    assert status
    assert tpwcs.meta['fit_info']['status'] == 'SUCCESS'
    assert tpwcs.meta['fit_info']['fitgeom'] == fitgeom
    assert np.allclose(tpwcs.meta['fit_info']['shift'], shift)
    assert np.allclose(tpwcs.meta['fit_info']['matrix'], m)
    assert np.allclose(tpwcs.meta['fit_info']['rot'], rot)
    assert tpwcs.meta['fit_info']['proper']
    assert np.allclose(tpwcs.meta['fit_info']['scale'], scale)
    assert tpwcs.meta['fit_info']['rmse'] < 1.0e-8
Пример #10
0
def test_align_wcs_simple_twpwcs_ref(mock_fits_wcs):
    shift = (12, -34)
    rot = (15, 17)
    scale = (1.0123, 0.9876)

    xy = 1024 * np.random.random((100, 2))
    m = build_fit_matrix(rot, scale)
    xyr = np.dot(xy, m.T) + shift
    imcat = Table(xy, names=('x', 'y'))
    refcat = Table(xyr, names=('x', 'y'))
    tpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': imcat})
    reftpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': refcat})
    status = align_wcs(tpwcs,
                       reftpwcs,
                       ref_tpwcs=tpwcs,
                       fitgeom='general',
                       match=None)

    assert status
    assert tpwcs.meta['fit_info']['status'] == 'SUCCESS'
    assert tpwcs.meta['fit_info']['fitgeom'] == 'general'
    assert np.allclose(tpwcs.meta['fit_info']['shift'], shift)
    assert np.allclose(tpwcs.meta['fit_info']['matrix'], m)
    assert np.allclose(tpwcs.meta['fit_info']['rot'], rot)
    assert tpwcs.meta['fit_info']['proper']
    assert np.allclose(tpwcs.meta['fit_info']['scale'], scale)
    assert tpwcs.meta['fit_info']['rmse'] < 1.0e-8
Пример #11
0
def test_align_wcs_tpwcs_type(mock_fits_wcs):
    imcat = Table([[1, 2, 3, 4], [0, 3, 1, 5]], names=('x', 'y'))

    class WrongTPWCS:
        def __init__(self, wcs):
            self.wcs = wcs
            self.meta = {'catalog': imcat}

    err = ("Input 'wcscat' must be either a single TPWCS-derived "
           " object or a list of TPWCS-derived objects.")

    tpwcs = WrongTPWCS(mock_fits_wcs)

    with pytest.raises(TypeError) as e:
        align_wcs(tpwcs)
    assert (e.value.args[0] == err)

    with pytest.raises(TypeError) as e:
        align_wcs([tpwcs, tpwcs])
    assert (e.value.args[0] == err)
Пример #12
0
def test_align_wcs_minobj(mock_fits_wcs):
    shift = (12, -34)
    rot = (15, 17)
    scale = (1.0123, 0.9876)

    crpix = mock_fits_wcs.wcs.crpix - 1
    xy = 1024 * np.random.random((100, 2))
    m = build_fit_matrix(rot, scale)
    xyr = np.dot(xy - crpix, m) + crpix + shift
    imcat = Table(xy, names=('x', 'y'))
    refcat = Table(xyr, names=('x', 'y'))
    tpwcs1 = FITSWCS(mock_fits_wcs, meta={'catalog': imcat})
    reftpwcs = FITSWCS(mock_fits_wcs, meta={'catalog': refcat})

    status = align_wcs([reftpwcs, tpwcs1],
                       refcat=None,
                       fitgeom='general',
                       minobj=200,
                       match=None,
                       enforce_user_order=True,
                       expand_refcat=False)
    assert status
    assert reftpwcs.meta['fit_info']['status'] == 'REFERENCE'
    assert tpwcs1.meta['fit_info']['status'] == 'FAILED: not enough matches'
Пример #13
0
    def process(self, input):

        try:
            images = datamodels.ModelContainer(input)
        except TypeError as e:
            e.args = ("Input to tweakreg must be a list of DataModels, an "
                      "association, or an already open ModelContainer "
                      "containing one or more DataModels.", ) + e.args[1:]
            raise e

        # Build the catalogs for input images
        for image_model in images:
            catalog = make_tweakreg_catalog(image_model,
                                            self.kernel_fwhm,
                                            self.snr_threshold,
                                            brightest=self.brightest,
                                            peakmax=self.peakmax)

            # filter out sources outside the image array if WCS validity
            # region is provided:
            wcs_bounds = image_model.meta.wcs.pixel_bounds
            if wcs_bounds is not None:
                ((xmin, xmax), (ymin, ymax)) = wcs_bounds
                xname = 'xcentroid' if 'xcentroid' in catalog.colnames else 'x'
                yname = 'ycentroid' if 'ycentroid' in catalog.colnames else 'y'
                x = catalog[xname]
                y = catalog[yname]
                mask = (x > xmin) & (x < xmax) & (y > ymin) & (y < ymax)
                catalog = catalog[mask]

            filename = image_model.meta.filename
            nsources = len(catalog)
            if nsources == 0:
                self.log.warning('No sources found in {}.'.format(filename))
            else:
                self.log.info('Detected {} sources in {}.'.format(
                    len(catalog), filename))

            if self.save_catalogs:
                catalog_filename = filename.replace(
                    '.fits', '_cat.{}'.format(self.catalog_format))
                if self.catalog_format == 'ecsv':
                    fmt = 'ascii.ecsv'
                elif self.catalog_format == 'fits':
                    # NOTE: The catalog must not contain any 'None' values.
                    #       FITS will also not clobber existing files.
                    fmt = 'fits'
                else:
                    raise ValueError(
                        '\'catalog_format\' must be "ecsv" or "fits".')
                catalog.write(catalog_filename, format=fmt, overwrite=True)
                self.log.info(
                    'Wrote source catalog: {}'.format(catalog_filename))
                image_model.meta.tweakreg_catalog = catalog_filename

            image_model.catalog = catalog

        # Now use the catalogs for tweakreg
        if len(images) == 0:
            raise ValueError("Input must contain at least one image model.")

        # group images by their "group id":
        grp_img = images.models_grouped

        self.log.info('')
        self.log.info("Number of image groups to be aligned: {:d}.".format(
            len(grp_img)))
        self.log.info("Image groups:")

        if len(grp_img) == 1:
            self.log.info("* Images in GROUP 1:")
            for im in grp_img[0]:
                self.log.info("     {}".format(im.meta.filename))
            self.log.info('')

            # we need at least two exposures to perform image alignment
            self.log.warning("At least two exposures are required for image "
                             "alignment.")
            self.log.warning("Nothing to do. Skipping 'TweakRegStep'...")
            self.skip = True
            for model in images:
                model.meta.cal_step.tweakreg = "SKIPPED"
            return input

        # create a list of WCS-Catalog-Images Info and/or their Groups:
        imcats = []
        for g in grp_img:
            if len(g) == 0:
                raise AssertionError("Logical error in the pipeline code.")
            else:
                group_name = _common_name(g)
                wcsimlist = list(map(self._imodel2wcsim, g))
                self.log.info("* Images in GROUP '{}':".format(group_name))
                for im in wcsimlist:
                    im.meta['group_id'] = group_name
                    self.log.info("     {}".format(im.meta['name']))
                imcats.extend(wcsimlist)

        self.log.info('')

        # align images:
        tpmatch = TPMatch(searchrad=self.searchrad,
                          separation=self.separation,
                          use2dhist=self.use2dhist,
                          tolerance=self.tolerance,
                          xoffset=self.xoffset,
                          yoffset=self.yoffset)

        try:
            align_wcs(imcats,
                      refcat=None,
                      enforce_user_order=self.enforce_user_order,
                      expand_refcat=self.expand_refcat,
                      minobj=self.minobj,
                      match=tpmatch,
                      fitgeom=self.fitgeometry,
                      nclip=self.nclip,
                      sigma=(self.sigma, 'rmse'))

        except ValueError as e:
            msg = e.args[0]
            if (msg == "Too few input images (or groups of images) with "
                    "non-empty catalogs."):
                # we need at least two exposures to perform image alignment
                self.log.warning(msg)
                self.log.warning("At least two exposures are required for "
                                 "image alignment.")
                self.log.warning("Nothing to do. Skipping 'TweakRegStep'...")
                self.skip = True
                for model in images:
                    model.meta.cal_step.tweakreg = "SKIPPED"
                return images

            else:
                raise e

        for imcat in imcats:
            imcat.meta['image_model'].meta.cal_step.tweakreg = 'COMPLETE'
            # retrieve fit status and update wcs if fit is successful:
            fit_info = imcat.meta.get('fit_info')
            if fit_info['status'] in 'SUCCESS':
                imcat.meta['image_model'].meta.wcs = imcat.wcs

        return images
Пример #14
0
    def process(self, input):

        try:
            images = datamodels.ModelContainer(input)
        except TypeError as e:
            e.args = ("Input to tweakreg must be a list of DataModels, an "
                      "association, or an already open ModelContainer "
                      "containing one or more DataModels.", ) + e.args[1:]
            raise e

        if self.align_to_gaia:
            # Set expand_refcat to True to eliminate possibility of duplicate
            # entries when aligning to GAIA
            self.expand_refcat = True

        if len(images) == 0:
            raise ValueError("Input must contain at least one image model.")

        # Build the catalogs for input images
        for image_model in images:
            catalog = make_tweakreg_catalog(image_model,
                                            self.kernel_fwhm,
                                            self.snr_threshold,
                                            brightest=self.brightest,
                                            peakmax=self.peakmax)

            # filter out sources outside the WCS bounding box
            bb = image_model.meta.wcs.bounding_box
            if bb is not None:
                ((xmin, xmax), (ymin, ymax)) = bb
                xname = 'xcentroid' if 'xcentroid' in catalog.colnames else 'x'
                yname = 'ycentroid' if 'ycentroid' in catalog.colnames else 'y'
                x = catalog[xname]
                y = catalog[yname]
                mask = (x > xmin) & (x < xmax) & (y > ymin) & (y < ymax)
                catalog = catalog[mask]

            filename = image_model.meta.filename
            nsources = len(catalog)
            if nsources == 0:
                self.log.warning('No sources found in {}.'.format(filename))
            else:
                self.log.info('Detected {} sources in {}.'.format(
                    len(catalog), filename))

            if self.save_catalogs:
                catalog_filename = filename.replace(
                    '.fits', '_cat.{}'.format(self.catalog_format))
                if self.catalog_format == 'ecsv':
                    fmt = 'ascii.ecsv'
                elif self.catalog_format == 'fits':
                    # NOTE: The catalog must not contain any 'None' values.
                    #       FITS will also not clobber existing files.
                    fmt = 'fits'
                else:
                    raise ValueError(
                        '\'catalog_format\' must be "ecsv" or "fits".')
                catalog.write(catalog_filename, format=fmt, overwrite=True)
                self.log.info(
                    'Wrote source catalog: {}'.format(catalog_filename))
                image_model.meta.tweakreg_catalog = catalog_filename

            # Temporarily attach catalog to the image model so that it follows
            # the grouping by exposure, to be removed after use below
            image_model.catalog = catalog

        # group images by their "group id":
        grp_img = list(images.models_grouped)

        self.log.info('')
        self.log.info("Number of image groups to be aligned: {:d}.".format(
            len(grp_img)))
        self.log.info("Image groups:")

        if len(grp_img) == 1:
            self.log.info("* Images in GROUP 1:")
            for im in grp_img[0]:
                self.log.info("     {}".format(im.meta.filename))
            self.log.info('')

            # we need at least two exposures to perform image alignment
            self.log.warning("At least two exposures are required for image "
                             "alignment.")
            self.log.warning("Nothing to do. Skipping 'TweakRegStep'...")
            self.skip = True
            for model in images:
                model.meta.cal_step.tweakreg = "SKIPPED"
                # Remove the attached catalogs
                del model.catalog
            return input

        # create a list of WCS-Catalog-Images Info and/or their Groups:
        imcats = []
        for g in grp_img:
            if len(g) == 0:
                raise AssertionError("Logical error in the pipeline code.")
            else:
                group_name = _common_name(g)
                wcsimlist = list(map(self._imodel2wcsim, g))
                # Remove the attached catalogs
                for model in g:
                    del model.catalog
                self.log.info("* Images in GROUP '{}':".format(group_name))
                for im in wcsimlist:
                    im.meta['group_id'] = group_name
                    self.log.info("     {}".format(im.meta['name']))
                imcats.extend(wcsimlist)

        self.log.info('')

        # align images:
        tpmatch = TPMatch(searchrad=self.searchrad,
                          separation=self.separation,
                          use2dhist=self.use2dhist,
                          tolerance=self.tolerance,
                          xoffset=self.xoffset,
                          yoffset=self.yoffset)

        try:
            align_wcs(imcats,
                      refcat=None,
                      enforce_user_order=self.enforce_user_order,
                      expand_refcat=self.expand_refcat,
                      minobj=self.minobj,
                      match=tpmatch,
                      fitgeom=self.fitgeometry,
                      nclip=self.nclip,
                      sigma=(self.sigma, 'rmse'))

        except ValueError as e:
            msg = e.args[0]
            if (msg == "Too few input images (or groups of images) with "
                    "non-empty catalogs."):
                # we need at least two exposures to perform image alignment
                self.log.warning(msg)
                self.log.warning("At least two exposures are required for "
                                 "image alignment.")
                self.log.warning("Nothing to do. Skipping 'TweakRegStep'...")
                self.skip = True
                for model in images:
                    model.meta.cal_step.tweakreg = "SKIPPED"
                return images

            else:
                raise e

        if self.align_to_gaia:
            # Get catalog of GAIA sources for the field
            #
            # NOTE:  If desired, the pipeline can write out the reference
            #        catalog as a separate product with a name based on
            #        whatever convention is determined by the JWST Cal Working
            #        Group.
            if self.save_gaia_catalog:
                output_name = 'fit_{}_ref.ecsv'.format(
                    self.gaia_catalog.lower())
            else:
                output_name = None
            ref_cat = amutils.create_astrometric_catalog(images,
                                                         self.gaia_catalog,
                                                         output=output_name)

            # Check that there are enough GAIA sources for a reliable/valid fit
            num_ref = len(ref_cat)
            if num_ref < self.min_gaia:
                msg = "Not enough GAIA sources for a fit: {}\n".format(num_ref)
                msg += "Skipping alignment to {} astrometric catalog!\n".format(
                    self.gaia_catalog)
                # Raise Exception here to avoid rest of code in this try block
                self.log.warning(msg)
            else:
                # align images:
                # Update to separation needed to prevent confusion of sources
                # from overlapping images where centering is not consistent or
                # for the possibility that errors still exist in relative overlap.
                tpmatch_gaia = TPMatch(searchrad=self.searchrad * 3.0,
                                       separation=self.separation / 10.0,
                                       use2dhist=self.use2dhist,
                                       tolerance=self.tolerance,
                                       xoffset=0.0,
                                       yoffset=0.0)

                # Set group_id to same value so all get fit as one observation
                # The assigned value, 987654, has been hard-coded to make it
                # easy to recognize when alignment to GAIA was being performed
                # as opposed to the group_id values used for relative alignment
                # earlier in this step.
                for imcat in imcats:
                    imcat.meta['group_id'] = 987654
                    if 'REFERENCE' in imcat.meta['fit_info']['status']:
                        del imcat.meta['fit_info']

                # Perform fit
                align_wcs(imcats,
                          refcat=ref_cat,
                          enforce_user_order=True,
                          expand_refcat=False,
                          minobj=self.minobj,
                          match=tpmatch_gaia,
                          fitgeom=self.fitgeometry,
                          nclip=self.nclip,
                          sigma=(self.sigma, 'rmse'))

        for imcat in imcats:
            imcat.meta['image_model'].meta.cal_step.tweakreg = 'COMPLETE'

            # retrieve fit status and update wcs if fit is successful:
            if 'SUCCESS' in imcat.meta.get('fit_info')['status']:

                # Update/create the WCS .name attribute with information
                # on this astrometric fit as the only record that it was
                # successful:
                if self.align_to_gaia:
                    # NOTE: This .name attrib agreed upon by the JWST Cal
                    #       Working Group.
                    #       Current value is merely a place-holder based
                    #       on HST conventions. This value should also be
                    #       translated to the FITS WCSNAME keyword
                    #       IF that is what gets recorded in the archive
                    #       for end-user searches.
                    imcat.wcs.name = "FIT-LVL3-{}".format(self.gaia_catalog)

                imcat.meta['image_model'].meta.wcs = imcat.wcs
                """
                # Also update FITS representation in input exposures for
                # subsequent reprocessing by the end-user.
                # Not currently enabled, but may be requested later...
                gwcs_header = imcat.wcs.to_fits_sip(max_pix_error=0.1,
                                                max_inv_pix_error=0.1,
                                                degree=3,
                                                npoints=128)
                imcat.meta['image_model'].wcs = wcs.WCS(header=gwcs_header)
                """

        return images
Пример #15
0
def test_multi_image_set(mock_fits_wcs):
    np.random.seed(1)
    v1 = 1e10 * np.finfo(np.double).eps
    v2 = 1 - v1
    corners = np.array([[v1, v1], [v1, v2], [v2, v2], [v2, v1]])
    n = 1

    def get_points():
        nonlocal n, v1, v2
        pts = []
        for _ in range(4):
            v1 *= n
            v2 = 1 - v1
            n += 1
            corners = np.array([[v1, v1], [v1, v2], [v2, v2], [v2, v1]])
            pts += [
                v1 + (v2 - v1) * np.random.random((250 - len(corners), 2)),
                corners
            ]
        return np.vstack(pts)

    # reference catalog sources:
    wcsref = copy.deepcopy(mock_fits_wcs)
    xyref = 512 * get_points()
    xyref[250:500, 0] += 512
    xyref[500:750, 1] += 512
    xyref[750:, :] += 512

    radec = wcsref.wcs_pix2world(xyref, 0)
    refcat = Table(radec, names=('RA', 'DEC'))

    wcsref = copy.deepcopy(mock_fits_wcs)
    refcat = Table(xyref, names=('x', 'y'))
    ref_img_tpwcs = FITSWCS(wcsref, meta={'catalog': refcat,
                                          'name': 'ref_img_tpwcs'})

    # single overlap catalog sources:
    wcsim1 = copy.deepcopy(mock_fits_wcs)
    wcsim1.wcs.crval += 1e-5

    xyim1 = 512 + 512 * np.vstack((np.random.random((1000, 2)), corners))
    xyim1[:250, :] = xyref[750:, :]  # overlap
    xyim1[250:500, 0] += 512
    xyim1[500:750, 1] += 512
    xyim1[750:, :] += 512

    imcat = Table(xyim1, names=('x', 'y'))
    im1_tpwcs = FITSWCS(wcsim1, meta={'catalog': imcat, 'name': 'im1_tpwcs'})

    # non-overlaping image:
    wcsim2 = copy.deepcopy(mock_fits_wcs)
    xyim2 = xyim1.copy()
    xyim2[:, 0] += 2000.0
    imcat = Table(xyim2, names=('x', 'y'))
    im2_tpwcs = FITSWCS(wcsim2, meta={'catalog': imcat, 'name': 'im2_tpwcs'})

    # grouped images overlap reference catalog sources:
    wcsim3 = copy.deepcopy(mock_fits_wcs)

    xyim3 = 512 * np.vstack((np.random.random((1000, 2)), corners))
    xyim3[:250, :] = xyref[250:500, :]  # overlap
    xyim3[250:750, 0] += 1024
    xyim3[750:, 0] += 512
    xyim3[500:, 1] -= 512

    imcat = Table(xyim3, names=('x', 'y'))
    im3_tpwcs = FITSWCS(wcsim3, meta={
        'catalog': imcat, 'group_id': 'group1', 'name': 'im3_tpwcs'
    })

    wcsim4 = copy.deepcopy(mock_fits_wcs)
    xyim4 = (512, -512) + 1024 * np.vstack((np.random.random((1000, 2)),
                                            corners))
    imcat = Table(xyim4, names=('x', 'y'))
    im4_tpwcs = FITSWCS(wcsim4, meta={  # noqa: F841
        'catalog': imcat, 'group_id': 'group1', 'name': 'im4_tpwcs'
    })

    wcsim5 = copy.deepcopy(mock_fits_wcs)
    xyim5 = (512, -512 - 1024) + 1024 * np.vstack((np.random.random((1000, 2)),
                                                   corners))
    imcat = Table(xyim5, names=('x', 'y'))
    im5_tpwcs = FITSWCS(wcsim5, meta={
        'catalog': imcat, 'group_id': 'group1', 'name': 'im5_tpwcs'
    })

    # Temporarily remove im4_tpwcs from imglist due to crashes in
    # spherical_geometry.
    imglist = [
        ref_img_tpwcs, im1_tpwcs, im2_tpwcs, im5_tpwcs, im3_tpwcs,  # im4_tpwcs
    ]

    status = align_wcs(imglist, None, fitgeom='general',
                       enforce_user_order=False, expand_refcat=True)

    assert status
    assert im1_tpwcs.meta['fit_info']['status'] == 'SUCCESS'
    assert im1_tpwcs.meta['fit_info']['fitgeom'] == 'general'
    assert im1_tpwcs.meta['fit_info']['rmse'] < 1e8 * _ATOL
    assert np.allclose(im1_tpwcs.wcs.wcs.crval, ref_img_tpwcs.wcs.wcs.crval,
                       rtol=0, atol=1.0e-10)
Пример #16
0
def test_align_wcs_tpwcs_missing_cat(mock_fits_wcs):
    tpwcs = FITSWCS(mock_fits_wcs)
    with pytest.raises(ValueError) as e:
        align_wcs(tpwcs)
    assert (e.value.args[0] == "Each object in 'wcscat' must have a valid "
            "catalog.")
Пример #17
0
def test_multichip_jwst_alignment(monkeypatch):
    # this test is fundamentally equivalent to test_multichip_alignment_step()
    # with the following differences:
    # 1. test_multichip_alignment_step() test includes parts of the JWST
    #    pipeline step itself;
    # 2. test_multichip_alignment_step() does not have access to 'fit_info'
    #    in the meta data and so test_multichip_jwst_alignment() can test
    #    the fit more extensively.
    monkeypatch.setattr(tweakreg_step, 'align_wcs', _align_wcs)
    monkeypatch.setattr(tweakreg_step, 'make_tweakreg_catalog',
                        _make_tweakreg_catalog)

    w1 = _make_gwcs_wcs('data/wfc3_uvis1.hdr')
    imcat1 = tweakwcs.JWSTgWCS(w1, {'v2_ref': 0, 'v3_ref': 0, 'roll_ref': 0})
    imcat1.meta['catalog'] = table.Table.read(
        get_pkg_data_filename('data/wfc3_uvis1.cat'),
        format='ascii.csv',
        delimiter=' ',
        names=['x', 'y'])
    imcat1.meta['catalog']['x'] += 1
    imcat1.meta['catalog']['y'] += 1
    imcat1.meta['group_id'] = 1
    imcat1.meta['name'] = 'ext1'

    w2 = _make_gwcs_wcs('data/wfc3_uvis2.hdr')
    imcat2 = tweakwcs.JWSTgWCS(w2, {'v2_ref': 0, 'v3_ref': 0, 'roll_ref': 0})
    imcat2.meta['catalog'] = table.Table.read(
        get_pkg_data_filename('data/wfc3_uvis2.cat'),
        format='ascii.csv',
        delimiter=' ',
        names=['x', 'y'])
    imcat2.meta['catalog']['x'] += 1
    imcat2.meta['catalog']['y'] += 1
    imcat2.meta['group_id'] = 1
    imcat2.meta['name'] = 'ext4'

    refcat = table.Table.read(get_pkg_data_filename('data/ref.cat'),
                              format='ascii.csv',
                              delimiter=' ',
                              names=['RA', 'DEC'])

    align_wcs([imcat1, imcat2],
              refcat,
              match=_match,
              nclip=None,
              sigma=3,
              fitgeom='general')

    fi1 = imcat1.meta['fit_info']
    fi2 = imcat2.meta['fit_info']

    w1m = imcat1.wcs
    w2m = imcat2.wcs

    assert np.allclose(w1m(*w1.crpix), (83.206917667519, -67.73275818507248),
                       rtol=0)
    assert np.allclose(w2m(*w2.crpix), (83.15167050722597, -67.74220306069903),
                       rtol=0)

    assert np.allclose(fi1['<scale>'], 1.0025, rtol=0, atol=2e-8)
    assert np.allclose(fi2['<scale>'], 1.0025, rtol=0, atol=2e-8)

    assert fi1['rmse'] < 5e-5
    assert fi2['rmse'] < 5e-5

    ra1, dec1 = imcat1.wcs(imcat1.meta['catalog']['x'],
                           imcat1.meta['catalog']['y'])
    ra2, dec2 = imcat2.wcs(imcat2.meta['catalog']['x'],
                           imcat2.meta['catalog']['y'])
    ra = np.concatenate([ra1, ra2])
    dec = np.concatenate([dec1, dec2])
    rra = refcat['RA']
    rdec = refcat['DEC']
    rmse_ra = np.sqrt(np.mean((ra - rra)**2))
    rmse_dec = np.sqrt(np.mean((dec - rdec)**2))

    assert rmse_ra < _REF_RMSE_RA
    assert rmse_dec < _REF_RMSE_DEC
Пример #18
0
def _align_wcs(imcats, **kwargs):
    new_kwargs = {k: v for k, v in kwargs.items() if k != 'match'}
    new_kwargs['match'] = _match
    return align_wcs(imcats, **new_kwargs)