picmaker Module

The picmaker package is organized into a handful of leaf modules plus an instruments subpackage; each symbol is documented at its canonical leaf-module location below. Click any function name to jump to its source via sphinx.ext.viewcode, or follow the Source link under each section to view the file on GitHub.

Public API

Every name listed below is re-exported from the top-level picmaker package, so callers should import from there:

from picmaker import images_to_pics, read_one_image_array, PicmakerOptions

The per-leaf-module sections after this one are the authoritative documentation for each symbol; the entries here are short pointers arranged by topic.

Top-level pipeline and CLI

images_to_pics(filenames: list[str], directory: str | None = None, verbose: bool = False, *, replace: str = 'all', proceed: bool = False, extension: str | None = 'jpg', suffix: str = '', strip: Any = None, quality: int = 75, twobytes: bool = False, bands: Any = None, lines: Any = None, samples: Any = None, obj: Any = None, pointer: Any = None, pds3_label_method: str = 'strict', size: Any = None, scale: Any = (100.0, 100.0), crop: Any = None, frame: Any = None, pad: bool = False, pad_color: Any = 'black', frame_max: int | None = None, wrap: bool = False, wrap_ratio: float | None = None, overlap: tuple[float, float] = (0.0, 0.0), gap_size: int = 1, gap_color: Any = 'white', hst: bool = False, valid: Any = None, limits: Any = None, percentiles: Any = None, trim: int = 0, trim_zeros: bool = False, footprint: int = 0, histogram: bool = False, colormap: Any = None, below_color: Any = None, above_color: Any = None, invalid_color: Any = None, gamma: float = 1.0, tint: bool = False, display_upward: bool = False, display_downward: bool = False, rotate: Any = None, filter_name: str = 'NONE', zebra: bool = False, reuse: Any = None) tuple[Any, Any, Any][source]

Convert one or more image files to picture files.

See picmaker --help for the meaning of each keyword argument. The CLI’s --filter flag binds to the filter_name keyword on this function (the rename in 2026-05 dropped the legacy builtin-shadowing filter kwarg).

Parameters:
  • filenames – List of image file names to convert.

  • directory – Output directory. None writes next to the input.

  • verbose – Print each input filename as it is processed.

Returns:

(low, high, reuse) — the lower / upper limits of the stretch and the reuse tuple if the caller wants to call again without re-reading the file.

process_images(filenames: list[str], directory: str | None, movie: bool, option_dicts: list[dict[str, Any]], verbose: bool = False) None[source]

Process a list of images using a list of option dictionaries.

In movie mode, all frames are converted with the same stretch derived from the median of the per-frame limits.

Parameters:
  • filenames – Files to process.

  • directory – Output directory (created if missing). None to write next to the input.

  • movie – Run as a movie (single shared stretch across frames).

  • option_dicts – A list of option_dict dicts (one per --versions line).

  • verbose – Print each file as it is processed.

find_common_path(directories: list[str]) str[source]

Return the longest directory prefix shared by every directory in the list.

Uses os.path.commonpath() so the result honors the current platform’s separator (/ on POSIX, \ on Windows).

Parameters:

directories – A list of directory path strings.

Returns:

The longest common directory path. An empty string if the list is empty or the directories share no common ancestor (e.g. paths on different drives on Windows, or a mix of absolute and relative paths).

main() None[source]

Picmaker CLI entry point.

Argparse usage errors raise SystemExit (passed through). Validation errors raise ValueError; the outer except Exception wrapper prints them via sys.excepthook() and exits with code 1.

class PicmakerOptions(replace: str = 'all', proceed: bool = False, extension: str | None = 'jpg', suffix: str = '', strip: Any = None, quality: int = 75, twobytes: bool = False, bands: Any = None, lines: Any = None, samples: Any = None, obj: Any = None, pointer: Any = None, pds3_label_method: str = 'strict', size: Any = None, scale: Any = (100.0, 100.0), crop: Any = None, frame: Any = None, pad: bool = False, pad_color: Any = 'black', frame_max: int | None = None, wrap: bool = False, wrap_ratio: float | None = None, overlap: tuple[float, float] = (0.0, 0.0), gap_size: int = 1, gap_color: Any = 'white', hst: bool = False, valid: Any = None, limits: Any = None, percentiles: Any = None, trim: int = 0, trim_zeros: bool = False, footprint: int = 0, histogram: bool = False, colormap: Any = None, below_color: Any = None, above_color: Any = None, invalid_color: Any = None, gamma: float = 1.0, tint: bool = False, display_upward: bool = False, display_downward: bool = False, rotate: Any = None, filter_name: str = 'NONE', zebra: bool = False)[source]

Bases: object

All post-normalization knobs that drive the pipeline.

Each field’s default matches the corresponding kwarg default on picmaker.pipeline.images_to_pics(). Call validate() once after construction (or after any in-place mutation) to enforce the cross-field invariants.

The to_kwargs() and from_kwargs() helpers let the legacy **option_dict call form continue to work unchanged.

Image I/O

read_image_array(filename: str | Sequence[str | PathLike[str]], labelfile: str | PathLike[str] | None, obj: int | str | list[int | str] | tuple[int | str, ...] | None = None, hst: bool = False, *, pds3_label_method: str = 'strict') ReadResult[source]

Read one or more image files and return a stacked 3-D array.

Parameters:
  • filename – An input file name, or a list of file names whose arrays should be stacked together. Files can be in VICAR, FITS, TIFF, .npy, or pickle format.

  • labelfile – Optional path to a PDS3 label file.

  • obj – Index or name of the object to load when the file contains multiple image objects. If a list/tuple, multiple objects are stacked.

  • hst – True to mosaic an HST image involving multiple CCDs.

  • pds3_label_method – Forwarded to pdsparser.PdsLabel as its method= argument when a PDS3 .LBL is parsed ('strict', 'loose', 'compound', or 'fast').

Returns:

  • array3d — 3-D numpy array.

  • display_upward — True if the default display orientation is upward.

  • filter_info — Optional (inst_host, inst_id, filter) tuple.

Return type:

(array3d, display_upward, filter_info)

read_one_image_array(filename: str | PathLike[str], labelfile: str | PathLike[str] | None, obj: int | str | list[int | str] | tuple[int | str, ...] | None = None, hst: bool = False, *, pds3_label_method: str = 'strict') ReadResult[source]

Read a single image array, trying each known format in turn.

The try-cascade is: pickle → numpy .npy → VICAR → FITS → PIL (or 16-bit TIFF) → PDS3 label. Each format is attempted in order and the first one that succeeds wins.

Parameters:
  • filename – Path to the input file.

  • labelfile – Optional path to a sibling PDS3 label file.

  • obj – Object index/name for multi-image files.

  • hst – True to mosaic an HST image involving multiple CCDs.

  • pds3_label_method – Forwarded to pdsparser.PdsLabel as its method= argument when the PDS3-label branch fires ('strict', 'loose', 'compound', or 'fast').

Returns:

(array3d, display_upward, filter_info). filter_info is None if no instrument is detected.

Raises:

OSError – If none of the format readers succeed. The per-reader failure causes are attached via __cause__ as an ExceptionGroup so callers can inspect what each reader rejected.

read_pds_labeled_image_array(filename: str | PathLike[str], obj: int | str | list[int | str] | tuple[int | str, ...] | None = None, *, pds3_label_method: str = 'strict') ReadResult | None[source]

Read a PDS3-labeled image and return the same triple as read_one_image_array().

