panoptes.utils.images package

Submodules

panoptes.utils.images.bayer module

class panoptes.utils.images.bayer.RGB(value)[source]

Bases: IntEnum

Helper class for array index access.

B = 2
BLUE = 2
G = 1
G1 = 1
GREEN = 1
R = 0
RED = 0
panoptes.utils.images.bayer.get_pixel_color(x, y)[source]

Given a zero-indexed x,y position, return the corresponding color.

Note

See get_rgb_data() for a description of the RGGB pattern.

Returns:

one of ‘R’, ‘G1’, ‘G2’, ‘B’

Return type:

str

panoptes.utils.images.bayer.get_rgb_background(data, box_size=(79, 84), filter_size=(11, 11), estimator='mmm', interpolator='zoom', sigma=5, iters=10, exclude_percentile=100, return_separate=False, *args, **kwargs)[source]

Get the background for each color channel.

Note: This funtion does not perform any additional calibration, such as flat, bias, or dark correction. It is expected you have performed any necessary pre-processing to data before passing to this function.

By default this uses a box size of (79, 84), which gives an integer number of boxes. The size of the median filter box for the low resolution background is on the order of the stamp size.

Most of the options are described in the photutils.Background2D page: https://photutils.readthedocs.io/en/stable/background.html#d-background-and-noise-estimation

>>> from panoptes.utils.images.bayer import RGB
>>> from panoptes.utils.images import fits as fits_utils
>>> # Get our data and pre-process (basic bias subtract here).
>>> fits_fn = getfixture('solved_fits_file')
>>> camera_bias = 2048
>>> data = fits_utils.getdata(fits_fn) - camera_bias

>> The default is to return a single array for the background. >>> rgb_back = get_rgb_background(data) >>> rgb_back.mean() 136… >>> rgb_back.std() 36…

>>> # Can also return the Background2D objects, which is the input to save_rgb_bg_fits
>>> rgb_backs = get_rgb_background(data, return_separate=True)
>>> rgb_backs[RGB.RED]
<photutils.background.background_2d.Background2D...>
>>> {color.name:int(rgb_back[color].mean()) for color in RGB}
{'RED': 145, 'GREEN': 127, 'BLUE': 145}
Parameters:
  • data (np.array) – The data to use if no fits_fn is provided.

  • box_size (tuple, optional) – The box size over which to compute the 2D-Background, default (79, 84).

  • filter_size (tuple, optional) – The filter size for determining the median, default (11, 12).

  • estimator (str, optional) – The estimator object to use, default ‘mmm’.

  • interpolator (str, optional) – The interpolater object to user, default ‘zoom’.

  • sigma (int, optional) – The sigma on which to filter values, default 5.

  • iters (int, optional) – The number of iterations to sigma filter, default 10.

  • exclude_percentile (int, optional) – The percentage of the data (per channel) that can be masked, default 100 (i.e. all).

  • return_separate (bool, optional) – If the function should return a separate array for color channel, default False.

  • *args – Description

  • **kwargs – Description

Returns:

Either a single numpy array representing the entire

background, or a list of masked numpy arrays in RGB order. The background for each channel has full interploation across all pixels, but the mask covers them.

Return type:

`numpy.array`|list(Background2D)

panoptes.utils.images.bayer.get_rgb_data(data, separate_green=False)[source]

Get the data split into separate channels for RGB.

data can be a 2D (W x H) or 3D (N x W x H) array where W=width and H=height of the data, with N=number of frames.

The return array will be a 3 x W x H or 3 x N x W x H array.

The Bayer array defines a superpixel as a collection of 4 pixels set in a square grid:

R G
G B

ds9 and other image viewers define the coordinate axis from the lower left corner of the image, which is how a traditional x-y plane is defined and how most images would expect to look when viewed. This means that the (0, 0) coordinate position will be in the lower left corner of the image.

When the data is loaded into a numpy array the data is flipped on the vertical axis in order to maintain the same indexing/slicing features. This means the the (0, 0) coordinate position is in the upper-left corner of the array when output. When plotting this array one can use the origin='lower' option to view the array as would be expected in a normal image although this does not change the actual index.

Image dimensions:

----------------------------
x | width  | i | columns |  5208
y | height | j | rows    |  3476

Bayer pattern as seen in ds9:

                             x / j

             0     1    2     3 ... 5204 5205 5206 5207
           --------------------------------------------
      3475 |  R   G1    R    G1        R   G1    R   G1
      3474 | G2    B   G2     B       G2    B   G2    B
      3473 |  R   G1    R    G1        R   G1    R   G1
      3472 | G2    B   G2     B       G2    B   G2    B
         . |
