aboutsummaryrefslogtreecommitdiffstats
path: root/astro/fits
diff options
context:
space:
mode:
authorAaron LI <aly@aaronly.me>2017-12-05 22:55:43 +0800
committerAaron LI <aly@aaronly.me>2017-12-05 22:55:43 +0800
commitbe6297a655249cf963b7a50e65ed0881833de806 (patch)
tree5872df5bbba2b5d8d5062a0cbff251b5fd0998ba /astro/fits
parent09805bcdb9a948d434893e24fcbe515dc4ef5d2a (diff)
downloadatoolbox-be6297a655249cf963b7a50e65ed0881833de806.tar.bz2
Add astro/fitsimage.py: FITS image manipulation tool
Replace fitsimgadd.py and fitsimgsub.py
Diffstat (limited to 'astro/fits')
-rwxr-xr-xastro/fits/fitsimage.py236
-rwxr-xr-xastro/fits/fitsimgadd.py57
-rwxr-xr-xastro/fits/fitsimgsub.py55
3 files changed, 236 insertions, 112 deletions
diff --git a/astro/fits/fitsimage.py b/astro/fits/fitsimage.py
new file mode 100755
index 0000000..74b03cf
--- /dev/null
+++ b/astro/fits/fitsimage.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) Weitian LI <weitian@aaronly.me>
+# MIT license
+#
+
+"""
+FITS image manipulate tool.
+"""
+
+import sys
+import argparse
+
+import numpy as np
+from astropy.io import fits
+
+
+class FITSImage:
+ """
+ FITS image class that deals with plain 2D image (NAXIS=2), but also
+ handles single-frequency single-polarized image cube (NAXIS=3, 4),
+ e.g., created by WSClean.
+ """
+ def __init__(self, infile):
+ with fits.open(infile) as f:
+ self.header = f[0].header
+ self.data = f[0].data
+ self.ndim = self.data.ndim
+ self.shape = self.data.shape
+
+ @property
+ def bunit(self):
+ return self.header.get("BUNIT")
+
+ @property
+ def image(self):
+ """
+ Deal with single-frequency and single-polarized image cube.
+ """
+ if self.ndim == 2:
+ # NAXIS=2: [Y, X]
+ image = self.data[:, :]
+ elif self.ndim == 3 and self.shape[0] == 1:
+ # NAXIS=3: [FREQ=1, Y, X]
+ image = self.data[0, :, :]
+ elif self.ndim == 4 and self.shape[0] == 1 and self.shape[1] == 1:
+ # NAXIS=4: [STOKES=1, FREQ=1, Y, X]
+ image = self.data[0, 0, :, :]
+ else:
+ raise ValueError("invalid data shape: {1}".format(self.shape))
+ return image
+
+ @image.setter
+ def image(self, value):
+ if self.ndim == 2:
+ # NAXIS=2: [Y, X]
+ self.data[:, :] = value
+ elif self.ndim == 3:
+ # NAXIS=3: [FREQ=1, Y, X]
+ self.data[0, :, :] = value
+ else:
+ # NAXIS=4: [STOKES=1, FREQ=1, Y, X]
+ self.data[0, 0, :, :] = value
+
+ def write(self, outfile, clobber=False):
+ self.header.add_history(" ".join(sys.argv))
+ hdu = fits.PrimaryHDU(data=self.data, header=self.header)
+ try:
+ hdu.writeto(outfile, overwrite=clobber)
+ except TypeError:
+ hdu.writeto(outfile, clobber=clobber)
+
+
+def cmd_info(args):
+ """
+ Sub-command: "info", show FITS image information
+ """
+ fimage = FITSImage(args.infile)
+ print("Image data shape: {0}".format(fimage.shape))
+ print("Data unit: %s" % fimage.bunit)
+ image = fimage.image
+ if args.abs:
+ image = np.abs(image)
+ if args.center:
+ print("Central box size: %d" % args.center)
+ rows, cols = image.shape
+ rc, cc = rows//2, cols//2
+ cs1, cs2 = args.center//2, (args.center+1)//2
+ image = image[(rc-cs1):(rc+cs2), (cc-cs1):(cc+cs2)]
+ mean = np.mean(image)
+ median = np.median(image)
+ iqr = np.diff(np.percentile(image, q=(25, 75)))
+ std = np.std(image)
+ rms = np.sqrt(np.mean(image**2))
+ print("mean: %-12.6e" % mean)
+ print("median: %-12.6e" % median)
+ print("std: %-12.6e (standard deviation)" % std)
+ print("iqr: %-12.6e (interquartile range)" % iqr)
+ print("rms: %-12.6e (root-mean-squared)" % rms)
+
+
+def cmd_add(args):
+ """
+ Sub-command: "add", add the image by a number or other image(s)
+ """
+ fimage = FITSImage(args.infile)
+ image = fimage.image
+ if args.number:
+ print("Add by number: %g" % args.number)
+ image += args.number
+ else:
+ for fn in args.files:
+ print("Add by another image from: %s" % fn)
+ fimage2 = FITSImage(fn)
+ image += fimage2.image
+ fimage.image = image
+ fimage.write(args.outfile, clobber=args.clobber)
+ print("Saved FITS image to: %s" % args.outfile)
+
+
+def cmd_sub(args):
+ """
+ Sub-command: "sub", subtract the image by a number or other image(s)
+ """
+ fimage = FITSImage(args.infile)
+ image = fimage.image
+ if args.number:
+ print("Subtract by number: %g" % args.number)
+ image -= args.number
+ else:
+ for fn in args.files:
+ print("Subtract by another image from: %s" % fn)
+ fimage2 = FITSImage(fn)
+ image -= fimage2.image
+ fimage.image = image
+ fimage.write(args.outfile, clobber=args.clobber)
+ print("Saved FITS image to: %s" % args.outfile)
+
+
+def cmd_mul(args):
+ """
+ Sub-command: "mul", multiply the image by a number or other image(s)
+ """
+ fimage = FITSImage(args.infile)
+ image = fimage.image
+ if args.number:
+ print("Multiply by number: %g" % args.number)
+ image *= args.number
+ else:
+ for fn in args.files:
+ print("Multiply by another image from: %s" % fn)
+ fimage2 = FITSImage(fn)
+ image *= fimage2.image
+ fimage.image = image
+ fimage.write(args.outfile, clobber=args.clobber)
+ print("Saved FITS image to: %s" % args.outfile)
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="FITS image manipulation tool")
+ subparsers = parser.add_subparsers(dest="subparser_name",
+ title="sub-commands",
+ help="additional help")
+
+ # sub-command: "info"
+ parser_info = subparsers.add_parser(
+ "info", aliases=["show"],
+ help="show FITS image info")
+ parser_info.add_argument("-c", "--center", dest="center", type=int,
+ help="choose central region of specified size")
+ parser_info.add_argument("-a", "--abs", dest="abs", action="store_true",
+ help="take absolute values of image pixels")
+ parser_info.add_argument("infile", help="FITS image filename")
+ parser_info.set_defaults(func=cmd_info)
+
+ # sub-command: "add"
+ parser_add = subparsers.add_parser(
+ "add",
+ help="add the image by a number or other image(s)")
+ parser_add.add_argument("-C", "--clobber", dest="clobber",
+ action="store_true",
+ help="overwrite existing output file")
+ parser_add.add_argument("-i", "--infile", dest="infile", required=True,
+ help="input FITS image")
+ parser_add.add_argument("-o", "--outfile", dest="outfile", required=True,
+ help="output FITS image")
+ exgrp_add = parser_add.add_mutually_exclusive_group(required=True)
+ exgrp_add.add_argument("-n", "--number", dest="number", type=float,
+ help="number to be added by")
+ exgrp_add.add_argument("-f", "--files", dest="files", nargs="+",
+ help="FITS image(s) to be added by")
+ parser_add.set_defaults(func=cmd_add)
+
+ # sub-command: "sub"
+ parser_sub = subparsers.add_parser(
+ "sub", aliases=["subtract"],
+ help="subtract the image by a number or other image(s)")
+ parser_sub.add_argument("-C", "--clobber", dest="clobber",
+ action="store_true",
+ help="overwrite existing output file")
+ parser_sub.add_argument("-i", "--infile", dest="infile", required=True,
+ help="input FITS image")
+ parser_sub.add_argument("-o", "--outfile", dest="outfile", required=True,
+ help="output FITS image")
+ exgrp_sub = parser_sub.add_mutually_exclusive_group(required=True)
+ exgrp_sub.add_argument("-n", "--number", dest="number", type=float,
+ help="number to be subtracted by")
+ exgrp_sub.add_argument("-f", "--files", dest="files", nargs="+",
+ help="FITS image(s) to be subtracted by")
+ parser_sub.set_defaults(func=cmd_sub)
+
+ # sub-command: "mul"
+ parser_mul = subparsers.add_parser(
+ "mul", aliases=["multiply"],
+ help="multiply the image by a number or other image(s)")
+ parser_mul.add_argument("-C", "--clobber", dest="clobber",
+ action="store_true",
+ help="overwrite existing output file")
+ parser_mul.add_argument("-i", "--infile", dest="infile", required=True,
+ help="input FITS image")
+ parser_mul.add_argument("-o", "--outfile", dest="outfile", required=True,
+ help="output FITS image")
+ exgrp_mul = parser_mul.add_mutually_exclusive_group(required=True)
+ exgrp_mul.add_argument("-n", "--number", dest="number", type=float,
+ help="number to be multiplied by")
+ exgrp_mul.add_argument("-f", "--files", dest="files", nargs="+",
+ help="FITS image(s) to be multiplied by")
+ parser_mul.set_defaults(func=cmd_mul)
+
+ args = parser.parse_args()
+ args.func(args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/astro/fits/fitsimgadd.py b/astro/fits/fitsimgadd.py
deleted file mode 100755
index 52afb95..0000000
--- a/astro/fits/fitsimgadd.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (c) Weitian LI <weitian@aaronly.me>
-# MIT license
-#
-
-"""
-Add multiple FITS images of same shape.
-"""
-
-import os
-import sys
-import argparse
-
-from astropy.io import fits
-
-
-def main():
- parser = argparse.ArgumentParser(
- description="Add two or more FITS images of same shape")
- parser.add_argument("-C", "--clobber", action="store_true",
- help="overwrite existing files")
- parser.add_argument("-i", "--infile", nargs="+",
- help=">=2 FITS images to be added")
- parser.add_argument("-o", "--outfile", required=True,
- help="filename of added FITS image")
- args = parser.parse_args()
-
- nimg = len(args.infile)
- if nimg < 2:
- raise RuntimeError("more than 2 input FITS images required")
-
- if os.path.exists(args.outfile):
- if args.clobber:
- os.remove(args.outfile)
- print("WARNING: removed existing output file: %s" % args.outfile)
- else:
- raise OSError("output file already exists: %s" % args.outfile)
-
- with fits.open(args.infile[0]) as f:
- image = f[0].data
- header = f[0].header
- print("Opened 1st image: %s" % args.infile[0])
- print("Image shape: %s" % str(list(reversed(image.shape))))
-
- for fn in args.infile[1:]:
- print("Adding FITS image: %s ..." % fn)
- image += fits.open(fn)[0].data
-
- header.add_history(" ".join(sys.argv))
- hdu = fits.PrimaryHDU(data=image, header=header)
- hdu.writeto(args.outfile)
- print("Wrote added FITS image to: %s" % args.outfile)
-
-
-if __name__ == "__main__":
- main()
diff --git a/astro/fits/fitsimgsub.py b/astro/fits/fitsimgsub.py
deleted file mode 100755
index cae0190..0000000
--- a/astro/fits/fitsimgsub.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (c) Weitian LI <weitian@aaronly.me>
-# MIT license
-#
-
-"""
-Subtract a FITS image by one or more FITS images of same shape.
-"""
-
-import os
-import sys
-import argparse
-
-from astropy.io import fits
-
-
-def main():
- parser = argparse.ArgumentParser(
- description="Subtract a FITS image by >=1 images of same shape")
- parser.add_argument("-C", "--clobber", action="store_true",
- help="overwrite existing files")
- parser.add_argument("-1", "--infile1", required=True,
- help="the FITS image from which to be subtracted")
- parser.add_argument("-2", "--infile2", nargs="+",
- help="one or more FITS images to be subtracted by")
- parser.add_argument("-o", "--outfile", required=True,
- help="filename of subtracted FITS image")
- args = parser.parse_args()
-
- if os.path.exists(args.outfile):
- if args.clobber:
- os.remove(args.outfile)
- print("WARNING: removed existing output file: %s" % args.outfile)
- else:
- raise OSError("output file already exists: %s" % args.outfile)
-
- with fits.open(args.infile1) as f:
- image = f[0].data
- header = f[0].header
- print("Opened FITS image: %s" % args.infile1)
- print("Image shape: %s" % str(list(reversed(image.shape))))
-
- for fn in args.infile2:
- print("Subtracting FITS image: %s ..." % fn)
- image -= fits.open(fn)[0].data
-
- header.add_history(" ".join(sys.argv))
- hdu = fits.PrimaryHDU(data=image, header=header)
- hdu.writeto(args.outfile)
- print("Wrote subtracted FITS image to: %s" % args.outfile)
-
-
-if __name__ == "__main__":
- main()