Parameters:
  • filename – Path to a .LBL (or matching) PDS3 label file.

  • obj – Optional pointer name or index.

  • pds3_label_method – Forwarded to pdsparser.PdsLabel as its method= argument ('strict', 'loose', 'compound', or 'fast').

Returns:

(array3d, False, (inst_host, inst_name, filter_name)) or None if no parseable label is found.

read_pil(infile: str | PathLike[str]) Image | list[Image][source]

Read a PIL image (or 16-bit TIFF expanded to a PIL image) from a file.

Parameters:

infile – Path to the input file.

Returns:

A PIL image or a list of three PIL images (16-bit RGB).

read_array(infile: str | PathLike[str], rescale: bool) ndarray[tuple[Any, ...], dtype[Any]][source]

Read a numpy array from a PIL-readable file (or a 16-bit TIFF).

Parameters:
  • infile – Path to the input file.

  • rescale – True to scale values to the range 0-1.

Returns:

A 2-D or 3-D numpy array. The dtype depends on the input format and on rescale: uint8 for 8-bit PIL inputs without rescaling, uint16 for 16-bit TIFF, and float64 whenever rescale is true.

get_outfile(infile: str | PathLike[str], outdir: str | PathLike[str] | None = None, strip: Any = None, suffix: str | None = '', extension: str = 'jpg', replace: str = 'all') str[source]

Derive the output filename for one input.

Parameters:
  • infile – Name of the input file.

  • outdir – Output directory, or None for the input’s directory.

  • strip – A string or list of strings to strip from the input filename before adding the suffix. None is equivalent to [''].

  • suffix – Extra string added before the extension.

  • extension – Output file extension (e.g. 'jpg').

  • replace – Replacement policy when the output already exists: 'all' (silent overwrite), 'none' (skip silently), 'warn' (warn and overwrite), or 'error'.

Returns:

The output file path, or an empty string when replace='none' and the file already exists.

Raises:

OSError – If replace='error' and the file already exists.

Side Effects:

Creates the output directory tree if it does not exist.

write_pil(image: Any, outfile: str | PathLike[str], quality: int = 75) None[source]

Write a PIL image (or list of RGB images) to a file.

Parameters:
  • image – A PIL image or a list of three images.

  • outfile – The output file to write.

  • quality – Quality factor 0-100 to use for JPEG output.

class ReadResult(array3d: ndarray[tuple[Any, ...], dtype[Any]], default_is_up: bool, filter_info: tuple[str, str, Any] | None)[source]

Bases: NamedTuple

Triple returned by the reader cascade.

A typing.NamedTuple so callers can use either positional unpacking (array, up, info = read_one_image_array(...)) or attribute access (result.array3d) interchangeably.

The FilterInfo type alias is documented in the I/O section below.

Enhancement

get_limits(array2d: Any, mask: Any, limits: Any = None, percentiles: tuple[float, float] = (0.0, 100.0), *, assume_int: bool = False, trim: int = 0, trim_zeros: bool = False, footprint: int = 0) Any[source]

Compute stretch limits from numeric limits and/or percentiles.

Parameters:
  • array2d – The 2-D numpy array, which may be masked.

  • mask – 2-D mask array (True for masked pixels), or None.

  • limits – Numeric (lower, upper) to confine the histogram, or None for the full dynamic range.

  • percentiles(lower%, upper%) corresponding to the returned limits.

  • assume_int – Treat the array as integers even if it isn’t. Integer histograms extend from 0.5 below the minimum to 0.5 above the maximum.

  • trim – Number of pixels around the image edge to exclude.

  • trim_zeros – If True, trim exterior rows/columns that contain all zeros before calculating limits.

  • footprint – Size of the 2-D median filter applied as an alternative way to set limits.

Returns:

The (lower, upper) stretch limits.

apply_gamma(array: Any, gamma: float) Any[source]

Apply a gamma curve to an array already scaled 0-1.

Parameters:
  • array – A 2-D or 3-D numpy array.

  • gamma – Gamma factor. > 1 brightens midtones; < 1 darkens.

Returns:

The rescaled array.

apply_colormap(array2d: Any, limits: tuple[Any, Any], histogram: bool = False, colormap: Any = None, invalid_mask: Any = None, below_color: Any = None, above_color: Any = None, invalid_color: Any = 'black') Any[source]

Apply a colormap to a grayscale image.

Produces a 3-D array with one band (grayscale) or three bands (RGB), in axis order (line, sample, band).

Parameters:
  • array2d – A 2-D numpy array.

  • limits – The values that correspond to the first and last colors of the mapping.

  • histogram – True to use histogram shading (uniform DN distribution).

  • colormap – An N-tuple of colors or color names (e.g. "red-blue"). None keeps the image grayscale.

  • invalid_mask – Boolean mask of invalid pixels.

  • below_color – Color for pixels below the lower limit. Defaults to the first color of the colormap.

  • above_color – Color for pixels above the upper limit. Defaults to the last color of the colormap.

  • invalid_color – Color for invalid pixels.

Returns:

A 3-D array in axis order (line, sample, band) scaled 0-1.

fill_zebra_stripes(array2d: Any) Any[source]

Fill zero zebra stripes around the edges of rows.

Fills lines of zeros at the beginning and end of each row when the rows immediately above and below have nonzero values at the same columns. This removes an artifact associated with some spacecraft compression procedures.

Parameters:

array2d – A 2-D numpy array (modified in place).

Returns:

The same array, modified in place.

tinted_colormap(filter_info: Any) list[tuple[int, int, int]] | None[source]

Return a colormap based on filter info.

Parameters:

filter_info – A tuple (instrument_host, instrument_id, filter), where filter may be a single name or a 2-tuple of names (e.g. HST/ACS).

Returns:

A colormap as a list of (R, G, B) tuples (typically [black, tint, white]), or None if no colormap can be derived.

Geometry

crop_array(array: Any, value: float = 0.0, samples: bool = True, lines: bool = True) Any[source]

Crop constant-valued borders from a 2-D or 3-D image.

Parameters:
  • array – A 2-D image (lines, samples) or 3-D image (bands, lines, samples).

  • value – Constant pixel value to trim from the border.

  • samples – True to trim sample borders; False to leave them.

  • lines – True to trim line borders; False to leave them.

Returns:

A view (or sliced copy) of the input with the constant border removed.

slice_array(array3d: Any, samples: Any = None, lines: Any = None, bands: Any = None, valid: Any = None, crop: Any = None) tuple[Any, Any][source]

Return the requested slice of a 3-D array as a 2-D array.

Parameters:
  • array3d – 3-D image array indexed (bands, lines, samples).

  • samples – Tuple (s0, s1) selecting the sample range, or None for all samples.

  • lines – Tuple (l0, l1) selecting the line range, or None for all lines.

  • bands – Tuple (b0, b1) selecting the band range to average; None for all bands.

  • valid – Tuple (vmin, vmax) of valid pixel values; pixels outside this range are masked. None keeps all pixels valid.

  • crop – Numeric value to crop from the border of the image (e.g. 0 to crop zero borders). None disables cropping.

Returns:

(array2d, invalid_mask). array2d is the band-averaged 2-D array; invalid pixels are excluded from the average. invalid_mask may be None if no pixels are masked.

wrap_image(image: Any, wrapped_size: Any, sections: int, wrap_axis: int, gap_size: int, gap_color: Any) Any[source]

Wrap a PIL image into sections sub-images separated by gaps.

