Oh MyUtils

图片颜色提取器 - 从图片提取调色板 在线

从任意图片提取主色调并生成调色板。获取HEX、RGB和HSL值 — 100%客户端处理,图片不上传。

常见问题

什么是图像颜色提取器,为什么需要它?

图像颜色提取器分析上传图像的像素并识别最主要的颜色,生成颜色调色板。对于需要从照片、艺术作品或品牌图像中派生一致色彩方案的设计师非常有用。该工具无需使用吸管手动采样颜色,而是自动识别最具代表性的颜色,并以网页就绪格式(HEX、RGB、HSL)提供,可直接用于CSS、设计工具或品牌指南。

如何使用这个图像颜色提取工具?

1. 将图像拖放到上传区域(或点击浏览文件)。2. 工具自动提取主要颜色并显示为调色板。3. 使用调色板大小滑块调整颜色数量(2-16种颜色)。4. 点击任何色块复制其HEX、RGB或HSL值。5. 使用导出选项将整个调色板复制为CSS变量、JSON或纯文本。6. 可选择使用排序控件按频率、色调或亮度排序颜色。

我的图像数据安全吗?图像会上传到服务器吗?

您的图像100%安全,永远不会离开您的浏览器。所有颜色提取都使用HTML5 Canvas API在客户端执行。该工具直接在浏览器中读取图像的像素数据,在本地应用颜色量化算法并显示结果。没有任何图像数据传输到任何服务器,使该工具完全私密。

使用什么颜色量化算法?

该工具使用Median Cut算法,这是一种成熟的颜色量化技术。它通过递归地将图像的颜色空间划分为更小的区域来工作,在范围最宽的通道(红色、绿色或蓝色)的中值处进行分割。然后对每个区域取平均值以产生单个代表颜色。Median Cut快速、确定性强,能生成准确表示主要颜色的视觉效果良好的调色板。

支持哪些图像格式?

该工具支持PNG、JPEG(JPG)、WebP和GIF图像,文件大小最大10MB。PNG、WebP和GIF图像中的透明像素在颜色提取时会自动忽略,不会影响调色板。对于GIF图像,仅分析第一个(静态)帧。

可以从图像中提取多少种颜色?

可以提取2到16种颜色。默认值为8种颜色,为大多数用例提供了良好的平衡。对于简单图像或极简调色板,4-6种颜色效果很好。对于具有许多不同颜色区域的复杂照片,10-16种颜色将捕获更多细微差别。调整滑块时提取会实时更新。

按频率、色调和亮度排序有什么区别?

频率(默认)按颜色在图像中占据的面积排序,最主要的颜色排在最前面。色调按颜色在色轮上的位置(0-360度)排序,创建从红色到橙色、黄色、绿色、蓝色和紫色的彩虹般序列。亮度根据HSL明度值从最亮到最暗排序,适用于创建渐变或识别对比度范围。

可以导出颜色调色板用于代码中吗?

是的。该工具提供多种针对开发者优化的导出格式:CSS变量生成 :root { --color-1: #FF5733; ... },可直接用于CSS。JSON导出包含hex、rgb和hsl值的对象数组,用于编程使用。纯文本提供每行一个HEX代码的简单列表。

代码示例

// Extract dominant colors from an image using Canvas API + Median Cut
function extractColorsFromImage(imageSource, colorCount = 8) {
  return new Promise((resolve, reject) => {
    const img = new Image();

    img.onload = () => {
      const maxPixels = 10000;
      const scale = Math.min(1, Math.sqrt(maxPixels / (img.width * img.height)));
      const width = Math.max(1, Math.floor(img.width * scale));
      const height = Math.max(1, Math.floor(img.height * scale));

      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;

      const ctx = canvas.getContext("2d");
      ctx.drawImage(img, 0, 0, width, height);

      const imageData = ctx.getImageData(0, 0, width, height);
      const pixels = [];

      for (let i = 0; i < imageData.data.length; i += 4) {
        if (imageData.data[i + 3] < 128) continue;
        pixels.push({
          r: imageData.data[i],
          g: imageData.data[i + 1],
          b: imageData.data[i + 2],
        });
      }

      const colors = medianCut(pixels, colorCount);
      resolve(colors);
    };

    img.onerror = () => reject(new Error("Failed to load image"));

    if (imageSource instanceof File) {
      img.src = URL.createObjectURL(imageSource);
    } else {
      img.src = imageSource;
    }
  });
}

function medianCut(pixels, targetCount) {
  if (pixels.length === 0) return [];
  let buckets = [pixels];

  while (buckets.length < targetCount) {
    let maxRange = -1, maxIndex = 0, splitChannel = "r";

    for (let i = 0; i < buckets.length; i++) {
      if (buckets[i].length < 2) continue;
      for (const ch of ["r", "g", "b"]) {
        const values = buckets[i].map((p) => p[ch]);
        const range = Math.max(...values) - Math.min(...values);
        if (range > maxRange) {
          maxRange = range;
          maxIndex = i;
          splitChannel = ch;
        }
      }
    }

    if (maxRange <= 0) break;

    const toSplit = buckets[maxIndex];
    toSplit.sort((a, b) => a[splitChannel] - b[splitChannel]);
    const mid = Math.floor(toSplit.length / 2);
    buckets.splice(maxIndex, 1, toSplit.slice(0, mid), toSplit.slice(mid));
  }

  const totalPixels = pixels.length;
  return buckets.map((bucket) => {
    const r = Math.round(bucket.reduce((s, p) => s + p.r, 0) / bucket.length);
    const g = Math.round(bucket.reduce((s, p) => s + p.g, 0) / bucket.length);
    const b = Math.round(bucket.reduce((s, p) => s + p.b, 0) / bucket.length);
    const hex = "#" + [r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("").toUpperCase();
    return { hex, rgb: { r, g, b }, percentage: ((bucket.length / totalPixels) * 100).toFixed(1) };
  });
}

// Usage
const input = document.querySelector('input[type="file"]');
input.addEventListener("change", async (e) => {
  const file = e.target.files[0];
  const colors = await extractColorsFromImage(file, 8);
  colors.forEach((c) => console.log(`${c.hex} - ${c.percentage}%`));
});

相关工具