利用canvas的getImageData,我们可以获取到一张图片每一个像素的信息,而通过对每一个像素信息的对比,我们就可以找到需要消去的像素点。比如下面这一张图片,如果我们想要扣去白色部分(粉色是body的背景颜色)。

let canvas = document.querySelector('#canvas');

let context = canvas.getContext('2d');

let img = document.createElement('img');

img.src = './head2.png';

img.onload = function () {

canvas.height = img.height;

canvas.width = img.width;

context.drawImage(img, 0, 0);

cutout(canvas, [255, 255, 255], 0.2); // 对白色进行抠除,容差为0.2

}

function cutout(canvas, color, range = 0) {

let context = canvas.getContext('2d');

let imageInfo = context.getImageData(0, 0, canvas.width, canvas.height);

// pixiArr是一个数组,每四个数组元素代表一个像素点,这四个数组元素分别对应一个像素的r,g,b,a值。

let pixiArr = imageInfo.data;

for (let i = 0; i < pixiArr.length; i += 4) {

// 匹配到目标像素就将目标像素的alpha设为0

if (testColor([pixiArr[i], pixiArr[i + 1], pixiArr[i + 2]], color, range)) pixiArr[i + 3] = 0;

}

context.putImageData(imageInfo, 0, 0);

}

function testColor(current, target, range) {

for (let i = 0; i < 3; i++) {

if (!((1 - range) * target[i] <= current[i] && (1 + range) * target[i] >= current[i])) return false;

}

return true;

}

testColor(current, target, range) 方法三个参数分别为 待检测像素点的rgb数组 、 目标像素点的rgb数组 和 容差范围 ,这里的容差只是简单用r、g、b的值分别乘以(1 + range)和(1 - range)来计算并对比,不同的容差参数会得到不同的效果↓

range = 0.095

range = 0.1

range = 0.2

当然对于底色是标准的纯色的图片就不需要容差了。

边界处理

但是有时候我们希望有一个边界,让抠图操作不对边界内部的像素造成影响。比如上面的图片,我们希望不会对人物头像内部的像素造成影响。 如果我们一行一行来看,是不是只要在碰到不是边界像素的时候停止操作,就可以达到效果了呢?

我们对每一行分别进行扫描,定义一个左指针 left 指向这一行的第一个像素,定义一个右指针 right 指向这一行的最后一个像素,并用一个 leftF 标识左边是否碰到边界,一个 rightF 标识右边是否碰到边界,当没碰到边界时指针就一直向内收缩,直到两个指针都碰到边界或者左右指针重合就跳到下一行,直到所有行都扫描完毕。

function cutout(canvas, color, range = 0) {

let context = canvas.getContext('2d');

let imageInfo = context.getImageData(0, 0, canvas.width, canvas.height);

let pixiArr = imageInfo.data;

for (let row = 0; row < canvas.height; row++) {

let left = row * 4 * canvas.width; // 指向行首像素

let right = left + 4 * canvas.width - 1 - 3; // 指向行尾像素

let leftF = false; // 左指针是否碰到边界的标识

let rightF = false; // 右指针是否碰到边界的标识

while (!leftF || !rightF) { // 当左右指针都为true,即都碰到边界时结束

if (!leftF) {

if (testColor([pixiArr[left], pixiArr[left + 1], pixiArr[left + 2]], color, range)) {

pixiArr[left + 3] = 0; // 此像素的alpha设为0

left += 4; // 移到下一个像素

} else leftF = true; // 碰到边界

}

if (!rightF) {

if (testColor([pixiArr[right], pixiArr[right + 1], pixiArr[right + 2]], color, range)) {

pixiArr[right + 3] = 0;

right -= 4;

} else rightF = true;

}

if (left == right) { // 左右指针重合

leftF = true;

rightF = true;

};

}

}

context.putImageData(imageInfo, 0, 0);

}

虽然大概完成了我们的需求,但是看一下上面头发那为啥会多了一块白色

