import numpy as np def apply_s_curve_tone_mapping( x: np.ndarray, shadow_boost: float = 0., highlight_boost: float = 0., contrast: float = 0., exposure: float = 0. ) -> np.ndarray: """ Apply an S-curve tone mapping to input x with given parameters. Parameters: x : array-like Input values in the range [0, 1]. shadow_boost : float Shadow boost parameter. Positive values increase the steepness in shadows. highlight_boost : float Highlight boost parameter. Positive values increase the steepness in highlights. contrast : float Contrast parameter. Positive values increase the steepness at midtones. exposure : float Exposure adjustment parameter. When exposure=0, 0.5 maps to 0.5. Exposure defines the mapping of 0.5 to 0.5 * (2 ** exposure). Returns: y : array-like Tone-mapped output values in the range [0, 1]. """ # Compute the midtone mapped value based on exposure midtone_mapped_value = 0.5 * (2 ** exposure) midtone_mapped_value = np.clip(midtone_mapped_value, 0, 1) # Define the slopes (derivatives) at the three key points s0 = 1 + shadow_boost # Slope at x = 0 (shadows) s1 = 1 + contrast # Slope at x = 0.5 (midtone) s2 = 1 + highlight_boost # Slope at x = 1 (highlights) # Initialize the output array y = np.zeros_like(x) # Segment 1: x in [0, 0.5] idx1 = x <= 0.5 x0, x1 = 0.0, 0.5 y0, y1 = 0.0, midtone_mapped_value m0, m1 = s0, s1 delta_x = x1 - x0 t = (x[idx1] - x0) / delta_x # Normalize x to parameter t in [0, 1] # Hermite basis functions h00 = 2 * t ** 3 - 3 * t ** 2 + 1 h10 = t ** 3 - 2 * t ** 2 + t h01 = -2 * t ** 3 + 3 * t ** 2 h11 = t ** 3 - t ** 2 # Calculate the spline for the first segment y[idx1] = (h00 * y0 + h10 * m0 * delta_x + h01 * y1 + h11 * m1 * delta_x) # Segment 2: x in (0.5, 1] idx2 = x > 0.5 x0, x1 = 0.5, 1.0 y0, y1 = midtone_mapped_value, 1.0 m0, m1 = s1, s2 delta_x = x1 - x0 t = (x[idx2] - x0) / delta_x # Normalize x to parameter t in [0, 1] # Hermite basis functions for the second segment h00 = 2 * t ** 3 - 3 * t ** 2 + 1 h10 = t ** 3 - 2 * t ** 2 + t h01 = -2 * t ** 3 + 3 * t ** 2 h11 = t ** 3 - t ** 2 # Calculate the spline for the second segment y[idx2] = (h00 * y0 + h10 * m0 * delta_x + h01 * y1 + h11 * m1 * delta_x) # Ensure the output is within [0, 1] y = np.clip(y, 0, 1) return y