44# subclasses of astropy classes.
55#
66
7- import matplotlib . pyplot as plt
7+ import os
88
9+ import matplotlib .pyplot as plt
910import numpy as numpy
10-
11- from astropy .wcs import WCS
11+ from astropy .coordinates import SkyCoord
1212from astropy .io import fits
13+ from astropy .wcs import WCS
1314from astropy .wcs .utils import skycoord_to_pixel , pixel_to_skycoord
14- from astropy .coordinates import SkyCoord
15+ from reproject import reproject_interp
16+
1517from arl .skycomponent import SkyComponent , create_skycomponent
1618
1719"""
1820Functions that define and manipulate images. Images are just data and a World Coordinate System.
1921"""
2022
23+
2124class Image :
2225 """Image class with Image data (as a numpy.array) and optionally the AstroPy WCS.
2326
@@ -31,13 +34,29 @@ class Image:
3134 - in numpy, the order is (frequency, polarisation, latitude, longitude)
3235
3336 """
34-
37+
3538 def __init__ (self ):
3639 self .data = None
3740 self .wcs = None
3841
3942
40- def image_show (im : Image , fig = None , title : str = '' ):
43+ def create_test_image (canonical = True ):
44+ """Create a useful test image
45+
46+ This is the test image M31 widely used in ALMA and other simulations. It is actually part of an Halpha region in
47+ M31.
48+
49+ :param canonical: Make the image into a 4 dimensional image
50+ :returns: Image
51+ """
52+ chome = os .environ ['CROCODILE' ]
53+ im = image_from_fits ("%s/data/models/M31.MOD" % chome )
54+ if canonical :
55+ im = image_replicate (im )
56+ return im
57+
58+
59+ def image_show (im : Image , fig = None , title : str = '' ):
4160 """ Show an Image with coordinates using matplotlib
4261
4362 :param im:
@@ -52,7 +71,11 @@ def image_show(im: Image, fig = None, title: str = ''):
5271 fig = plt .figure ()
5372 fig .add_subplot (111 , projection = im .wcs .sub (['longitude' , 'latitude' ]))
5473 plt .clf ()
55- plt .imshow (im .data [0 , 0 , :, :], origin = 'lower' , cmap = 'rainbow' )
74+ if len (im .data .shape ) == 4 :
75+ print (im .data [0 , 0 , :, :])
76+ plt .imshow (im .data [0 , 0 , :, :], origin = 'lower' , cmap = 'rainbow' )
77+ elif len (im .data .shape ) == 2 :
78+ plt .imshow (im .data [:, :], origin = 'lower' , cmap = 'rainbow' )
5679 plt .xlabel ('RA---SIN' )
5780 plt .ylabel ('DEC--SIN' )
5881 plt .title (title )
@@ -95,7 +118,7 @@ def image_to_fits(im: Image, fitsfile: str = 'imaging.fits'):
95118 :param fitsfile: Name of output fits file
96119 :type str:
97120 """
98- return fits .writeto (fitsfile , im .data , im .wcs .to_header (), clobber = True )
121+ return fits .writeto (filename = fitsfile , data = im .data , header = im .wcs .to_header (), clobber = True )
99122
100123
101124def image_from_fits (fitsfile : str ):
@@ -109,11 +132,12 @@ def image_from_fits(fitsfile: str):
109132 if fitsfile [0 ] == '.' :
110133 import os
111134 chome = os .environ ['CROCODILE' ]
112- fitsfile = "%s/%s" % (chome , fitsfile )
135+ fitsfile = "%s/%s" % (chome , fitsfile )
113136 hdulist = fits .open (fitsfile )
114137 fim = Image ()
115138 fim .data = hdulist [0 ].data
116139 fim .wcs = WCS (fitsfile )
140+ hdulist .close ()
117141 print ("image_from_fits: Max, min in %s = %.6f, %.6f" % (fitsfile , fim .data .max (), fim .data .min ()))
118142 return fim
119143
@@ -131,7 +155,7 @@ def image_add_wcs(im: Image, wcs: WCS):
131155 return im
132156
133157
134- def image_replicate (im : Image , shape = None ):
158+ def image_replicate (im : Image , shape = None , frequency = 1.4e9 ):
135159 """ Make a new canonical shape Image, extended along third and fourth axes by replication.
136160
137161 The order is [chan, pol, dec, ra]
@@ -143,46 +167,47 @@ def image_replicate(im: Image, shape=None):
143167 :type 4-sequence:
144168 :returns: Image
145169 """
146- # TODO: Fill in extra axes in wcs
147- if shape is None :
148- shape = [ 1 , 1 , 1 , 1 ]
170+ if shape == None :
171+ shape = [ 1 , 1 , 1 , 1 ]
172+
149173 if len (im .data .shape ) == 2 :
150174 fim = Image ()
151- image_add_wcs (fim , im .wcs )
175+
176+ newwcs = WCS (naxis = 4 )
177+
178+ newwcs .wcs .crpix = [im .wcs .wcs .crpix [0 ], im .wcs .wcs .crpix [1 ], 1.0 , 1.0 ]
179+ newwcs .wcs .cdelt = [im .wcs .wcs .cdelt [0 ], im .wcs .wcs .cdelt [1 ], 1.0 , 1.0 ]
180+ newwcs .wcs .crval = [im .wcs .wcs .crval [0 ], im .wcs .wcs .crval [1 ], 1.0 , frequency ]
181+ newwcs .wcs .ctype = [im .wcs .wcs .ctype [0 ], im .wcs .wcs .ctype [1 ], 'STOKES' , 'FREQ' ]
182+
183+ image_add_wcs (fim , newwcs )
152184 fshape = [shape [3 ], shape [2 ], im .data .shape [1 ], im .data .shape [0 ]]
153185 fim .data = numpy .zeros (fshape )
154186 print ("image_replicate: replicating shape %s to %s" % (im .data .shape , fim .data .shape ))
155187 for i3 in range (shape [3 ]):
156188 for i2 in range (shape [2 ]):
157189 fim .data [i3 , i2 , :, :] = im .data [:, :]
158- print (fim .wcs )
159- elif len (im .data .shape ) == 3 :
160- fim = Image ()
161- image_add_wcs (fim , im .wcs )
162- # TODO: fix this for non-square images!
163- fshape = [shape [3 ], im .data .shape [2 ], im .data .shape [1 ], im .data .shape [0 ]]
164- fim .data = numpy .array (fshape )
165- print ("Image: replicating shape %s to %s" % (im .data .shape , fim .data .shape ))
166- for i3 in range (shape [3 ]):
167- fim .data [i3 , :, :, :] = im .data [:, :, :]
168190 else :
169191 fim = im
170-
192+
171193 return fim
172194
173195
174196def image_reproject (im : Image , newwcs : WCS , shape = None ):
175197 """ Reproject an image to a new coordinate system
198+
199+ Currently uses the reproject python package.
200+ TODO: Write tailored reproject routine
176201
177- :param im:
202+ :param im: Image to be reprojected
178203 :type Image:
179204 :param newwcs: New WCS
180205 :type WCS:
181- :returns: Image
206+ :returns: Reprojected Image, Footprint Image
182207 """
183- print ( "image_reproject: Not yet implemented" )
184-
185- return im
208+ rep , foot = reproject_interp (( im . data , im . wcs ), newwcs , shape , order = 'bicubic' ,
209+ independent_celestial_slices = False )
210+ return image_from_array ( rep , newwcs ), image_from_array ( foot , newwcs )
186211
187212
188213def image_FFT (im : Image , ** kwargs ):
@@ -211,6 +236,7 @@ def image_add(im1: Image, im2: Image, checkwcs=False):
211236 assert not checkwcs , "Checking WCS not yet implemented"
212237 return image_from_array (im1 .data + im2 .data , im1 .wcs )
213238
239+
214240def point_source_find (im : Image , ** kwargs ) -> SkyComponent :
215241 """ Find components in Image, return SkyComponent, just find the peak for now
216242
@@ -220,14 +246,14 @@ def point_source_find(im: Image, **kwargs) -> SkyComponent:
220246 """
221247 # TODO: Implement full image fitting of components
222248 print ("imaging.point_source_find: Finding components in Image" )
223-
249+
224250 # Beware: The index sequencing is opposite in wcs and Python!
225251 locpeak = numpy .array (numpy .unravel_index ((numpy .abs (im .data )).argmax (), im .data .shape ))
226252 print ("imaging.point_source_find: Found peak at pixel coordinates %s" % str (locpeak ))
227253 w = im .wcs .sub (['longitude' , 'latitude' ])
228254 sc = pixel_to_skycoord (locpeak [3 ], locpeak [2 ], im .wcs , 0 , 'wcs' )
229255 print ("imaging.point_source_find: Found peak at world coordinates %s" % str (sc ))
230- flux = im .data [:,:, locpeak [2 ],locpeak [3 ]]
256+ flux = im .data [:, :, locpeak [2 ], locpeak [3 ]]
231257 print ("imaging.point_source_find: Flux is %s" % flux )
232258 # We also need the frequency values
233259 w = im .wcs .sub (['spectral' ])
@@ -249,18 +275,20 @@ def flux_at_direction(im: Image, sc: SkyCoord, **kwargs) -> SkyComponent:
249275 w = im .wcs .sub (['longitude' , 'latitude' ])
250276 pixloc = skycoord_to_pixel (sc , im .wcs , 0 , 'wcs' )
251277 print ("imaging.flux_at_direction: Extracting flux at pixel coordinates %d %d" % (pixloc [0 ], pixloc [1 ]))
252- flux = im .data [:,:, int (pixloc [1 ]+ 0.5 ),int (pixloc [0 ]+ 0.5 )]
278+ flux = im .data [:, :, int (pixloc [1 ] + 0.5 ), int (pixloc [0 ] + 0.5 )]
253279 print ("imaging.flux_at_direction: Flux is %s" % flux )
254-
280+
255281 # We also need the frequency values
256282 w = im .wcs .sub (['spectral' ])
257283 frequency = w .wcs_pix2world (range (im .data .shape [0 ]), 0 )
258-
284+
259285 return create_skycomponent (direction = sc , flux = flux , frequency = frequency , shape = 'point' )
260286
287+
261288if __name__ == '__main__' :
262289 import os
263- chome = os .environ ['CROCODILE' ]
290+
291+ chome = os .environ ['CROCODILE' ]
264292 kwargs = {}
265293 m31model = image_from_fits ("%s/data/models/M31.MOD" % chome )
266294 m31model_by_array = image_from_array (m31model .data , m31model .wcs )
0 commit comments