You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过canvas/p5.js给图片加滤镜使其平均RGB趋近指定参考色

实现思路

你要的效果不需要用复杂的滤镜算法,核心逻辑是计算原图平均RGB和目标参考RGB的差值,给每个像素的对应通道统一叠加偏移量即可,处理后的图片平均RGB会和目标值非常接近,同时保留原图的明暗、纹理差异。

原生Canvas实现代码

// 工具函数:获取图片平均RGB
function getAverageRGB(imgEl) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = imgEl.naturalWidth;
  canvas.height = imgEl.naturalHeight;
  ctx.drawImage(imgEl, 0, 0);
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;
  let r = 0, g = 0, b = 0;
  const pixelCount = data.length / 4;
  for (let i = 0; i < data.length; i += 4) {
    r += data[i];
    g += data[i+1];
    b += data[i+2];
  }
  return [
    Math.round(r / pixelCount),
    Math.round(g / pixelCount),
    Math.round(b / pixelCount)
  ];
}

// 滤镜实现函数
function addFilterToImg(targetRGB, imgEl) {
  const oldAvg = getAverageRGB(imgEl);
  // 计算三个通道的偏移量
  const offset = [
    targetRGB[0] - oldAvg[0],
    targetRGB[1] - oldAvg[1],
    targetRGB[2] - oldAvg[2]
  ];

  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = imgEl.naturalWidth;
  canvas.height = imgEl.naturalHeight;
  ctx.drawImage(imgEl, 0, 0);
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = imageData.data;

  // 遍历每个像素调整通道值
  for (let i = 0; i < data.length; i += 4) {
    data[i] = Math.max(0, Math.min(255, data[i] + offset[0]));
    data[i+1] = Math.max(0, Math.min(255, data[i+1] + offset[1]));
    data[i+2] = Math.max(0, Math.min(255, data[i+2] + offset[2]));
  }

  ctx.putImageData(imageData, 0, 0);
  // 替换原图片的src为处理后的canvas内容
  imgEl.src = canvas.toDataURL();
}

// 调用示例
const img = document.getElementById('my-face');
// 等图片加载完成后执行
img.onload = () => {
  const refRgb = [255, 244, 50];
  addFilterToImg(refRgb, img);
}

p5.js实现代码

let img;
const refRgb = [255, 244, 50];

function preload() {
  img = loadImage('/my-face.jpg');
}

function setup() {
  createCanvas(img.width, img.height);
  image(img, 0, 0);
  // 获取原图平均RGB
  loadPixels();
  let r = 0, g = 0, b = 0;
  const pixelCount = pixels.length / 4;
  for (let i = 0; i < pixels.length; i += 4) {
    r += pixels[i];
    g += pixels[i+1];
    b += pixels[i+2];
  }
  const oldAvg = [r/pixelCount, g/pixelCount, b/pixelCount];
  const offset = [refRgb[0] - oldAvg[0], refRgb[1] - oldAvg[1], refRgb[2] - oldAvg[2]];
  // 调整像素
  for (let i = 0; i < pixels.length; i += 4) {
    pixels[i] = constrain(pixels[i] + offset[0], 0, 255);
    pixels[i+1] = constrain(pixels[i+1] + offset[1], 0, 255);
    pixels[i+2] = constrain(pixels[i+2] + offset[2], 0, 255);
  }
  updatePixels();
}

注意事项

如果通道偏移量过大,会有大量像素的通道值被裁剪到0或255,此时最终平均色和目标值会有微小误差。如果对精度要求极高,可以把固定偏移替换为线性比例缩放:将整个通道的取值范围按目标平均和原平均的比例做映射,就能避免裁剪带来的误差。

内容的提问来源于stack exchange,提问作者user2272048

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 01:45:00