chandra_aca.aca_image ====================== The `aca_image` module contains classes and utilities related to ACA readout images. This includes the `ACAImage class`_ for manipulating images in ACA coordinates, a first moment centroiding routine, and a library for generating synthetic ACA images that have a high-fidelity point spread function (PSF). .. automodule:: chandra_aca.aca_image :members: ACAImage class ^^^^^^^^^^^^^^ ACAImage is an ndarray subclass that supports functionality for the Chandra ACA. Most importantly it allows image indexing and slicing in absolute "aca" coordinates, where the image lower left coordinate is specified by object ``row0`` and ``col0`` attributes. It also provides a ``meta`` dict that can be used to store additional useful information. Any keys which are all upper-case will be exposed as object attributes, e.g. ``img.BGDAVG`` <=> ``img.meta['BGDAVG']``. The ``row0`` attribute is a proxy for ``img.meta['IMGROW0']``, and likewise for ``col0``. Creation """""""" When initializing an ``ACAImage``, additional ``*args`` and ``**kwargs`` are used to try initializing via ``np.array(*args, **kwargs)``. If this fails then ``np.zeros(*args, **kwargs)`` is tried. In this way one can either initialize from array data or create a new array of zeros. One can easily create a new ``ACAImage`` as shown below. Note that it must always be 2-dimensional. The initial ``row0`` and ``col0`` values default to zero. :: >>> from chandra_aca.aca_image import ACAImage >>> im4 = np.arange(16).reshape(4, 4) >>> a = ACAImage(im4, row0=10, col0=20) >>> a <ACAImage row0=10 col0=20 array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]])> One could also initialize by providing a ``meta`` dict:: >>> a = ACAImage(im4, meta={'IMGROW0': 10, 'IMGCOL0': 20, 'BGDAVG': 5.2}) >>> a <ACAImage row0=10 col0=20 array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]])> .. Note:: The printed representation of an ``ACAImage`` is always shown as the rounded integer version of the values, but the full float value is stored internally. If you ``print()`` the image then the floating point values are shown. Image access and setting """""""""""""""""""""""" You can access array elements as usual, and in fact do any normal numpy array operations:: >>> a[3, 3] 15 The special nature of ``ACAImage`` comes by doing array access via the ``aca`` attribute. In this case all index values are in absolute coordinates, which might be negative. In this case we can access the pixel at ACA coordinates row=13, col=23, which is equal to ``a[3, 3]`` for the given ``row0`` and ``col0`` offset:: >>> a.aca[13, 23] 15 Creating a new array by slicing adjusts the ``row0`` and ``col0`` values like you would expect:: >>> a2 = a.aca[12:, 22:] >>> a2 <ACAImage row0=12 col0=22 array([[500, 11], [ 14, 15]])> You can set values in absolute coordinates:: >>> a.aca[11:13, 21:23] = 500 >>> a <ACAImage row0=10 col0=20 array([[ 0, 1, 2, 3], [ 4, 500, 500, 7], [ 8, 500, 500, 11], [ 12, 13, 14, 15]])> Now let's make an image that represents the full ACA CCD and set a sub-image from our 4x4 image ``a``. This uses the absolute location of ``a`` to define a slice into ``b``:: >>> b = ACAImage(shape=(1024,1024), row0=-512, col0=-512) >>> b[a] = a >>> b.aca[8:16, 18:26] <ACAImage row0=8 col0=18 array([[ 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 1, 2, 3, 0, 0], [ 0, 0, 4, 500, 500, 7, 0, 0], [ 0, 0, 8, 500, 500, 11, 0, 0], [ 0, 0, 12, 13, 14, 15, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0]])> You can also do things like adding 100 to every pixel in ``b`` within the area of ``a``:: >>> b[a] += 100 >>> b.aca[8:16, 18:26] <ACAImage row0=8 col0=18 array([[ 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 100, 101, 102, 103, 0, 0], [ 0, 0, 104, 600, 600, 107, 0, 0], [ 0, 0, 108, 600, 600, 111, 0, 0], [ 0, 0, 112, 113, 114, 115, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0]])> Image arithmetic operations """"""""""""""""""""""""""" In addition to doing image arithmetic operations using explicit slices as shown previously, one can also use normal arithmetic operators like ``+`` (for addition) or ``+=`` for in-place addition. **When the right-side operand is included via its ``.aca`` attribute, then the operation is done in ACA coordinates.** This means that the operation is only done on overlapping pixels. This is shown in the examples below. The supported operations for this are: - Addition (``+`` and ``+=``) - Subtraction (``-`` and ``-=``) - Multiplication (``*`` and ``*=``) - Division (``/`` and ``/=``) - True division (``/`` and ``/=`` in Py3+ and with __future__ division) - Floor division (``//`` and ``//=``) - Modulus (``%`` and ``%=``) - Power (``**`` and ``**=``) **Initialize images (different shape and offset)** >>> a = ACAImage(shape=(6, 6), row0=10, col0=20) + 1 >>> a <ACAImage row0=10 col0=20 array([[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]])> >>> b = ACAImage(np.arange(1, 17).reshape(4, 4), row0=8, col0=18) * 10 >>> b <ACAImage row0=8 col0=18 array([[ 10, 20, 30, 40], [ 50, 60, 70, 80], [ 90, 100, 110, 120], [130, 140, 150, 160]])> **Add images (output has shape and row0/col0 of left side input)** >>> a + b.aca <ACAImage row0=10 col0=20 array([[111, 121, 1, 1, 1, 1], [151, 161, 1, 1, 1, 1], [ 1, 1, 1, 1, 1, 1], [ 1, 1, 1, 1, 1, 1], [ 1, 1, 1, 1, 1, 1], [ 1, 1, 1, 1, 1, 1]])> >>> b + a.aca <ACAImage row0=8 col0=18 array([[ 10, 20, 30, 40], [ 50, 60, 70, 80], [ 90, 100, 111, 121], [130, 140, 151, 161]])> >>> b += a.aca >>> b <ACAImage row0=8 col0=18 array([[ 10, 20, 30, 40], [ 50, 60, 70, 80], [ 90, 100, 111, 121], [130, 140, 151, 161]])> **Make ``b`` image be fully contained in ``a``** >>> b.row0 = 11 >>> b.col0 = 21 >>> a += b.aca >>> a <ACAImage row0=10 col0=20 array([[ 1, 1, 1, 1, 1, 1], [ 1, 11, 21, 31, 41, 1], [ 1, 51, 61, 71, 81, 1], [ 1, 91, 101, 112, 122, 1], [ 1, 131, 141, 152, 162, 1], [ 1, 1, 1, 1, 1, 1]])> **Normal image addition fails if shape is mismatched** >>> a + b Traceback (most recent call last): File "<ipython-input-19-f96fb8f649b6>", line 1, in <module> a + b File "chandra_aca/aca_image.py", line 68, in _operator out = op(self, other) # returns self for inplace ops ValueError: operands could not be broadcast together with shapes (6,6) (4,4) Meta-data """"""""" Finally, the ``ACAImage`` object can store arbitrary metadata in the ``meta`` dict attribute. However, in order to make this convenient and distinct from native numpy attributes, the ``meta`` attributes should have UPPER CASE names. In this case they can be directly accessed as object attributes instead of going through the ``meta`` dict:: >>> a.IMGROW0 10 >>> a.meta {'IMGCOL0': 20, 'IMGROW0': 10} >>> a.NEWATTR = 'hello' >>> a.meta {'IMGCOL0': 20, 'NEWATTR': 'hello', 'IMGROW0': 10} >>> a.NEWATTR 'hello' >>> a.meta['fail'] = 1 >>> a.fail Traceback (most recent call last): AttributeError: 'ACAImage' object has no attribute 'fail'