因为我们仅仅只进行了行扫描,当左指针碰到头发时就会停止扫描,但是头发弧度里面的就无法被扫描到了,我们还需要进行列扫描,改造一下上面的方法:

function cutout(canvas, color, range = 0) {

let context = canvas.getContext('2d');

let imageInfo = context.getImageData(0, 0, canvas.width, canvas.height);

let pixiArr = imageInfo.data;

for (let row = 0; row < canvas.height; row++) {

let left = row * 4 * canvas.width;

let right = left + 4 * canvas.width - 1 - 3;

let leftF = false;

let rightF = false;

while (!leftF || !rightF) {

if (!leftF) {

if (testColor([pixiArr[left], pixiArr[left + 1], pixiArr[left + 2]], color, range)) {

pixiArr[left + 3] = 0;

left += 4;

} else leftF = true;

}

if (!rightF) {

if (testColor([pixiArr[right], pixiArr[right + 1], pixiArr[right + 2]], color, range)) {

pixiArr[right + 3] = 0;

right -= 4;

} else rightF = true;

}

if (left == right) {

leftF = true;

rightF = true;

};

}

}

// 同理进行列扫描

for (let col = 0; col < canvas.width; col++) {

let top = col * 4; // 指向列头

let bottom = top + (canvas.height - 2) * canvas.width * 4 + canvas.width * 4; // 指向列尾

let topF = false;

let bottomF = false;

while (!topF || !bottomF) {

if (!topF) {

if (testColor([pixiArr[top], pixiArr[top + 1], pixiArr[top + 2]], color, range)) {

pixiArr[top + 3] = 0;

top += canvas.width * 4;

} else topF = true;

}

if (!bottomF) {

if (testColor([pixiArr[bottom], pixiArr[bottom + 1], pixiArr[bottom + 2]], color, range)) {

pixiArr[bottom + 3] = 0;

bottom -= canvas.width * 4;

} else bottomF = true;

}

if (top == bottom) {

topF = true;

bottomF = true;

};

}

}

context.putImageData(imageInfo, 0, 0);

}

至于top和bottom为啥是那样计算画个矩阵图大概就知道了。

处理后↓

其实还可以先将 pixiArr 包装为以一个像素点为单位的矩阵

[

[{r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}],

[{r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}]

[{r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}, {r: 255, g: 255, b: 255, a: 1}]

]

处理后计算像素下标也就会更简单,列扫描时直接先将这个矩阵旋转,再用行扫描处理一遍就行了。这样处理pixiArr也有利于进一步对算法进行优化。

上述方法虽然大概完成了抠图效果,但是这种简单处理还会有许多情况没有考虑到。

比如右边头发这里是行扫描和列扫描都无法触碰到的区域↓

下面的衣服也因为颜色和底色一样且没有边界在列扫描中被直接抹去了↓

最后

对于主体和底色区分度很大的图片来说,最开始的那种方法就已经够用了。这篇中我采用的容差和边界处理算法的优化空间还很大,但是它们是非常容易实现与理解的,这篇权当做一个引子,各位完全可以根据自己的能力继续去实现边界与容差算法。

总结

以上所述是小编给大家介绍的html5利用canvas实现颜色容差抠图功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

