Source code for owl.color.colorconv

import cv2
import numpy as np
import xarray as xr
from skimage import img_as_float

from ..array._image_operations import _VectorizedImageOperationArray

GRAY_VECTOR_BT709 = np.array([0.2126, 0.7152, 0.0722], dtype='float32')
GRAY_VECTOR_BT601 = np.array([0.299, 0.587, 0.114], dtype='float32')


[docs] def rgb2gray( data, *, preserve_range=False, output_array=None, casting='same_kind', gray_vector=None, ): """Convert ND images from rgb color space to grayscale color space. Conversion from RGB color space to grayscale color space is done with the following coefficients: [0.299, 0.587, 0.114] Parameters ---------- data: ndarray [..., 3] Multi-dimensional image where the last dimension has a dimension of 3. The color coordinates correspond to the red, green and blue channels respectively. preserve_range: If True, integer images will not be scaled between 0 and 1 prior to color space conversion. If False, integer images will be converted to floating point numbers between 0 and 1 following to scikit-image conventions. .. versionchanged :: 0.14.0 In Version 0.14.0 the default value of preserved_range was changed to ``False``. output_array: ArrayLike[N, M] or None The output array of the operation. If provided, this should be a C contiguous array. casting: {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional gray_vector: array_like The coefficients to convert RGB to grayscale. By default, it is [0.299, 0.587, 0.114] but can be changed to another set of coefficients. """ # Fastpath for the common case of converting to BT601 grayscale # converting to floating point and using numpy in general is much slower # than optimized opencv code if ( preserve_range and data.dtype in (np.uint8, np.uint16, np.uint32) and ((output_array is None) or (output_array.dtype == data.dtype)) and # BT601 is the default from opencv ((gray_vector is None) or np.allclose(gray_vector, GRAY_VECTOR_BT601)) ): return cv2.cvtColor(data, cv2.COLOR_RGB2GRAY, dst=output_array) if not preserve_range: data = img_as_float(data) # Mostly the same as the scikit-image function but supports ND arrays. if gray_vector is None: dtype = data.dtype if isinstance(data.dtype, np.floating) else 'float32' gray_vector = GRAY_VECTOR_BT601.astype(dtype) return np.matmul( data, gray_vector, out=output_array, casting=casting, )
[docs] def rgba2gray(data, *, preserve_range=False): """Convert ND images from RGBA color space to grayscale color space. Conversion from RGBA color space to grayscale color space is done with the following coefficients: [0.299, 0.587, 0.114, 0] Parameters ---------- data: ndarray [..., 4] Multi-dimensional image where the last dimension has a dimension of 4. The color coordinates correspond to the red, green, blue and alpha channels respectively. preserve_range: If True, integer images will not be scaled between 0 and 1 prior to color space conversion. If False, integer images will be converted to floating point numbers between 0 and 1 following to scikit-image conventions. .. versionchanged :: 0.14.0 In Version 0.14.0 the default value of preserved_range was changed to ``False``. """ if not preserve_range: data = img_as_float(data) dtype = data.dtype if isinstance(data.dtype, np.floating) else 'float32' coefficients = np.array([ GRAY_VECTOR_BT601[0], GRAY_VECTOR_BT601[1], GRAY_VECTOR_BT601[2], 0., ], dtype=dtype) return data @ coefficients
def _apply_conversion_matrix(image, conversion_matrix, output_array): # User may pass a lazy array, cast it for opencv to use it image = np.ascontiguousarray(image) # image must have 3 dimensions to work correctly with cv2.transform if image.ndim == 2: image = image[..., np.newaxis] conversion_matrix = conversion_matrix[:image.shape[-1], :] cv2.transform( image, conversion_matrix, dst=output_array, )
[docs] def get_converted_data(dataset, conversion_matrix): if not np.issubdtype(np.float64, dataset.images): # Cast down to float32 unless the original images are of 64 bit float conversion_matrix = conversion_matrix.astype('float32') # We want to broadcast the image corrections on the # 'image_y', 'image_x' and stack dims to_broadcast_dims = tuple( dim for dim in dataset.images.dims if dim not in ('y', 'x', 'rgb', 'rgba') ) to_broadcast_shape = tuple( dataset.sizes[dim] if dim in ('image_y', 'image_x') else 1 for dim in to_broadcast_dims ) funcs = np.empty( (dataset.sizes['image_y'], dataset.sizes['image_x']), dtype=object ) import functools for i in np.ndindex(funcs.shape): funcs[i] = functools.partial( _apply_conversion_matrix, conversion_matrix=conversion_matrix[i] ) funcs = funcs.reshape(to_broadcast_shape) array = _VectorizedImageOperationArray( dataset.images.variable, dtype=dataset.images.dtype, broadcast_dims=len(to_broadcast_shape), func=funcs ) return array
[docs] def get_converted_dataset(dataset, conversion_matrix): """Apply photometric response to the dataset images variable Parameters ---------- dataset : xarray Dataset MCAM data containing image data as well as additional metadata. Image data should be RGB. conversion_matrix : numpy Array NxMx3x3 Matrix to be applied to the image data. NxM should match the shape image_y and image_x dimension of the dataset. Returns ------- converted_dataset : xarray Dataset """ from xarray.core.indexing import LazilyIndexedArray converted_data = LazilyIndexedArray( get_converted_data(dataset, conversion_matrix=conversion_matrix) ) dims = dataset.images.dims images = xr.Variable(dims, converted_data) converted_dataset = dataset.copy() converted_dataset['images'] = images return converted_dataset