示例#1
0
def _buildStaticDQUserMask(img, ext, sky_bits, use_static, umask, umaskext,
                           in_memory):
    # creates a temporary mask by combining 'static' mask,
    # DQ image, and user-supplied mask.

    def merge_masks(m1, m2):
        if m1 is None: return m2
        if m2 is None: return m1
        return np.logical_and(m1, m2).astype(np.uint8)

    mask = None

    # build DQ mask
    if sky_bits is not None:
        mask = img.buildMask(img[ext]._chip, bits=sky_bits)

    # get correct static mask mask filenames/objects
    staticMaskName = img[ext].outputNames['staticMask']
    smask = None
    if use_static:
        if img.inmemory:
            if staticMaskName in img.virtualOutputs:
                smask = img.virtualOutputs[staticMaskName].data
        else:
            if staticMaskName is not None and os.path.isfile(staticMaskName):
                sm, dq = openImageEx(staticMaskName,
                                     mode='readonly',
                                     memmap=False,
                                     saveAsMEF=False,
                                     clobber=False,
                                     imageOnly=True,
                                     openImageHDU=True,
                                     openDQHDU=False,
                                     preferMEF=False,
                                     verbose=False)
                if sm.hdu is not None:
                    smask = sm.hdu[0].data
                    sm.release()
            else:
                log.warning("Static mask for file \'{}\', ext={} NOT FOUND." \
                            .format(img._filename, ext))
        # combine DQ and static masks:
        mask = merge_masks(mask, smask)

    # combine user mask with the previously computed mask:
    if umask is not None and not umask.closed:
        if mask is None:
            # return user-supplied mask:
            umask.hold()
            return (umask, umaskext)
        else:
            # combine user mask with the previously computed mask:
            dtm = umask.hdu[umaskext].data
            mask = merge_masks(mask, dtm)

    if mask is None:
        return (None, None)
    elif mask.sum() == 0:
        log.warning("All pixels masked out when applying DQ, " \
                    "static, and user masks!")

    # save mask to a temporary file:
    (root, suffix, fext) = file_name_components(img._filename)
    if in_memory:
        tmpmask = in_memory_mask(mask)
        strext = ext2str(ext, compact=True, default_extver=None)
        tmpmask.original_fname = "{1:s}{0:s}{2:s}{0:s}{3:s}" \
            .format('_', root, suffix, 'in-memory_skymatch_mask')
    else:
        (tmpfname, tmpmask) = temp_mask_file(mask,
                                             root,
                                             prefix='',
                                             suffix='skymatch_mask',
                                             ext=ext,
                                             randomize_prefix=False)
        img[ext].outputNames['skyMatchMask'] = tmpfname

    return (tmpmask, 0)
示例#2
0
def tweakback(drzfile,
              input=None,
              origwcs=None,
              newname=None,
              wcsname=None,
              extname='SCI',
              force=False,
              verbose=False):
    """
    Apply WCS solution recorded in drizzled file to distorted input images
    (``_flt.fits`` files) used to create the drizzled file.  This task relies on
    the original WCS and updated WCS to be recorded in the drizzled image's
    header as the last 2 alternate WCSs.

    Parameters
    ----------
    drzfile : str (Default = '')
        filename of undistorted image which contains the new WCS
        and WCS prior to being updated

    newname : str (Default = None)
        Value of ``WCSNAME`` to be used to label the updated solution in the
        output (eq., ``_flt.fits``) files.  If left blank or None, it will
        default to using the current ``WCSNAME`` value from the input drzfile.

    input : str (Default = '')
        filenames of distorted images to be updated using new WCS
        from 'drzfile'.  These can be provided either as an ``@-file``,
        a comma-separated list of filenames or using wildcards.

        .. note:: A blank value will indicate that the task should derive the
           filenames from the 'drzfile' itself, if possible. The filenames will be
           derived from the ``D*DATA`` keywords written out by
           ``AstroDrizzle``. If they can not be found, the task will quit.

    origwcs : str (Default = None)
        Value of ``WCSNAME`` keyword prior to the drzfile image being updated
        by ``TweakReg``.  If left blank or None, it will default to using the
        second to last ``WCSNAME*`` keyword value found in the header.

    wcsname : str (Default = None)
        Value of WCSNAME for updated solution written out by ``TweakReg`` as
        specified by the `wcsname` parameter from ``TweakReg``.  If this is
        left blank or `None`, it will default to the current ``WCSNAME``
        value from the input drzfile.

    extname : str (Default = 'SCI')
        Name of extension in `input` files to be updated with new WCS

    force : bool  (Default = False)
        This parameters specified whether or not to force an update of the WCS
        even though WCS already exists with this solution or `wcsname`?

    verbose : bool (Default = False)
        This parameter specifies whether or not to print out additional
        messages during processing.


    Notes
    -----
    The algorithm used by this function is based on linearization of
    the exact compound operator that converts input image coordinates
    to the coordinates (in the input image) that would result in
    alignment with the new drizzled image WCS.

    If no input distorted files are specified as input, this task will attempt
    to generate the list of filenames from the drizzled input file's own
    header.

    EXAMPLES
    --------
    An image named ``acswfc_mos2_drz.fits`` was created from 4 images using
    astrodrizzle. This drizzled image was then aligned to another image using
    tweakreg and the header was updated using the ``WCSNAME`` = ``TWEAK_DRZ``.
    The new WCS can then be used to update each of the 4 images that were
    combined to make up this drizzled image using:

    >>> from drizzlepac import tweakback
    >>> tweakback.tweakback('acswfc_mos2_drz.fits')

    If the same WCS should be applied to a specific set of images, those images
    can be updated using:

    >>> tweakback.tweakback('acswfc_mos2_drz.fits',
    ...                     input='img_mos2a_flt.fits,img_mos2e_flt.fits')

    See Also
    --------
    stwcs.wcsutil.altwcs: Alternate WCS implementation

    """
    print("TweakBack Version {:s} started at: {:s}\n".format(
        __version__,
        util._ptime()[0]))

    # Interpret input list/string into list of filename(s)
    fltfiles = parseinput.parseinput(input)[0]

    if fltfiles is None or len(fltfiles) == 0:
        # try to extract the filenames from the drizzled file's header
        fltfiles = extract_input_filenames(drzfile)
        if fltfiles is None:
            print('*' * 60)
            print('*')
            print('* ERROR:')
            print('*    No input filenames found! ')
            print(
                '*    Please specify "fltfiles" or insure that input drizzled')
            print('*    image contains D*DATA keywords. ')
            print('*')
            print('*' * 60)
            raise ValueError

    if not isinstance(fltfiles, list):
        fltfiles = [fltfiles]

    sciext = determine_extnum(drzfile, extname='SCI')
    scihdr = fits.getheader(drzfile, ext=sciext, memmap=False)

    ### Step 1: Read in updated and original WCS solutions
    # determine keys for all alternate WCS solutions in drizzled image header
    wkeys = wcsutil.altwcs.wcskeys(drzfile, ext=sciext)
    if len(wkeys) < 2:
        raise ValueError(
            f"'{drzfile}' must contain at least two valid WCS: original and updated."
        )
    wnames = wcsutil.altwcs.wcsnames(drzfile, ext=sciext)
    if not util.is_blank(newname):
        final_name = newname
    else:
        final_name = wnames[wkeys[-1]]

    # Read in HSTWCS objects for final,updated WCS and previous WCS from
    # from drizzled image header
    # The final solution also serves as reference WCS when using updatehdr

    if not util.is_blank(wcsname):
        for wkey, wname in wnames.items():
            if wname == wcsname:
                wcskey = wkey
                break
        else:
            raise ValueError(
                f"WCS with name '{wcsname}' not found in '{drzfile}'")
    else:
        wcskey = wkeys[-1]

    final_wcs = wcsutil.HSTWCS(drzfile, ext=sciext, wcskey=wcskey)

    if not util.is_blank(origwcs):
        for wkey, wname in wnames.items():
            if wname == origwcs:
                orig_wcskey = wkey
                break
        else:
            raise ValueError(
                f"WCS with name '{origwcs}' not found in '{drzfile}'")
    else:
        _, orig_wcskey = determine_orig_wcsname(scihdr, wnames, wkeys)

    orig_wcs = wcsutil.HSTWCS(drzfile, ext=sciext, wcskey=orig_wcskey)

    # read in RMS values reported for new solution
    crderr1kw = 'CRDER1' + wkeys[-1]
    crderr2kw = 'CRDER2' + wkeys[-1]

    if crderr1kw in scihdr:
        crderr1 = fits.getval(drzfile, crderr1kw, ext=sciext, memmap=False)
    else:
        crderr1 = 0.0

    if crderr2kw in scihdr:
        crderr2 = fits.getval(drzfile, crderr2kw, ext=sciext, memmap=False)
    else:
        crderr2 = 0.0
    del scihdr

    ### Step 2: Apply solution to input file headers
    for fname in fltfiles:
        logstr = "....Updating header for {:s}...".format(fname)
        if verbose:
            print("\n{:s}\n".format(logstr))
        else:
            log.info(logstr)

        # reset header WCS keywords to original (OPUS generated) values
        imhdulist = fits.open(fname, mode='update', memmap=False)
        extlist = get_ext_list(imhdulist, extname='SCI')
        if not extlist:
            extlist = [0]

        # Process MEF images...
        for ext in extlist:
            logstr = "Processing {:s}[{:s}]".format(imhdulist.filename(),
                                                    ext2str(ext))
            if verbose:
                print("\n{:s}\n".format(logstr))
            else:
                log.info(logstr)
            chip_wcs = wcsutil.HSTWCS(imhdulist, ext=ext)

            update_chip_wcs(chip_wcs,
                            orig_wcs,
                            final_wcs,
                            xrms=crderr1,
                            yrms=crderr2)

            # Update FITS file with newly updated WCS for this chip
            extnum = imhdulist.index(imhdulist[ext])
            updatehdr.update_wcs(imhdulist,
                                 extnum,
                                 chip_wcs,
                                 wcsname=final_name,
                                 reusename=False,
                                 verbose=verbose)

        imhdulist.close()
