35 """Computes average and variance of guide using Gaussian filtering.
39 guide : `FloatImagePlane`
40 2D array representing the guide image.
42 Standard deviation for Gaussian kernel.
46 result : `numpy.ndarray`
47 Array where each pixel has [average, variance].
50 mu_guide = gaussian_filter(guide, sigma=sigma)
53 guide_squared = guide**2
54 mu_guide_squared = gaussian_filter(guide_squared, sigma=sigma)
57 var_guide = mu_guide_squared - mu_guide**2
60 output = np.stack((mu_guide, var_guide), axis=2)
66 """Applies blending without a mask using averages and variances.
70 image : `FloatImagePlane`
71 2D input image array. Modified in-place.
73 Array with shape (height, width, 2) containing averages and variances.
75 Feathering parameter for blending.
77 Blending type: 0 for linear, 1 for geometric mean.
80 av_reshaped = av.reshape(image.shape[0], image.shape[1], -1)
82 avg_g = av_reshaped[..., 0]
83 var_g = av_reshaped[..., 1]
85 norm_g = np.maximum(avg_g * image, 1e-6)
86 normalized_var_guide = var_g / norm_g
88 a = normalized_var_guide / (normalized_var_guide + feathering)
93 image[:] = np.maximum(image * a + b, np.finfo(float).min)
95 image[:] *= np.maximum(image * a + b, np.finfo(float).min)
96 image[:] = np.sqrt(image[:])
100 image: FloatImagePlane, sigma: float, feathering: float, iterations: int = 1, filter_type: int = 1
102 """Applies exposure-independent guided blur with down-scaling and up-sampling.
106 image : `FloatImagePlane`
107 Input image array of shape (height, width). Modified in-place.
109 Standard deviation for Gaussian kernel.
111 Feathering parameter.
112 iterations : `int`, optional
113 Number of iterations to model diffusion. Default is 1.
114 filter_type : `int`, optional
115 Blending type: 0 for linear, 1 for geometric mean. Default is 1.
117 scaling = np.maximum(np.minimum(sigma, 4.0), 1.0)
118 ds_sigma = np.maximum(sigma / scaling, 1.0)
122 for _
in range(iterations):
128 image: FloatImagePlane,
129 tone_factors: list[float],
134 filter_type: int = 1,
136 """Enhance image brightness using exposure-dependent correction.
138 This function adjusts image brightness by applying exposure-dependent
139 corrections based on tone factors. It uses exposure centers spanning from
140 0 to 1 (10 levels) and applies Gaussian-weighted adjustments using edge
141 informed guided filters. A copy of the input image is made before processing.
145 image : `FloatImagePlane`
146 Input image array of shape (height, width).
147 tone_factors : `list` of `float`
148 List of 10 tone correction factors, one for each exposure level.
150 Width of the Gaussian kernel for exposure weighting.
152 Standard deviation for Gaussian blur of luminance.
154 Feathering parameter for exposure-independent guided blur.
155 iterations : `int`, optional
156 Number of iterations for the blur process. Default is 1.
157 filter_type : `int`, optional
158 Blending type: 0 for linear, 1 for geometric mean. Default is 1.
162 result : `FloatImagePlane`
163 Image with brightness adjusted based on tone factors.
165 luminance = np.copy(image)
168 corrections = np.zeros_like(luminance)
169 EXPOSURE_CENTERS = np.linspace(0, 1, 10)
170 for eq_val, factor
in zip(EXPOSURE_CENTERS, tone_factors):
171 corrections += np.exp(-1 * (exposure - eq_val) ** 2 / (2 * weight**2)) * factor
172 return image + corrections
176 """Enhance image contrast using Laplacian pyramid adjustment.
178 This function performs contrast equalization by modifying the Laplacian
179 pyramid coefficients of the input image. Each level of the pyramid
180 corresponds to a different spatial scale, allowing for scale-dependent
181 contrast adjustments. A padded copy of the input image is created for
186 image : `FloatImagePlane`
187 Input image array of shape (height, width).
188 contrast_factors : `list` of `float`
189 List of factors to multiply each pyramid level. Values > 1 increase
190 contrast, values < 1 decrease contrast. The list should specify
191 factors for the largest scales first; unspecified levels use a factor
196 result : `FloatImagePlane`
197 Image with contrast adjusted at multiple spatial scales.
199 maxLevel = int(np.min(np.log2(image.shape)))
200 support = 1 << (maxLevel - 1)
201 padY_amounts =
levelPadder(image.shape[0] + support, maxLevel)
202 padX_amounts =
levelPadder(image.shape[1] + support, maxLevel)
203 imagePadded = cv2.copyMakeBorder(
204 image, *(0, support), *(0, support), cv2.BORDER_REPLICATE,
None,
None
205 ).astype(image.dtype)
206 lap =
makeLapPyramid(imagePadded, padY_amounts, padX_amounts,
None,
None)
207 for i, factor
in enumerate(contrast_factors):
211 lap[-1 * i] *= factor
213 for i
in range(-2, -1 * len(lap) - 1, -1):
214 upsampled = cv2.pyrUp(output)
215 upsampled = upsampled[
216 : upsampled.shape[0] - 2 * padY_amounts[i + 1], : upsampled.shape[1] - 2 * padX_amounts[i + 1]
218 output = lap[i] + upsampled
219 return output[: image.shape[0], : image.shape[1]]
FloatImagePlane tone_equalizer(FloatImagePlane image, list[float] tone_factors, float weight, float sigma, float feathering, int iterations=1, int filter_type=1)