Spaces:
Sleeping
Sleeping
add basic image editing pipeline with tone mapping
Browse files- app.py +71 -0
- global_tone_mapping.py +44 -0
- image_sample.png +0 -0
- requirements.txt +1 -0
app.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from interactive_pipe import interactive_pipeline, interactive
|
| 2 |
+
from interactive_pipe.data_objects.curves import SingleCurve, Curve
|
| 3 |
+
from interactive_pipe.data_objects.image import Image
|
| 4 |
+
from global_tone_mapping import apply_s_curve_tone_mapping
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def histogram(img: np.ndarray) -> Curve:
|
| 9 |
+
hist_curves = []
|
| 10 |
+
for ch in range(img.shape[-1]):
|
| 11 |
+
hist, bins = np.histogram(
|
| 12 |
+
img[..., ch].flatten(), bins=128, range=(0, 1))
|
| 13 |
+
hist_curves.append(SingleCurve(bins[:-1], hist, style='rgb'[ch]+"-"))
|
| 14 |
+
hist_curve = Curve(hist_curves,
|
| 15 |
+
xlabel="Intensity", ylabel="Frequency")
|
| 16 |
+
return hist_curve
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@interactive()
|
| 20 |
+
def set_tone_mapping_params(
|
| 21 |
+
shadow_boost: float = (0., [-1., 1.]),
|
| 22 |
+
highlight_boost: float = (0., [-1., 1.]),
|
| 23 |
+
exposure: float = (0., [-1., 1.]),
|
| 24 |
+
global_params: dict = {}
|
| 25 |
+
) -> None:
|
| 26 |
+
global_params["shadow_boost"] = shadow_boost
|
| 27 |
+
global_params["highlight_boost"] = highlight_boost
|
| 28 |
+
global_params["exposure"] = exposure
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def s_curve_tone_mapping(img: np.ndarray, global_params={}) -> np.ndarray:
|
| 32 |
+
exposure = global_params.get("exposure", 0.)
|
| 33 |
+
shadow_boost = global_params.get("shadow_boost", 0.)
|
| 34 |
+
highlight_boost = global_params.get("highlight_boost", 0.)
|
| 35 |
+
return apply_s_curve_tone_mapping(
|
| 36 |
+
img,
|
| 37 |
+
shadow_boost,
|
| 38 |
+
highlight_boost,
|
| 39 |
+
exposure
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def s_curve_visualization(global_params={}) -> Curve:
|
| 44 |
+
exposure = global_params.get("exposure", 0.)
|
| 45 |
+
shadow_boost = global_params.get("shadow_boost", 0.)
|
| 46 |
+
highlight_boost = global_params.get("highlight_boost", 0.)
|
| 47 |
+
x = np.linspace(0, 1, 256)
|
| 48 |
+
y = apply_s_curve_tone_mapping(x, shadow_boost,
|
| 49 |
+
highlight_boost,
|
| 50 |
+
exposure)
|
| 51 |
+
return Curve(
|
| 52 |
+
[
|
| 53 |
+
SingleCurve(x, y, style='m-'),
|
| 54 |
+
SingleCurve(x, x, style='k--'),
|
| 55 |
+
],
|
| 56 |
+
xlabel="Input", ylabel="Output"
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
@ interactive_pipeline(gui="gradio")
|
| 61 |
+
def image_editing_pipeline(input_image):
|
| 62 |
+
set_tone_mapping_params()
|
| 63 |
+
tc_image = s_curve_tone_mapping(input_image)
|
| 64 |
+
tone_curve = s_curve_visualization()
|
| 65 |
+
histogram_curve = histogram(tc_image)
|
| 66 |
+
return tc_image, histogram_curve, tone_curve
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
if __name__ == "__main__":
|
| 70 |
+
img = Image.load_image("image_sample.png")
|
| 71 |
+
image_editing_pipeline(img)
|
global_tone_mapping.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def apply_s_curve_tone_mapping(
|
| 5 |
+
x: np.ndarray,
|
| 6 |
+
shadow_boost: float = 0.,
|
| 7 |
+
highlight_boost: float = 0.,
|
| 8 |
+
exposure: float = 0.,
|
| 9 |
+
) -> np.ndarray:
|
| 10 |
+
"""
|
| 11 |
+
Apply an S-curve tone mapping to input x with given parameters.
|
| 12 |
+
|
| 13 |
+
Parameters:
|
| 14 |
+
x : array-like
|
| 15 |
+
Input values in the range [0, 1].
|
| 16 |
+
shadow_boost : float
|
| 17 |
+
Shadow boost parameter (tangent).
|
| 18 |
+
Positive values decreases the steepness in shadows.
|
| 19 |
+
highlight_boost : float
|
| 20 |
+
Highlight boost parameter (tangent).
|
| 21 |
+
Positive values increase the steepness in highlights.
|
| 22 |
+
exposure : float
|
| 23 |
+
Exposure adjustment parameter. When exposure=0, 0.5 maps to 0.5.
|
| 24 |
+
|
| 25 |
+
Returns:
|
| 26 |
+
y : array-like
|
| 27 |
+
Tone-mapped output values in the range [0, 1].
|
| 28 |
+
"""
|
| 29 |
+
x_expo = np.clip(x * 2 ** exposure, 0, 1)
|
| 30 |
+
|
| 31 |
+
# Adjust s and h parameters
|
| 32 |
+
s = 1 - shadow_boost
|
| 33 |
+
h = 1 + highlight_boost
|
| 34 |
+
|
| 35 |
+
# Compute numerator and denominator for the S-curve formula
|
| 36 |
+
numerator = x_expo ** s
|
| 37 |
+
denominator = numerator + (1 - x_expo) ** h
|
| 38 |
+
|
| 39 |
+
# Avoid division by zero
|
| 40 |
+
with np.errstate(divide='ignore', invalid='ignore'):
|
| 41 |
+
y = numerator / denominator
|
| 42 |
+
y = np.nan_to_num(y)
|
| 43 |
+
|
| 44 |
+
return y
|
image_sample.png
ADDED
|
requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
interactive-pipe
|