图片主色提取
从图片提取 N 个主色 · 渐变生成 · 配色取色
图片→主色 N 个/渐变
从图片提取 N 个主色 · 渐变生成 · 配色取色
了解工具定位 · 使用场景 · 对比优势
设计师拿到一张竞品海报或 LOGO 截图,需要快速提取其主色调用于品牌分析或配色参考。本工具上传图片后一键提取 5-8 个主色,并自动生成渐变色方案,省去手动取色和肉眼判断的繁琐,30 秒内获得可复用的色板。
移动端 UI 设计师从灵感截图(如 Dribbble 作品)中提取色彩体系,但手动吸取颜色容易遗漏中间过渡色。本工具提取主色后自动排序并输出 HEX 值,可直接粘贴到 Sketch/Figma 的色板库,避免因取色不准导致的界面色差。
装修业主拍下喜欢的家具或软装单品照片,想以此确定全屋墙面、窗帘、地毯的配色。本工具提取图片中的 3-5 个主要颜色,并给出从浅到深的渐变序列,帮助判断哪些颜色适合大面积使用、哪些适合点缀,避免买错油漆后返工。
数据分析师需要从品牌 LOGO 或产品主图中提取色彩,用于制作风格统一的图表。本工具提取主色后提供渐变过渡色,确保柱状图、折线图、饼图的配色在视觉上连贯且不刺眼,避免因手动选色导致的图表可读性差或色彩冲突。
用户拍下自己现有衣物的照片,想判断哪些颜色可以搭配出协调的日常穿搭。本工具提取图片中的主色并生成相邻渐变,帮助用户发现衣物中隐藏的配色规律(如蓝灰配、米白配),避免因色系冲突导致的搭配失败,提升衣橱利用率。
| 维度 | 本工具 | 竞品 A (Adobe Color) | 传统方法 |
|---|---|---|---|
| 数据隐私 | 纯浏览器处理,图片不上传服务器 | 需上传图片至 Adobe 服务器 | 依赖人工肉眼判断,无数据泄露风险但主观性强 |
| 处理速度 | 1 秒内完成 | 3-5 秒(含上传与处理) | 数分钟至数小时(人工选色、调色) |
| 离线可用 | 完全离线(浏览器本地运行) | 必须联网 | 完全离线 |
| 输出格式 | HEX / RGB / HSL / 渐变 CSS 代码 | HEX / RGB / CMYK / 色板文件 (.ase) | 无标准输出,需手动记录 |
| 色数控制 | 自由设定提取 N 个主色(1-20) | 固定提取 5 个主色 | 无限制,但效率低 |
| 渐变生成 | 支持从主色自动生成渐变 | 不支持 | 需手动混合 |
| 收费模式 | 免费 | 免费(需 Adobe 账号) | 免费(时间成本) |
上手步骤 · 输入输出 · 避坑提示
| 输入 | 输出 | 说明 |
|---|---|---|
| https://example.com/photo.jpg | #2E5A88 (32.1%) | #D4A76A (24.5%) | #8B4513 (18.2%) | #F5F5DC (15.0%) | #3C3C3C (10.2%) | 典型场景:风景照片,提取天空、沙漠、树木等主色 |
| https://example.com/logo.png | #E60012 (45.0%) | #FFFFFF (35.0%) | #000000 (20.0%) | 典型场景:品牌 Logo,提取品牌色与辅助色 |
| https://example.com/gradient.jpg | 线性渐变: #FF0000 → #0000FF (角度: 90°) | 典型场景:检测图片中的渐变过渡区域 |
| https://example.com/black_white.png | #000000 (50.0%) | #FFFFFF (50.0%) | 边界 case:纯黑白图片,仅两种颜色 |
| https://example.com/1x1_pixel.png | #FF6600 (100.0%) | 边界 case:单像素图片,仅一种主色 |
| https://example.com/transparent.png | 未检测到有效颜色区域(图片完全透明) | 边界 case:完全透明的 PNG 图片,无颜色数据 |
| https://example.com/very_large.jpg | #A1B2C3 (12.5%) | #D4E5F6 (11.3%) | ... (共 10 种颜色) | 易错 case:超大尺寸图片,工具自动降采样处理 |
上传一个透明背景的 logo.png(如白色文字 + 透明底),期望提取到白色先给图片加纯色背景(如白色或黑色),再上传提取主色透明像素的 RGB 值通常为 (0,0,0) 或 (255,255,255) 但 alpha=0,算法可能忽略或错误采样,导致结果中出现意外黑色
上传一张从红到蓝的渐变图片,设置提取 2 个主色,期望得到红和蓝提取渐变图片的主色时,增加提取数量(如 5-8 个),或改用工具中的「渐变提取」模式渐变图包含大量中间色,K-Means 聚类会强行将过渡色归为少数几个中心色,结果往往是红紫蓝而非纯红纯蓝
直接上传一张 8000×6000 像素的 RAW 格式照片(约 48MB)先用图片工具压缩到 2000px 以内(或使用工具内置的自动降采样)纯前端处理(FE)时,Canvas 操作高分辨率图片会占用大量内存(约 4× 像素数),浏览器可能崩溃或提示内存不足
提取到 RGB(255, 0, 0) 后直接设为 CMYK 印刷品的专色使用色彩转换工具将 RGB 转为 CMYK(如 Photoshop 或在线转换),并确认色域警告sRGB 色域比 CMYK 大,高饱和 RGB 色(如纯红 #FF0000)在印刷中无法准确还原,会变暗或偏色
设置提取 3 个主色,结果只看到 2 种明显颜色(如蓝天白云),认为工具不准理解「主色数量」是算法聚类数,不是「肉眼可见的不同颜色数」;若图片颜色少,多余聚类会分裂相近色K-Means 或中位切分算法会强制将像素分成 N 组,即使图片只有 2 种颜色,N=3 时也会把某色再分裂成两个近似色
上传一张质量设为 10% 的 JPEG 图片(有明显块状伪影和色斑)使用 PNG 或高质量 JPEG(质量 ≥ 80%)作为源图JPEG 有损压缩会在平坦区域引入伪色(如蓝天出现绿色噪点),这些噪点会被聚类算法识别为独立颜色,干扰主色结果
一张红色背景上有一个小蓝点,提取结果显示蓝色占比 5%,认为蓝点实际面积只有 1%理解「占比」是像素聚类后的权重,不是精确的物理面积;算法可能把相近色合并计算聚类算法基于颜色距离,相近色会被归为一类,因此占比反映的是「颜色出现频率的加权」,与视觉面积不完全对应
公式推导 · 流程图解 · 依据出处
d(p, q) = sqrt((R - R')^2 + (G - G')^2 + (B - B')^2)
p — 图像中一个像素的颜色向量 (R,G,B)q — 候选主色的颜色向量 (R',G',B')R, G, B — 像素的 RGB 分量值(0-255)R', G', B' — 候选主色的 RGB 分量值(0-255)d(p, q) — 像素与候选主色的欧氏距离图像中某像素颜色为 (200, 50, 30),候选主色为 (180, 60, 40)。则 d = sqrt((200-180)^2 + (50-60)^2 + (30-40)^2) = sqrt(400 + 100 + 100) = sqrt(600) ≈ 24.5。该距离越小,说明像素越接近该候选主色。工具通过 K-means 聚类迭代,将所有像素分配到距离最近的候选主色,最终收敛得到 N 个主色。
基于 RGB 颜色空间的欧氏距离,适用于大多数自然图像的主色提取。不适用于对色差感知要求极高的场景(如印刷色彩匹配),此时应改用 CIEDE2000 色差公式(CIE 2001 标准)。
3 种主流语言 · 复制即用
from PIL import Image
import numpy as np
from sklearn.cluster import KMeans
# 加载图片并降采样(加速聚类)
img = Image.open("photo.jpg").resize((150, 150))
pixels = np.array(img).reshape(-1, 3)
# KMeans 提取 5 种主色
kmeans = KMeans(n_clusters=5, random_state=0, n_init='auto').fit(pixels)
colors = kmeans.cluster_centers_.astype(int)
# 按出现频率排序
counts = np.bincount(kmeans.labels_)
sorted_idx = np.argsort(-counts)
print("主色 (R,G,B):")
for i in sorted_idx:
r, g, b = colors[i]
print(f" #{r:02x}{g:02x}{b:02x} ({r},{g},{b})")package main
import (
"fmt"
"image"
_ "image/jpeg"
"os"
"sort"
)
func main() {
f, _ := os.Open("photo.jpg")
defer f.Close()
img, _, _ := image.Decode(f)
bounds := img.Bounds()
// 统计所有像素颜色出现次数
colorCount := make(map[[3]uint8]int)
for y := bounds.Min.Y; y < bounds.Max.Y; y += 2 { // 隔行采样加速
for x := bounds.Min.X; x < bounds.Max.X; x += 2 {
r, g, b, _ := img.At(x, y).RGBA()
key := [3]uint8{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)}
colorCount[key]++
}
}
// 按频率排序取前 5
type kv struct {
k [3]uint8
v int
}
var sorted []kv
for k, v := range colorCount {
sorted = append(sorted, kv{k, v})
}
sort.Slice(sorted, func(i, j int) bool { return sorted[i].v > sorted[j].v })
fmt.Println("主色 (R,G,B):")
for i := 0; i < 5 && i < len(sorted); i++ {
r, g, b := sorted[i].k[0], sorted[i].k[1], sorted[i].k[2]
fmt.Printf(" #%02x%02x%02x (%d,%d,%d)\n", r, g, b, r, g, b)
}
}const { createCanvas, loadImage } = require('canvas');
async function extractPalette(imagePath, n = 5) {
const img = await loadImage(imagePath);
const canvas = createCanvas(150, 150);
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, 150, 150);
const data = ctx.getImageData(0, 0, 150, 150).data;
const colorMap = new Map();
// 统计每个像素颜色
for (let i = 0; i < data.length; i += 16) { // 每 4 像素采样一次
const r = data[i], g = data[i+1], b = data[i+2];
const key = `${r},${g},${b}`;
colorMap.set(key, (colorMap.get(key) || 0) + 1);
}
// 按频率排序
const sorted = [...colorMap.entries()]
.sort((a, b) => b[1] - a[1])
.slice(0, n);
console.log('主色 (R,G,B):');
sorted.forEach(([key]) => {
const [r, g, b] = key.split(',');
console.log(` #${(+r).toString(16).padStart(2,'0')}${(+g).toString(16).padStart(2,'0')}${(+b).toString(16).padStart(2,'0')} (${r},${g},${b})`);
});
}
extractPalette('photo.jpg').catch(console.error);8 个高频疑问
「生成 / 占位」下的其他工具