Parameters:
  • image – A PIL image.

  • wrapped_size(width, height) of the final wrapped image.

  • sections – Number of sections to wrap.

  • wrap_axis – 0 for horizontal wrapping; 1 for vertical.

  • gap_size – Width of gap in pixels between sections.

  • gap_color – Gap color, either an X11 name or an (R, G, B) triple.

Returns:

A new PIL image of the requested size.

pad_image(image: Any, frame: Any, pad_color: Any) Any[source]

Pad a PIL image to fill a target frame size.

Parameters:
  • image – A PIL image.

  • frame(width, height) target frame size, or None to skip padding.

  • pad_color – Gap fill color (X11 name or (R, G, B) triple).

Returns:

A padded PIL image of the requested size, or the original if no padding is needed.

resize_image(image: Any, new_size: tuple[int, int]) Any[source]

Resize a PIL image or a list of three PIL images.

Parameters:
  • image – A single PIL image or a list/tuple of three images.

  • new_size(width, height) of the output.

Returns:

The resized image(s).

rotate_array_rgb(array_rgb: Any, display_upward: bool, rotation_name: str | None) Any[source]

Apply an orientation to an RGB array.

Parameters:
  • array_rgb – An RGB array.

  • display_upward – True to flip the image upward (top-of-image becomes top-of-display); False leaves it as is.

  • rotation_name – One of 'NONE', 'FLIPLR', 'FLIPTB', 'ROT90', 'ROT180', 'ROT270'. Case-insensitive.

Returns:

The rotated array.

Raises:

KeyError – If rotation_name is not one of the recognized choices.

circle_mask(diameter: float) Any[source]

Return a boolean disk mask of the given diameter.

Parameters:

diameter – The disk diameter in pixels.

Returns:

A 2-D boolean numpy array where True marks pixels inside the disk. The returned array is trimmed by one pixel on each side if the outer row is all-empty.

get_size(array_shape: Any, size: Any = None, scale: Any = (100.0, 100.0), frame: Any = None, wrap: bool = False, wrap_ratio: float | None = None, overlap: tuple[float, float] = (0.0, 0.0), gap_size: int = 1, frame_max: int | None = None) tuple[Any, Any, int, int][source]

Compute the output image size and wrap properties.

Parameters:
  • array_shape – Shape of the source numpy array (lines, samples) or (lines, samples, bands).

  • size – Target (width, height). A scalar means square. None keeps the array size.

  • scale – Percentage scale to apply. A scalar applies to both axes; pass a tuple to scale width/height independently.

  • frame – Hard outer (width, height) constraint. None for no constraint.

  • wrap – True to consider wrapping the image to reduce distortion.

  • wrap_ratio – Wrap if the width:height (or height:width) ratio exceeds this value.

  • overlap(min%, max%) allowed overlap between wrap sections.

  • gap_size – Pixels of gap between wrap sections.

  • frame_max – Maximum percentage scale applied when frame is set and wrap is False.

Returns:

(unwrapped_size, wrapped_size, sections, wrap_axis).

Filters and conversion

filter_image(image: Any, filter_name: str) Any[source]

Apply an arbitrary filter to a PIL image.

Two-byte (16-bit) images are not supported and raise ValueError.

Parameters:
  • image – A PIL image as 8-bit RGB or grayscale.

  • filter_name – Name of the filter to be applied. Valid choices are the keys of FILTER_DICT (case-insensitive). 'NONE' returns the input image unchanged.

Returns:

The filtered PIL image. For filter_name='NONE' the input image is returned unchanged.

Raises:
  • ValueError – If image is a list (16-bit two-byte image).

  • KeyError – If filter_name (case-folded) is not a key of picmaker._filters.FILTER_DICT.

array_to_pil(array: Any, twobytes: bool = False, rescale: bool = True) Any[source]

Convert an array to a PIL image.

For the special case of a 16-bit RGB image, the result is a list of three PIL images (one per channel).

Parameters:
  • array – Image array containing one band of grayscale or three bands if RGB.

  • twobytes – True for 16-bit images, False for 8-bit.

  • rescale – True to scale values from unit; False to leave them alone.

Returns:

A PIL image, or a list of three PIL images for 16-bit RGB.

pil_to_array(image: Any, rescale: bool = True) Any[source]

Convert a PIL image (or list of RGB images) to a numpy array.

The shape of the returned array is (lines, samples, bands) for an RGB image or (lines, samples) for a grayscale image.

Parameters:
  • image – A PIL image or list/tuple of three.

  • rescale – True to scale values to the range 0-1; False to leave them alone.

Returns:

A numpy array.

Re-exported constants

The following module-level data names are re-exported from picmaker and documented in their leaf-module sections below (Color and Filters):

  • picmaker.color.RGB_BY_NM

  • picmaker.color.RFUNC, picmaker.color.GFUNC, picmaker.color.BFUNC

  • picmaker._filters.FILTER_DICT

Package

Source: src/picmaker/__init__.py.

rms-picmaker — convert PDS3/VICAR/FITS images to JPEG, TIFF, etc.

The package’s public surface is re-exported from this module so that consumers can write:

from picmaker import images_to_pics, read_one_image_array

The top-level pipeline entry point is picmaker.pipeline.images_to_pics(); the single-image reader is picmaker.io.read_one_image_array(). The full set of public names is in this module’s __all__ list.

Backward-compatibility shim

Source: src/picmaker/picmaker.py.

Alternate import path that re-exports the full picmaker public API.

Every name surfaced here is the same object as the canonical leaf-module symbol (verified by tests/test_api_compat.py), so from picmaker.picmaker import images_to_pics and from picmaker import images_to_pics resolve to the same function.

Wavelength → RGB tables

Source: src/picmaker/_rgb.py.

Wavelength → RGB lookup tables.

Maps wavelengths in the 380-750 nm range to an (R, G, B) triple suitable for tinting a grayscale image. RGB_BY_NM holds the raw table; RFUNC, GFUNC, and BFUNC are tabulation.Tabulation splines that interpolate between adjacent rows so the caller can query at any wavelength.

This module imports nothing from the rest of picmaker, which lets picmaker.color and picmaker.instruments.hst both read the same tables without forming an import cycle.

Pipeline

Source: src/picmaker/pipeline.py.

Top-level orchestration: walk directories, process one image, drive a movie.

process_images() and images_to_pics() are the CLI’s main entry points; the module-private helpers _pds3_resolve_pointer(), _hst_mosaic_rgb(), and _process_one_image() each handle one phase of the per-image pipeline so that images_to_pics() reads as a flat loop.

find_common_path(directories: list[str]) str[source]

Return the longest directory prefix shared by every directory in the list.

Uses os.path.commonpath() so the result honors the current platform’s separator (/ on POSIX, \ on Windows).

Parameters:

directories – A list of directory path strings.

Returns:

The longest common directory path. An empty string if the list is empty or the directories share no common ancestor (e.g. paths on different drives on Windows, or a mix of absolute and relative paths).