示例#3
0
def tweakback(drzfile, input=None,  origwcs = None,
                newname = None, wcsname = None,
                extname='SCI', force=False, verbose=False):
    """
    Apply WCS solution recorded in drizzled file to distorted input images
    (``_flt.fits`` files) used to create the drizzled file.  This task relies on
    the original WCS and updated WCS to be recorded in the drizzled image's
    header as the last 2 alternate WCSs.

    Parameters
    ----------
    drzfile : str (Default = '')
        filename of undistorted image which contains the new WCS
        and WCS prior to being updated

    newname : str (Default = None)
        Value of ``WCSNAME`` to be used to label the updated solution in the
        output (eq., ``_flt.fits``) files.  If left blank or None, it will
        default to using the current ``WCSNAME`` value from the input drzfile.

    input : str (Default = '')
        filenames of distorted images to be updated using new WCS
        from 'drzfile'.  These can be provided either as an ``@-file``,
        a comma-separated list of filenames or using wildcards.

        .. note:: A blank value will indicate that the task should derive the
           filenames from the 'drzfile' itself, if possible. The filenames will be
           derived from the ``D*DATA`` keywords written out by
           ``AstroDrizzle``. If they can not be found, the task will quit.

    origwcs : str (Default = None)
        Value of ``WCSNAME`` keyword prior to the drzfile image being updated
        by ``TweakReg``.  If left blank or None, it will default to using the
        second to last ``WCSNAME*`` keyword value found in the header.

    wcsname : str (Default = None)
        Value of WCSNAME for updated solution written out by ``TweakReg`` as
        specified by the `wcsname` parameter from ``TweakReg``.  If this is
        left blank or `None`, it will default to the current ``WCSNAME``
        value from the input drzfile.

    extname : str (Default = 'SCI')
        Name of extension in `input` files to be updated with new WCS

    force : bool  (Default = False)
        This parameters specified whether or not to force an update of the WCS
        even though WCS already exists with this solution or `wcsname`?

    verbose : bool (Default = False)
        This parameter specifies whether or not to print out additional
        messages during processing.


    Notes
    -----
    The algorithm used by this function is based on linearization of
    the exact compound operator that converts input image coordinates
    to the coordinates (in the input image) that would result in
    alignment with the new drizzled image WCS.

    If no input distorted files are specified as input, this task will attempt
    to generate the list of filenames from the drizzled input file's own
    header.

    EXAMPLES
    --------
    An image named ``acswfc_mos2_drz.fits`` was created from 4 images using
    astrodrizzle. This drizzled image was then aligned to another image using
    tweakreg and the header was updated using the ``WCSNAME`` = ``TWEAK_DRZ``.
    The new WCS can then be used to update each of the 4 images that were
    combined to make up this drizzled image using:

    >>> from drizzlepac import tweakback
    >>> tweakback.tweakback('acswfc_mos2_drz.fits')

    If the same WCS should be applied to a specific set of images, those images
    can be updated using:

    >>> tweakback.tweakback('acswfc_mos2_drz.fits',
    ...                     input='img_mos2a_flt.fits,img_mos2e_flt.fits')

    See Also
    --------
    stwcs.wcsutil.altwcs: Alternate WCS implementation

    """
    print("TweakBack Version {:s}({:s}) started at: {:s}\n"
          .format(__version__,__version_date__,util._ptime()[0]))

    # Interpret input list/string into list of filename(s)
    fltfiles = parseinput.parseinput(input)[0]

    if fltfiles is None or len(fltfiles) == 0:
        # try to extract the filenames from the drizzled file's header
        fltfiles = extract_input_filenames(drzfile)
        if fltfiles is None:
            print('*'*60)
            print('*')
            print('* ERROR:')
            print('*    No input filenames found! ')
            print('*    Please specify "fltfiles" or insure that input drizzled')
            print('*    image contains D*DATA keywords. ')
            print('*')
            print('*'*60)
            raise ValueError

    if not isinstance(fltfiles,list):
        fltfiles = [fltfiles]

    sciext = determine_extnum(drzfile, extname='SCI')
    scihdr = fits.getheader(drzfile, ext=sciext, memmap=False)

    ### Step 1: Read in updated and original WCS solutions
    # determine keys for all alternate WCS solutions in drizzled image header
    wkeys = wcsutil.altwcs.wcskeys(drzfile, ext=sciext)
    wnames = wcsutil.altwcs.wcsnames(drzfile, ext=sciext)
    if not util.is_blank(newname):
        final_name = newname
    else:
        final_name = wnames[wkeys[-1]]

    # Read in HSTWCS objects for final,updated WCS and previous WCS from
    # from drizzled image header
    # The final solution also serves as reference WCS when using updatehdr
    if not util.is_blank(wcsname):
        for k in wnames:
            if wnames[k] == wcsname:
                wcskey = k
                break
    else:
        wcskey = wkeys[-1]
    final_wcs = wcsutil.HSTWCS(drzfile, ext=sciext, wcskey=wkeys[-1])

    if not util.is_blank(origwcs):
        for k in wnames:
            if wnames[k] == origwcs:
                orig_wcskey = k
                orig_wcsname = origwcs
                break
    else:
        orig_wcsname,orig_wcskey = determine_orig_wcsname(scihdr,wnames,wkeys)

    orig_wcs = wcsutil.HSTWCS(drzfile,ext=sciext,wcskey=orig_wcskey)

    # read in RMS values reported for new solution
    crderr1kw = 'CRDER1'+wkeys[-1]
    crderr2kw = 'CRDER2'+wkeys[-1]

    if crderr1kw in scihdr:
        crderr1 = fits.getval(drzfile, crderr1kw, ext=sciext, memmap=False)
    else:
        crderr1 = 0.0

    if crderr2kw in scihdr:
        crderr2 = fits.getval(drzfile, crderr2kw, ext=sciext, memmap=False)
    else:
        crderr2 = 0.0
    del scihdr

    ### Step 2: Apply solution to input file headers
    for fname in fltfiles:
        logstr = "....Updating header for {:s}...".format(fname)
        if verbose:
            print("\n{:s}\n".format(logstr))
        else:
            log.info(logstr)

        # reset header WCS keywords to original (OPUS generated) values
        imhdulist = fits.open(fname, mode='update', memmap=False)
        extlist = get_ext_list(imhdulist, extname='SCI')
        if not extlist:
            extlist = [0]

        # insure that input PRIMARY WCS has been archived before overwriting
        # with new solution
        wcsutil.altwcs.archiveWCS(imhdulist, extlist, reusekey=True)

        # Process MEF images...
        for ext in extlist:
            logstr = "Processing {:s}[{:s}]".format(imhdulist.filename(),
                                                    ext2str(ext))
            if verbose:
                print("\n{:s}\n".format(logstr))
            else:
                log.info(logstr)
            chip_wcs = wcsutil.HSTWCS(imhdulist, ext=ext)

            update_chip_wcs(chip_wcs, orig_wcs, final_wcs,
                            xrms=crderr1, yrms = crderr2)

            # Update FITS file with newly updated WCS for this chip
            extnum = imhdulist.index(imhdulist[ext])
            updatehdr.update_wcs(imhdulist, extnum, chip_wcs,
                                 wcsname=final_name, reusename=False,
                                 verbose=verbose)

        imhdulist.close()
