将彩色图片转成黑白——纯前端实现

在数字化发达的今天,黑白图像在我们日常生活中越来越少见了,黑白图像虽不比彩图的真实和美感,却能突出严肃和年代感,也更具纪念意义。本文将介绍如何在前端将彩色位图转为黑白,供有兴趣的小伙伴参考。

这是我参与更文挑战的第11天,活动详情查看: 更文挑战

实现思路

话不多说,直接开搞。

位图其实是一个个的小格子(像素点)组成的图像:

image.png
上图中的每个像素格子代表了一个颜色值,我们用rgba表示:r表示红色,g表示绿色,b表示蓝色,a表示透明度。

所以,我们需要将位图中每个像素点的rgba转换为相应灰度的rgba

如何计算呢?

我们采用均值法:将rgb三个值相加后取平均数作为新颜色值的rgb、alpha统一为不透明。

例如,将红色rgba(255,0,0,1)转为黑白色rgba(85,85,85,1)

接下来,我们需要拿到位图图像所有的rgba颜色值。

获取图像的颜色值

这里我们需要借助一个方法ctx.getImageData(sx, sy, sw, sh):表示从cavas2d上下文获取某个区的图像数据。sx、sy表示该区域图像的起点,sw和sh表示该区域图像的尺寸。

我们要获取一张图片的rgba颜色值,该怎么做呢?

  • 将这张图片绘制到canvas上
  • 借助canvas2d渲染上下文的getImageData方法获取数据
<img src="./wife.png" alt="">
<canvas id="canvas"></canvas>
<script>
    var img = document.querySelector('img');
    var canvas = document.querySelector('#canvas');

    img.onload = function(){
        var naturalImgSize = [img.naturalWidth,img.naturalHeight];

        var ctx = canvas.getContext("2d");
        canvas.width = naturalImgSize[0];
        canvas.height = naturalImgSize[1];
        ctx.drawImage(img,0,0);

        //获取imageData
        var imgData = ctx.getImageData(0,0,254,419);
        //打印该数据
        console.log(imgData)
    }
</script>
复制代码

我们拿到的数据是这样的:
image.png
height和width是图像的像素尺寸(254px * 419px),data就是图像rgba颜色值组成的一维数组。

例如,我们有一个2px*2px红色图片:

image.png

从左到右从上到下,它有4个像素点,
每个像素点包含rgba 4个值(一个字节表示一个值,范围从0-255,也就是00000000-11111111)。因此每个像素点要占4个位置。

该红色图像的rgba数据值对应的一维数组包含了4个255,0,0,255,即
[255,0,0,255,255,0,0,255,255,0,0,255,255,0,0,255]

例证完毕,再回到我们拿到的Unit8ClampedArray数组,它的长度为图像的像素宽高*4254*419*4 = 425704

展开不就是一个个的0-255的数据点么:
image.png

执行转换

上一节我们拿到了图像数据,接下来就是要执行灰度转换了,根据之前提到的,我们采用均值法

我们需要遍历上述的数组,然后将换算后的值同步到一个新的数组里,再根据新数组生成一份新的imageData并渲染到canvas上即可。

//定义一个新的数组
let newArr = [];
imgData.data.forEach((colorVal,i)=>{
    if(i % 4 === 0){
        // 获取rgb均值
        const average = Math.round((imgData.data[i]+imgData.data[i+1]+imgData.data[i+2])/3);
        newArr[i] = newArr[i+1] = newArr[i+2] = average;
        //alpha值统一为255
        newArr[i+3] = 255;
    }
})

//新建一个imgData对象
var newImgData = ctx.createImageData(254,419)
//将换算后的数据添加到新的imageData对象里
newImgData.data.set(newArr);

//将新建的imageData绘制到画布上
ctx.putImageData(newImgData, 0, 0);
复制代码

这一切执行完毕,我们可以看到:

image.png

现在我们已经将图像转换为灰色的了,我们再将canvas上的图像保存为图像文件:

canvas.toBlob(blob=>{
    var a = document.createElement("a");
    var file = new File([blob], "wife.jpeg");
    a.href = URL.createObjectURL(file);
    a.download = "wife.jpg";
    a.click()
},"image/jpeg")
复制代码

看到我们成功将该图像保存到本地了:

image.png

总结

彩色转黑白到这里基本结束了,我们借助getImageData的能力,实现了在前端直接操控rgba数据。

有了这个能力,一些操作如图像旋转图像的水平、垂直轴对称转换等通常需要借助客户端软件如ps等才能完成,在前端也都能完成,因为这些操作本质只是改变图像的像素点排列而已。用图示很好解释:

原图:

1.png

向右旋转90度:

2.png

水平轴对称:

3.png

再总结一下彩色位图转换为黑色的几个步骤:

  • 将image关联的图像文件绘制到一个canvas上
  • 获取canvas上图像的imageData数据
  • 将imageData数据的rgba部分均值计算后填充到你一个新数组里
  • 将新数组填充到一个新的ImageData对象上
  • 再将新的ImageData对象绘制回canvas元素
  • canvas转换成file对象本地保存

最后,感谢阅读,如果对文中内容有任何的疑问欢迎留言讨论、不吝赐教;如果本文对你有帮助,麻烦点个赞,谢谢哦。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享