Files
SurrealByDesign 67dcd49be0 Remove undocumented "project bible" references from docs/comments
Replaces appeals to an unnamed external document with self-contained
explanations: the README's key-ordering note now points to this repo's
own Nodes section instead of an outside source, the bbox docstring now
fully explains both accepted formats and why both are supported, and
the test_extractor module docstring describes the four image
categories directly instead of citing an external checklist section.
Documentation/comments only -- no behavior changes.
2026-06-20 23:50:48 -04:00

106 lines
3.3 KiB
Python

"""Tests for IdeogramPaletteExtractor's core extraction logic against four
image categories chosen to stress different extraction behaviors:
- photograph: smooth gradients, many similar colors near the Delta-E threshold.
- logo/graphic: a handful of flat, well-separated colors (the easy case).
- painting: a multi-color blend, exercising k-means cluster separation.
- near-monochrome (fog/snow): almost no color variation, exercising the
single/near-single-color fallback path.
These tests exercise `_extract_palette` directly (PIL in, hex list out) since
the full node's `extract()` requires torch, which is only present inside a
ComfyUI environment.
"""
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from PIL import Image
from nodes.palette_extractor import _extract_palette
from utils.color_utils import hex_to_rgb, rgb_to_lab, delta_e
IMG_DIR = Path(__file__).resolve().parent / "test_images"
def _load(name):
return Image.open(IMG_DIR / name)
def _assert_min_delta_e(hex_colors, min_delta_e):
labs = [rgb_to_lab(hex_to_rgb(h)) for h in hex_colors]
for i in range(len(labs)):
for j in range(i + 1, len(labs)):
d = delta_e(labs[i], labs[j])
assert d >= min_delta_e - 1e-6, f"{hex_colors[i]} vs {hex_colors[j]} delta_e={d}"
def test_photograph():
img = _load("photograph.png")
colors = _extract_palette(img, num_colors=8, min_delta_e=10.0)
assert 4 <= len(colors) <= 8
_assert_min_delta_e(colors, 10.0)
def test_logo():
img = _load("logo.png")
colors = _extract_palette(img, num_colors=8, min_delta_e=10.0)
# flat 4-color logo should yield close to 4 distinct colors, not 8
assert 3 <= len(colors) <= 5
_assert_min_delta_e(colors, 10.0)
def test_painting():
img = _load("painting.png")
colors = _extract_palette(img, num_colors=8, min_delta_e=10.0)
assert len(colors) >= 4
_assert_min_delta_e(colors, 10.0)
def test_monochrome_fallback():
img = _load("monochrome.png")
colors = _extract_palette(img, num_colors=8, min_delta_e=10.0)
# near-uniform gray should collapse to very few colors, never crash, never empty
assert 1 <= len(colors) <= 3
_assert_min_delta_e(colors, 10.0)
def test_json_serializable():
img = _load("logo.png")
colors = _extract_palette(img, num_colors=8, min_delta_e=10.0)
s = json.dumps(colors)
assert json.loads(s) == colors
def test_element_palette_clamps_to_five():
img = _load("painting.png")
colors = _extract_palette(img, num_colors=5, min_delta_e=10.0)[:5]
assert 1 <= len(colors) <= 5
_assert_min_delta_e(colors, 10.0)
def test_element_palette_monochrome_fallback():
img = _load("monochrome.png")
colors = _extract_palette(img, num_colors=5, min_delta_e=10.0)[:5]
assert 1 <= len(colors) <= 5
if __name__ == "__main__":
test_photograph()
print("photograph: OK")
test_logo()
print("logo: OK")
test_painting()
print("painting: OK")
test_monochrome_fallback()
print("monochrome: OK")
test_json_serializable()
print("json_serializable: OK")
test_element_palette_clamps_to_five()
print("element_palette_clamps_to_five: OK")
test_element_palette_monochrome_fallback()
print("element_palette_monochrome_fallback: OK")
print("All extractor tests passed.")