示例#4
0
def updatewcs_with_shift(image,reference,wcsname=None, reusename=False,
                         fitgeom='rscale',
                         rot=0.0,scale=1.0,xsh=0.0,ysh=0.0,fit=None,
                         xrms=None, yrms = None,
                         verbose=False,force=False,sciext='SCI'):

    """
    Update the SCI headers in 'image' based on the fit provided as determined
    in the WCS specified by 'reference'.  The fit should be a 2-D matrix as
    generated for use with 'make_vector_plot()'.

    Notes
    -----
    The algorithm used to apply the provided fit solution to the image
    involves applying the following steps to the WCS of each of the
    input image's chips:

    1. compute RA/Dec with full distortion correction for
            reference point as (Rc_i,Dc_i)

    2. find the Xc,Yc for each Rc_i,Dc_i and get the difference from the
            CRPIX position for the reference WCS as (dXc_i,dYc_i)

    3. apply fit (rot&scale) to (dXc_i,dYc_i) then apply shift, then add
            CRPIX back to get new (Xcs_i,Ycs_i) position

    4. compute (Rcs_i,Dcs_i) as the sky coordinates for (Xcs_i,Ycs_i)

    5. compute delta of (Rcs_i-Rc_i, Dcs_i-Dcs_i) as (dRcs_i,dDcs_i)

    6. apply the fit to the chip's undistorted CD matrix, the apply linear
            distortion terms back in to create a new CD matrix

    7. add (dRcs_i,dDcs_i) to CRVAL of the reference chip's WCS

    8. update header with new WCS values

    Parameters
    ----------
    image : str or PyFITS.HDUList object
        Filename, or PyFITS object, of image with WCS to be updated.
        All extensions with EXTNAME matches the value of the 'sciext'
        parameter value (by default, all 'SCI' extensions) will be updated.

    reference : str
        Filename of image/headerlet (FITS file) which contains the WCS
        used to define the tangent plane in which all the fit parameters
        (shift, rot, scale) were measured.

    wcsname : str
        Label to give to new WCS solution being created by this fit. If
        a value of None is given, it will automatically use 'TWEAK' as the
        label. If a WCS has a name with this specific value, the code will
        automatically append a version ID using the format '_n', such as
        'TWEAK_1', 'TWEAK_2',or 'TWEAK_update_1'.
        [Default =None]

    reusename : bool
        User can specify whether or not to over-write WCS with same name.
        [Default: False]

    rot : float
        Amount of rotation measured in fit to be applied.
        [Default=0.0]

    scale : float
        Amount of scale change measured in fit to be applied.
        [Default=1.0]

    xsh : float
        Offset in X pixels from defined tangent plane to be applied to image.
        [Default=0.0]

    ysh : float
        Offset in Y pixels from defined tangent plane to be applied to image.
        [Default=0.0]

    fit : arr
        Linear coefficients for fit
        [Default = None]

    xrms : float
        RMS of fit in RA (in decimal degrees) that will be recorded as
        CRDER1 in WCS and header
        [Default = None]

    yrms : float
        RMS of fit in Dec (in decimal degrees) that will be recorded as
        CRDER2 in WCS and header
        [Default = None]

    verbose : bool
        Print extra messages during processing? [Default=False]

    force : bool
        Update header even though WCS already exists with this solution or
        wcsname? [Default=False]

    sciext : string
        Value of FITS EXTNAME keyword for extensions with WCS headers to
        be updated with the fit values. [Default='SCI']

    """
    # if input reference is a ref_wcs file from tweakshifts, use it
    if isinstance(reference, wcsutil.HSTWCS) or isinstance(reference, pywcs.WCS):
        wref = reference
    else:
        refimg = fits.open(reference, memmap=False)
        wref = None
        for extn in refimg:
            if 'extname' in extn.header and extn.header['extname'] == 'WCS':
                wref = pywcs.WCS(refimg['wcs'].header)
                break
        refimg.close()
        # else, we have presumably been provided a full undistorted image
        # as a reference, so use it with HSTWCS instead
        if wref is None:
            wref = wcsutil.HSTWCS(reference)

    if isinstance(image, fits.HDUList):
        open_image = False
        filename = image.filename()
        if image.fileinfo(0)['filemode'] is 'update':
            image_update = True
        else:
            image_update = False
    else:
        open_image = True
        filename = image
        image_update = None

    # Now that we are sure we have a good reference WCS to use,
    # continue with the update
    logstr = "....Updating header for {:s}...".format(filename)
    if verbose:
        print("\n{:s}\n".format(logstr))
    else:
        log.info(logstr)

    # reset header WCS keywords to original (OPUS generated) values
    extlist = get_ext_list(image, extname='SCI')
    if extlist:
        if image_update:
            # Create initial WCSCORR extension
            wcscorr.init_wcscorr(image,force=force)
    else:
        extlist = [0]

    # insure that input PRIMARY WCS has been archived before overwriting
    # with new solution
    if open_image:
        fimg = fits.open(image, mode='update', memmap=False)
        image_update = True
    else:
        fimg = image

    if image_update:
        wcsutil.altwcs.archiveWCS(fimg,extlist,reusekey=True)

    # Process MEF images...
    for ext in extlist:
        logstr = "Processing {:s}[{:s}]".format(fimg.filename(),
                                                ext2str(ext))
        if verbose:
            print("\n{:s}\n".format(logstr))
        else:
            log.info(logstr)
        chip_wcs = wcsutil.HSTWCS(fimg,ext=ext)

        update_refchip_with_shift(chip_wcs, wref, fitgeom=fitgeom,
                    rot=rot, scale=scale, xsh=xsh, ysh=ysh,
                    fit=fit, xrms=xrms, yrms=yrms)
        #if util.is_blank(wcsname):
            #wcsname = 'TWEAK'

        # Update FITS file with newly updated WCS for this chip
        extnum = fimg.index(fimg[ext])
        update_wcs(fimg, extnum, chip_wcs, wcsname=wcsname,
                   reusename=reusename, verbose=verbose)

    if open_image:
        fimg.close()