images_to_pics(filenames: list[str], directory: str | None = None, verbose: bool = False, *, replace: str = 'all', proceed: bool = False, extension: str | None = 'jpg', suffix: str = '', strip: Any = None, quality: int = 75, twobytes: bool = False, bands: Any = None, lines: Any = None, samples: Any = None, obj: Any = None, pointer: Any = None, pds3_label_method: str = 'strict', size: Any = None, scale: Any = (100.0, 100.0), crop: Any = None, frame: Any = None, pad: bool = False, pad_color: Any = 'black', frame_max: int | None = None, wrap: bool = False, wrap_ratio: float | None = None, overlap: tuple[float, float] = (0.0, 0.0), gap_size: int = 1, gap_color: Any = 'white', hst: bool = False, valid: Any = None, limits: Any = None, percentiles: Any = None, trim: int = 0, trim_zeros: bool = False, footprint: int = 0, histogram: bool = False, colormap: Any = None, below_color: Any = None, above_color: Any = None, invalid_color: Any = None, gamma: float = 1.0, tint: bool = False, display_upward: bool = False, display_downward: bool = False, rotate: Any = None, filter_name: str = 'NONE', zebra: bool = False, reuse: Any = None) tuple[Any, Any, Any][source]

Convert one or more image files to picture files.

See picmaker --help for the meaning of each keyword argument. The CLI’s --filter flag binds to the filter_name keyword on this function (the rename in 2026-05 dropped the legacy builtin-shadowing filter kwarg).

Parameters:
  • filenames – List of image file names to convert.

  • directory – Output directory. None writes next to the input.

  • verbose – Print each input filename as it is processed.

Returns:

(low, high, reuse) — the lower / upper limits of the stretch and the reuse tuple if the caller wants to call again without re-reading the file.

process_images(filenames: list[str], directory: str | None, movie: bool, option_dicts: list[dict[str, Any]], verbose: bool = False) None[source]

Process a list of images using a list of option dictionaries.

In movie mode, all frames are converted with the same stretch derived from the median of the per-frame limits.

Parameters:
  • filenames – Files to process.

  • directory – Output directory (created if missing). None to write next to the input.

  • movie – Run as a movie (single shared stretch across frames).

  • option_dicts – A list of option_dict dicts (one per --versions line).

  • verbose – Print each file as it is processed.

Options

Source: src/picmaker/options.py.

Configuration dataclass for picmaker.pipeline.images_to_pics().

PicmakerOptions consolidates the ~45 keyword arguments accepted by picmaker.pipeline.images_to_pics() into a single value object, and owns the cross-field mutex / value-validity checks that previously lived inline in both picmaker.cli._normalize_and_validate (the private helper that builds the CLI’s option dict) and picmaker.pipeline.images_to_pics().

The public function signature of picmaker.pipeline.images_to_pics() is unchanged for backward compatibility — internally it builds a PicmakerOptions and calls PicmakerOptions.validate() so the duplicated mutex checks live in exactly one place.

class PicmakerOptions(replace: str = 'all', proceed: bool = False, extension: str | None = 'jpg', suffix: str = '', strip: Any = None, quality: int = 75, twobytes: bool = False, bands: Any = None, lines: Any = None, samples: Any = None, obj: Any = None, pointer: Any = None, pds3_label_method: str = 'strict', size: Any = None, scale: Any = (100.0, 100.0), crop: Any = None, frame: Any = None, pad: bool = False, pad_color: Any = 'black', frame_max: int | None = None, wrap: bool = False, wrap_ratio: float | None = None, overlap: tuple[float, float] = (0.0, 0.0), gap_size: int = 1, gap_color: Any = 'white', hst: bool = False, valid: Any = None, limits: Any = None, percentiles: Any = None, trim: int = 0, trim_zeros: bool = False, footprint: int = 0, histogram: bool = False, colormap: Any = None, below_color: Any = None, above_color: Any = None, invalid_color: Any = None, gamma: float = 1.0, tint: bool = False, display_upward: bool = False, display_downward: bool = False, rotate: Any = None, filter_name: str = 'NONE', zebra: bool = False)[source]

Bases: object

All post-normalization knobs that drive the pipeline.

Each field’s default matches the corresponding kwarg default on picmaker.pipeline.images_to_pics(). Call validate() once after construction (or after any in-place mutation) to enforce the cross-field invariants.

The to_kwargs() and from_kwargs() helpers let the legacy **option_dict call form continue to work unchanged.

classmethod from_kwargs(**kwargs: Any) PicmakerOptions[source]

Build a PicmakerOptions from a kwargs dict.

to_kwargs() dict[str, Any][source]

Return a kwargs dict that unpacks into the pipeline entry point.

Specifically, the dict can be passed as picmaker.pipeline.images_to_pics(**options.to_kwargs()).

validate() None[source]

Run cross-field mutex / value-validity checks.

Raises:

ValueError – When two options that cannot be set together are both set, or when twobytes is combined with a non-TIFF extension or a non-trivial filter.

I/O

Source: src/picmaker/io.py.

File I/O helpers — read raw image arrays, write output, and label parsing.

This module is named io but it is NOT the stdlib io module. To avoid the shadowing trap, always use absolute imports (from picmaker.io import X) and never write import io inside the picmaker package. The stdlib io module is reachable from any non-picmaker file unchanged.

class ReadResult(array3d: ndarray[tuple[Any, ...], dtype[Any]], default_is_up: bool, filter_info: tuple[str, str, Any] | None)[source]

Bases: NamedTuple

Triple returned by the reader cascade.

A typing.NamedTuple so callers can use either positional unpacking (array, up, info = read_one_image_array(...)) or attribute access (result.array3d) interchangeably.

array3d: ndarray[tuple[Any, ...], dtype[Any]]

3-D numpy array indexed (bands, lines, samples).

default_is_up: bool

True if the per-instrument default display orientation is upward (line numbers increase upward).

filter_info: tuple[str, str, Any] | None

(inst_host, inst_id, filter_name) or None if no registered instrument matched. filter_name is usually a string but is a 2-tuple for some HST conventions.

get_outfile(infile: str | PathLike[str], outdir: str | PathLike[str] | None = None, strip: Any = None, suffix: str | None = '', extension: str = 'jpg', replace: str = 'all') str[source]

Derive the output filename for one input.

Parameters:
  • infile – Name of the input file.

  • outdir – Output directory, or None for the input’s directory.

  • strip – A string or list of strings to strip from the input filename before adding the suffix. None is equivalent to [''].

  • suffix – Extra string added before the extension.

  • extension – Output file extension (e.g. 'jpg').

  • replace – Replacement policy when the output already exists: 'all' (silent overwrite), 'none' (skip silently), 'warn' (warn and overwrite), or 'error'.

Returns:

The output file path, or an empty string when replace='none' and the file already exists.

Raises:

OSError – If replace='error' and the file already exists.

Side Effects:

Creates the output directory tree if it does not exist.

read_array(infile: str | PathLike[str], rescale: bool) ndarray[tuple[Any, ...], dtype[Any]][source]

Read a numpy array from a PIL-readable file (or a 16-bit TIFF).

Parameters:
  • infile – Path to the input file.

  • rescale – True to scale values to the range 0-1.

Returns:

A 2-D or 3-D numpy array. The dtype depends on the input format and on rescale: uint8 for 8-bit PIL inputs without rescaling, uint16 for 16-bit TIFF, and float64 whenever rescale is true.

read_image_array(filename: str | Sequence[str | PathLike[str]], labelfile: str | PathLike[str] | None, obj: int | str | list[int | str] | tuple[int | str, ...] | None = None, hst: bool = False, *, pds3_label_method: str = 'strict') ReadResult[source]

Read one or more image files and return a stacked 3-D array.