y / i    . |
         . |
         3 |  R   G1    R    G1        R   G1    R   G1
         2 | G2    B   G2     B       G2    B   G2    B
         1 |  R   G1    R    G1        R   G1    R   G1
         0 | G2    B   G2     B       G2    B   G2    B

The RGGB super-pixels thus start in the upper-left.

Bayer pattern as seen in a numpy array:

                             x / j

             0     1    2     3 ... 5204 5205 5206 5207
           --------------------------------------------
         0 | G2    B   G2     B       G2    B   G2    B
         1 |  R   G1    R    G1        R   G1    R   G1
         2 | G2    B   G2     B       G2    B   G2    B
         3 |  R   G1    R    G1        R   G1    R   G1
         . |
y / i    . |
         . |
      3472 | G2    B   G2     B       G2    B   G2    B
      3473 |  R   G1    R    G1        R   G1    R   G1
      3474 | G2    B   G2     B       G2    B   G2    B
      3475 |  R   G1    R    G1        R   G1    R   G1

Here the RGGB super-pixels are flipped upside down.

In both cases the data is in the following format:

    | row (y) |  col (x)
--------------| ------
 R  |  odd i, |  even j
 G1 |  odd i, |   odd j
 G2 | even i, |  even j
 B  | even i, |   odd j

And a mask can therefore be generated as:

bayer[1::2, 0::2] = 1 # Red
bayer[1::2, 1::2] = 1 # Green
bayer[0::2, 0::2] = 1 # Green
bayer[0::2, 1::2] = 1 # Blue
panoptes.utils.images.bayer.get_rgb_masks(data, separate_green=False)[source]

Get the RGGB Bayer pattern for the given data.

Note

See get_rgb_data() for a description of the RGGB pattern.

Parameters:
  • data (np.array) – An array of data representing an image.

  • separate_green (bool, optional) – If the two green channels should be separated, default False.

Returns:

A 3-tuple of numpy arrays of bool type.

Return type:

tuple(np.array, np.array, np.array)

panoptes.utils.images.bayer.get_stamp_slice(x, y, stamp_size=(14, 14), ignore_superpixel=False, as_slices=True)[source]

Get the slice around a given position with fixed Bayer pattern.

Given an x,y pixel position, get the slice object for a stamp of a given size but make sure the first position corresponds to a red-pixel. This means that x,y will not necessarily be at the center of the resulting stamp.

>>> from panoptes.utils.images import bayer
>>> # Make a super-pixel as represented in numpy (see full stamp below).
>>> superpixel = np.array(['G2', 'B', 'R', 'G1']).reshape(2, 2)
>>> superpixel
array([['G2', 'B'],
       ['R', 'G1']], dtype='<U2')
>>> # Tile it into a 5x5 grid of super-pixels, i.e. a 10x10 stamp.
>>> stamp0 = np.tile(superpixel, (5, 5))
>>> stamp0
array([['G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B'],
       ['R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1'],
       ['G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B'],
       ['R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1'],
       ['G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B'],
       ['R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1'],
       ['G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B'],
       ['R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1'],
       ['G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B', 'G2', 'B'],
       ['R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1', 'R', 'G1']],
      dtype='<U2')
>>> stamp1 = np.arange(100).reshape(10, 10)
>>> stamp1
array([[ 0,  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]])
>>> x = 7
>>> y = 5
>>> pixel_index = (y, x)  # y=rows, x=columns
>>> stamp0[pixel_index]
'G1'
>>> stamp1[pixel_index]
57
>>> slice0 = bayer.get_stamp_slice(x, y, stamp_size=(6, 6))
>>> slice0
(slice(2, 8, None), slice(4, 10, None))
>>> stamp0[slice0]
array([['G2', 'B', 'G2', 'B', 'G2', 'B'],
       ['R', 'G1', 'R', 'G1', 'R', 'G1'],
       ['G2', 'B', 'G2', 'B', 'G2', 'B'],
       ['R', 'G1', 'R', 'G1', 'R', 'G1'],
       ['G2', 'B', 'G2', 'B', 'G2', 'B'],
       ['R', 'G1', 'R', 'G1', 'R', 'G1']], dtype='<U2')
>>> stamp1[slice0]
array([[24, 25, 26, 27, 28, 29],
       [34, 35, 36, 37, 38, 39],
       [44, 45, 46, 47, 48, 49],
       [54, 55, 56, 57, 58, 59],
       [64, 65, 66, 67, 68, 69],
       [74, 75, 76, 77, 78, 79]])
>>> # Return y_min, y_max, x_min, x_max
>>> bayer.get_stamp_slice(x, y, stamp_size=(6, 6), as_slices=False)
(2, 8, 4, 10)

