在数字化发达的今天,黑白图像在我们日常生活中越来越少见了,黑白图像虽不比彩图的真实和美感,却能突出严肃和年代感,也更具纪念意义。本文将介绍如何在前端将彩色位图转为黑白,供有兴趣的小伙伴参考。
这是我参与更文挑战的第11天,活动详情查看: 更文挑战
实现思路
话不多说,直接开搞。
位图其实是一个个的小格子(像素点)组成的图像:
上图中的每个像素格子代表了一个颜色值,我们用rgba
表示:r表示红色,g表示绿色,b表示蓝色,a表示透明度。
所以,我们需要将位图中每个像素点的rgba
转换为相应灰度的rgba
。
如何计算呢?
我们采用均值法:将r
、g
、b
三个值相加后取平均数作为新颜色值的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>
复制代码
我们拿到的数据是这样的:
height和width是图像的像素尺寸(254px * 419px),data就是图像rgba颜色值组成的一维数组。
例如,我们有一个2px*2px的红色
图片:
从左到右从上到下,它有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数组,它的长度为图像的像素宽高*4:254*419*4 = 425704
。
展开不就是一个个的0-255
的数据点么:
执行转换
上一节我们拿到了图像数据,接下来就是要执行灰度转换了,根据之前提到的,我们采用均值法。
我们需要遍历上述的数组,然后将换算后的值同步到一个新的数组里,再根据新数组生成一份新的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);
复制代码
这一切执行完毕,我们可以看到:
现在我们已经将图像转换为灰色的了,我们再将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")
复制代码
看到我们成功将该图像保存到本地了:
总结
彩色转黑白到这里基本结束了,我们借助getImageData
的能力,实现了在前端直接操控rgba数据。
有了这个能力,一些操作如图像旋转、图像的水平、垂直轴对称转换等通常需要借助客户端软件如ps等才能完成,在前端也都能完成,因为这些操作本质只是改变图像的像素点排列而已。用图示很好解释:
原图:
向右旋转90度:
水平轴对称:
再总结一下彩色位图转换为黑色的几个步骤:
- 将image关联的图像文件绘制到一个canvas上
- 获取canvas上图像的imageData数据
- 将imageData数据的rgba部分均值计算后填充到你一个新数组里
- 将新数组填充到一个新的ImageData对象上
- 再将新的ImageData对象绘制回canvas元素
- canvas转换成file对象本地保存
最后,感谢阅读,如果对文中内容有任何的疑问欢迎留言讨论、不吝赐教;如果本文对你有帮助,麻烦点个赞,谢谢哦。