Source code for pydl.pydlutils.rgbcolor

# Licensed under a 3-clause BSD style license - see LICENSE.rst
# -*- coding: utf-8 -*-
"""This module corresponds to the rgbcolor directory in idlutils.

This code reproduces the algorithms of Lupton *et al.* (2004) [2]_.
The purpose is to produce nice color (RGB, JPEG, PNG, etc.) from FITS
images in astronomical filter bands.

References
----------

.. [2] `Lupton, Robert, et al., 2004 PASP 116, 113
   <https://ui.adsabs.harvard.edu/abs/2004PASP..116..133L/abstract>`_.
"""
import numpy as np
from warnings import warn
from . import PydlutilsUserWarning


[docs] def nw_arcsinh(colors, nonlinearity=3.0): """Scales `colors` by a degree of nonlinearity specified by user. The input image must have zero background (*i.e.*, it must already be background-subtracted). Parameters ---------- colors : :class:`~numpy.ndarray` 3D Array containing RGB image. The dimensions should be (X, Y, 3). nonlinearity : :class:`float` Amount of nonlinear scaling. If set to zero, no scaling will be performed (this is equivalent to linear scaling). Returns ------- :class:`~numpy.ndarray` The scaled image. Raises ------ :exc:`ValueError` If `colors` has the wrong shape. """ if nonlinearity == 0: return colors if colors.ndim != 3: raise ValueError("A 3D image is required!") nx, ny, nc = colors.shape if nc != 3: raise ValueError("The 3D image must have 3 image planes.") radius = colors.sum(2) w = radius == 0 radius[w] = radius[w] + 1.0 fac = np.arcsinh(radius*nonlinearity)/nonlinearity/radius fitted_colors = np.zeros(colors.shape, dtype=colors.dtype) for k in range(3): fitted_colors[:, :, k] = colors[:, :, k]*fac return fitted_colors
[docs] def nw_cut_to_box(colors, origin=(0.0, 0.0, 0.0)): """Limits the pixel values of the image to a 'box', so that the colors do not saturate to white but to a specific color. Parameters ---------- colors : :class:`~numpy.ndarray` 3D Array containing RGB image. The dimensions should be (X, Y, 3). origin : :class:`tuple` or :class:`~numpy.ndarray` An array with 3 elements. The "distance" from this origin is considered saturated. Returns ------- :class:`~numpy.ndarray` The "boxed" image. Raises ------ :exc:`ValueError` If `colors` or `origin` has the wrong shape. """ if len(origin) != 3: raise ValueError("The origin array must contain 3 elements!") if colors.ndim != 3: raise ValueError("A 3D image is required!") nx, ny, nc = colors.shape if nc != 3: raise ValueError("The 3D image must have 3 image planes.") pos_dist = 1.0 - np.array(origin, dtype=colors.dtype) factors = np.zeros(colors.shape, dtype=colors.dtype) for k in range(3): factors[:, :, k] = colors[:, :, k]/pos_dist[k] factor = factors.max(2) factor = np.where(factor > 1.0, factor, 1.0) boxed_colors = np.zeros(colors.shape, dtype=colors.dtype) for k in range(3): boxed_colors[:, :, k] = origin[k] + colors[:, :, k]/factor return boxed_colors
[docs] def nw_float_to_byte(image, bits=8): """Converts an array of floats in [0.0, 1.0] to bytes in [0, 255]. Parameters ---------- image : :class:`~numpy.ndarray` Image to convert. bits : :class:`int`, optional Number of bits in final image. Returns ------- :class:`~numpy.ndarray` Converted image. """ if bits > 8: warn("bits > 8 not supported; setting bits = 8.", PydlutilsUserWarning) bits = 8 fmax = 1 << bits bmax = fmax - 1 f1 = np.floor(image * fmax) f2 = np.where(f1 > 0, f1, 0) f3 = np.where(f2 < bmax, f2, bmax) return f3.astype(np.uint8)
[docs] def nw_scale_rgb(colors, scales=(1.0, 1.0, 1.0)): """Multiply RGB image by color-dependent scale factor. Parameters ---------- colors : :class:`~numpy.ndarray` 3D Array containing RGB image. The dimensions should be (X, Y, 3). scales : :class:`tuple` or :class:`~numpy.ndarray`, optional An array with 3 elements. Returns ------- :class:`~numpy.ndarray` The scaled image. Raises ------ :exc:`ValueError` If `colors` or `scales` has the wrong shape. """ if len(scales) != 3: raise ValueError("The scale factor must contain 3 elements!") if colors.ndim != 3: raise ValueError("A 3D image is required!") nx, ny, nc = colors.shape if nc != 3: raise ValueError("The 3D image must have 3 image planes.") scaled_colors = np.zeros(colors.shape, dtype=colors.dtype) for k in range(3): scaled_colors[:, :, k] = colors[:, :, k]*scales[k] return scaled_colors