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).
- class chandra_aca.aca_image.ACAImage(*args, **kwargs)[source]¶
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
andcol0
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']
. Therow0
attribute is a proxy forimg.meta['IMGROW0']
, and likewise forcol0
.When initializing an
ACAImage
, additional*args
and**kwargs
are used to try initializing vianp.array(*args, **kwargs)
. If this fails thennp.zeros(*args, **kwargs)
is tried. In this way one can either initialize from array data or create a new array of zeros.Examples:
>>> import numpy as np >>> from chandra_aca.aca_image import ACAImage >>> dat = np.random.uniform(size=(1024, 1024)) >>> a = ACAImage(dat, row0=-512, col0=-512) >>> a = ACAImage([[1,2], [3,4]], meta={'BGDAVG': 5.2}) >>> a = ACAImage(shape=(1024, 1024), row0=-512, col0=-512)
- Parameters:
row0 – row coordinate of lower left image pixel (int, default=0)
col0 – col coordinate of lower left image pixel (int, default=0)
meta – dict of object attributes
*args – additional args passed to np.array() or np.zeros()
**kwargs – additional kwargs passed to np.array() or np.zeros()
- property aca¶
Return a light copy (same data) of self but with the _aca_coords attribute switched on so that indexing is absolute.
- centroid_fm(bgd=None, pix_zero_loc='center', norm_clip=None)[source]¶
First moment centroid of
self
using 6x6 mousebitten image for input 6x6 or 8x8 images.Note that the returned
norm
is the sum of the background-subtracted 6x6 mousebitten image, not the entire image.- Parameters:
- bgd
background to subtract, scalar or NxN ndarray (float)
- pix_zero_loc
row/col coords are integral at ‘edge’ or ‘center’
- norm_clipclip image norm at this min value (default is None and
implies Exception for non-positive norm)
- Returns:
- row, col, norm float
- flicker_init(flicker_mean_time=10000, flicker_scale=1.0, seed=None)[source]¶
Initialize instance variables to allow for flickering pixel updates.
The
flicker_scale
can be interpreted as follows: if the pixel was going to flicker by a multiplicative factor of (1 + x), now make it flicker by (1 + x * flicker_scale). This applies for flickers that increase the amplitude. For flickers that make the value smaller, then it would be 1 / (1 + x) => 1 / (1 + x * flicker_scale).The flicker_cdf file here was created using: /proj/sot/ska/www/ASPECT/ipynb/chandra_aca/flickering-pixel-model.ipynb
Examples and performance details at: /proj/sot/ska/www/ASPECT/ipynb/chandra_aca/flickering-implementation.ipynb
The model was reviewed and approved at SS&AWG on 2019-05-22.
- Parameters:
- flicker_mean_time
mean flickering time (sec, default=10000)
- flicker_scalemultiplicative factor beyond model default for
flickering amplitude (default=1.0)
- seed
random seed for reproducibility (default=None => no seed)
- flicker_update(dt, use_numba=True)[source]¶
Propagate the image forward by
dt
seconds and update any pixels that have flickered during that interval.This has the option to use one of two implementations. The default is to use the numba-based version which is about 6 times faster. The vectorized version is left in for reference.
- Parameters:
- dt
time (secs) to propagate image
- use_numba
use the numba version of updating (default=True)
- class chandra_aca.aca_image.AcaPsfLibrary(filename=None)[source]¶
Access the ACA PSF library, whch is a library of 8x8 images providing the integrated (pixelated) ACA PSF over a grid of subpixel locations.
Example:
>>> from chandra_aca.aca_image import AcaPsfLibrary >>> apl = AcaPsfLibrary() # Reads in PSF library data file >>> img = apl.get_psf_image(row=-10.456, col=250.123, norm=100000) >>> img <ACAImage row0=-14 col0=247 array([[ 39, 54, 56, 52, 37, 33, 30, 21], [ 79, 144, 260, 252, 156, 86, 67, 36], [ 162, 544, 2474, 5269, 2012, 443, 163, 57], [ 255, 1420, 10083, 12688, 11273, 1627, 302, 78], [ 186, 1423, 8926, 8480, 12292, 2142, 231, 64], [ 80, 344, 1384, 6509, 4187, 665, 111, 43], [ 40, 78, 241, 828, 616, 188, 65, 29], [ 24, 39, 86, 157, 139, 69, 48, 32]])>
- Parameters:
filename – file name of ACA PSF library (default=built-in file)
- Returns:
AcaPsfLibrary object
- get_psf_image(row, col, norm=1.0, pix_zero_loc='center', interpolation='bilinear', aca_image=True)[source]¶
Get interpolated ACA PSF image that corresponds to pixel location
row
,col
.- Parameters:
- row
(float) row value of PSF centroid
- col
(float) col value of PSF centroid
- norm
(float) summed intensity of PSF image
- pix_zero_loc
row/col coords are integral at ‘edge’ or ‘center’
- interpolation
‘nearest’ | ‘bilinear’ (default)
- aca_image
return ACAImage if True, else return ndarray
- Returns:
- ACAImage if (aca_image is True) else (ndarray image, row0, col0)
- chandra_aca.aca_image.EIGHT_LABELS = array([['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1'], ['I1', 'J1', 'K1', 'L1', 'M1', 'N1', 'O1', 'P1'], ['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2'], ['I2', 'J2', 'K2', 'L2', 'M2', 'N2', 'O2', 'P2'], ['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3'], ['I3', 'J3', 'K3', 'L3', 'M3', 'N3', 'O3', 'P3'], ['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4'], ['I4', 'J4', 'K4', 'L4', 'M4', 'N4', 'O4', 'P4']], dtype='<U2')¶
Constant for labeling ACA image pixels using the EQ-278 spec format. Pixel A1 has the lowest values of row and column; pixel H1 has the lowest row and highest col; pixel I4 has the highest row and lowest column.
- chandra_aca.aca_image.centroid_fm(img, bgd=None, pix_zero_loc='center', norm_clip=None)[source]¶
First moment centroid of
img
.Return FM centroid in coords where lower left pixel of image has value (0.0, 0.0) at the center (for pix_zero_loc=’center’) or the lower-left edge (for pix_zero_loc=’edge’).
- Parameters:
- img
NxN ndarray
- bgd
background to subtract, float of NXN ndarray
- pix_zero_loc
row/col coords are integral at ‘edge’ or ‘center’
- norm_clipclip image norm at this min value (default is None and
implies Exception for non-positive norm)
- Returns:
- row, col, norm float
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'