Create a linear 2D transformation for the axes of an image
tr = imextent(img, xlo, xhi, ylo, yhi, limits='center') The limits argument canbe either 'center' or 'edge'. The image is available from both the crates_contrib.images and chips_contrib.images modules, so either of the following will load in the routine: from crates_contrib.images import imextent from chips_contrib.images import imextent
The imextent routine creates a 2D linear transform object - offset and scale changes only - that can be used to:
- label an image axes with the physical rather than pixel coordinates (analagous to the extent argument of matplotlib's imshow command).
- convert between logical and physical coordinate systems (the SimpleCoordTransform class in the crates_contrib.utils module may also be of use for this).
Loading the routine
Since the routine is useful to both Crates and ChIPS users it is exported by both the crates_contrib and chips_contrib modules; so either of the following commands will load the routine:
from crates_contrib.images import imextent from chips_contrib.images import imextent
The imextent routine has 5 required arguments - img, xlo, xhi, ylo, yhi - and one optional argument, limits. The img argument should be the image being transformed, as a NumPy array (the shape field is used to find the number of pixels in both axes), whereas the *lo/hi arguments give the minimum and maximum coordinate values for the image along the two axes. The limits argument determines how these limits are used, as described below.
Here we create a transform for a 30 (x) by 20 (y) pixel image with an X axis going from 0 to 1.5 and Y axis from 0 to 1:
chips> img = np.ones((20, 30)) chips> tr1 = imextent(img, 0, 1.5, 0, 1) chips> tr2 = imextent(img, 0, 1.5, 0, 1, limits='edge')
How the limits are calculated
The default setting of limits='center' means that the *lo/hi values refer to the center of the first and last pixel along each axis. So, for the tr1 transform, the bottom-left pixel of the image is centered at (0, 0) and the top-right pixel is centered at (1.5, 1). Since in this example the pixels are square with a width of 0.05, the bottom-left corner of the image is at (-0.05, -0.05) and the top-right corner is at (1.525, 1.025).
When limits='edge', the *lo/hi values refer to the start and end edges of the pixels. So for the tr2 transform, the bottom-left corner of the image is at (0,0) and the top-right corner is at (1.5, 1). This means that the center of the first (bottom left) pixel is at (0.025, 0.025) and the center of the last (top right) pixel is (1.475, 0.975).
chips> yi, xi = np.mgrid[10:20:20j, 40:60:40j] chips> zi = 100.0 / np.sqrt((xi-45.62)**2 + (yi- 14.7)**2) chips> add_image(np.log10(zi), imextent(zi, 40, 60, 10, 20)) chips> set_image(['depth', 50]) chips> set_plot_aspect_ratio('fit') chips> add_point(45.62, 14.7, ['color', 'red'])
Here we create an image - zi - which has it's peak at x=45.62 and y=14.7 - and is evaluated on a grid that has 40 points along x=40 to 60 and 20 along y=10 to 20 (see the NumPy documentation for mgrid for more information). We use imextent in the add_image call to ensure that the axes are displayed using the physical scale of the data (i.e. x=40 to 60) rather than the pixel scale (i.e. x=1 to 40). The set_plot_aspect_ratio() call ensures that the plot just covers the image data and no more. We decorate the image with a point located at the peak coordinate of the image.
The output can be compared to the version without the imextent call, namely:
chips> add_window() chips> add_image(np.log10(zi)) chips> set_image(['depth', 50]) chips> set_plot_aspect_ratio('fit')
chips> tr = imextent(zi, 40, 60, 10, 20) chips> print(tr.apply([[1,1], [40,20]])) [[40 10] [60 20]] chips> pix = tr.invert([[45.62, 14.7]]) chips> print(pix) [ 11.959 9.93 ]
Here we use the transform object returned by imextent to convert between physical and logical (i.e. pixel) coordinates. First we convert the pixel coordinates (1,1) and (40,20) to physical coordinates via the apply() method; by construction we would expect these to map to (40,10) and (60,20) given the arguments to the imextent call. Note that here we use the FITS definition for the logical coordinate system: (1,1) refers to the center of the bottom-left pixel.
We can also use the transform object to convert from physical to pixel coortinates by using the invert() method; here we use it to find the pixel location of the peak of the image, which has physical coordinates of (45.62,14.7). Given these limits we can mark the peak position on the image when using logical coordinates:
chips> add_window() chips> add_image(np.log10(zi), ['depth', 50]) chips> set_plot_aspect_ratio('fit') chips> add_point(pix, pix, ['color', 'red'])
Note that the transform object expects a list of lists as input, and returns the same, hence the use of "[[45.62, 14.7]]" and pix/pix above.
chips> img = np.arange(0, 12).reshape(3, 4) chips> print(img) [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] chips> add_window() chips> add_image(img, imextent(img, 10, 13, -10, -6)) chips> set_image(['depth', 50]) chips> set_plot_aspect_ratio('fit') chips> print(get_plot_xrange()) [9.5, 13.5] chips> print(get_plot_yrange()) [-11.0, -5.0]
Here we create a 4 by 3 pixel image (4 pixels along the x direction) and display it so that the centers of the pixels have
x = 10, 11, 12, 13
y = -10, -8, -6
Since the width of each pixel is 1 and it's height is 2 then the axes span from x=10-0.5 to 13+0.5 and y=-10-1 to -6+1.
chips> add_window() chips> add_image(img, imextent(img, 10, 13, -10, -6, limits='edge')) chips> set_image(['depth', 50]) chips> set_plot_aspect_ratio('fit') chips> print(get_plot_xrange()) [10.0, 13.0] chips> print(get_plot_yrange()) [-10.0, -6.0]
Here we repeat the previous example but this time set the limits option of imextent to 'edge', which means that this time the axes span from x=10 to 13 and x=-10 to -6, so that the pixel width is 3/4 and height is 4/3. The pixel centers are therefore located at
x = 10 + 3.0/8, 10 + 9.0/8, 10 + 15.0/8, 10 + 21.0/8
y = -10 + 2.0/3, -10 + 6.0/3, -10 + 10.0/3
Changes in the June 2012 Release
The imextent routine is new in this release.
See the bugs pages for an up-to-date listing of known bugs.
Refer to the CIAO bug pages for an up-to-date listing of known issues.