File size: 1,326 Bytes
b527c11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Illumination correction for nail/skin crops.



Shades-of-Gray (Finlayson & Trezzi 2004) — Minkowski p-norm generalisation

of the gray-world assumption. Standard in photometric calibration pipelines

and what the Tilburg/Sanquin BNAIC paper used (p=6).

"""
from __future__ import annotations

import numpy as np


def shades_of_gray(img: np.ndarray, p: int = 6) -> np.ndarray:
    """Apply Shades-of-Gray colour constancy with Minkowski exponent p.



    Args:

        img: HxWx3 uint8 OR float in [0,1].

        p: Minkowski exponent. p=1 is gray-world, p=inf is max-RGB.

            BNAIC used p=6 — a robust middle ground.



    Returns:

        Corrected HxWx3 uint8.

    """
    was_uint8 = img.dtype == np.uint8
    x = img.astype(np.float64)
    if was_uint8:
        x = x / 255.0

    # per-channel Lp norm of pixel intensities
    Lp = np.power(np.mean(np.power(x, p), axis=(0, 1)), 1.0 / p)  # shape (3,)

    # normalise so the corrected image preserves overall brightness
    # scale by sqrt(3) / ||Lp||_2 so the illuminant is treated as a unit vector toward gray
    norm = np.sqrt((Lp ** 2).sum()) + 1e-12
    illuminant = Lp / norm * np.sqrt(3)

    corrected = x / illuminant
    corrected = np.clip(corrected, 0.0, 1.0)
    return (corrected * 255.0).astype(np.uint8)