示例#5
0
def _buildStaticDQUserMask(img, ext, sky_bits, use_static, umask,
                           umaskext, in_memory):
    # creates a temporary mask by combining 'static' mask,
    # DQ image, and user-supplied mask.

    def merge_masks(m1, m2):
        if m1 is None: return m2
        if m2 is None: return m1
        return np.logical_and(m1, m2).astype(np.uint8)

    mask = None

    # build DQ mask
    if sky_bits is not None:
        mask = img.buildMask(img[ext]._chip,bits=sky_bits)

    # get correct static mask mask filenames/objects
    staticMaskName = img[ext].outputNames['staticMask']
    smask = None
    if use_static:
        if img.inmemory:
            if staticMaskName in img.virtualOutputs:
                smask = img.virtualOutputs[staticMaskName].data
        else:
            if staticMaskName is not None and os.path.isfile(staticMaskName):
                sm, dq = openImageEx(staticMaskName, mode='readonly',
                            saveAsMEF=False, clobber=False,
                            imageOnly=True, openImageHDU=True, openDQHDU=False,
                            preferMEF=False, verbose=False)
                if sm.hdu is not None:
                    smask = sm.hdu[0].data
                    sm.release()
            else:
                log.warning("Static mask for file \'{}\', ext={} NOT FOUND." \
                            .format(img._filename, ext))
        # combine DQ and static masks:
        mask = merge_masks(mask, smask)

    # combine user mask with the previously computed mask:
    if umask is not None and not umask.closed:
        if mask is None:
            # return user-supplied mask:
            umask.hold()
            return (umask, umaskext)
        else:
            # combine user mask with the previously computed mask:
            dtm  = umask.hdu[umaskext].data
            mask = merge_masks(mask, dtm)

    if mask is None:
        return (None, None)
    elif mask.sum() == 0:
        log.warning("All pixels masked out when applying DQ, " \
                    "static, and user masks!")

    # save mask to a temporary file:
    (root,suffix,fext) = file_name_components(img._filename)
    if in_memory:
        tmpmask = in_memory_mask(mask)
        strext = ext2str(ext, compact=True, default_extver=None)
        tmpmask.original_fname = "{1:s}{0:s}{2:s}{0:s}{3:s}" \
            .format('_', root, suffix, 'in-memory_skymatch_mask')
    else:
        (tmpfname, tmpmask) = temp_mask_file(mask, root,
            prefix='', suffix='skymatch_mask', ext=ext,
            randomize_prefix=False)
        img[ext].outputNames['skyMatchMask'] = tmpfname

    return (tmpmask, 0)
