颜色量化中八叉树算法如何压缩调色板
从真彩色像素建立八叉树
八叉树量化解决的是“颜色种类太多”的问题:它把真彩色图像中的大量 RGB 颜色压缩成不超过 256 个代表色。这个过程不是直接压缩 PNG、JPEG 或 SVG 文件,而是先减少颜色集合,再把每个像素映射到调色板中的颜色。
真彩色图像通常使用 24 位 RGB 表示,每个通道占 8 位,理论上可以表达 2^24 = 16,777,216 种颜色。目标调色板若限定为 256 色,就是把每个像素从 24 位颜色映射到一个 8 位索引。
八叉树是一种每个内部节点最多拥有 8 个子节点的树结构。原因很直接:R、G、B 三个通道在当前二进制位上各有高、低两种分支,组合数为 2^3 = 8。从最高位开始,三个通道的当前位组成一个三位索引,例如 101 就指向其中一个子节点。
插入一个像素时,算法按颜色位深从高位到低位逐层选择分支。颜色相近的像素会在较深层级之前经过相同路径,最终聚集到同一个区域。节点通常需要保存以下数据:
- 当前层级和子节点指针;
- 落入该节点的像素计数;
- R、G、B 通道的累计值;
- 是否为叶节点,以及叶节点引用。
图片转SVG工具往往会把颜色量化放在矢量化之前。量化后的颜色数量会影响后续路径追踪:颜色较少,可能形成更少的填充区域;颜色较多,则更容易保留渐变,却可能生成更多路径和更大的 SVG。
按层级合并叶节点减少颜色
设目标调色板为 256 色。八叉树建立完成后,如果叶节点数量超过 256,就要把多个叶节点合并。这里的叶节点数量代表当前可输出的代表色数量,不等于原始图像中的像素数量。
- 读取位图中的每个像素,并沿八叉树路径插入。
- 在叶节点中累计像素数量和三个颜色通道的总和。
- 统计当前叶节点数,并与 256 比较。
- 从较深层级寻找拥有多个叶子后代的内部节点。
- 将这些叶节点合并成一个新叶节点,更新统计值和叶节点数量。
- 重复合并,直到叶节点数不超过 256。
合并时,不能简单平均每个叶节点的颜色。正确做法是按像素数量加权。假设一个节点包含的像素总数为 N,R 通道累计值为 Rsum,则代表色的红色分量为 Rsum / N;G、B 通道同理。这样,一个包含 10,000 个像素的颜色区域不会和只出现 2 个像素的区域拥有相同权重。
合并顺序取决于具体实现。有的算法优先合并最深层节点,因为它们覆盖的颜色范围较小;有的算法优先合并像素数量少的节点,试图减少对主体区域的影响;也有实现会估算合并后的误差代价。选择不同,最终调色板可能不同。
用误差指标选择合并顺序
“优先合并最深层节点”和“优先合并像素数最少节点”各有适用条件。前者通常计算简单、速度稳定,适合实时或批量处理;后者更关注视觉主体,但需要维护像素计数,某些图像上还可能牺牲细小边缘颜色。
可以用均方误差(MSE)比较量化前后的颜色差异。对每个像素,将原始 RGB 值与量化后的 RGB 值分别相减,计算三个通道误差的平方,再对所有像素求平均。MSE 越小,表示 RGB 数值上的总体偏差越小,但它不等于人眼感知质量一定更好。
八叉树量化默认工作在 RGB 空间,而 RGB 的欧氏距离并不是感知均匀的颜色距离。例如,数值上相同的变化,在不同色相和亮度区域可能产生不同程度的视觉差异。对照片、渐变或品牌色要求较高的场景,可以先转换到 CIELAB,再使用感知距离选取代表色;也可以加入抖动,用相邻像素的微小误差扩散来减轻色带。
透明度不能一概而论。有的实现把 Alpha 作为独立通道参与量化,有的实现先将半透明像素合成到白色或其他背景色,之后只处理 RGB。因此,八叉树算法本身不保证保留半透明边缘,实际结果取决于工具的透明度处理方式。
把量化结果用于图片转SVG
一个可执行的处理流程如下:
- 读取位图,确认 RGB 或 RGBA 格式。
- 建立八叉树,并把每个像素插入对应路径。
- 逐层合并叶节点,直到颜色数达到 256 或更低。
- 计算每个叶节点的代表色,生成调色板。
- 把每个原始像素映射到最近或所属的调色板颜色。
- 对同色相邻区域进行合并,再执行路径追踪。
- 输出 SVG,并检查路径、填充色和透明背景。
颜色数会直接影响矢量化结果。64 色通常适合颜色有限的图标或像素画;128 色适合包含少量阴影的插图;256 色更适合照片缩略图和渐变,但不代表一定能保留平滑过渡。
- 图标、像素画:优先保留硬边,避免平滑插值破坏轮廓。
- 照片:分别测试 64、128、256 色,观察主体、细节和文件体积。
- 渐变图:重点检查色带,可比较更高颜色数或加入抖动后的结果。
颜色增加可能减少色带,却会产生更多填充区域和路径;颜色减少可以缩小颜色表,但可能丢失渐变和细节。最终 SVG 大小还取决于路径数量、路径坐标、元数据和工具实现,不能仅凭颜色数判断。
检查输出时,确认 SVG 是否真的完成了矢量化:
- 是否存在数量异常庞大的路径;
- 是否仍通过
<image>标签嵌入原始 PNG 或 JPEG; - 实际填充颜色数量是否达到预期;
- 透明背景和半透明边缘是否正确。
如果文件只是把原图放进 <image> 标签,图片转SVG工具可能只完成了封装,并未完成颜色区域合并和路径追踪。八叉树压缩的是颜色种类,把大量 RGB 颜色映射到不超过 256 个代表色;它不等于 PNG、JPEG 或 SVG 的文件压缩,最终大小仍由像素编码、路径数量、元数据和具体实现共同决定。
Alpha通道预乘在圆角抗锯齿中的作用
圆角黑边的根源:边缘颜色和透明度没有对齐 圆角抗锯齿把边界覆盖率转换成 Alpha,所以一个边缘像素可能只有 A=128,约等于 0.502,而不是非黑即白。黑边、白边通常不是圆角算法本身造成的,而是 RGB 的存储方式、插值方式和合成公式
从RGB到CMYK印刷档的色彩空间转换
RGB与CMYK的成色机制差异 RGB与CMYK不是两套可以直接互换的数值,而是两种不同的成色机制。RGB用光叠加形成颜色,CMYK用油墨吸收光线形成颜色;把RGB模式切换成CMYK,只改变了数据表达方式,并不等于完成印刷准备。 RGB是
JPEG压缩中DCT变换与量化表的配合
从像素块到频率系数 JPEG 压缩的关键,不是把整张图片一次性“变小”,而是把图像拆成许多 8×8 像素块,再分别处理每个块中的明暗变化。二维离散余弦变换(DCT)负责重新表达这些变化,量化表则决定哪些频率信息保留得细、哪些信息更容易被舍
双线性插值在缩放中如何计算目标像素
从目标坐标反推四个邻近像素 双线性插值解决的是一个具体问题:缩放后的目标像素,通常对应原图中一个非整数坐标,不能直接读取单个像素。它会找到该坐标周围的4 个邻近像素,再按水平和垂直距离分配权重,计算出目标颜色。 设原图中的采样坐标为 (
K-Means聚类在图片主色提取中的实现
说明主色提取要解决的实际问题 图片主色提取的目标,是从一张图的像素中找出 3~8 个代表颜色,生成可用于配色、设计参考或图片取色工具的主色板。输出不应只有几个色块,还应包含每种颜色的 HEX、RGB、像素占比,以及明确的排序依据。 直接按