The original index had a value of 57, which is within the center superpixel.

Notice that the resulting stamp has a super-pixel in the center and is bordered on all sides by a complete superpixel. This is required by default and an invalid size

We can use ignore_superpixel=True to get an odd-sized stamp.

>>> slice1 = bayer.get_stamp_slice(x, y, stamp_size=(5, 5), ignore_superpixel=True)
>>> slice1
(slice(3, 8, None), slice(5, 10, None))
>>> stamp0[slice1]
array([['G1', 'R', 'G1', 'R', 'G1'],
       ['B', 'G2', 'B', 'G2', 'B'],
       ['G1', 'R', 'G1', 'R', 'G1'],
       ['B', 'G2', 'B', 'G2', 'B'],
       ['G1', 'R', 'G1', 'R', 'G1']], dtype='<U2')
>>> stamp1[slice1]
array([[35, 36, 37, 38, 39],
       [45, 46, 47, 48, 49],
       [55, 56, 57, 58, 59],
       [65, 66, 67, 68, 69],
       [75, 76, 77, 78, 79]])

This puts the requested pixel in the center but does not offer any guarantees about the RGGB pattern.

Parameters:
  • x (float) – X pixel position.

  • y (float) – Y pixel position.

  • stamp_size (tuple, optional) – The size of the cutout, default (14, 14).

  • ignore_superpixel (bool) – If superpixels should be ignored, default False.

  • as_slices (bool) – Return slice objects, default True. Otherwise returns: y_min, y_max, x_min, x_max

Returns:

A list of row and

column slice objects or a list defining the bounding box: y_min, y_max, x_min, x_max. Return type depends on the as_slices parameter and defaults to a list of two slices.

Return type:

list(slice, slice) or list(int, int, int, int)

panoptes.utils.images.bayer.save_rgb_bg_fits(rgb_bg_data, output_filename, header=None, fpack=True, overwrite=True)[source]

Save a FITS file containing a combined background as well as separate channels.

Parameters:
  • rgb_bg_data (list[photutils.Background2D]) – The RGB background data as returned by calling panoptes.utils.images.bayer.get_rgb_background with return_separate=True.

  • output_filename (str) – The output name for the FITS file.

  • header (astropy.io.fits.Header) – FITS header to be saved with the file.

  • fpack (bool) – If the FITS file should be compressed, default True.

  • overwrite (bool) – If FITS file should be overwritten, default True.

panoptes.utils.images.cr2 module

panoptes.utils.images.cr2.cr2_to_fits(cr2_fname: Union[str, Path], fits_fname: Optional[str] = None, overwrite: bool = False, headers: Optional[dict] = None, fits_headers: Optional[dict] = None, remove_cr2: bool = False, **kwargs) Optional[Path][source]

Convert a CR2 file to FITS.

This is a convenience function that first converts the CR2 to PGM via ~cr2_to_pgm. Also adds keyword headers to the FITS file.

Note

The intermediate PGM file is automatically removed

Parameters:
  • cr2_fname (str) – Name of the CR2 file to be converted.

  • fits_fname (str, optional) – Name of the FITS file to output. Default is None, in which case the cr2_fname is used as the base.

  • overwrite (bool, optional) – Overwrite existing FITS, default False.

  • headers (dict, optional) – Header data added to the FITS file.

  • fits_headers (dict, optional) – Header data added to the FITS file without filtering.

  • remove_cr2 (bool, optional) – If CR2 should be removed after processing, default False.

  • **kwargs – Description

Returns:

The full path to the generated FITS file.

Return type:

str

panoptes.utils.images.cr2.cr2_to_jpg(cr2_fname: Path, jpg_fname: Optional[str] = None, title: str = '', overwrite: bool = False, remove_cr2: bool = False) Optional[Path][source]

Extract a JPG image from a CR2, return the new path name.

panoptes.utils.images.cr2.cr2_to_pgm(cr2_fname, pgm_fname=None, overwrite=True, *args, **kwargs)[source]

Convert CR2 file to PGM

Converts a raw Canon CR2 file to a netpbm PGM file via dcraw. Assumes dcraw is installed on the system

Note

This is a blocking call

Parameters:
  • convert (cr2_fname {str} -- Name of CR2 file to) –

  • script (**kwargs {dict} -- Additional keywords to pass to) –

Keyword Arguments:
  • None (pgm_fname {str} -- Name of PGM file to output, if) – use same name as CR2 (default: {None})

  • (default (dcraw {str} -- Path to installed dcraw) – {‘dcraw’})

  • overwritten (overwrite {bool} -- A bool indicating if existing PGM should be) – (default: {True})

