aboutsummaryrefslogtreecommitdiffstats
path: root/bin/correct_exposure.py
blob: f95cd91d12990ab45aa8db1dd4870c565cce95f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python3
#
# Copyright (c) 2017 Weitian LI <liweitianux@live.com>
# MIT license

"""
Correct (counts) image for exposure by dividing the exposure map.

References
----------
* Chandra Memo: An Introduction to Exposure Map
  http://cxc.harvard.edu/ciao/download/doc/expmap_intro.ps
* CIAO: Single Chip ACIS Exposure Map and Exposure-corrected Image
  http://cxc.harvard.edu/ciao/threads/expmap_acis_single/
* CIAO: Multiple Chip ACIS Exposure Map and Exposure-corrected Image
  http://cxc.harvard.edu/ciao/threads/expmap_acis_multi/
"""

import os
import argparse
import subprocess
import logging

from _context import acispy
from acispy.manifest import get_manifest
from acispy.pfiles import setup_pfiles
from acispy.image import get_xygrid


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


def threshold_image(infile, outfile, expmap, cut="1.5%", clobber=False):
    """
    The strongly variable exposure near the edge of a dithered field
    may produce "hot" pixels when divided into an image.  Therefore,
    apply a threshold to the image pixels that cuts the pixels with
    value of exposure less than the threshold.
    """
    logger.info("Threshold cut the image: cut=%s" % cut)
    clobber = "yes" if clobber else "no"
    subprocess.check_call(["punlearn", "dmimgthresh"])
    subprocess.check_call([
        "dmimgthresh", "infile=%s" % infile,
        "outfile=%s" % outfile, "expfile=%s" % expmap,
        "cut=%s" % cut,
        "clobber=%s" % clobber
    ])


def correct_exposure(infile, outfile, expmap, clobber=False):
    """
    The strongly variable exposure near the edge of a dithered field
    may produce "hot" pixels when divided into an image.  Therefore,
    apply a threshold to the image pixels that cuts the pixels with
    value of exposure less than the threshold.
    """
    logger.info("Correct the image for exposure ...")
    clobber = "yes" if clobber else "no"
    subprocess.check_call(["punlearn", "dmimgcalc"])
    subprocess.check_call([
        "dmimgcalc", "infile=%s" % infile, "infile2=%s" % expmap,
        "outfile=%s[PFLUX_IMAGE]" % outfile,
        "operation=div",
        "clobber=%s" % clobber
    ])


def main():
    parser = argparse.ArgumentParser(
        description="Make spectral-weighted exposure map")
    parser.add_argument("-C", "--clobber", dest="clobber", action="store_true",
                        help="overwrite existing file")
    parser.add_argument("-M", "--update-manifest", dest="update_manifest",
                        action="store_true",
                        help="update manifest with newly created images")
    parser.add_argument("-i", "--infile", dest="infile", required=True,
                        help="input image file to apply exposure correction")
    parser.add_argument("-e", "--expmap", dest="expmap",
                        help="exposure map (default: 'expmap' from manifest)")
    parser.add_argument("-o", "--outfile", dest="outfile",
                        help="filename of output exposure-corrected image " +
                        "(default: append '_expcorr' to the base filename)")
    args = parser.parse_args()

    setup_pfiles(["dmimgcalc", "dmimgthresh"])

    manifest = get_manifest()
    if args.expmap:
        expmap = args.expmap
    else:
        expmap = manifest.getpath("expmap", relative=True)
    if args.outfile:
        img_expcorr = args.outfile
    else:
        img_expcorr = os.path.splitext(args.infile)[0] + "_expcorr.fits"
    img_thresh = os.path.splitext(args.infile)[0] + "_thresh.fits"
    logger.info("infile: %s" % args.infile)
    logger.info("expmap: %s" % expmap)
    logger.info("output exposure-corrected image: %s" % img_expcorr)
    logger.info("output threshold-cut counts image: %s" % img_thresh)

    xygrid_infile = get_xygrid(args.infile)
    xygrid_expmap = get_xygrid(expmap)
    logger.info("%s:xygrid: %s" % (args.infile, xygrid_infile))
    logger.info("%s:xygrid: %s" % (expmap, xygrid_expmap))
    if xygrid_infile != xygrid_expmap:
        raise ValueError("xygrid: input image and exposure map do not match")

    threshold_image(infile=args.infile, outfile=img_thresh, expmap=expmap,
                    clobber=args.clobber)
    correct_exposure(infile=img_thresh, outfile=img_expcorr,
                     expmap=expmap, clobber=args.clobber)

    if args.update_manifest:
        logger.info("Add newly created images to manifest ...")
        key = "img_expcorr"
        manifest.setpath(key, img_expcorr)
        logger.info("Added '%s' to manifest: %s" % (key, manifest.get(key)))
        key = "img_thresh"
        manifest.setpath(key, img_thresh)
        logger.info("Added '%s' to manifest: %s" % (key, manifest.get(key)))


if __name__ == "__main__":
    main()