YIXUNFE / blog

文章区
151 stars 25 forks source link

Canvas 操作图像像素 #12

Open YIXUNFE opened 8 years ago

YIXUNFE commented 8 years ago

Canvas 操作图像像素

HTML5 中最吸引我的新标签就是 canvas,canvas 元素为我们操作图像带来了更多的可能。


ImageData 对象

canvas 元素给我们提供了一系列绘制 2d 图像的 API,比如绘制一个正方形或者一个椭圆等,同时我们也可以利用 getImageData() 方法返回的 ImageData 对象,获取一个图像的像素点。

var ctx = canvas.getContext('2d')
// 获取 ImageData 对象
var imageData = ctx.getImageData(x, y, width, height)

getImageData() 方法的参数说明

参数 说明
x 选区横坐标起始点,默认 0
y 选区纵坐标起始点,默认 0
width 选区宽度,默认整幅图像宽度
height 选区高度,默认整幅图像高度

getImageData() 方法返回的 ImageData 对象包含下列三个属性:

ImageData 对象的 width 与 height 属性是只读的。

ImageData 对象的 data 属性是一个数组,具有 4 * width * height 的整数值。因为每个像素点包含 4 个整数值,即 RGBA 值。

拿到了像素点的值,我们就能够对图像进行操作了。

getImageData() 方法只能获取同源图片的像素信息哦


遍历像素点

我们从 ImageData 的 data 属性中遍历图像的像素点:

var imageData =  ctx.getImageData(x, y, width, height)
var i = 0,  l = imageData.data.length, r, g, b, a

//循环像素点
for (i; i < l; i += 4) {
    r = imageData.data[i]    //红
    g = imageData.data[i + 1] //绿
    b = imageData.data[i + 2] //蓝
    a = imageData.data[i + 3] //透明
}

由于每个像素点的 RGBA 四个数值都依次存放在 data 属性中,所以我们需要将4个数值一组放在循环体中,以明确当前像素点的 RGBA 值。

这种方法的好处在于可以明确的知道某个像素点的 RGBA 值,缺点也很明显,无法直观的获取像素点所在的行和列。更加重要的是在只需要对行或列进行相关计算的时候,每个像素的遍历会造成很多重复计算,按行与列遍历像素点的方法可以弥补这些缺点:

var imageData =  ctx.getImageData(x, y, width, height)
var x = 0, y = 0,  l = imageData.data.length, r, g, b, a, w = imageData.width, h = imageData.height

//循环行
for (y; y < h; y++) {
    //循环列
    for (x = 0; x < w; x++) {
        r = imageData.data[(x + y * w) * 4]    //红
        g = imageData.data[(x + y * w) * 4 + 1] //绿
        b = imageData.data[(x + y * w) * 4 + 2] //蓝
        a = imageData.data[(x + y * w) * 4 + 3] //透明
    }
}


简易效果实现

学会了遍历像素点,我们可以对其修改,达到一些出乎意料的效果。这里我们使用 YIXUNFE 的 LOGO 图做一些效果介绍。

原图: 原图


反相

反相效果就是用 255 减去每个像素点的 RGB 值。

data[i] = 255 - data[i]
data[i + 1] = 255 - data[i + 1]
data[i + 2] = 255 - data[i + 2]

实现的效果如图:

反相


黑白

黑白效果的特征就是像素点的 RGB 三个值相同。

var a = Math.ceil((data[i] + data[i + 1] + data[i + 2]) / 3)
data[i] = a
data[i + 1] = a
data[i + 2] = a

实现的效果如图:

黑白


模糊

模糊效果是通过获取当前像素点周围的点的 RGB 各平均值实现。这里我们仅通过当前像素点的周围8格实现该效果。

//像素点副本,用来获取原图的像素值
var copy = data.slice(0)

//循环行
for (y; y < h; y++) {
    //循环列
    for (x = 0; x < w; x++) {
        index = (x + y * w) * 4
        l = (x - 1 + y * w) * 4
        r = (x + 1 + y * w) * 4
        t = (x + (y - 1) * w) * 4
        b = (x + (y + 1) * w) * 4
        lt = (x - 1 + (y - 1) * w) * 4
        rt = (x + 1 + (y - 1) * w) * 4
        lb = (x - 1 + (y + 1) * w) * 4
        rb = (x + 1 + (y + 1) * w) * 4

        //获取R平均值
        data[index] = (copy[index] + copy[l] + copy[r] + copy[t] + copy[b] + copy[lt] + copy[rt] + copy[lb] + copy[rb]) / 9

        //获取G平均值
        data[index + 1] = (copy[index + 1] + copy[l + 1] + copy[r + 1] + copy[t + 1] + copy[b + 1] + copy[lt + 1] + copy[rt + 1] + copy[lb + 1] + copy[rb + 1]) / 9

        //获取B平均值
        data[index + 2] = (copy[index + 2] + copy[l + 2] + copy[r + 2] + copy[t + 2] + copy[b + 2] + copy[lt + 2] + copy[rt + 2] + copy[lb + 2] + copy[rb + 2]) / 9
    }
}

模糊


查看更多效果

对角线反相 噪点 扭曲


Thanks


rubickecho commented 6 years ago

很赞,学习了!