Returns:

str – Filename of PGM that was created

panoptes.utils.images.cr2.read_exif(fname, exiftool='exiftool')[source]

Read the EXIF information

Gets the EXIF information using exiftool

Note

Assumes the exiftool is installed

Parameters:

file (fname {str} -- Name of) –

Keyword Arguments:

(default (exiftool {str} -- Location of exiftool) – {‘/usr/bin/exiftool’})

Returns:

dict – Dictionary of EXIF information

panoptes.utils.images.cr2.read_pgm(fname, byteorder='>', remove_after=False)[source]

Return image data from a raw PGM file as numpy array.

Note

This is correctly processed as a Big endian even though the CR2 itself marks it as a Little endian. See the notes in Source page above as well as the comment about significant bit in the Format Spec

Parameters:
  • fname (str) – Filename of PGM to be converted

  • byteorder (str) – Big endian

  • remove_after (bool) – Delete fname file after reading, defaults to False.

  • overwrite (bool) – overwrite existing PGM or not, defaults to True

Returns:

The raw data from the PGMx

Return type:

numpy.array

panoptes.utils.images.fits module

class panoptes.utils.images.fits.ObservationPathInfo(unit_id: Optional[str] = None, camera_id: Optional[str] = None, field_name: Optional[str] = None, sequence_time: Optional[Union[str, datetime, Time]] = None, image_time: Optional[Union[str, datetime, Time]] = None, path: Optional[Union[str, Path]] = None)[source]

Bases: object

Parse the location path for an image.

This is a small dataclass that offers some convenience methods for dealing with a path based on the image id.

This would usually be instantiated via path:

>>> from panoptes.utils.images.fits import ObservationPathInfo  # noqa
>>> bucket_path = 'gs://panoptes-images-background/PAN012/Hd189733/358d0f/20180824T035917/20180824T040118.fits'
>>> path_info = ObservationPathInfo(path=bucket_path)
>>> path_info.id
'PAN012_358d0f_20180824T035917_20180824T040118'
>>> path_info.unit_id
'PAN012'
>>> path_info.sequence_id
'PAN012_358d0f_20180824T035917'
>>> path_info.image_id
'PAN012_358d0f_20180824T040118'
>>> path_info.as_path(base='/tmp', ext='jpg')
PosixPath('/tmp/PAN012/358d0f/20180824T035917/20180824T040118.jpg')
>>> ObservationPathInfo(path='foobar')
Traceback (most recent call last):
  ...
ValueError: Invalid path received: self.path='foobar'
>>> # Works from a fits file directly, which reads header.
>>> fits_fn = getfixture('unsolved_fits_file')
>>> path_info = ObservationPathInfo.from_fits(fits_fn)
>>> path_info.unit_id
'PAN001'
as_path(base: Optional[Union[str, Path]] = None, ext: Optional[str] = None) Path[source]

Return a Path object.

camera_id: str = None
field_name: str = None
classmethod from_fits(fits_file)[source]
classmethod from_fits_header(header)[source]
get_full_id(sep='_') str[source]

Returns the full path id with the given separator.

property id

Full path info joined with underscores

property image_id: str

The matched image id.

image_time: Union[str, datetime, Time] = None
path: Union[str, Path] = None
property sequence_id: str

The sequence id.

sequence_time: Union[str, datetime, Time] = None
unit_id: str = None
panoptes.utils.images.fits.extract_metadata(header: Header) dict[source]

Get the metadata from a FITS image.

This function parses some of the more common headers (some from the update_observation_headers but others as well) and puts them into a dict with the obvious data types converted into objects (e.g. dates and times).

>>> # Check the headers
>>> from panoptes.utils.images import fits as fits_utils
>>> fits_fn = getfixture('unsolved_fits_file')
>>> header = fits_utils.getheader(fits_fn)
>>> metadata = extract_metadata(header)
>>> metadata['unit']['name']
'PAN001'
Parameters:

header (astropy.io.fits.Header) – The Header object from a FITS file.

panoptes.utils.images.fits.fits_to_jpg(fname=None, title=None, figsize=(10, 7.547169811320755), dpi=150, alpha=0.2, number_ticks=7, clip_percent=99.9, **kwargs)[source]
panoptes.utils.images.fits.fpack(fits_fname, unpack=False, overwrite=True)[source]

Compress/Decompress a FITS file

Uses fpack (or funpack if unpack=True) to compress a FITS file

Parameters:
  • fits_fname ({str}) – Name of a FITS file that contains a WCS.

  • unpack ({bool}, optional) – file should decompressed instead of compressed, default False.

Returns:

Filename of compressed/decompressed file.