示例#6
0
def photeq(files='*_flt.fits', sciext='SCI', errext='ERR',
           ref_phot=None, ref_phot_ext=None,
           phot_kwd='PHOTFLAM', aux_phot_kwd='PHOTFNU',
           search_primary=True,
           readonly=True, clobber=False, logfile='photeq.log'):
    """
    Adjust data values of images by equalizing each chip's PHOTFLAM value
    to a single common value so that all chips can be treated equally
    by `AstroDrizzle`.

    Parameters
    ----------

    files : str (Default = ``'*_flt.fits'``)

        A string containing one of the following:

            * a comma-separated list of valid science image file names,
              e.g.: ``'j1234567q_flt.fits, j1234568q_flt.fits'``;

            * an @-file name, e.g., ``'@files_to_match.txt'``. See notes
              section for details on the format of the @-files.

        .. note::

            **Valid science image file names** are:

            * file names of existing FITS, GEIS, or WAIVER FITS files;

            * partial file names containing wildcard characters, e.g.,
              ``'*_flt.fits'``;

            * Association (ASN) tables (must have ``_asn``, or ``_asc``
              suffix), e.g., ``'j12345670_asn.fits'``.

    sciext : str (Default = 'SCI')
        Extension *name* of extensions whose data and/or headers should
        be corrected.

    errext : str (Default = 'ERR')
        Extension *name* of the extensions containing corresponding error
        arrays. Error arrays are corrected in the same way as science data.

    ref_phot : float, None (Default = None)
        A number indicating the new value of PHOTFLAM or PHOTFNU
        (set by 'phot_kwd') to which the data should be adjusted.

    ref_phot_ext : int, str, tuple, None (Default = None)
        Extension from which the `photeq` should get the reference photometric
        value specified by the `phot_kwd` parameter. This parameter is ignored
        if `ref_phot` **is not** `None`. When `ref_phot_ext` is `None`, then
        the reference inverse sensitivity value will be picked from the
        first `sciext` of the first input image containing `phot_kwd`.

    phot_kwd : str (Default = 'PHOTFLAM')
        Specifies the primary keyword which contains inverse sensitivity
        (e.g., PHOTFLAM). It is used to compute conversion factors by
        which data should be rescaled.

    aux_phot_kwd : str, None, list of str (Default = 'PHOTFNU')
        Same as `phot_kwd` but describes *other* photometric keyword(s)
        that should be corrected by inverse of the scale factor used to correct
        data. These keywords are *not* used to compute conversion factors.
        Multiple keywords can be specified as a Python list of strings:
        ``['PHOTFNU', 'PHOTOHMY']``.

        .. note::

            If specifying multiple secondary photometric keywords in the TEAL
            interface, use a comma-separated list of keywords.

    search_primary : bool (Default = True)
        Specifies whether to first search the primary header for the
        presence of `phot_kwd` keyword and compute conversion factor based on
        that value. This is (partially) ignored when `ref_phot` is not `None` in
        the sense that the value specified by `ref_phot` will be used as the
        reference *but* in all images primary will be searched for `phot_kwd`
        and `aux_phot_kwd` and those values will be corrected
        (if ``search_primary=True``).

    readonly : bool (Default = True)
        If `True`, `photeq` will not modify input files (nevertheless, it will
        convert input GEIS or WAVERED FITS files to MEF and could overwrite
        existing MEF files if `clobber` is set to `True`).
        The (console or log file) output however will be identical to the case
        when ``readonly=False`` and it can be examined before applying these
        changes to input files.

    clobber : bool (Default = False)
        Overwrite existing MEF files when converting input WAVERED FITS or GEIS
        to MEF.

    logfile : str, None (Default = 'photeq.log')
        File name of the log file.

    Notes
    -----

    By default, `photeq` will search for the first inverse sensitivity
    value (given by the header keyword specified by the `phot_kwd` parameter,
    e.g., PHOTFLAM or PHOTFNU) found in the input images and it will equalize
    all other images to this reference value.

    It is possible to tell `photeq` to look for the reference inverse
    sensitivity value only in a specific extension of input images, e.g.: 3,
    ('sci',3), etc. This can be done by setting `ref_phot_ext` to a specific
    extension. This may be useful, for example, for WFPC2 images: WF3 chip was
    one of the better calibrated chips, and so, if one prefers to have
    inverse sensitivities equalized to the inverse sensitivity of the WF3 chip,
    one can set ``ref_phot_ext=3``.

    Alternatively, one can provide their own reference inverse sensitivity
    value to which all other images should be "equalized" through the
    parameter `ref_phot`.

    .. note::

       Default parameter values (except for `files`, `readonly`, and `clobber`)
       should be acceptable for most HST images.

    .. warning::

       If images are intended to be used with `AstroDrizzle`, it is recommended
       that sky background measurement be performed on "equalized" images as
       the `photeq` is not aware of sky user keyword in the image headers and
       thus it cannot correct sky values already recorded in the headers.

    Examples
    --------

    #. In most cases the default parameters should suffice:

           >>> from drizzlepac import photeq
           >>> photeq.photeq(files='*_flt.fits', readonly=False)

    #. If the re-calibration needs to be done on PHOTFNU rather than
       PHOTFLAM, then:

           >>> photeq.photeq(files='*_flt.fits', ref_phot='PHOTFNU',
           ... aux_phot_kwd='PHOTFLAM')

    #. If for WFPC2 data one desires that PHOTFLAM from WF3 be used as the
       reference in WFPC2 images, then:

           >>> photeq.photeq(files='*_flt.fits', ref_phot_ext=3) # or ('sci',3)

    """

    # Time it
    runtime_begin = datetime.now()

    # check that input file name is a string:
    if not isinstance(files, str):
        raise TypeError("Argument 'files' must be a comma-separated list of "
                        " file names")

    # Set-up log files:
    if isinstance(logfile, str):
        # first, in case there are any "leftover" file handlers,
        # close and remove them:
        for h in _log.handlers:
            if h is not _sh_log and isinstance(h, logging.FileHandler):
                h.close()
                _log.removeHandler(h)
        # create file handler:
        log_formatter = logging.Formatter('[%(levelname)s:] %(message)s')
        log_file_handler = logging.FileHandler(logfile)
        log_file_handler.setFormatter(log_formatter)
        # add log_file_handler to logger
        _log.addHandler(log_file_handler)

    elif logfile is not None:
        raise TypeError("Unsupported 'logfile' type")

    #  BEGIN:
    _mlinfo("***** {0} started on {1}".format(__taskname__, runtime_begin))
    _mlinfo("      Version {0} ({1})".format(__version__, __vdate__))

    # check that extension names are strings (or None for error ext):
    if sciext is None:
        sci_ext4parse = '*'
        ext2get = None
    else:
        if not isinstance(sciext, str):
            raise TypeError("Argument 'sciext' must be a string or None")
        sciext = sciext.strip()
        if sciext.upper() == 'PRIMARY':
            sciext = sciext.upper()
            ext2get = (sciext, 1)
        else:
            ext2get = (sciext, '*')

        sci_ext4parse = ext2get

    if errext is not None and not isinstance(errext, str):
        raise TypeError("Argument 'errext' must be a string or None")

    # check that phot_kwd is supported:
    if not isinstance(phot_kwd, str):
        raise TypeError("Argument 'phot_kwd' must be a string")
    phot_kwd = phot_kwd.strip().upper()

    # check that ref_phot_ext has correct type:
    if ref_phot_ext is not None and not \
       (isinstance(ref_phot_ext, int) or isinstance(ref_phot_ext, str) \
        or (isinstance(ref_phot_ext, tuple) and len(ref_phot_ext) == 2 \
            and isinstance(ref_phot_ext[0], str) and \
            isinstance(ref_phot_ext[1], int))):
        raise TypeError("Unsupported 'ref_phot_ext' type")
    if isinstance(ref_phot_ext, str):
        ref_phot_ext = (ref_phot_ext, 1)

    if aux_phot_kwd is None:
        aux_phot_kwd = []

    elif isinstance(aux_phot_kwd, str):
        aux_phot_kwd = [aux_phot_kwd.strip().upper()]
        if phot_kwd == aux_phot_kwd:
            raise ValueError("Auxiliary photometric keyword must be different "
                             "from the main photometric keyword 'phot_kwd'.")

    elif hasattr(aux_phot_kwd, '__iter__'):
        if not all([isinstance(phot, str) for phot in aux_phot_kwd]):
            raise TypeError("Argument 'aux_phot_kwd' must be a string, list of "
                        "strings, or None")
        aux_phot_kwd = [phot.strip().upper() for phot in aux_phot_kwd]
        if ref_phot in aux_phot_kwd:
            raise ValueError("Auxiliary photometric keyword(s) must be "
                             "different from the main photometric keyword "
                             "'phot_kwd'.")

    else:
        raise TypeError("Argument 'aux_phot_kwd' must be a string, list of "
                        "strings, or None")

    # read input file list:
    fl = parseat.parse_cs_line(csline=files, default_ext=sci_ext4parse,
                               im_fmode='readonly' if readonly else 'update',
                               clobber=clobber, fnamesOnly=True,
                               doNotOpenDQ=True)

    # check if user supplied file extensions, set them to the sciext,
    # and warn that they will be ignored:
    for f in fl:
        if f.count > 1 or f.fext[0] != sci_ext4parse:
            _mlwarn("WARNING: Extension specifications for file {:s} "
                    "will be ignored. Using all {:s} extensions instead."
                    .format(f.image,  'image-like' if sciext is None else \
                            "{:s}".format(utils.ext2str(sciext,
                                                        default_extver=None))))

    # find the reference PHOTFLAM/PHOTNU:
    flc = fl[:]
    ref_hdu = None
    ref_ext = None
    ref_user = True

    if ref_phot is None:
        ref_user = False
        for f in flc:
            f.convert2ImageRef()

            # get primary hdu:
            pri_hdu = f.image.hdu[0]

            # find all valid extensions:
            if ref_phot_ext is None:
                if sciext == 'PRIMARY':
                    extnum = [0]
                else:
                    extnum = utils.get_ext_list(f.image, sciext)

                is_pri_hdu = [f.image.hdu[ext] is pri_hdu for ext in extnum]

                # if necessary, add primary header to the hdu list:
                if search_primary:
                    try:
                        pri_index = is_pri_hdu.index(True)
                        extnum.insert(0, extnum.pop(pri_index))
                    except ValueError:
                        extnum.insert(0, 0)

            else:
                extnum = [ref_phot_ext]

            for ext in extnum:
                hdu = f.image.hdu[ext]
                if phot_kwd in hdu.header:
                    ref_phot = hdu.header[phot_kwd]
                    ref_ext = ext
                    ref_hdu = hdu
                    break

            if ref_phot is None:
                _mlwarn("WARNING: Could not find specified inverse "
                        "         sensitivity keyword '{:s}'\n"
                        "         in any of the {} extensions of file '{}'.\n"
                        "         This input file will be ignored."
                        .format(phot_kwd, 'image-like' if sciext is None else \
                                "{:s}".format(utils.ext2str(sciext,
                                                            default_extver=None)),
                                os.path.basename(f.image.original_fname)))
                f.release_all_images()
                fl.remove(f)

            else:
                break

    if ref_phot is None:
        raise RuntimeError("Could not find the inverse sensitivity keyword "
                           "'{:s}' in the specified headers of "
                           "the input image(s).\nCannot continue."
                           .format(phot_kwd))

    aux_phot_kwd_list = ','.join(aux_phot_kwd)

    _mlinfo("\nPRIMARY PHOTOMETRIC KEYWORD: {:s}".format(phot_kwd))
    _mlinfo("SECONDARY PHOTOMETRIC KEYWORD(S): {:s}"
              .format(aux_phot_kwd_list if aux_phot_kwd_list else 'None'))
    if ref_user:
        _mlinfo("REFERENCE VALUE PROVIDED BY USER: '******'={}\n"
                .format(phot_kwd, ref_phot))
    else:
        _mlinfo("REFERENCE VALUE FROM FILE: '{:s}[{:s}]'\n"
                .format(os.path.basename(f.image.original_fname),
                          utils.ext2str(ref_ext)))
        _mlinfo("REFERENCE '{:s}' VALUE IS: {}".format(phot_kwd, ref_phot))

    # equalize PHOTFLAM/PHOTNU
    for f in fl:
        # open the file if necessary:
        if f.fnamesOnly:
            _mlinfo("\nProcessing file '{:s}'".format(f.image))
            f.convert2ImageRef()
        else:
            _mlinfo("\nProcessing file '{:s}'".format(f.image.original_fname))

        # first, see if photflam is in the primary header and save this value:
        pri_conv = None
        if search_primary:
            whdu = f.image.hdu[0]
            if phot_kwd in whdu.header:
                _mlinfo("   * Primary header:")
                if whdu is ref_hdu:
                    pri_conv = 1.0
                    _mlinfo("     - '{}' = {} found in the primary header."
                            .format(phot_kwd, whdu.header[phot_kwd]))
                    _mlinfo("     - Data conversion factor based on primary "
                              "header: {}".format(pri_conv))
                else:
                    _mlinfo("     - '{}' found in the primary header."
                            .format(phot_kwd))
                    pri_conv = whdu.header[phot_kwd] / ref_phot
                    _mlinfo("     - Setting {:s} in the primary header to {} "
                              "(old value was {})"
                            .format(phot_kwd, ref_phot, whdu.header[phot_kwd]))
                    _mlinfo("     - Data conversion factor based on primary "
                            "header: {}".format(pri_conv))
                    whdu.header[phot_kwd] = ref_phot

            # correct the "other" photometric keyword, if present:
            if pri_conv is not None and whdu is not ref_hdu:
                for aux_kwd in aux_phot_kwd:
                    if aux_kwd in whdu.header:
                        old_aux_phot = whdu.header[aux_kwd]
                        new_aux_phot = old_aux_phot / pri_conv
                        whdu.header[aux_kwd] = new_aux_phot
                        _mlinfo("     - Setting {:s} in the primary header "
                                "to {} (old value was {})"
                                .format(aux_kwd, new_aux_phot, old_aux_phot))

            # process data and error arrays when 'sciext' was specifically set to
            # 'PRIMARY':
            if sciext == 'PRIMARY' and pri_conv is not None:
                has_data = (hasattr(whdu, 'data') and
                            whdu.data is not None)

                # correct data:
                if has_data:
                    if np.issubdtype(whdu.data.dtype, np.float):
                        whdu.data *= pri_conv
                        _mlinfo("     - Data have been multiplied by {}"
                                .format(pri_conv))
                    else:
                        _mlwarn("WARNING: Data not converted because it is of "
                                "non-floating point type.")

                # correct error array:
                if errext is not None:
                    eext = (errext, 1)
                    try:
                        whdu = f.image.hdu[eext]
                    except KeyError:
                        _mlwarn("     - WARNING: Error extension {:s} not found."
                                .format(utils.ext2str(eext)))

                        f.release_all_images()
                        continue

                    if hasattr(whdu, 'data') and whdu.data is not None:
                        if np.issubdtype(whdu.data.dtype, np.float):
                            whdu.data *= pri_conv
                            _mlinfo("     - Error array (ext={}) has been "
                                    "multiplied by {}".format(eext, pri_conv))
                        else:
                            _mlinfo("     - Error array in extension {:s} "
                                    "contains non-floating point data.\n"
                                    "       Skipping this extension"
                                    .format(utils.ext2str(ext)))

                f.release_all_images()
                continue

        # find all valid extensions:
        extnum = utils.get_ext_list(f.image, sciext)

        for ext in extnum:
            whdu = f.image.hdu[ext]
            conv = None

            if whdu is ref_hdu:
                _mlinfo("   * EXT: {} - This is the \"reference\" extension.\n"
                        "          Nothing to do. Skipping this extension..."
                        .format(ext))
                continue

            has_data = (hasattr(whdu, 'data') and
                        whdu.data is not None)

            if has_data and not np.issubdtype(whdu.data.dtype, np.float):
                _mlinfo("   * EXT: {} contains non-floating point data. "
                        "Skipping this extension".format(ext))

            # find all auxiliary photometric keywords present in the header:
            paux = [aux_kwd for aux_kwd in aux_phot_kwd if aux_kwd \
                    in whdu.header]

            if phot_kwd in whdu.header:
                _mlinfo("   * EXT: {}".format(ext))
                old_phot = whdu.header[phot_kwd]
                conv = old_phot / ref_phot
                _mlinfo("     - Setting {:s} to {} (old value was {})"
                        .format(phot_kwd, ref_phot, old_phot))
                whdu.header[phot_kwd] = ref_phot
                _mlinfo("     - Computed conversion factor for data: {}"
                        .format(conv))

            elif pri_conv is None:
                _mlinfo("   * EXT: {}".format(ext))
                _mlinfo("     - '{:s} not found. Skipping this extension..."
                        .format(phot_kwd))
                continue

            else:
                _mlinfo("   * EXT: {}".format(ext))

                # if paux:
                    # print("ERROR: Primary photometric keyword ('{:s}') is "
                          # "missing but\n       the secondary keywords ('{:s}') "
                          # "are present. This extension cannot be processed."
                          # .format(phot_kwd, ','.join(paux)))
                    # continue

                _mlinfo("     - '{:s} not found. Using conversion factor "
                        "based\n       on the primary header: {}"
                        .format(phot_kwd, pri_conv))
                conv = pri_conv

            # correct the "other" photometric keyword, if present:
            if conv is not None:
                for aux_kwd in paux:
                    old_aux_phot = whdu.header[aux_kwd]
                    new_aux_phot = old_aux_phot / conv
                    whdu.header[aux_kwd] = new_aux_phot
                    _mlinfo("     - Setting {:s} to {} (old value was {})"
                            .format(aux_kwd, new_aux_phot, old_aux_phot))

            # correct data:
            if has_data:
                if conv is None:
                    _mlinfo("   * EXT: {}".format(ext))

                if np.issubdtype(whdu.data.dtype, np.float):
                    whdu.data *= conv
                    _mlinfo("     - Data have been multiplied by {}"
                            .format(conv))
                else:
                    _mlinfo("WARNING: Non-floating point data. Data cannot "
                            "be re-scaled.")

            # correct error array:
            if errext is not None and isinstance(ext, tuple) and len(ext) == 2:
                eext = (errext, ext[1])
                try:
                    whdu = f.image.hdu[eext]
                except KeyError:
                    continue

                if hasattr(whdu, 'data') and whdu.data is not None:
                    if np.issubdtype(whdu.data.dtype, np.float):
                        whdu.data *= conv
                        _mlinfo("     - Error array (ext={}) has been "
                                "multiplied by {}".format(eext, conv))
                    else:
                        _mlinfo("     - Error array in extension {:s} "
                                "contains non-floating point data.\n"
                                "       Skipping this extension"
                                .format(utils.ext2str(ext)))

        f.release_all_images()

    _mlinfo("\nDone.")

    if readonly:
        _mlinfo("\nNOTE: '{:s}' was run in READONLY mode\n"
                "       and input image(s)' content WAS NOT MODIFIED."
                .format(__taskname__))

    # close all log file handlers:
    for h in _log.handlers:
        if h is not _sh_log and isinstance(h, logging.FileHandler):
            h.close()
            _log.removeHandler(h)