Parameters:
  • filename – An input file name, or a list of file names whose arrays should be stacked together. Files can be in VICAR, FITS, TIFF, .npy, or pickle format.

  • labelfile – Optional path to a PDS3 label file.

  • obj – Index or name of the object to load when the file contains multiple image objects. If a list/tuple, multiple objects are stacked.

  • hst – True to mosaic an HST image involving multiple CCDs.

  • pds3_label_method – Forwarded to pdsparser.PdsLabel as its method= argument when a PDS3 .LBL is parsed ('strict', 'loose', 'compound', or 'fast').

Returns:

  • array3d — 3-D numpy array.

  • display_upward — True if the default display orientation is upward.

  • filter_info — Optional (inst_host, inst_id, filter) tuple.

Return type:

(array3d, display_upward, filter_info)

read_one_image_array(filename: str | PathLike[str], labelfile: str | PathLike[str] | None, obj: int | str | list[int | str] | tuple[int | str, ...] | None = None, hst: bool = False, *, pds3_label_method: str = 'strict') ReadResult[source]

Read a single image array, trying each known format in turn.

The try-cascade is: pickle → numpy .npy → VICAR → FITS → PIL (or 16-bit TIFF) → PDS3 label. Each format is attempted in order and the first one that succeeds wins.

Parameters:
  • filename – Path to the input file.

  • labelfile – Optional path to a sibling PDS3 label file.

  • obj – Object index/name for multi-image files.

  • hst – True to mosaic an HST image involving multiple CCDs.

  • pds3_label_method – Forwarded to pdsparser.PdsLabel as its method= argument when the PDS3-label branch fires ('strict', 'loose', 'compound', or 'fast').

Returns:

(array3d, display_upward, filter_info). filter_info is None if no instrument is detected.

Raises:

OSError – If none of the format readers succeed. The per-reader failure causes are attached via __cause__ as an ExceptionGroup so callers can inspect what each reader rejected.

read_pds_labeled_image_array(filename: str | PathLike[str], obj: int | str | list[int | str] | tuple[int | str, ...] | None = None, *, pds3_label_method: str = 'strict') ReadResult | None[source]

Read a PDS3-labeled image and return the same triple as read_one_image_array().

Parameters:
  • filename – Path to a .LBL (or matching) PDS3 label file.

  • obj – Optional pointer name or index.

  • pds3_label_method – Forwarded to pdsparser.PdsLabel as its method= argument ('strict', 'loose', 'compound', or 'fast').

Returns:

(array3d, False, (inst_host, inst_name, filter_name)) or None if no parseable label is found.

read_pil(infile: str | PathLike[str]) Image | list[Image][source]

Read a PIL image (or 16-bit TIFF expanded to a PIL image) from a file.

Parameters:

infile – Path to the input file.

Returns:

A PIL image or a list of three PIL images (16-bit RGB).

Enhancement

Source: src/picmaker/enhance.py.

Pixel-value enhancement: stretch limits, gamma, colormap lookup, zebra fill.

These functions take 2-D numpy arrays (post slicing/cropping) and adjust intensity or map intensity to color. None of them resize or rotate.

apply_colormap(array2d: Any, limits: tuple[Any, Any], histogram: bool = False, colormap: Any = None, invalid_mask: Any = None, below_color: Any = None, above_color: Any = None, invalid_color: Any = 'black') Any[source]

Apply a colormap to a grayscale image.

Produces a 3-D array with one band (grayscale) or three bands (RGB), in axis order (line, sample, band).

Parameters:
  • array2d – A 2-D numpy array.

  • limits – The values that correspond to the first and last colors of the mapping.

  • histogram – True to use histogram shading (uniform DN distribution).

  • colormap – An N-tuple of colors or color names (e.g. "red-blue"). None keeps the image grayscale.

  • invalid_mask – Boolean mask of invalid pixels.

  • below_color – Color for pixels below the lower limit. Defaults to the first color of the colormap.

  • above_color – Color for pixels above the upper limit. Defaults to the last color of the colormap.

  • invalid_color – Color for invalid pixels.

Returns:

A 3-D array in axis order (line, sample, band) scaled 0-1.

apply_gamma(array: Any, gamma: float) Any[source]

Apply a gamma curve to an array already scaled 0-1.

Parameters:
  • array – A 2-D or 3-D numpy array.

  • gamma – Gamma factor. > 1 brightens midtones; < 1 darkens.

Returns:

The rescaled array.

fill_zebra_stripes(array2d: Any) Any[source]

Fill zero zebra stripes around the edges of rows.

Fills lines of zeros at the beginning and end of each row when the rows immediately above and below have nonzero values at the same columns. This removes an artifact associated with some spacecraft compression procedures.

Parameters:

array2d – A 2-D numpy array (modified in place).

Returns:

The same array, modified in place.

get_limits(array2d: Any, mask: Any, limits: Any = None, percentiles: tuple[float, float] = (0.0, 100.0), *, assume_int: bool = False, trim: int = 0, trim_zeros: bool = False, footprint: int = 0) Any[source]

Compute stretch limits from numeric limits and/or percentiles.

Parameters:
  • array2d – The 2-D numpy array, which may be masked.

  • mask – 2-D mask array (True for masked pixels), or None.

  • limits – Numeric (lower, upper) to confine the histogram, or None for the full dynamic range.

  • percentiles(lower%, upper%) corresponding to the returned limits.

  • assume_int – Treat the array as integers even if it isn’t. Integer histograms extend from 0.5 below the minimum to 0.5 above the maximum.

  • trim – Number of pixels around the image edge to exclude.

  • trim_zeros – If True, trim exterior rows/columns that contain all zeros before calculating limits.

  • footprint – Size of the 2-D median filter applied as an alternative way to set limits.

Returns:

The (lower, upper) stretch limits.

Geometry

Source: src/picmaker/geometry.py.

Geometry helpers: slicing, cropping, rotation, sizing, wrapping, padding.

These functions take numpy arrays or PIL images and shape them — they do not change pixel values (rotation/flips notwithstanding) or apply colormaps.

circle_mask(diameter: float) Any[source]

Return a boolean disk mask of the given diameter.

Parameters:

diameter – The disk diameter in pixels.

Returns:

A 2-D boolean numpy array where True marks pixels inside the disk. The returned array is trimmed by one pixel on each side if the outer row is all-empty.

crop_array(array: Any, value: float = 0.0, samples: bool = True, lines: bool = True) Any[source]

Crop constant-valued borders from a 2-D or 3-D image.

Parameters:
  • array – A 2-D image (lines, samples) or 3-D image (bands, lines, samples).

  • value – Constant pixel value to trim from the border.

  • samples – True to trim sample borders; False to leave them.

  • lines – True to trim line borders; False to leave them.

Returns:

A view (or sliced copy) of the input with the constant border removed.

get_size(array_shape: Any, size: Any = None, scale: Any = (100.0, 100.0), frame: Any = None, wrap: bool = False, wrap_ratio: float | None = None, overlap: tuple[float, float] = (0.0, 0.0), gap_size: int = 1, frame_max: int | None = None) tuple[Any, Any, int, int][source]

Compute the output image size and wrap properties.

Parameters:
  • array_shape – Shape of the source numpy array (lines, samples) or (lines, samples, bands).

  • size – Target (width, height). A scalar means square. None keeps the array size.

  • scale – Percentage scale to apply. A scalar applies to both axes; pass a tuple to scale width/height independently.

  • frame – Hard outer (width, height) constraint. None for no constraint.

  • wrap – True to consider wrapping the image to reduce distortion.

  • wrap_ratio – Wrap if the width:height (or height:width) ratio exceeds this value.

  • overlap(min%, max%) allowed overlap between wrap sections.

  • gap_size – Pixels of gap between wrap sections.

  • frame_max – Maximum percentage scale applied when frame is set and wrap is False.

