-
Notifications
You must be signed in to change notification settings - Fork 0
/
cutout.py
142 lines (122 loc) · 4.83 KB
/
cutout.py
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""
======
Cutout
======
Generate a cutout image from a .fits file
"""
try:
import astropy.io.fits as pyfits
import astropy.wcs as pywcs
except ImportError:
import pyfits
import pywcs
import numpy
try:
import coords
except ImportError:
pass # maybe should do something smarter here, but I want agpy to install...
try:
import montage_wrapper as montage
import os
CanUseMontage=True
except ImportError:
CanUseMontage=False
class DimensionError(ValueError):
pass
def cutoutimg(filename, xc, yc, xw=25, yw=25, units='pixels', outfile=None,
overwrite=True, useMontage=False, coordsys='celestial',
verbose=False, centerunits=None):
"""
Simple cutout function. Should be replaced by a function in astropy
eventually - keep an eye on
https://github.com/astropy/astropy/pull/3823
Parameters
----------
file : str or fits.HDUList
.fits filename or pyfits HDUList (must be 2D)
xc,yc : float,float
x and y coordinates in the fits files' coordinate system (CTYPE)
or in pixel units
xw,yw : float
x and y half-width (pixels or wcs)
(the output file will be size xw*2 * yw*2)
units : str
specify units to use: either pixels or wcs
outfile : str
optional output file
centerunits : None or str
If None, is the same as 'units'. Can be 'wcs' or 'pixels'
"""
if centerunits is None:
centerunits = units
if units not in ('wcs','pixels'):
raise ValueError("units must be wcs or pixels")
if isinstance(filename,str):
file = pyfits.open(filename)
opened=True
elif isinstance(filename,pyfits.HDUList):
file = filename
opened=False
else:
raise Exception("cutout: Input file is wrong type (string or HDUList are acceptable).")
head = file[1].header.copy()
if head['NAXIS'] > 2:
raise DimensionError("Too many (%i) dimensions!" % head['NAXIS'])
cd1 = head.get('CDELT1') if head.get('CDELT1') else head.get('CD1_1')
cd2 = head.get('CDELT2') if head.get('CDELT2') else head.get('CD2_2')
if cd1 is None or cd2 is None:
raise Exception("Missing CD or CDELT keywords in header")
wcs = pywcs.WCS(head)
if units == 'wcs':
if coordsys=='celestial' and wcs.wcs.lngtyp=='GLON':
xc,yc = coords.Position((xc,yc),system=coordsys).galactic()
elif coordsys=='galactic' and wcs.wcs.lngtyp=='RA':
xc,yc = coords.Position((xc,yc),system=coordsys).j2000()
if useMontage and CanUseMontage:
head['CRVAL1'] = xc
head['CRVAL2'] = yc
if units == 'pixels':
head['CRPIX1'] = xw
head['CRPIX2'] = yw
head['NAXIS1'] = int(xw*2)
head['NAXIS2'] = int(yw*2)
elif units == 'wcs':
cdelt = numpy.sqrt(cd1**2+cd2**2)
head['CRPIX1'] = xw / cdelt
head['CRPIX2'] = yw / cdelt
head['NAXIS1'] = int(xw*2 / cdelt)
head['NAXIS2'] = int(yw*2 / cdelt)
head.toTxtFile('temp_montage.hdr', overwrite=True)
newfile = montage.wrappers.reproject_hdu(file[0],header='temp_montage.hdr',exact_size=True)
os.remove('temp_montage.hdr')
else:
if centerunits == 'wcs':
xx,yy = wcs.wcs_world2pix(xc,yc,0)
else:
xx,yy = xc,yc
if units=='pixels':
xmin,xmax = numpy.max([0,xx-xw]),numpy.min([head['NAXIS1'],xx+xw])
ymin,ymax = numpy.max([0,yy-yw]),numpy.min([head['NAXIS2'],yy+yw])
elif units=='wcs':
xmin,xmax = numpy.max([0,xx-xw/numpy.abs(cd1)]),numpy.min([head['NAXIS1'],xx+xw/numpy.abs(cd1)])
ymin,ymax = numpy.max([0,yy-yw/numpy.abs(cd2)]),numpy.min([head['NAXIS2'],yy+yw/numpy.abs(cd2)])
else:
raise Exception("Can't use units %s." % units)
if xmax < 0 or ymax < 0:
raise ValueError("Max Coordinate is outside of map: %f,%f." % (xmax,ymax))
if ymin >= head.get('NAXIS2') or xmin >= head.get('NAXIS1'):
raise ValueError("Min Coordinate is outside of map: %f,%f." % (xmin, ymin))
head['CRPIX1'] -= xmin
head['CRPIX2'] -= ymin
head['NAXIS1'] = int(xmax-xmin)
head['NAXIS2'] = int(ymax-ymin)
if head.get('NAXIS1') == 0 or head.get('NAXIS2') == 0:
raise ValueError("Map has a 0 dimension: %i,%i." % (head.get('NAXIS1'),head.get('NAXIS2')))
img = file[1].data[int(ymin):int(ymax), int(xmin):int(xmax)]
newfile = pyfits.PrimaryHDU(data=img, header=head)
if verbose: print("Cut image %s with dims %s to %s. xrange: %f:%f, yrange: %f:%f" % (filename, file[0].data.shape,img.shape,xmin,xmax,ymin,ymax))
if isinstance(outfile,str):
newfile.writeto(outfile, overwrite=overwrite)
if opened:
file.close()
return newfile