← back to Fentucci Naturals
scripts/color-names.py
68 lines
#!/usr/bin/env python3
"""Derive interior-designer-lexicon color names from dominant hue of each local image.
Simple hue/sat/lightness bucket -> name table (enrichment pass comes later).
Writes data/colors.json: { mfr_sku: {hex, name} }
"""
import json, os, sys, colorsys
from PIL import Image
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
products = json.load(open(os.path.join(ROOT, 'data', 'products.json')))
def dominant_color(path):
im = Image.open(path).convert('RGB')
# center crop 60% to avoid borders/shadows, then shrink
w, h = im.size
im = im.crop((int(w*0.2), int(h*0.2), int(w*0.8), int(h*0.8))).resize((50, 50))
px = list(im.getdata())
r = sum(p[0] for p in px)//len(px); g = sum(p[1] for p in px)//len(px); b = sum(p[2] for p in px)//len(px)
return r, g, b
def name_for(r, g, b):
hh, ll, ss = colorsys.rgb_to_hls(r/255, g/255, b/255)
hue = hh*360
# neutrals first
if ss < 0.09:
if ll > 0.88: return 'Alabaster'
if ll > 0.75: return 'Ivory'
if ll > 0.60: return 'Greige'
if ll > 0.45: return 'Pewter'
if ll > 0.30: return 'Slate'
if ll > 0.15: return 'Charcoal'
return 'Ebony'
warm_neutral = ss < 0.25
if hue < 15 or hue >= 345:
return 'Blush' if ll > 0.7 else ('Terracotta' if ll > 0.4 else 'Garnet')
if hue < 45:
if warm_neutral:
return 'Oatmeal' if ll > 0.7 else ('Fawn' if ll > 0.45 else 'Espresso')
return 'Sand' if ll > 0.7 else ('Amber' if ll > 0.45 else 'Sienna')
if hue < 70:
return 'Champagne' if ll > 0.72 else ('Ochre' if ll > 0.45 else 'Bronze')
if hue < 100:
return 'Flax' if ll > 0.72 else ('Sage' if ll > 0.45 else 'Olive')
if hue < 165:
return 'Celadon' if ll > 0.65 else ('Moss' if ll > 0.4 else 'Forest')
if hue < 200:
return 'Seaglass' if ll > 0.65 else ('Verdigris' if ll > 0.4 else 'Teal')
if hue < 250:
return 'Sky' if ll > 0.65 else ('Denim' if ll > 0.4 else 'Indigo')
if hue < 290:
return 'Lilac' if ll > 0.65 else ('Plum' if ll > 0.4 else 'Aubergine')
return 'Rose' if ll > 0.65 else ('Mulberry' if ll > 0.4 else 'Bordeaux')
out, errs = {}, []
for p in products:
path = os.path.join(ROOT, p['image_local'])
if not os.path.exists(path):
errs.append(p['mfr_sku']); continue
try:
r, g, b = dominant_color(path)
out[p['mfr_sku']] = {'hex': '#%02x%02x%02x' % (r, g, b), 'name': name_for(r, g, b)}
except Exception as e:
errs.append(p['mfr_sku'])
json.dump(out, open(os.path.join(ROOT, 'data', 'colors.json'), 'w'), indent=1)
print(f"colored: {len(out)}, missing/error: {len(errs)}")
if errs: print('errors:', errs[:20])