html5 自动扣图,html5利用canvas实现颜色容差抠图功能相关推荐

  1. html5 自动扣图,利用canvas实现一个抠图小工具

    本文作者:IMWeb 孙世吉 未经同意,禁止转载 利用canvas实现一个抠图小工具 0 前言 作为新一代的前端开发工程师,PS抠图切图已经不是必备技能了,我们有UI/交互/视觉等更专业的设计同学帮我 ...

  2. html5 自动扣图,js+html5 canvas实现ps钢笔抠图

    html5 canvas+js实现ps钢笔抠图 1. 项目要求需要用js实现photoshop中钢笔抠图功能,就用了近三四天的时间去解决它,最终还是基本上把他实现了. 做的过程中走了不少弯路,最终一同 ...

  3. html5 自动扣图,canvas像素点操作之视频绿幕抠图

    本文介绍了canvas像素点操作之视频绿幕抠图,分享给大家,具体如下: 用法: context.putimagedata(imgdata, x, y, dx, dy, dwidth, dheight) ...

  4. html5 自动扣图,5 秒实现自动抠图?见过 remove.bg 这款神器

    雷锋网(公众号:雷锋网) AI 科技评论按:是否为了简单的抠图功能,还在苦苦修炼 Photoshop 大法?即使修炼成功了,是否觉得在抠图这件事情上花费的时间依然太多?如今一个名叫 remove.bg ...

  5. html5 自动扣图,Remove.bg – 只需5秒!一键自动抠图移除背景工具 人工智能代替PhotoShop...

    无论是专业的设计师.摄影师还是普通办公者,可能都经历过用 PS 抠图去除背景的苦难日子吧.简单来说,抠图就是将照片的主体人或物品从图片中抠出来,以便贴到别处使用. 然而抠图虽然是 PhotoShop ...

  6. html5 自动生成迷宫,HTML5 Canvas随机迷宫生成动画

    JavaScript 语言: JaveScriptBabelCoffeeScript 确定 class Line { constructor(x, y, a, c) { this.x1 = x; th ...

  7. html5随机圆不重叠,利用canvas在一个盒子里画不重叠的圆,

    1. 前言 使用canvas在一个矩形内画不重复的圆,解题思路. 2. html代码 Document * { margin: 0; padding: 0; background: #efefef; ...

  8. HTML5自动生成相框,HTML5拖放API实现自动生成相框功能

    实现功能: 将桌面图片拖入指定地方,生成相框和相关信息. 相框需要自己配置,设置为背景,在CSS中设置. 效果如图: html部分: H5拖放API之图片相框效果 > 请将图片拖放至此处 CSS ...

  9. html5极地区域图,HTML5 + JS极地散点图

    任何人都可以推荐现有的HTMl5/JS数据可视化工具包或lib,可以帮助生成类似于此的极性散点图吗?代码示例将很酷!HTML5 + JS极地散点图 不幸的是,我必须做出对平板电脑这个工作来看待数据的实 ...

最新文章

  1. 移动端开发框架Zepto.js
  2. 以短带长进军网综,西瓜视频能否干过“优爱腾”?
  3. 2021-07-09
  4. 发现一个好的索引-阳神
  5. 从css样式表中抽取元素尺寸
  6. Opencv3编程入门学习笔记(五)之通道分离(split)与合并(merge)
  7. 自动以及手动清除手机垃圾文件
  8. 标准I/O小程序-文件拷贝
  9. LwIP应用开发笔记之九:LwIP无操作系统TELNET服务器
  10. CRUD全栈式编程架构之控制器的设计
  11. RDS还原数据库时报错:ERROR 1227 (42000) at line 78664
  12. TableViewCell,TableView,UITableViewCell
  13. python静态方法的作用_@staticmethod和@classmethod如何作用于python中的...
  14. 网络安全工程师必备浏览器插件
  15. linux shell脚本开发工具,技术|10个工具让你的 shell 脚本更强大
  16. 关于appium环境搭建
  17. Spring 整合Hibernate 开发实例
  18. 期权的定义与BSM定价
  19. 阿里P8级大佬详解并发编程里的设计模式之Guarded Suspension
  20. 期权学习之常见收益结构

热门文章

  1. Git Bash命令行使用Git
  2. freeswitch hangup hook lua脚本处理
  3. 基于Pytorch的强化学习(DQN)之 REINFORCE with baseline
  4. Linux配置ssh远程连接服务
  5. macd金叉不涨又死叉准确率_MACD金叉周线选股公式怎么设置及计算
  6. 只能替换有源晶振 时钟发生器_有源晶振选型与替换原则
  7. MOSFET的SOA或者ASO是什么?
  8. Machine Learning With Spark--读书笔记
  9. 读取佳能单反相机快门次数的方法
  10. sql 纵向求和_SQL里边的求和语句怎么写