66 rArray: FloatImagePlane,
67 gArray: FloatImagePlane,
68 bArray: FloatImagePlane,
69 local_contrast: LocalContrastFunction |
None | _SentinalDefault = DEFAULT_FUNCTION,
70 scale_lum: ScaleLumFunction |
None | _SentinalDefault = DEFAULT_FUNCTION,
71 scale_color: ScaleColorFunction |
None | _SentinalDefault = DEFAULT_FUNCTION,
72 remap_bounds: RemapBoundsFunction |
None | _SentinalDefault = DEFAULT_FUNCTION,
73 bracketing_function: BracketingFunction |
None | _SentinalDefault = DEFAULT_FUNCTION,
74 gamut_remapping_function: GamutRemappingFunction |
None | _SentinalDefault = DEFAULT_FUNCTION,
75 psf: FloatImagePlane |
None =
None,
76 cieWhitePoint: tuple[float, float] = (0.28, 0.28),
78 """Enhance the lightness and color preserving hue using perceptual methods.
82 rArray : `numpy.ndarray`
83 The array used as the red channel
84 gArray : `numpy.ndarray`
85 The array used as the green channel
86 bArray : `numpy.ndarray`
87 The array used as the blue channel
88 local_contrast : `LocalContrastFunction` or `None`
89 This is a callable that's passed the luminance values, and is expected
90 to do local contrast enhancement. Set to None to bypass.
91 scale_lum : `ScaleLumFunction` or `None`
92 This is a callable that's passed the luminance values and should
93 return a scaled luminance array the same shape as the input.
94 Set to None for no scaling.
95 scale_color : `ScaleColorFunction` or `None`
96 This is a callable that's passed the original luminance, the remapped
97 luminance values, the a values for each pixel, and the b values for
98 each pixel. This function is responsible for scaling chroma
99 values. This should return two arrays corresponding to the scaled a and
100 b values. Set to None for no modification.
101 remap_bounds : `RemapBoundsFunction` or `None`
102 This is a callable that remaps the input arrays such that each of
103 them fall within a zero to one range. This callable is given the
104 initial image. Set to None for no remapping. If this is None
105 input arrays should already be in 0-1 range.
106 bracketing_function : `BracketingFunction` or `None`
107 This is a callable that is passed the input luminance, and should
108 create various exposure levels and then fuse them together.
109 Set to None for no bracketing.
110 gamut_remapping_function : `GamutRemappingFunction` or `None`
111 This is a callable that is passed the final OkLab image. Its job
112 is to detect and correct any pixel values that would fall outside
113 an RGB P3 colorspace. Set to None for no fixes.
114 psf : `FloatImagePlane` or `None`
115 If this parameter is an image of a PSF kernel the luminance channel is
116 deconvolved with it. Set to None to skip deconvolution.
117 cieWhitePoint : `tuple` of `float`, `float`
118 This is the white point of the input of the input arrays in CIE XY
119 coordinates. Altering this affects the relative balance of colors
120 in the input image, and therefore also the output image.
125 The brightness and color calibrated image.
130 Raised if the shapes of the input array don't match
134 if local_contrast
is DEFAULT_FUNCTION:
136 if scale_lum
is DEFAULT_FUNCTION:
138 if scale_color
is DEFAULT_FUNCTION:
140 if remap_bounds
is DEFAULT_FUNCTION:
142 if bracketing_function
is DEFAULT_FUNCTION:
144 if gamut_remapping_function
is DEFAULT_FUNCTION:
148 if rArray.shape != gArray.shape
or rArray.shape != bArray.shape:
149 raise ValueError(
"The shapes of all the input arrays must be the same")
152 img: RGBImage = np.empty((*rArray.shape, 3))
153 img[:, :, 0] = rArray
154 img[:, :, 1] = gArray
155 img[:, :, 2] = bArray
158 img[np.isnan(img)] = 0
160 logger.debug(
"Starting color processing pipeline")
163 if remap_bounds
is not None:
164 img = remap_bounds(img)
165 logger.debug(
"doing remap took %.3fs", time.time() - t1)
170 Lab = rgb.RGB_to_Oklab(img, cieWhitePoint)
171 logger.debug(
"lab conversion took %.3fs", time.time() - t1)
176 lum_save = np.copy(lum)
178 if bracketing_function
is not None:
179 lum = bracketing_function(lum)
180 logger.debug(
"bracketing took %.3fs", time.time() - t1)
183 if scale_lum
is not None:
185 logger.debug(
"lum scale took %.3fs", time.time() - t1)
188 if local_contrast
is not None:
189 lum = local_contrast(lum)
190 logger.debug(
"local_contrast took %.3fs", time.time() - t1)
194 lum = skimage.restoration.richardson_lucy(lum, psf=psf, clip=
False, num_iter=2)
195 logger.debug(
"psf took %.3fs", time.time() - t1)
198 if scale_color
is not None:
199 new_a, new_b = scale_color(lum_save, lum, Lab[..., 1], Lab[..., 2])
202 logger.debug(
"color correction took %.3fs", time.time() - t1)
207 cie_white_point_d65 = (0.31272, 0.32903)
208 if gamut_remapping_function
is not None:
209 result = gamut_remapping_function(Lab, cie_white_point_d65)
210 logger.debug(
"gamut fixing took %.3fs", time.time() - t1)
213 result = rgb.Oklab_to_RGB(np.ascontiguousarray(Lab), cie_white_point_d65)
214 logger.debug(
"RGB conversion took %.3fs", time.time() - t1)
217 result = np.clip(result, 0, 1)
RGBImage lsstRGB(FloatImagePlane rArray, FloatImagePlane gArray, FloatImagePlane bArray, LocalContrastFunction|None|_SentinalDefault local_contrast=DEFAULT_FUNCTION, ScaleLumFunction|None|_SentinalDefault scale_lum=DEFAULT_FUNCTION, ScaleColorFunction|None|_SentinalDefault scale_color=DEFAULT_FUNCTION, RemapBoundsFunction|None|_SentinalDefault remap_bounds=DEFAULT_FUNCTION, BracketingFunction|None|_SentinalDefault bracketing_function=DEFAULT_FUNCTION, GamutRemappingFunction|None|_SentinalDefault gamut_remapping_function=DEFAULT_FUNCTION, FloatImagePlane|None psf=None, tuple[float, float] cieWhitePoint=(0.28, 0.28))