Return type:

str

panoptes.utils.images.fits.funpack(*args, **kwargs)[source]

Unpack a FITS file.

Note

This is a thin-wrapper around the ~fpack function with the unpack=True option specified. See ~fpack documentation for details.

Parameters:
  • *args – Arguments passed to ~fpack.

  • **kwargs – Keyword arguments passed to ~fpack.

Returns:

Path to uncompressed FITS file.

Return type:

str

panoptes.utils.images.fits.get_solve_field(fname: Union[str, Path], replace: bool = True, overwrite: bool = True, timeout: float = 30, **kwargs) Dict[source]

Convenience function to wait for solve_field to finish.

This function merely passes the fname of the image to be solved along to solve_field, which returns a subprocess.Popen object. This function then waits for that command to complete, populates a dictonary with the EXIF informaiton and returns. This is often more useful than the raw solve_field function.

Example:

>>> from panoptes.utils.images import fits as fits_utils
>>> # Get our fits filename.
>>> fits_fn = getfixture('unsolved_fits_file')
>>> # Perform the solve.
>>> solve_info = fits_utils.get_solve_field(fits_fn)  
>>> # Show solved filename.
>>> solve_info['solved_fits_file']  
'.../unsolved.fits'
>>> # Pass a suggested location.
>>> ra = 15.23
>>> dec = 90
>>> radius = 5 # deg
>>> solve_info = fits_utils.solve_field(fits_fn, ra=ra, dec=dec, radius=radius)  
>>> # Pass kwargs to `solve-field` program.
>>> solve_kwargs = {'--pnm': '/tmp/awesome.bmp'}
>>> solve_info = fits_utils.get_solve_field(fits_fn, skip_solved=False, **solve_kwargs) 
>>> assert os.path.exists('/tmp/awesome.bmp') 
Parameters:
  • fname ({str}) – Name of FITS file to be solved.

  • replace (bool, optional) – Saves the WCS back to the original file, otherwise output base filename with .new extension. Default True.

  • overwrite (bool, optional) – Clobber file, default True. Required if replace=True.

  • timeout (int, optional) – The timeout for solving, default 30 seconds.

  • **kwargs ({dict}) – Options to pass to solve_field should start with .

Returns:

Keyword information from the solved field.

Return type:

dict

panoptes.utils.images.fits.get_wcsinfo(fits_fname, **kwargs)[source]

Returns the WCS information for a FITS file.

Uses the wcsinfo astrometry.net utility script to get the WCS information from a plate-solved file.

Parameters:
  • fits_fname ({str}) – Name of a FITS file that contains a WCS.

  • **kwargs – Args that can be passed to wcsinfo.

Returns:

Output as returned from wcsinfo.

Return type:

dict

Raises:

error.InvalidCommand – Raised if wcsinfo is not found (part of astrometry.net)

panoptes.utils.images.fits.getdata(fn, *args, **kwargs)[source]

Get the FITS data.

Small wrapper around astropy.io.fits.getdata to auto-determine the FITS extension. This will return the data associated with the image.

>>> fits_fn = getfixture('solved_fits_file')
>>> d0 = getdata(fits_fn)
>>> d0
array([[2215, 2169, 2200, ..., 2169, 2235, 2168],
       [2123, 2191, 2133, ..., 2212, 2127, 2217],
       [2208, 2154, 2209, ..., 2159, 2233, 2187],
       ...,
       [2120, 2201, 2120, ..., 2209, 2126, 2195],
       [2219, 2151, 2199, ..., 2173, 2214, 2166],
       [2114, 2194, 2122, ..., 2202, 2125, 2204]], dtype=uint16)
>>> d1, h1 = getdata(fits_fn, header=True)
>>> (d0 == d1).all()
True
>>> h1['FIELD']
'KIC 8462852'
Parameters:
  • fn (str) – Path to FITS file.

  • *args – Passed to astropy.io.fits.getdata.

  • **kwargs – Passed to astropy.io.fits.getdata.

Returns:

The FITS data.

Return type:

np.ndarray

panoptes.utils.images.fits.getheader(fn, *args, **kwargs)[source]

Get the FITS header.

Small wrapper around astropy.io.fits.getheader to auto-determine the FITS extension. This will return the header associated with the image. If you need the compression header information use the astropy module directly.

>>> fits_fn = getfixture('tiny_fits_file')
>>> os.path.basename(fits_fn)
'tiny.fits'
>>> header = getheader(fits_fn)
>>> header['IMAGEID']
'PAN001_XXXXXX_20160909T081152'
>>> # Works with fpacked files
>>> fits_fn = getfixture('solved_fits_file')
>>> os.path.basename(fits_fn)
'solved.fits.fz'
>>> header = getheader(fits_fn)
>>> header['IMAGEID']
'PAN001_XXXXXX_20160909T081152'
Parameters:
  • fn (str) – Path to FITS file.

  • *args – Passed to astropy.io.fits.getheader.

  • **kwargs – Passed to astropy.io.fits.getheader.