Returns:

(unwrapped_size, wrapped_size, sections, wrap_axis).

pad_image(image: Any, frame: Any, pad_color: Any) Any[source]

Pad a PIL image to fill a target frame size.

Parameters:
  • image – A PIL image.

  • frame(width, height) target frame size, or None to skip padding.

  • pad_color – Gap fill color (X11 name or (R, G, B) triple).

Returns:

A padded PIL image of the requested size, or the original if no padding is needed.

resize_image(image: Any, new_size: tuple[int, int]) Any[source]

Resize a PIL image or a list of three PIL images.

Parameters:
  • image – A single PIL image or a list/tuple of three images.

  • new_size(width, height) of the output.

Returns:

The resized image(s).

rotate_array_rgb(array_rgb: Any, display_upward: bool, rotation_name: str | None) Any[source]

Apply an orientation to an RGB array.

Parameters:
  • array_rgb – An RGB array.

  • display_upward – True to flip the image upward (top-of-image becomes top-of-display); False leaves it as is.

  • rotation_name – One of 'NONE', 'FLIPLR', 'FLIPTB', 'ROT90', 'ROT180', 'ROT270'. Case-insensitive.

Returns:

The rotated array.

Raises:

KeyError – If rotation_name is not one of the recognized choices.

slice_array(array3d: Any, samples: Any = None, lines: Any = None, bands: Any = None, valid: Any = None, crop: Any = None) tuple[Any, Any][source]

Return the requested slice of a 3-D array as a 2-D array.

Parameters:
  • array3d – 3-D image array indexed (bands, lines, samples).

  • samples – Tuple (s0, s1) selecting the sample range, or None for all samples.

  • lines – Tuple (l0, l1) selecting the line range, or None for all lines.

  • bands – Tuple (b0, b1) selecting the band range to average; None for all bands.

  • valid – Tuple (vmin, vmax) of valid pixel values; pixels outside this range are masked. None keeps all pixels valid.

  • crop – Numeric value to crop from the border of the image (e.g. 0 to crop zero borders). None disables cropping.

Returns:

(array2d, invalid_mask). array2d is the band-averaged 2-D array; invalid pixels are excluded from the average. invalid_mask may be None if no pixels are masked.

wrap_image(image: Any, wrapped_size: Any, sections: int, wrap_axis: int, gap_size: int, gap_color: Any) Any[source]

Wrap a PIL image into sections sub-images separated by gaps.

Parameters:
  • image – A PIL image.

  • wrapped_size(width, height) of the final wrapped image.

  • sections – Number of sections to wrap.

  • wrap_axis – 0 for horizontal wrapping; 1 for vertical.

  • gap_size – Width of gap in pixels between sections.

  • gap_color – Gap color, either an X11 name or an (R, G, B) triple.

Returns:

A new PIL image of the requested size.

Color

Source: src/picmaker/color.py.

Mission-agnostic colormap dispatch.

tinted_colormap() takes a (host, instrument, filter) triple from the I/O cascade and returns a per-filter colormap (or None). The mission-specific logic lives in picmaker.instruments; this module’s only job is to normalize the filter description and forward to picmaker.instruments.lookup().

The wavelength → RGB constants RGB_BY_NM, RFUNC, GFUNC, and BFUNC are surfaced here as well as on picmaker._rgb, so that from picmaker.color import RGB_BY_NM is a single short import.

tinted_colormap(filter_info: Any) list[tuple[int, int, int]] | None[source]

Return a colormap based on filter info.

Parameters:

filter_info – A tuple (instrument_host, instrument_id, filter), where filter may be a single name or a 2-tuple of names (e.g. HST/ACS).

Returns:

A colormap as a list of (R, G, B) tuples (typically [black, tint, white]), or None if no colormap can be derived.

PIL utilities

Source: src/picmaker/pil_utils.py.

PIL image conversion + I/O helpers.

These wrap PIL.Image so callers can move between numpy arrays and PIL images and write JPEG / TIFF / 16-bit TIFF without owning the mode-specific details themselves.

array_to_pil(array: Any, twobytes: bool = False, rescale: bool = True) Any[source]

Convert an array to a PIL image.

For the special case of a 16-bit RGB image, the result is a list of three PIL images (one per channel).

Parameters:
  • array – Image array containing one band of grayscale or three bands if RGB.

  • twobytes – True for 16-bit images, False for 8-bit.

  • rescale – True to scale values from unit; False to leave them alone.

Returns:

A PIL image, or a list of three PIL images for 16-bit RGB.

pil_to_array(image: Any, rescale: bool = True) Any[source]

Convert a PIL image (or list of RGB images) to a numpy array.

The shape of the returned array is (lines, samples, bands) for an RGB image or (lines, samples) for a grayscale image.

Parameters:
  • image – A PIL image or list/tuple of three.

  • rescale – True to scale values to the range 0-1; False to leave them alone.

Returns:

A numpy array.

write_pil(image: Any, outfile: str | PathLike[str], quality: int = 75) None[source]

Write a PIL image (or list of RGB images) to a file.

Parameters:
  • image – A PIL image or a list of three images.

  • outfile – The output file to write.

  • quality – Quality factor 0-100 to use for JPEG output.

Filters

Source: src/picmaker/_filters.py.

PIL filter dispatch helpers.

filter_image(image: Any, filter_name: str) Any[source]

Apply an arbitrary filter to a PIL image.

Two-byte (16-bit) images are not supported and raise ValueError.

Parameters:
  • image – A PIL image as 8-bit RGB or grayscale.

  • filter_name – Name of the filter to be applied. Valid choices are the keys of FILTER_DICT (case-insensitive). 'NONE' returns the input image unchanged.

Returns:

The filtered PIL image. For filter_name='NONE' the input image is returned unchanged.

Raises:
  • ValueError – If image is a list (16-bit two-byte image).

  • KeyError – If filter_name (case-folded) is not a key of picmaker._filters.FILTER_DICT.

CLI

Source: src/picmaker/cli.py.

Command-line entry point for picmaker.

The main() function builds an argparse parser covering every option documented in picmaker --help and dispatches to picmaker.pipeline.process_images(). Each long flag also has an underscore alias (--alt_strip for --alt-strip, etc.) so older scripts that use the underscore spelling keep working.

Error handling follows three layers:

  • SystemExit raised by argparse’s own usage errors propagates through unchanged.

  • Mutex / value-validity checks raise ValueError; the outer except Exception wrapper prints the traceback via sys.excepthook() (so any plugin / IDE / profiler hook attached to sys.excepthook still fires) and exits with code 1.

  • KeyboardInterrupt prints *** KeyboardInterrupt *** and exits with code 2.

The filter argparse dest deliberately shadows the builtin so the option_dict it builds passes straight through to picmaker.pipeline.images_to_pics(). The per-file A002 ruff ignore lives in pyproject.toml.

main() None[source]

Picmaker CLI entry point.

Argparse usage errors raise SystemExit (passed through). Validation errors raise ValueError; the outer except Exception wrapper prints them via sys.excepthook() and exits with code 1.

Instruments

Source: src/picmaker/instruments/.

Instrument-specific detection and tint logic.

