如何通过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
相关产品推荐
相关产品推荐

