File size: 2,525 Bytes
2501dd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
"""Visual enhancement modes for the displayed cutout (apoyo al revisor).

Same transforms and parameters as the static review site (docs/app.js), ported
to numpy/skimage. These are cosmetic, applied to the 8-bit RGB only — they do
NOT touch the image the model sees (that stays the raw Lupton composite).
"""

from __future__ import annotations

import numpy as np
from skimage.exposure import equalize_adapthist
from skimage.filters import unsharp_mask

MODES = ["Original", "Percentil", "Unsharp", "Asinh", "CLAHE", "Estructuras"]


def percentile_stretch(rgb: np.ndarray, p_lo: float = 1.0, p_hi: float = 99.5) -> np.ndarray:
    out = rgb.astype(np.float32)
    for c in range(3):
        lo, hi = np.percentile(out[..., c], [p_lo, p_hi])
        rng = (hi - lo) or 1.0
        out[..., c] = np.clip((out[..., c] - lo) / rng * 255.0, 0, 255)
    return out.astype(np.uint8)


def unsharp(rgb: np.ndarray, radius: float = 6.0, amount: float = 1.5) -> np.ndarray:
    f = rgb.astype(np.float32) / 255.0
    out = unsharp_mask(f, radius=radius, amount=amount, channel_axis=-1)
    return (np.clip(out, 0, 1) * 255).astype(np.uint8)


def asinh_stretch(rgb: np.ndarray, a: float = 1.5) -> np.ndarray:
    f = rgb.astype(np.float32) / 255.0
    out = np.arcsinh(a * f) / np.arcsinh(a)
    return (np.clip(out, 0, 1) * 255).astype(np.uint8)


def clahe(rgb: np.ndarray, clip_limit: float = 0.01) -> np.ndarray:
    f = rgb.astype(np.float32) / 255.0
    out = np.zeros_like(f)
    for c in range(3):
        out[..., c] = equalize_adapthist(f[..., c], clip_limit=clip_limit)
    return (np.clip(out, 0, 1) * 255).astype(np.uint8)


def estructuras(rgb: np.ndarray, amount: float = 1.5) -> np.ndarray:
    x = percentile_stretch(rgb, 1.0, 99.5)
    x = unsharp(x, 6.0, amount)
    x = asinh_stretch(x, amount * 0.5)
    return x


def to_gray(rgb: np.ndarray) -> np.ndarray:
    lum = (0.2126 * rgb[..., 0] + 0.7152 * rgb[..., 1] + 0.0722 * rgb[..., 2]).astype(np.uint8)
    return np.dstack([lum, lum, lum])


def apply(rgb: np.ndarray, mode: str = "Original", gray: bool = False) -> np.ndarray:
    """Apply the chosen enhancement mode (and optional grayscale) to an RGB."""
    if mode == "Percentil":
        rgb = percentile_stretch(rgb)
    elif mode == "Unsharp":
        rgb = unsharp(rgb)
    elif mode == "Asinh":
        rgb = asinh_stretch(rgb)
    elif mode == "CLAHE":
        rgb = clahe(rgb)
    elif mode == "Estructuras":
        rgb = estructuras(rgb)
    if gray:
        rgb = to_gray(rgb)
    return rgb