node-canvas的模糊实现

在较新的HTML版本中,图像的模糊可以用canvas的filter指定为blur的方式实现,但是node-canvas库目前还不支持filter。不过它开放了像素的访问和设置,所以业务需要自己实现了一个简单的图像模糊。

具体思路为,使用某种算法,对一个像素p,通过计算其周边的像素点(模糊半径)得到一个新的像素值,写入p,并且按顺序对于每一个像素操作一边。这里举例最简单的,均值模糊,即一个像素等于它周边像素的平均值。通常称为均值滤波器。常见的还有中值模糊、高斯模糊等。

代码实现如下,由于是自行操作像素点,效率比较低,实测大概需要500-800ms完成一张800*600的图片处理。而node-canvas自带的方法通常在100ms内可以完成。

const blur = 3; // 模糊半径
const url = 'https://sijie.wang/images/UeUkxVrtbEFwqCux.jpg'; // 图片

const canvas = Canvas.createCanvas(width, height);
const ctx = canvas.getContext('2d');
const image = await Canvas.loadImage(url);
ctx.drawImage(image, 0, 0);
const imageData = ctx.getImageData(0, 0, width, height);
ctx.clearRect(0, 0, width, height);
const { data } = imageData;
// 简单的均值模糊
for (let i = 0; i < data.length; i += 4) { // rgba,一个像素有4个值
    // 从相邻格子中计算平均值
    // 例如blur为1,即相邻1像素的格子,总共组成9个
    // 9宫格
    // 1|2|3
    // 4|5|6
    // 7|8|9
    const rectWidth = 2 * blur + 1;
    const points = Array.apply(null, { length: rectWidth * rectWidth }).map((item, index) => {
        const row = Math.floor(index / rectWidth);
        const column = Math.floor(index % rectWidth);
        return i + (row - blur) * width * 4 + (column - blur) * 4;
    });
    // 边界上有些点不可用
    const availablePoints = points.filter(p => p >= 0 && p < data.length);
    const r = availablePoints.reduce((p, point) => p + data[point], 0);
    const g = availablePoints.reduce((p, point) => p + data[point + 1], 0);
    const b = availablePoints.reduce((p, point) => p + data[point + 2], 0);
    data[i] = Math.floor(r / availablePoints.length);
    data[i + 1] = Math.floor(g / availablePoints.length);
    data[i + 2] = Math.floor(b / availablePoints.length);
}
ctx.putImageData(imageData, 0, 0);

处理前后效果:

处理前
处理后(距离为1)
处理后(距离为3)

文章原始链接:https://sijie.wang/posts/canvas-blur/

本站文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请保留原始链接