事情是这样的,前几天闲来无事,手机上下载了个图片找茬游戏,本以为是小孩子玩玩的很简单,没想到开头几关就把我难住了,有些细节真是藏的很深呀,不仔细看真的找不到。于是乎我想,肉眼很难辨别的东西是不是可以通过代码帮我快速找到?前端有 canvas
可以用来处理图像,查了下 API
也可以读取像素点,感觉可行,说干就干!
canvas 读取像素点
我们知道,canvas
是可以拷贝图像的,通过 ctx.drawImage
,我们可以方便的将图片绘制在 canvas
里面,下面是例子
tsx
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const image = new Image();
image.src = 'xxx';
image.onload = ()=> {
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
}
但应该没多少童鞋知道,canvas
其实也可以读取像素点,通过 ctx.getImageData
我们可以方便的读取当前绘制图片的像素点数组。
tsx
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
然后我们会得到如下数组
tsx
// [255,254,255,255,...]
这里每4个值一组就代表了一个像素点,聪明的童鞋已经看出来了,没错,这4个值分别对应了 RGBA
,如下图所示 我们可以通过对比每一个像素点,来实现图片的差异化对比。
绘制差异化像素点
既然能读取,我们能不能修改里面的像素点呢?答案是可以的,通过ctx.putImageData
,我们可以方便的对现有图像的像素进行随意改造,比较常用的就是 滤镜,通过不同的滤镜算法,我们可以在网页上实现类似美图秀秀的功能,当然在我们这个需求里,也可以用来标记差异化的像素点。具体流程如下:
- 绘制需要对比的两张图片到
canvas
- 获取原图的像素点数组作为对照
- 遍历对比图的像素点,将和原图不同的部分用蓝色重新赋值
- 通过
ctx.putImageData
,重新生成新的图像信息 - 最后通过
canvas
的toDataURL
api 生成base64
图像
简单写了个 demo
,效果类似这样
我们可以很快发现,diff 图
里蓝色明显的地方即为差异点
源代码我放在 github 上了,有兴趣的童鞋自取~ 👉 image-diff-demo
抛砖引玉
经过这个小 demo
的实战,深入理解了 ctx.getImageData
和 ctx.putImageData
的用法,虽然原理差不多是这样子,但是还是有很多可以优化的地方,比如对比原图发现,很多没改动的地方也染上了一些蓝色,这个原因是因为两张图的相同部分的像素点也是不一致的,受限于图片压缩问题,很多颜色也是会有些许出入,虽然肉眼看不出来,但实际上从像素角度出发是不一样的,大家也可以想想怎么优化 diff 算法
可以让误差尽量减小😁
总结
image-diff
也不是新东西了,市面上也有不少成熟的产品了,开源社区耳熟能详的就是大名鼎鼎的 argos-ci,还有开源产品,比如 Resemble.js,都是比较好的 image-diff
库,大家可以按需使用,当然也可以自研,原理其实并不难。希望本篇文章对大家有帮助~