示例#7
0
def photeq(files='*_flt.fits',
           sciext='SCI',
           errext='ERR',
           ref_phot=None,
           ref_phot_ext=None,
           phot_kwd='PHOTFLAM',
           aux_phot_kwd='PHOTFNU',
           search_primary=True,
           readonly=True,
           clobber=False,
           logfile='photeq.log'):
    """
    Adjust data values of images by equalizing each chip's PHOTFLAM value
    to a single common value so that all chips can be treated equally
    by ``AstroDrizzle``.

    Parameters
    ----------

    files : str (Default = ``'*_flt.fits'``)

        A string containing one of the following:

            * a comma-separated list of valid science image file names,
              e.g.: ``'j1234567q_flt.fits, j1234568q_flt.fits'``;

            * an @-file name, e.g., ``'@files_to_match.txt'``. See notes
              section for details on the format of the @-files.

        .. note::

            **Valid science image file names** are:

            * file names of existing FITS, GEIS, or WAIVER FITS files;

            * partial file names containing wildcard characters, e.g.,
              ``'*_flt.fits'``;

            * Association (ASN) tables (must have ``_asn``, or ``_asc``
              suffix), e.g., ``'j12345670_asn.fits'``.

    sciext : str (Default = 'SCI')
        Extension *name* of extensions whose data and/or headers should
        be corrected.

    errext : str (Default = 'ERR')
        Extension *name* of the extensions containing corresponding error
        arrays. Error arrays are corrected in the same way as science data.

    ref_phot : float, None (Default = None)
        A number indicating the new value of PHOTFLAM or PHOTFNU
        (set by 'phot_kwd') to which the data should be adjusted.

    ref_phot_ext : int, str, tuple, None (Default = None)
        Extension from which the `photeq` should get the reference photometric
        value specified by the `phot_kwd` parameter. This parameter is ignored
        if `ref_phot` **is not** `None`. When `ref_phot_ext` is `None`, then
        the reference inverse sensitivity value will be picked from the
        first `sciext` of the first input image containing `phot_kwd`.

    phot_kwd : str (Default = 'PHOTFLAM')
        Specifies the primary keyword which contains inverse sensitivity
        (e.g., PHOTFLAM). It is used to compute conversion factors by
        which data should be rescaled.

    aux_phot_kwd : str, None, list of str (Default = 'PHOTFNU')
        Same as `phot_kwd` but describes *other* photometric keyword(s)
        that should be corrected by inverse of the scale factor used to correct
        data. These keywords are *not* used to compute conversion factors.
        Multiple keywords can be specified as a Python list of strings:
        ``['PHOTFNU', 'PHOTOHMY']``.

        .. note::

            If specifying multiple secondary photometric keywords in the TEAL
            interface, use a comma-separated list of keywords.

    search_primary : bool (Default = True)
        Specifies whether to first search the primary header for the
        presence of `phot_kwd` keyword and compute conversion factor based on
        that value. This is (partially) ignored when `ref_phot` is not `None` in
        the sense that the value specified by `ref_phot` will be used as the
        reference *but* in all images primary will be searched for `phot_kwd`
        and `aux_phot_kwd` and those values will be corrected
        (if ``search_primary=True``).

    readonly : bool (Default = True)
        If `True`, `photeq` will not modify input files (nevertheless, it will
        convert input GEIS or WAVERED FITS files to MEF and could overwrite
        existing MEF files if `clobber` is set to `True`).
        The (console or log file) output however will be identical to the case
        when ``readonly=False`` and it can be examined before applying these
        changes to input files.

    clobber : bool (Default = False)
        Overwrite existing MEF files when converting input WAVERED FITS or GEIS
        to MEF.

    logfile : str, None (Default = 'photeq.log')
        File name of the log file.

    Notes
    -----

    By default, `photeq` will search for the first inverse sensitivity
    value (given by the header keyword specified by the `phot_kwd` parameter,
    e.g., PHOTFLAM or PHOTFNU) found in the input images and it will equalize
    all other images to this reference value.

    It is possible to tell `photeq` to look for the reference inverse
    sensitivity value only in a specific extension of input images, e.g.: 3,
    ('sci',3), etc. This can be done by setting `ref_phot_ext` to a specific
    extension. This may be useful, for example, for WFPC2 images: WF3 chip was
    one of the better calibrated chips, and so, if one prefers to have
    inverse sensitivities equalized to the inverse sensitivity of the WF3 chip,
    one can set ``ref_phot_ext=3``.

    Alternatively, one can provide their own reference inverse sensitivity
    value to which all other images should be "equalized" through the
    parameter `ref_phot`.

    .. note::

       Default parameter values (except for `files`, `readonly`, and `clobber`)
       should be acceptable for most HST images.

    .. warning::

       If images are intended to be used with ``AstroDrizzle``, it is
       recommended that sky background measurement be performed on "equalized"
       images as the `photeq` is not aware of sky user keyword in the image
       headers and thus it cannot correct sky values already recorded in the
       headers.

    Examples
    --------

    #. In most cases the default parameters should suffice:

           >>> from drizzlepac import photeq
           >>> photeq.photeq(files='*_flt.fits', readonly=False)

    #. If the re-calibration needs to be done on PHOTFNU rather than
       PHOTFLAM, then:

           >>> photeq.photeq(files='*_flt.fits', ref_phot='PHOTFNU',
           ... aux_phot_kwd='PHOTFLAM')

    #. If for WFPC2 data one desires that PHOTFLAM from WF3 be used as the
       reference in WFPC2 images, then:

           >>> photeq.photeq(files='*_flt.fits', ref_phot_ext=3) # or ('sci',3)

    """

    # Time it
    runtime_begin = datetime.now()

    # check that input file name is a string:
    if not isinstance(files, str):
        raise TypeError("Argument 'files' must be a comma-separated list of "
                        " file names")

    # Set-up log files:
    if isinstance(logfile, str):
        # first, in case there are any "leftover" file handlers,
        # close and remove them:
        for h in _log.handlers:
            if h is not _sh_log and isinstance(h, logging.FileHandler):
                h.close()
                _log.removeHandler(h)
        # create file handler:
        log_formatter = logging.Formatter('[%(levelname)s:] %(message)s')
        log_file_handler = logging.FileHandler(logfile)
        log_file_handler.setFormatter(log_formatter)
        # add log_file_handler to logger
        _log.addHandler(log_file_handler)

    elif logfile is not None:
        raise TypeError("Unsupported 'logfile' type")

    #  BEGIN:
    _mlinfo("***** {0} started on {1}".format(__taskname__, runtime_begin))
    _mlinfo("      Version {0} ({1})".format(__version__, __version_date__))

    # check that extension names are strings (or None for error ext):
    if sciext is None:
        sci_ext4parse = '*'
        ext2get = None
    else:
        if not isinstance(sciext, str):
            raise TypeError("Argument 'sciext' must be a string or None")
        sciext = sciext.strip()
        if sciext.upper() == 'PRIMARY':
            sciext = sciext.upper()
            ext2get = (sciext, 1)
        else:
            ext2get = (sciext, '*')

        sci_ext4parse = ext2get

    if errext is not None and not isinstance(errext, str):
        raise TypeError("Argument 'errext' must be a string or None")

    # check that phot_kwd is supported:
    if not isinstance(phot_kwd, str):
        raise TypeError("Argument 'phot_kwd' must be a string")
    phot_kwd = phot_kwd.strip().upper()

    # check that ref_phot_ext has correct type:
    if ref_phot_ext is not None and not \
       (isinstance(ref_phot_ext, int) or isinstance(ref_phot_ext, str) \
        or (isinstance(ref_phot_ext, tuple) and len(ref_phot_ext) == 2 \
            and isinstance(ref_phot_ext[0], str) and \
            isinstance(ref_phot_ext[1], int))):
        raise TypeError("Unsupported 'ref_phot_ext' type")
    if isinstance(ref_phot_ext, str):
        ref_phot_ext = (ref_phot_ext, 1)

    if aux_phot_kwd is None:
        aux_phot_kwd = []

    elif isinstance(aux_phot_kwd, str):
        aux_phot_kwd = [aux_phot_kwd.strip().upper()]
        if phot_kwd == aux_phot_kwd:
            raise ValueError("Auxiliary photometric keyword must be different "
                             "from the main photometric keyword 'phot_kwd'.")

    elif hasattr(aux_phot_kwd, '__iter__'):
        if not all([isinstance(phot, str) for phot in aux_phot_kwd]):
            raise TypeError(
                "Argument 'aux_phot_kwd' must be a string, list of "
                "strings, or None")
        aux_phot_kwd = [phot.strip().upper() for phot in aux_phot_kwd]
        if ref_phot in aux_phot_kwd:
            raise ValueError("Auxiliary photometric keyword(s) must be "
                             "different from the main photometric keyword "
                             "'phot_kwd'.")

    else:
        raise TypeError("Argument 'aux_phot_kwd' must be a string, list of "
                        "strings, or None")

    # read input file list:
    fl = parseat.parse_cs_line(csline=files,
                               default_ext=sci_ext4parse,
                               im_fmode='readonly' if readonly else 'update',
                               clobber=clobber,
                               fnamesOnly=True,
                               doNotOpenDQ=True)

    # check if user supplied file extensions, set them to the sciext,
    # and warn that they will be ignored:
    for f in fl:
        if f.count > 1 or f.fext[0] != sci_ext4parse:
            _mlwarn("WARNING: Extension specifications for file {:s} "
                    "will be ignored. Using all {:s} extensions instead."
                    .format(f.image,  'image-like' if sciext is None else \
                            "{:s}".format(utils.ext2str(sciext,
                                                        default_extver=None))))

    # find the reference PHOTFLAM/PHOTNU:
    flc = fl[:]
    ref_hdu = None
    ref_ext = None
    ref_user = True

    if ref_phot is None:
        ref_user = False
        for f in flc:
            f.convert2ImageRef()

            # get primary hdu:
            pri_hdu = f.image.hdu[0]

            # find all valid extensions:
            if ref_phot_ext is None:
                if sciext == 'PRIMARY':
                    extnum = [0]
                else:
                    extnum = utils.get_ext_list(f.image, sciext)

                is_pri_hdu = [f.image.hdu[ext] is pri_hdu for ext in extnum]

                # if necessary, add primary header to the hdu list:
                if search_primary:
                    try:
                        pri_index = is_pri_hdu.index(True)
                        extnum.insert(0, extnum.pop(pri_index))
                    except ValueError:
                        extnum.insert(0, 0)

            else:
                extnum = [ref_phot_ext]

            for ext in extnum:
                hdu = f.image.hdu[ext]
                if phot_kwd in hdu.header:
                    ref_phot = hdu.header[phot_kwd]
                    ref_ext = ext
                    ref_hdu = hdu
                    break

            if ref_phot is None:
                _mlwarn("WARNING: Could not find specified inverse "
                        "         sensitivity keyword '{:s}'\n"
                        "         in any of the {} extensions of file '{}'.\n"
                        "         This input file will be ignored."
                        .format(phot_kwd, 'image-like' if sciext is None else \
                                "{:s}".format(utils.ext2str(sciext,
                                                            default_extver=None)),
                                os.path.basename(f.image.original_fname)))
                f.release_all_images()
                fl.remove(f)

            else:
                break

    if ref_phot is None:
        raise RuntimeError(
            "Could not find the inverse sensitivity keyword "
            "'{:s}' in the specified headers of "
            "the input image(s).\nCannot continue.".format(phot_kwd))

    aux_phot_kwd_list = ','.join(aux_phot_kwd)

    _mlinfo("\nPRIMARY PHOTOMETRIC KEYWORD: {:s}".format(phot_kwd))
    _mlinfo("SECONDARY PHOTOMETRIC KEYWORD(S): {:s}".format(
        aux_phot_kwd_list if aux_phot_kwd_list else 'None'))
    if ref_user:
        _mlinfo("REFERENCE VALUE PROVIDED BY USER: '******'={}\n".format(
            phot_kwd, ref_phot))
    else:
        _mlinfo("REFERENCE VALUE FROM FILE: '{:s}[{:s}]'\n".format(
            os.path.basename(f.image.original_fname), utils.ext2str(ref_ext)))
        _mlinfo("REFERENCE '{:s}' VALUE IS: {}".format(phot_kwd, ref_phot))

    # equalize PHOTFLAM/PHOTNU
    for f in fl:
        # open the file if necessary:
        if f.fnamesOnly:
            _mlinfo("\nProcessing file '{:s}'".format(f.image))
            f.convert2ImageRef()
        else:
            _mlinfo("\nProcessing file '{:s}'".format(f.image.original_fname))

        # first, see if photflam is in the primary header and save this value:
        pri_conv = None
        if search_primary:
            whdu = f.image.hdu[0]
            if phot_kwd in whdu.header:
                _mlinfo("   * Primary header:")
                if whdu is ref_hdu:
                    pri_conv = 1.0
                    _mlinfo(
                        "     - '{}' = {} found in the primary header.".format(
                            phot_kwd, whdu.header[phot_kwd]))
                    _mlinfo("     - Data conversion factor based on primary "
                            "header: {}".format(pri_conv))
                else:
                    _mlinfo("     - '{}' found in the primary header.".format(
                        phot_kwd))
                    pri_conv = whdu.header[phot_kwd] / ref_phot
                    _mlinfo("     - Setting {:s} in the primary header to {} "
                            "(old value was {})".format(
                                phot_kwd, ref_phot, whdu.header[phot_kwd]))
                    _mlinfo("     - Data conversion factor based on primary "
                            "header: {}".format(pri_conv))
                    whdu.header[phot_kwd] = ref_phot

            # correct the "other" photometric keyword, if present:
            if pri_conv is not None and whdu is not ref_hdu:
                for aux_kwd in aux_phot_kwd:
                    if aux_kwd in whdu.header:
                        old_aux_phot = whdu.header[aux_kwd]
                        new_aux_phot = old_aux_phot / pri_conv
                        whdu.header[aux_kwd] = new_aux_phot
                        _mlinfo("     - Setting {:s} in the primary header "
                                "to {} (old value was {})".format(
                                    aux_kwd, new_aux_phot, old_aux_phot))

            # process data and error arrays when 'sciext' was specifically set to
            # 'PRIMARY':
            if sciext == 'PRIMARY' and pri_conv is not None:
                has_data = (hasattr(whdu, 'data') and whdu.data is not None)

                # correct data:
                if has_data:
                    if np.issubdtype(whdu.data.dtype, np.floating):
                        whdu.data *= pri_conv
                        _mlinfo(
                            "     - Data have been multiplied by {}".format(
                                pri_conv))
                    else:
                        _mlwarn("WARNING: Data not converted because it is of "
                                "non-floating point type.")

                # correct error array:
                if errext is not None:
                    eext = (errext, 1)
                    try:
                        whdu = f.image.hdu[eext]
                    except KeyError:
                        _mlwarn(
                            "     - WARNING: Error extension {:s} not found.".
                            format(utils.ext2str(eext)))

                        f.release_all_images()
                        continue

                    if hasattr(whdu, 'data') and whdu.data is not None:
                        if np.issubdtype(whdu.data.dtype, np.floating):
                            whdu.data *= pri_conv
                            _mlinfo("     - Error array (ext={}) has been "
                                    "multiplied by {}".format(eext, pri_conv))
                        else:
                            _mlinfo("     - Error array in extension {:s} "
                                    "contains non-floating point data.\n"
                                    "       Skipping this extension".format(
                                        utils.ext2str(ext)))

                f.release_all_images()
                continue

        # find all valid extensions:
        extnum = utils.get_ext_list(f.image, sciext)

        for ext in extnum:
            whdu = f.image.hdu[ext]
            conv = None

            if whdu is ref_hdu:
                _mlinfo("   * EXT: {} - This is the \"reference\" extension.\n"
                        "          Nothing to do. Skipping this extension...".
                        format(ext))
                continue

            has_data = (hasattr(whdu, 'data') and whdu.data is not None)

            if has_data and not np.issubdtype(whdu.data.dtype, np.floating):
                _mlinfo("   * EXT: {} contains non-floating point data. "
                        "Skipping this extension".format(ext))

            # find all auxiliary photometric keywords present in the header:
            paux = [aux_kwd for aux_kwd in aux_phot_kwd if aux_kwd \
                    in whdu.header]

            if phot_kwd in whdu.header:
                _mlinfo("   * EXT: {}".format(ext))
                old_phot = whdu.header[phot_kwd]
                conv = old_phot / ref_phot
                _mlinfo("     - Setting {:s} to {} (old value was {})".format(
                    phot_kwd, ref_phot, old_phot))
                whdu.header[phot_kwd] = ref_phot
                _mlinfo(
                    "     - Computed conversion factor for data: {}".format(
                        conv))

            elif pri_conv is None:
                _mlinfo("   * EXT: {}".format(ext))
                _mlinfo("     - '{:s} not found. Skipping this extension...".
                        format(phot_kwd))
                continue

            else:
                _mlinfo("   * EXT: {}".format(ext))

                # if paux:
                # print("ERROR: Primary photometric keyword ('{:s}') is "
                # "missing but\n       the secondary keywords ('{:s}') "
                # "are present. This extension cannot be processed."
                # .format(phot_kwd, ','.join(paux)))
                # continue

                _mlinfo("     - '{:s} not found. Using conversion factor "
                        "based\n       on the primary header: {}".format(
                            phot_kwd, pri_conv))
                conv = pri_conv

            # correct the "other" photometric keyword, if present:
            if conv is not None:
                for aux_kwd in paux:
                    old_aux_phot = whdu.header[aux_kwd]
                    new_aux_phot = old_aux_phot / conv
                    whdu.header[aux_kwd] = new_aux_phot
                    _mlinfo(
                        "     - Setting {:s} to {} (old value was {})".format(
                            aux_kwd, new_aux_phot, old_aux_phot))

            # correct data:
            if has_data:
                if conv is None:
                    _mlinfo("   * EXT: {}".format(ext))

                if np.issubdtype(whdu.data.dtype, np.floating):
                    whdu.data *= conv
                    _mlinfo(
                        "     - Data have been multiplied by {}".format(conv))
                else:
                    _mlinfo("WARNING: Non-floating point data. Data cannot "
                            "be re-scaled.")

            # correct error array:
            if errext is not None and isinstance(ext, tuple) and len(ext) == 2:
                eext = (errext, ext[1])
                try:
                    whdu = f.image.hdu[eext]
                except KeyError:
                    continue

                if hasattr(whdu, 'data') and whdu.data is not None:
                    if np.issubdtype(whdu.data.dtype, np.floating):
                        whdu.data *= conv
                        _mlinfo("     - Error array (ext={}) has been "
                                "multiplied by {}".format(eext, conv))
                    else:
                        _mlinfo("     - Error array in extension {:s} "
                                "contains non-floating point data.\n"
                                "       Skipping this extension".format(
                                    utils.ext2str(ext)))

        f.release_all_images()

    _mlinfo("\nDone.")

    if readonly:
        _mlinfo("\nNOTE: '{:s}' was run in READONLY mode\n"
                "       and input image(s)' content WAS NOT MODIFIED.".format(
                    __taskname__))

    # close all log file handlers:
    for h in _log.handlers:
        if h is not _sh_log and isinstance(h, logging.FileHandler):
            h.close()
            _log.removeHandler(h)