这篇文章记录我自研色板 R-Chroma 从一个模糊的”不爽”,到最终落地为可运行的程序化提取逻辑的完整过程。它不是某个算法的搬运,而是一次试图把”人类的主观审美”写进代码的工程尝试——包括中间的困惑、与 AI 的反复探讨,以及最后我对”全自动”这件事的清醒取舍。
一、起点:机械取色到底哪里不对
事情源于我的色彩作品集页面。我给每幅画作配了多套自动生成的色板,默认展示前两个:一个基于 kmeans(聚类),一个基于 luma(亮度分层)。但实际看下来有两个很别扭的问题:一是我根本看不出它们的取色逻辑,二是有几张图,这两个色板肉眼看起来几乎一模一样。
我去查了底层原理,才明白主流自动取色(中位切分、K-Means 之类)本质上只是把图片当成一堆像素做数学统计,它们的盲点集中在三处:
- 像素面积 ≠ 视觉心理权重。算法只认像素数量。一片占 70% 面积的灰白背景会统治结果,而占 5% 却真正有灵魂的主体色,可能被当成噪点直接抹掉。人看图却是”语义优先”,会本能忽略大面积无意义背景。
- RGB 空间的物理平均会出”脏色”。RGB 对人眼并不均匀,大红和亮蓝在数学上平均可能得到一个灰蒙蒙的暗灰,而这个颜色在原图里根本不存在,也毫无美感。
- 渐变、光影、噪点被机械切分。一个红色物体从亮到暗有几十种过渡,算法会把它们全抓出来,色板里堆满高度相似、毫无规律的过渡色,而不是纯粹的色彩意象。
这也解释了 kmeans 和 luma 为什么会”撞车”:在统一环境色的画面里,像素密度集中在同一片中间调,kmeans 靠密度切、luma 靠亮度切,切出来的区间在数学上严重重合——殊途同归,得到一团相似的”背景脏色”。
二、思路演进:能不能把”主观”写成规则
我最初的想法很直接:既然机器不懂,那我能不能搞一套自己的”芮氏色板”,完全按主观意识挑主要色彩?在和 AI 多轮探讨后,结论是——可行,但要分清哪些能交给程序,哪些必须留给人。
关键的认知转折是:不要让算法”自由发挥”地吐出 N 个聚类,而是反过来,先用人类的视觉逻辑硬性规定色板的”职能结构”,再让算法去每个职能里找色。这在工程上叫”带规则约束的算法管线(Rule-constrained Pipeline)”,也是 Adobe、苹果这类大厂内部高级取色的真实思路。
我拿一张白底白花的画做了主观拆解,验证这个想法:花瓣受光呈现淡蓝、本体是代表性的紫、花蕊的亮黄、占比极小却关键的绿色花杆,再加上作为明快基调基石必须破例保留的纯白,最后补一个定型结构的深色——正好六个。问题变成:这六个”职能”能不能被程序稳定地识别和填充?答案是可以,于是有了 R-Chroma 的六槽架构。
三、R-Chroma 的六槽设计
R-Chroma 抛弃”凑数制”,把六个名额写成”职能制”。无论画面是高调还是低调,都强制按这六个维度提取:
- 明度锚定色:先算全图平均明度判断调性。高调画面(平均 V > 0.8)强制抓取最亮、最低饱和的极值(纯白),低调画面则抓极致深黑。这就是”白色豁免”——传统算法首轮就会把纯白当无意义底色过滤,而我认为它恰恰是明快作品的基石。
- 核心固有色 / 主色①:过滤背景后聚类,取最具辨识度的物体基调。
- 核心主色②:第二大色彩区间,与主色①共同撑起画面的固有色关系。
- 环境光影过渡色:在中间调、主色邻近区域捕捉受光/背光产生的色相偏移,比如花瓣上那一抹冷色阴影。
- 高饱和点睛色:扫描饱和度 S > 0.3 的”色彩孤岛”,抓面积小但对比最剧烈的视觉兴奋点(花蕊的黄、鱼身上的鲜绿)。
- 结构暗部:在最暗的 10% 像素里聚类,取带微弱色相倾向的深色作压舱石,完成从白到暗的光影闭环。
四、ΔE 色差去重:专治”两个色一样”
只规定职能还不够,两个不同槽位仍可能取到肉眼难分的颜色。为此我加了一道基于 CIELAB 色彩空间的 ΔE 色差去重。之所以用 Lab,是因为它的颜色距离最接近人眼实际感知(RGB 做不到这点)。
流程是贪心的:按职能顺序逐个选槽,每个新颜色与已选颜色逐一算 ΔE,一旦发现 ΔE 小于阈值(我定为 12),就从一个更大的候选池(量化候选 + 极值 + 各亮度段)里,挑一个与所有已选都可区分、且最接近该槽首选的颜色替换;实在找不到才保留首选。这样既守住了职能,又保证六色彼此分得清。
色差(CIE76):
ΔE = sqrt( (L1-L2)^2 + (a1-a2)^2 + (b1-b2)^2 )
若 ΔE < 12,判定为肉眼近似,触发合并 / 换色
五、工程落地:浏览器工具 + 古腾堡块
我把这套逻辑落在了已有的两个载体上,全程没有引入新环境:
- 本地浏览器取色工具:在静态测试页加了上传入口,选图后纯浏览器端用 Canvas 读取像素(先缩到 200px、过滤近白近黑背景),JS 实现中位切分、HSV 转换、色相环与冷暖分析,一键生成包含 R-Chroma 在内的完整成对行,并能按约定格式导出色板文本。图片全程不离开本地。
- 古腾堡块(rzxme/zoom-pair):色板不在 WP 内自动生成,而是依赖外部数据导入或手动逐条编辑。我把 R-Chroma 固定排在第三位常驻、不折叠(前两位是 kmeans、luma),其余模式收进折叠;前端渲染规则同步改为前三条常驻,编辑端名称预设也加了 r-chroma。
数据格式特意做得很”轻”,不用 JSON,一行就是一条色板,手敲都能输入,也方便往后增减色板数量:
r-chroma #E9D670 #2E2121 #555D69 #738281 #CBB822 #938E67
六、实测结果与最后的取舍
拿静物画 frde44(柠檬水瓶)实测,R-Chroma 给出六色:#E9D670(亮黄锚定)、#2E2121(深褐)、#555D69(灰蓝)、#738281(灰绿过渡)、#CBB822(金黄点睛)、#938E67(暗部)。计算六色两两 ΔE,最小值为 16.3,高于阈值,说明去重确实生效,六色互不重复,且覆盖了从高光到暗部的完整色彩关系——比 kmeans/luma 那种”一团相似中间调”明显更有结构。
但我并不想把它吹成”全自动就准”。做下来很清楚,六槽里明度锚定、点睛、暗部这三个靠硬规则很稳定;两个主色仍需 ΔE 兜底;而最模糊的”环境过渡色”,以及阈值(V>0.8、S>0.3)在不同图上的适配,都带有经验性。真正的”语义显著性识别”——让机器像人一样知道哪一笔是点睛、哪块只是底色——在纯前端管线里依然不可靠,这也是整套里最重、我没有硬啃的部分。
算法负责把”数据粗筛”做到 80%,提供一个比 kmeans 更合理的初稿;最后那一下审美裁决,留给我自己。
所以 R-Chroma 的准确定位是一个半自动的初稿生成器:它用职能槽位和 ΔE 去重,把机械取色最容易犯的”撞车””脏色””漏掉点睛色”系统性地解决掉,再把人工微调那一步保留下来。这不是妥协,而是我认真做完之后,认为在艺术创作领域唯一诚实、也最能保证审美上限的方案。







发表回复