Spaces:
Sleeping
Sleeping
| from interactive_pipe import interactive_pipeline, interactive | |
| from interactive_pipe import CircularControl | |
| from interactive_pipe.data_objects.curves import SingleCurve, Curve | |
| from interactive_pipe.data_objects.image import Image | |
| from global_tone_mapping import apply_s_curve_tone_mapping | |
| from color_conversions import rgb_to_hsv, hsv_to_rgb | |
| from synthetic_charts import generate_color_wheel | |
| import argparse | |
| import numpy as np | |
| def histogram(img: np.ndarray) -> Curve: | |
| hist_curves = [] | |
| for ch in range(img.shape[-1]): | |
| hist, bins = np.histogram( | |
| img[..., ch].flatten(), | |
| bins=128, | |
| range=(0, 1) | |
| ) | |
| hist_curves.append( | |
| SingleCurve(bins[:-1], hist, style='rgb'[ch], linestyle="-") | |
| ) | |
| hist_curve = Curve( | |
| hist_curves, | |
| xlabel="Intensity", | |
| ylabel="Frequency", | |
| # title="Histogram" | |
| ) | |
| return hist_curve | |
| def set_tone_mapping_params( | |
| # shadow_boost: float = (0., [-1., 1.]), | |
| shadow_boost: float = CircularControl(0., [-1., 1.]), | |
| highlight_boost: float = (0., [-1., 1.]), | |
| exposure: float = (0., [-1., 1.]), | |
| contrast: float = (0., [-1., 1.]), | |
| global_params: dict = {} | |
| ) -> None: | |
| global_params["shadow_boost"] = shadow_boost | |
| global_params["highlight_boost"] = highlight_boost | |
| global_params["contrast"] = contrast | |
| global_params["exposure"] = exposure | |
| def tone_map( | |
| hsv: np.ndarray, | |
| apply_tone_curve_on_luma: bool = True, | |
| global_params={} | |
| ) -> np.ndarray: | |
| shadow_boost = global_params.get("shadow_boost", 0.) | |
| highlight_boost = global_params.get("highlight_boost", 0.) | |
| contrast = global_params.get("contrast", 0.) | |
| exposure = global_params.get("exposure", 0.) | |
| if not apply_tone_curve_on_luma: | |
| return rgb_to_hsv( | |
| apply_s_curve_tone_mapping( | |
| hsv_to_rgb(hsv), | |
| shadow_boost, | |
| highlight_boost, | |
| contrast, | |
| exposure | |
| ) | |
| ) | |
| luma_tone_mapped = apply_s_curve_tone_mapping( | |
| hsv[..., -1], | |
| shadow_boost, | |
| highlight_boost, | |
| contrast, | |
| exposure | |
| ) | |
| hsv[..., -1] = luma_tone_mapped | |
| return hsv | |
| def modify_vibrance( | |
| hsv_in: np.ndarray, | |
| vibrance: float = 0., | |
| vibrance_mask_smoothness: float = 3. | |
| ) -> np.ndarray: | |
| gain_luma = np.interp( | |
| hsv_in[..., 2], | |
| [0., 0.5, 1.], | |
| [0., 0., 1.] | |
| ) | |
| gain_sat = np.interp(hsv_in[..., 1], | |
| [0., 0.25, 1.], | |
| [0., 0., 1.] | |
| )**(1+vibrance_mask_smoothness) | |
| red_thresh = 55. | |
| pink_thresh = 10. | |
| overlap = 10. | |
| gain_hue = np.interp( | |
| hsv_in[..., 0], | |
| [0., red_thresh, red_thresh+overlap, | |
| 360-pink_thresh-overlap, 360-pink_thresh, 360], | |
| [0., 0., 1., 1., 0., 0.] | |
| )**(1+vibrance_mask_smoothness) | |
| gain = 1. + gain_luma * (gain_sat + gain_hue).clip(0., 1.)*(vibrance / 100) | |
| hsv_in[..., 1] = np.clip(gain*hsv_in[..., 1], 0, 1) | |
| return hsv_in | |
| def s_curve_visualization(global_params={}) -> Curve: | |
| shadow_boost = global_params.get("shadow_boost", 0.) | |
| highlight_boost = global_params.get("highlight_boost", 0.) | |
| contrast = global_params.get("contrast", 0.) | |
| exposure = global_params.get("exposure", 0.) | |
| x = np.linspace(0, 1, 256) | |
| y = apply_s_curve_tone_mapping(x, shadow_boost, | |
| highlight_boost, | |
| contrast, | |
| exposure) | |
| return Curve( | |
| [ | |
| SingleCurve(x, y, style='m', linestyle='-'), | |
| SingleCurve(x, x, style='k', linestyle='--'), | |
| ], | |
| xlabel="Input", ylabel="Output", | |
| title=f"Tone Curve {shadow_boost:.2f}, {highlight_boost:.2f}, {contrast:.2f}, {exposure:.2f}" | |
| ) | |
| def pick_image( | |
| default_image: np.ndarray, | |
| second_image: np.ndarray, | |
| input_image: str = "sample image", | |
| global_params={} | |
| ) -> np.ndarray: | |
| if input_image == "sample image": | |
| return default_image | |
| elif input_image == "alternative image": | |
| return second_image | |
| elif input_image == "color wheel": | |
| color_wheel = global_params.get("color wheel", None) | |
| if color_wheel is None: | |
| color_wheel = generate_color_wheel(resolution=496) | |
| global_params["color wheel"] = color_wheel | |
| return color_wheel | |
| else: | |
| return default_image | |
| def image_editing_pipeline(sample_image, second_image): | |
| input_image = pick_image(sample_image, second_image) | |
| set_tone_mapping_params() | |
| hsv_in = rgb_to_hsv(input_image) | |
| hsv_tc = tone_map(hsv_in) | |
| hsv_out = modify_vibrance(hsv_tc) | |
| tc_image = hsv_to_rgb(hsv_out) | |
| tone_curve = s_curve_visualization() | |
| histogram_curve = histogram(tc_image) | |
| return tc_image, histogram_curve, tone_curve | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("-b", "--backend", type=str, | |
| choices=["gradio", "qt", "mpl"], default="gradio") | |
| args = parser.parse_args() | |
| img = Image.load_image("image_sample_2.jpg") | |
| img2 = Image.load_image("image_sample.jpg") | |
| markdown_description = r"# Tone mapping: S-curve" + "\n" \ | |
| r"$$\text{shadow boost} \in [-1, 1]$$ $$\text{highlight boost} \in [-1, 1]$$ $$\text{contrast} \in [-1, 1]$$ $$\text{exposure} \in [-1, 1]$$" + "\n" \ | |
| "If Tone curves are applied to RGB colors separately, you'll get serious color shifts. It is much better when tone curve is applied to the luminance channel." + "\n" | |
| interactive_pipeline( | |
| gui=args.backend, | |
| markdown_description=markdown_description, | |
| sliders_layout=["compact", "group", | |
| "vertical", "collapsible", "smart"][2], | |
| sliders_per_row_layout=1 | |
| )(image_editing_pipeline)(img, img2) | |