Returns:

The FITS header for the data.

Return type:

astropy.io.fits.header.Header

panoptes.utils.images.fits.getval(fn, *args, **kwargs)[source]

Get a value from the FITS header.

Small wrapper around astropy.io.fits.getval to auto-determine the FITS extension. This will return the value from the header associated with the image (not the compression header). If you need the compression header information use the astropy module directly.

>>> fits_fn = getfixture('tiny_fits_file')
>>> getval(fits_fn, 'IMAGEID')
'PAN001_XXXXXX_20160909T081152'
Parameters:

fn (str) – Path to FITS file.

Returns:

Value from header (with no type conversion).

Return type:

str or float

panoptes.utils.images.fits.getwcs(fn, *args, **kwargs)[source]

Get the WCS for the FITS file.

Small wrapper around astropy.wcs.WCS.

>>> from panoptes.utils.images import fits as fits_utils
>>> fits_fn = getfixture('solved_fits_file')
>>> wcs = fits_utils.getwcs(fits_fn)
>>> wcs.is_celestial
True
>>> fits_fn = getfixture('unsolved_fits_file')
>>> wcs = fits_utils.getwcs(fits_fn)
>>> wcs.is_celestial
False
Parameters:
  • fn (str) – Path to FITS file.

  • *args – Passed to astropy.io.fits.getheader.

  • **kwargs – Passed to astropy.io.fits.getheader.

Returns:

The World Coordinate System information.

Return type:

astropy.wcs.WCS

panoptes.utils.images.fits.solve_field(fname, timeout=15, solve_opts=None, *args, **kwargs)[source]

Plate solves an image.

Note: This is a low-level wrapper around the underlying solve-field

program. See get_solve_field for more typical usage and examples.

Parameters:
  • fname (str, required) – Filename to solve in .fits extension.

  • timeout (int, optional) – Timeout for the solve-field command, defaults to 60 seconds.

  • solve_opts (list, optional) – List of options for solve-field.

panoptes.utils.images.fits.update_observation_headers(file_path, info)[source]

Update FITS headers with items from the Observation status.

>>> # Check the headers
>>> from panoptes.utils.images import fits as fits_utils
>>> fits_fn = getfixture('unsolved_fits_file')
>>> # Show original value
>>> fits_utils.getval(fits_fn, 'FIELD')
'KIC 8462852'
>>> info = {'field_name': 'Tabbys Star'}
>>> update_observation_headers(fits_fn, info)
>>> # Show new value
>>> fits_utils.getval(fits_fn, 'FIELD')
'Tabbys Star'
Parameters:
  • file_path (str) – Path to a FITS file.

  • info (dict) – The return dict from pocs.observatory.Observation.status, which includes basic information about the observation.

panoptes.utils.images.fits.write_fits(data, header, filename, exposure_event=None, **kwargs)[source]

Write FITS file to requested location.

>>> from panoptes.utils.images import fits as fits_utils
>>> data = np.random.normal(size=100)
>>> header = { 'FILE': 'delete_me', 'TEST': True }
>>> filename = str(getfixture('tmpdir').join('temp.fits'))
>>> fits_utils.write_fits(data, header, filename)
>>> assert os.path.exists(filename)
>>> fits_utils.getval(filename, 'FILE')
'delete_me'
>>> data2 = fits_utils.getdata(filename)
>>> assert np.array_equal(data, data2)
Parameters:
  • data (array_like) – The data to be written.

  • header (dict) – Dictionary of items to be saved in header.

  • filename (str) – Path to filename for output.

  • exposure_event (None|`threading.Event`, optional) – A threading.Event that can be triggered when the image is written.

  • kwargs (dict) – Options that are passed to the astropy.io.fits.PrimaryHDU.writeto method.

panoptes.utils.images.focus module

panoptes.utils.images.focus.focus_metric(data, merit_function='vollath_F4', **kwargs)[source]

Compute the focus metric.

Computes a focus metric on the given data using a supplied merit function. The merit function can be passed either as the name of the function (must be defined in this module) or as a callable object. Additional keyword arguments for the merit function can be passed as keyword arguments to this function.

Parameters:
  • data (numpy array) –

  • merit_function (str/callable) – panoptes.utils.images) or a callable object.

Returns:

result of calling merit function on data

Return type:

scalar