Each instrument module exposes a uniform 4-method protocol:

  • detect_vicar(vic) -> tuple[str, str, str] | None — returns (inst_host, inst_id, filter_name) for a VICAR file or None if the instrument does not own this label.

  • detect_fits(hdulist) -> tuple[str, str, str] | None — same for FITS.

  • matches(inst_host, inst_id) -> bool — host-level predicate.

  • tint_for(inst_id, filter_name) -> list[tuple[int, int, int]] | None — returns the full colormap (NOT just the tint), or None for the HST unknown-wavelength case.

ALL_INSTRUMENTS: list[ModuleType] = [<module 'picmaker.instruments.cassini' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/cassini.py'>, <module 'picmaker.instruments.voyager' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/voyager.py'>, <module 'picmaker.instruments.galileo' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/galileo.py'>, <module 'picmaker.instruments.hst' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/hst.py'>, <module 'picmaker.instruments.nh' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/nh.py'>]

Every registered instrument module.

FITS_INSTRUMENTS: list[ModuleType] = [<module 'picmaker.instruments.hst' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/hst.py'>, <module 'picmaker.instruments.nh' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/nh.py'>]

Instrument modules whose detect_fits may match a FITS input.

VICAR_INSTRUMENTS: list[ModuleType] = [<module 'picmaker.instruments.cassini' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/cassini.py'>, <module 'picmaker.instruments.galileo' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/galileo.py'>, <module 'picmaker.instruments.voyager' from '/home/docs/checkouts/readthedocs.org/user_builds/rms-picmaker/checkouts/stable/src/picmaker/instruments/voyager.py'>]

Instrument modules whose detect_vicar may match a VICAR input.

lookup(inst_host: str | None, inst_id: str | None) Any | None[source]

Return the first instrument whose matches() returns True, else None.

Parameters:
  • inst_host – Instrument host string (e.g. 'CASSINI ORBITER').

  • inst_id – Instrument id (e.g. 'ISS'). Pass None if unknown.

Returns:

The matching instrument module, or None if no instrument matches.

Cassini ISS detection and tint.

detect_fits(hdulist: Any) tuple[str, str, str] | None[source]

Cassini ISS is not delivered as FITS — always returns None.

Parameters:

hdulist – An astropy.io.fits HDU list (unused).

Returns:

Always None.

detect_vicar(vic: Any) tuple[str, str, str] | None[source]

Detect a Cassini ISS VICAR image.

Looks at the INSTRUMENT_HOST_NAME and FILTER_NAME label fields; the filter is delivered as a 2-tuple of names that are joined with '+'.

Parameters:

vic – A vicar.VicarImage instance.

Returns:

('CASSINI', 'ISS', '<filter1>+<filter2>') if the label identifies a Cassini ISS image, None otherwise.

matches(inst_host: str, inst_id: str) bool[source]

Host-level predicate; sub-instrument dispatch happens in tint_for().

Parameters:
  • inst_host – Instrument host string (e.g. 'CASSINI ORBITER').

  • inst_id – Instrument id (e.g. 'ISS').

Returns:

True for any Cassini host.

tint_for(inst_id: str, filter_name: Any) list[tuple[int, int, int]] | None[source]

Return the full [black, tint, white] colormap for a Cassini filter.

Non-ISS Cassini instruments fall through to the 2-element [black, white] colormap (no tint).

Parameters:
  • inst_id – Instrument id (typically 'ISS').

  • filter_name – The Cassini filter string from detect_vicar().

Returns:

[(0, 0, 0), tint, (255, 255, 255)] for an ISS filter or [(0, 0, 0), (255, 255, 255)] otherwise.

Voyager ISS detection and tint.

detect_fits(hdulist: Any) tuple[str, str, str] | None[source]

Voyager ISS is not delivered as FITS — always returns None.

Parameters:

hdulist – An astropy.io.fits HDU list (unused).

Returns:

Always None.

detect_vicar(vic: Any) tuple[str, str, str] | None[source]

Detect a Voyager ISS VICAR image.

Voyager VICAR labels carry their identifying string in LAB02 (a VGR prefix) and the filter name in characters 37..43 of LAB03; trailing spaces are stripped.

Parameters:

vic – A vicar.VicarImage instance.

Returns:

('VOYAGER', 'ISS', filter_name) if the label identifies a Voyager ISS image, None otherwise.

matches(inst_host: str, inst_id: str) bool[source]

Host-level predicate; sub-instrument dispatch happens in tint_for().

Parameters:
  • inst_host – Instrument host string.

  • inst_id – Instrument id (e.g. 'ISS').

Returns:

True for any host whose name starts with 'VOYAGER'.

tint_for(inst_id: str, filter_name: Any) list[tuple[int, int, int]] | None[source]

Return the full [black, tint, white] colormap for a Voyager filter.

Non-ISS Voyager instruments fall through to the 2-element [black, white] colormap. Unknown filter names raise KeyError (the caller is expected to surface that as a “no colormap” condition).

Parameters:
  • inst_id – Instrument id (typically 'ISS').

  • filter_name – A key into picmaker.instruments.voyager.FILTER_DICT.

Returns:

[(0, 0, 0), tint, (255, 255, 255)] for an ISS filter or [(0, 0, 0), (255, 255, 255)] otherwise.

Raises:

KeyError – If filter_name is not in picmaker.instruments.voyager.FILTER_DICT and inst_id is an ISS instrument.

Galileo SSI detection and tint.

detect_fits(hdulist: Any) tuple[str, str, str] | None[source]

Galileo SSI is not delivered as FITS — always returns None.

Parameters:

hdulist – An astropy.io.fits HDU list (unused).

Returns:

Always None.

detect_vicar(vic: Any) tuple[str, str, str] | None[source]

Detect a Galileo SSI VICAR image.

Two label conventions are tried in order: the MISSION keyword with a numeric FILTER index into picmaker.instruments.galileo.FILTER_NAMES, then a GLL/SSI prefix in LAB01 with FILTER=<digit> somewhere in LAB03.

Parameters:

vic – A vicar.VicarImage instance.

Returns:

('GALILEO', 'SSI', filter_name) if the label identifies a Galileo SSI image, None otherwise.

matches(inst_host: str, inst_id: str) bool[source]

Host-level predicate; sub-instrument dispatch happens in tint_for().

Parameters:
  • inst_host – Instrument host string.

  • inst_id – Instrument id (e.g. 'SSI').

Returns:

True for any host whose name starts with 'GALILEO'.

tint_for(inst_id: str, filter_name: Any) list[tuple[int, int, int]] | None[source]

Return the full [black, tint, white] colormap for a Galileo filter.

Only the SSI camera and the SOLID-prefixed variants get a colored tint; every other Galileo instrument falls through to the 2-element [black, white] colormap.

Parameters:
  • inst_id – Instrument id.

  • filter_name – A key into picmaker.instruments.galileo.FILTER_DICT.

Returns:

[(0, 0, 0), tint, (255, 255, 255)] for an SSI filter or [(0, 0, 0), (255, 255, 255)] otherwise.

Raises:

KeyError – If filter_name is not in picmaker.instruments.galileo.FILTER_DICT and inst_id selects the SSI path.

HST detection and wavelength-based tint.

detect_fits(hdulist: Any) tuple[str, str, Any] | None[source]

Detect an HST FITS image.

The TELESCOP keyword identifies the host, INSTRUME (with an optional DETECTOR suffix) identifies the instrument, and the filter name comes from one of FILTER, FILTER1/FILTER2 (HST/ACS), or FILTNAM1/FILTNAM2 (HST/WFPC2).

Parameters:

hdulist – An astropy.io.fits HDU list.

Returns:

(inst_host, inst_id, filter_name) where filter_name may be a string or a 2-tuple of strings, or None if the file is not an HST FITS image.

detect_vicar(vic: Any) tuple[str, str, str] | None[source]

HST is not delivered as VICAR — always returns None.

Parameters:

vic – A vicar.VicarImage instance (unused).

Returns:

Always None.

matches(inst_host: str, inst_id: str) bool[source]

Host-level predicate: accept any host that mentions HUBBLE or HST.

Parameters:
  • inst_host – Instrument host string (e.g. 'HST' or 'HUBBLE SPACE TELESCOPE').

  • inst_id – Instrument id.

Returns:

True if either substring appears in inst_host.

tint_for(inst_id: str, filter_name: Any) list[tuple[int, int, int]] | None[source]

Return the full [black, tint, white] colormap for an HST filter.

The tint color comes from inferring a wavelength out of the numeric characters in filter_name (e.g. 'F606W' → 606 nm) and looking that wavelength up in picmaker._rgb.RGB_BY_NM via the picmaker._rgb.RFUNC / picmaker._rgb.GFUNC / picmaker._rgb.BFUNC splines. Each detector family has its own correction:

  • NICMOS scales the inferred number by 3.5 (its filter names encode tens of nm rather than nm).

  • WFC3/IR and ACS/SBC also scale by 3.5 when the inferred number is below 200.

  • WFPC2 quad-filters FQUV* and FQCH4* are pinned to 300 nm and 900 nm respectively.

  • NICMOS polarisers POL0S / POL0L are pinned to 110 nm and 220 nm before the NICMOS x3.5 scaling.

Broadband filters F350LP, F606W, and LONG_PASS short- circuit to a plain [black, white] colormap.

Parameters:
  • inst_id – Instrument id, possibly with detector suffix (e.g. 'WFC3/IR').

  • filter_name – HST filter name; passed through as-is from detect_fits().

Returns:

[(0, 0, 0), (r, g, b), (255, 255, 255)] for a successfully inferred wavelength, [(0, 0, 0), (255, 255, 255)] for the broadband short-circuits, or None when no wavelength can be inferred (a WARNING is logged).

New Horizons MVIC detection and tint.

detect_fits(hdulist: Any) tuple[str, str, Any] | None[source]

Detect a New Horizons FITS image.

The HOSTNAME keyword identifies the host and INSTRU identifies the instrument; the filter name comes from FILTER when present.

Parameters:

hdulist – An astropy.io.fits HDU list.

Returns:

(inst_host, inst_id, filter_name) if the file is an NH FITS image, None otherwise. filter_name may be None if no FILTER keyword is present.

detect_vicar(vic: Any) tuple[str, str, str] | None[source]

NH is not delivered as VICAR — always returns None.

Parameters:

vic – A vicar.VicarImage instance (unused).

Returns:

Always None.

matches(inst_host: str, inst_id: str) bool[source]

Host-level predicate; sub-instrument dispatch happens in tint_for().

Parameters:
  • inst_host – Instrument host string.

  • inst_id – Instrument id.

Returns:

True if inst_host is 'NEW HORIZONS' or 'NH'.

tint_for(inst_id: str, filter_name: Any) list[tuple[int, int, int]] | None[source]

Return the full [black, tint, white] colormap for an NH filter.

Only the MVIC camera gets a colored tint; every other New Horizons instrument falls through to the 2-element [black, white] colormap.

Parameters:
  • inst_id – Instrument id ('MVIC' / 'MVI' for the color path).

  • filter_name – A key into picmaker.instruments.nh.FILTER_DICT.

Returns:

[(0, 0, 0), tint, (255, 255, 255)] for an MVIC filter or [(0, 0, 0), (255, 255, 255)] otherwise.

Raises:

KeyError – If filter_name is not in picmaker.instruments.nh.FILTER_DICT and inst_id selects the MVIC path.

TIFF 16

Source: src/picmaker/tiff16.py.

ReadTiff16(filename: str, up: bool = False, transpose: Any = None) tuple[Any, Any][source]

Read a 16-bit TIFF file written by WriteTiff16().

No other TIFF file formats are supported.

Parameters:
  • filename – The name of the file to read.

  • up – True for line numbers to increase upward; False (default) for downward.

  • transpose – Optional geometric transformation to undo on the image before returning. Choices match PIL.Image.Transpose: FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, ROTATE_270.

Returns:

  • array — a numpy 2-D or 3-D uint16 array indexed (line, sample, band). The third axis is present only for RGB inputs (size >= 3).

  • palette — an optional (65536, 3) array. When present, the 0th band of array indexes into the palette to derive each pixel’s (R, G, B) triple. None for non-palette inputs.

Return type:

(array, palette)

WriteTiff16(filename: str, array: Any, palette: Any = None, up: bool = False, byteorder: str = 'native', translate: bool = True, transpose: Any = None) None[source]

Write a 16-bit TIFF file from a 2-D or 3-D numpy array.

Three TIFF formats are supported: grayscale, RGB, and palette.

Parameters:
  • filename – The name of the file to write.

  • array – A numpy 2-D or 3-D array containing the image pixels. Values are converted to unsigned 16-bit if they are not already in that format. Indices are (line, sample, band); the third axis is optional. If present with size >= 3 and no palette is provided, array[:, :, 0:3] is interpreted as the (R, G, B) values for each pixel.

  • palette – Optional (65536, 3) array. When provided, the 0th band of array indexes into the palette to derive each pixel’s (R, G, B) triple.

  • up – True for line numbers to increase upward; False (default) for downward.

  • byteorder – One of 'native', 'little', or 'big'. Default is 'native'.

  • translate – True to translate a palette image to RGB on write (default; many TIFF readers do not support 16-bit palettes). False writes the palette index plus the palette table.

  • transpose – Optional geometric transformation before writing. Choices match PIL.Image.Transpose: FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, ROTATE_270.

Color names

Source: src/picmaker/colornames.py.

class ColorNames[source]

Bases: object

Standard X11 color-name → RGB lookup.

Exposes the X11 rgb.txt color table as COLOR_NAME_DICT (and the case-insensitive, whitespace-stripped variant COLOR_NAME_LOWER_DICT) plus a single entry point lookup() that resolves the following input forms:

  • A canonical name ('IndianRed', 'red', 'white smoke').

  • A case-insensitive variant with spaces, dashes, or underscores stripped ('INDIAN_RED', 'mint-cream').

  • An RGB container expressed as '(r, g, b)' or '[r, g, b]'.

The class has no instance state; every entry point is a staticmethod().

static lookup(name: str) tuple[int, int, int][source]

Resolve a color name or RGB expression to a (r, g, b) triple.

Parameters:

name – A canonical X11 color name (case-sensitive), a case-insensitive variant with spaces / dashes / underscores stripped, or an RGB container expressed as '(r, g, b)' or '[r, g, b]' (parsed via ast.literal_eval()).

Returns:

The (r, g, b) triple with each channel in [0, 255]. Bracketed '[r, g, b]' inputs are also returned as tuples (the parser normalizes lists to tuples on the way out).

Raises:
  • TypeError – If name is not a string.

  • KeyError – If name matches no known X11 color and no RGB expression in () / [].

  • ValueError – If a parsed RGB component exceeds 255.