diff options
| -rw-r--r-- | src/caelestia/utils/colourfulness.py | 42 | ||||
| -rw-r--r-- | src/caelestia/utils/wallpaper.py | 12 |
2 files changed, 49 insertions, 5 deletions
diff --git a/src/caelestia/utils/colourfulness.py b/src/caelestia/utils/colourfulness.py new file mode 100644 index 0000000..f76c1b6 --- /dev/null +++ b/src/caelestia/utils/colourfulness.py @@ -0,0 +1,42 @@ +import math + +from PIL import Image + + +def mean(values: list[float]) -> float: + return sum(values) / len(values) if values else 0 + + +def stddev(values: list[float], mean_val: float) -> float: + return math.sqrt(sum((x - mean_val) ** 2 for x in values) / len(values)) if values else 0 + + +def calc_colourfulness(image: Image) -> float: + width, height = image.size + pixels = list(image.getdata()) # List of (R, G, B) tuples + + rg_diffs = [] + yb_diffs = [] + + for r, g, b in pixels: + rg = abs(r - g) + yb = abs(0.5 * (r + g) - b) + rg_diffs.append(rg) + yb_diffs.append(yb) + + mean_rg = mean(rg_diffs) + mean_yb = mean(yb_diffs) + std_rg = stddev(rg_diffs, mean_rg) + std_yb = stddev(yb_diffs, mean_yb) + + return math.sqrt(std_rg**2 + std_yb**2) + 0.3 * math.sqrt(mean_rg**2 + mean_yb**2) + + +def get_variant(image: Image) -> str: + colourfulness = calc_colourfulness(image) + + if colourfulness < 10: + return "neutral" + if colourfulness < 20: + return "content" + return "tonalspot" diff --git a/src/caelestia/utils/wallpaper.py b/src/caelestia/utils/wallpaper.py index d3550ab..abaf192 100644 --- a/src/caelestia/utils/wallpaper.py +++ b/src/caelestia/utils/wallpaper.py @@ -80,14 +80,16 @@ def get_smart_opts(wall: Path, cache: Path) -> str: except (IOError, json.JSONDecodeError): pass + from caelestia.utils.colourfulness import get_variant + + opts = {} + with Image.open(get_thumb(wall, cache)) as img: + opts["variant"] = get_variant(img) + img.thumbnail((1, 1), Image.LANCZOS) hct = Hct.from_int(argb_from_rgb(*img.getpixel((0, 0)))) - - opts = { - "mode": "light" if hct.tone > 60 else "dark", - "variant": "neutral" if hct.chroma < 20 else "tonalspot", - } + opts["mode"] = "light" if hct.tone > 60 else "dark" opts_cache.parent.mkdir(parents=True, exist_ok=True) with opts_cache.open("w") as f: |