panoptes.utils.images.focus.vollath_F4(data, axis=None)[source]

Compute F4 focus metric

Computes the F_4 focus metric as defined by Vollath (1998) for the given 2D numpy array. The metric can be computed in the y axis, x axis, or the mean of the two (default).

Parameters:
  • data (numpy array) –

  • axis (str, optional, default None) – be ‘Y’/’y’, ‘X’/’x’ or None, which will calculate the F4 value for both axes and return the mean.

Returns:

Calculated F4 value for y, x axis or both

Return type:

float64

panoptes.utils.images.misc module

panoptes.utils.images.misc.crop_data(data, box_width=200, center=None, data_only=True, wcs=None, **kwargs)[source]

Return a cropped portion of the image.

Shape is a box centered around the middle of the data

>>> from matplotlib import pyplot as plt
>>> from astropy.wcs import WCS
>>> from panoptes.utils.images.misc import crop_data
>>> from panoptes.utils.images.plot import add_colorbar, get_palette
>>> from panoptes.utils.images.fits import getdata
>>>
>>> fits_url = 'https://github.com/panoptes/panoptes-utils/raw/develop/tests/data/solved.fits.fz'
>>> data, header = getdata(fits_url, header=True)
>>> wcs = WCS(header)
>>> # Crop a portion of the image by WCS and get Cutout2d object.
>>> cropped = crop_data(data, center=(600, 400), box_width=100, wcs=wcs, data_only=False)
>>> fig, ax = plt.subplots()
>>> im = ax.imshow(cropped.data, origin='lower', cmap=get_palette())
>>> add_colorbar(im)
>>> plt.show()

(Source code, png, hires.png, pdf)

../_images/panoptes-utils-images-1.png
Parameters:
  • data (numpy.array) – Array of data.

  • box_width (int, optional) – Size of box width in pixels, defaults to 200px.

  • center (tuple(int, int), optional) – Crop around set of coords, default to image center.

  • data_only (bool, optional) – If True (default), return only data. If False return the Cutout2D object.

  • wcs (None|`astropy.wcs.WCS`, optional) – A valid World Coordinate System (WCS) that will be cropped along with the data if provided.

Returns:

A clipped (thumbnailed) version of the data if data_only=True, otherwise

a astropy.nddata.Cutout2D object.

Return type:

np.array

panoptes.utils.images.misc.make_timelapse(directory, fn_out=None, glob_pattern='20[1-9][0-9]*T[0-9]*.jpg', overwrite=False, timeout=60, **kwargs)[source]

Create a timelapse.

A timelapse is created from all the images in given directory

Parameters:
  • directory (str) – Directory containing image files.

  • fn_out (str, optional) – Full path to output file name, if not provided, defaults to directory basename.

  • glob_pattern (str, optional) – A glob file pattern of images to include, default ‘20[1-9][0-9]*T[0-9]*.jpg’, which corresponds to the observation images but excludes any pointing images. The pattern should be relative to the local directory.

  • overwrite (bool, optional) – Overwrite timelapse if exists, default False.

  • timeout (int) – Timeout for making movie, default 60 seconds.

  • **kwargs (dict) –

Returns:

Name of output file

Return type:

str

Raises:
panoptes.utils.images.misc.mask_saturated(data, saturation_level=None, threshold=0.9, bit_depth=None, dtype=None)[source]

Convert data to a masked array with saturated values masked.

>>> from matplotlib import pyplot as plt
>>> from astropy.wcs import WCS
>>> from panoptes.utils.images.misc import crop_data, mask_saturated
>>> from panoptes.utils.images.plot import add_colorbar, get_palette
>>> from panoptes.utils.images.fits import getdata
>>>
>>> fits_url = 'https://github.com/panoptes/panoptes-utils/raw/develop/tests/data/solved.fits.fz'
>>> data, header = getdata(fits_url, header=True)
>>> wcs = WCS(header)
>>> # Crop a portion of the image by WCS and get Cutout2d object.
>>> cropped = crop_data(data, center=(600, 400), box_width=100, wcs=wcs, data_only=False)
>>> masked = mask_saturated(cropped.data, saturation_level=11535)
>>> fig, ax = plt.subplots()
>>> im = ax.imshow(masked, origin='lower', cmap=get_palette())
>>> add_colorbar(im)
>>> fig.show()

(Source code, png, hires.png, pdf)

../_images/panoptes-utils-images-2.png
Parameters:
  • data (array_like) – The numpy data array.

  • saturation_level (scalar, optional) – The saturation level. If not given then the saturation level will be set to threshold times the maximum pixel value.

  • threshold (float, optional) – The fraction of the maximum pixel value to use as the saturation level, default 0.9.

  • bit_depth (astropy.units.Quantity or int, optional) – The effective bit depth of the data. If given the maximum pixel value will be assumed to be 2**bit_depth, otherwise an attempt will be made to infer the maximum pixel value from the data type of the data. If data is not an integer type the maximum pixel value cannot be inferred and an IllegalValue exception will be raised.

  • dtype (numpy.dtype, optional) – The requested dtype for the masked array. If not given the dtype of the masked array will be same as data.

Returns:

The masked numpy array.

Return type:

numpy.ma.array

Raises:
  • error.IllegalValue – Raised if bit_depth is an astropy.units.Quantity object but the units are not compatible with either bits or bits/pixel.

  • error.IllegalValue – Raised if neither saturation level or bit_depth are given, and data has a non integer data type.

panoptes.utils.images.plot module

panoptes.utils.images.plot.add_colorbar(axes_image, size='5%', pad=0.05, orientation='vertical')[source]

Add a colorbar to the image.

This is a simple convenience function to add a colorbar to a plot generated by matplotlib.pyplot.imshow.

>>> from matplotlib import pyplot as plt
>>> import numpy as np
>>> from panoptes.utils.images.plot import add_colorbar
>>>
>>> x = np.arange(0.0, 100.0)
>>> y = np.arange(0.0, 100.0)
>>> X, Y = np.meshgrid(x, y)
>>>
>>> func = lambda x, y: x**2 + y**2
>>> z = func(X, Y)
>>>
>>> fig, ax = plt.subplots()
>>> im1 = ax.imshow(z, origin='lower')
>>>
>>> # Add the colorbar to the Image object (not the Axes).
>>> add_colorbar(im1)
>>>
>>> fig.show()

(Source code, png, hires.png, pdf)

../_images/panoptes-utils-images-3.png

A colorbar with sane settings.

Parameters:

axes_image (matplotlib.image.AxesImage) – A matplotlib AxesImage.

panoptes.utils.images.plot.add_pixel_grid(ax1, grid_height, grid_width, show_axis_labels=True, show_superpixel=False, major_alpha=0.5, minor_alpha=0.25)[source]

Adds a pixel grid to a plot, including features for the Bayer array superpixel.

>>> from matplotlib import pyplot as plt
>>> import numpy as np
>>> from panoptes.utils.images.plot import add_pixel_grid
>>>
>>> x = np.arange(-5, 5)
>>> y = np.arange(-5, 5)
>>> X, Y = np.meshgrid(x, y)
>>> func = lambda x, y: x**2 - y**2
>>>
>>> fig, ax = plt.subplots()
>>> im1 = ax.imshow(func(X, Y), origin='lower', cmap='Greys')
>>>
>>> # Add the grid to the Axes object.
>>> add_pixel_grid(ax, grid_height=10, grid_width=10, show_superpixel=True, show_axis_labels=False)
>>>
>>> fig.show()

(Source code, png, hires.png, pdf)

../_images/panoptes-utils-images-4.png

The Bayer array superpixel pattern. Grid height and size must be even.

Parameters:
  • ax1 (matplotlib.axes.Axes) – The axes to add the grid to.

  • grid_height (int) – The height of the grid in pixels.

  • grid_width (int) – The width of the grid in pixels.

  • show_axis_labels (bool, optional) – Whether to show the axis labels. Default True.

  • show_superpixel (bool, optional) – Whether to show the superpixel pattern. Default False.

  • major_alpha (float, optional) – The alpha value for the major grid lines. Default 0.5.

  • minor_alpha (float, optional) – The alpha value for the minor grid lines. Default 0.25.

panoptes.utils.images.plot.get_palette(cmap='inferno')[source]

Get a palette for drawing.

Returns a copy of the colormap palette with bad pixels marked.

Parameters:

cmap (str, optional) – Colormap to use, default ‘inferno’.

Returns:

The colormap.

Return type:

matplotlib.cm

Module contents

panoptes.utils.images.make_pretty_image(fname, title=None, img_type=None, link_path=None, **kwargs) Optional[Path][source]

Make a pretty image.

This will create a jpg file from either a CR2 (Canon) or FITS file.

Parameters:
  • fname (str) – The path to the raw image.

  • title (None|str, optional) – Title to be placed on image, default None.

  • img_type (None|str, optional) – Image type of fname, one of ‘.cr2’ or ‘.fits’. The default is None, in which case the file extension of fname is used.

  • link_path (None|str, optional) – Path to location that image should be symlinked. The directory must exist.

  • script. (**kwargs {dict} -- Additional arguments to be passed to external) –

Returns:

str – Filename of image that was created.