canvas简单实现纯色背景图片抠图

算法聆风人
• 阅读 15376

最近在研究html5 canvas的过程中,发现,canvas为前端对图像的处理开辟了一条新的道路,canvas可以做到很多事情,甚至可以做个类似于PhotoShop的东西,曾经本人在一家软件工作就做类似的工作,可以看一下我之前开发的软件:
canvas简单实现纯色背景图片抠图

这个就是canvas实现的类似于Adobe Photoshop,足以见得canvas的强大之处!

本文就是抽出其中一个小的功能点,来简单聊聊canvas强大之处:我们来一步步实现一个简单的智能抠图功能:(具体的代码见我的github:monkeyWangs/Matting)

1.环境准备

本人采用ES6语法作为开发环境,选用webpack作为构建工具,于是乎,我们有了webpack.config.js

/**
 * @author monkeyWang
 *
 */

/* 引入操作路径模块和webpack */
var path = require('path');
var webpack = require('webpack');

module.exports = {
  /* 输入文件 */
  entry: './src/index.js',
  output: {
    /* 输出目录,没有则新建 */
    path: path.resolve(__dirname, './dist'),
    /* 静态目录,可以直接从这里取文件 */
    publicPath: '/dist/',
    /* 文件名 */
    filename: 'matting.js'
  },
  module: {
    rules: [
      /* 用babel来解析js文件并把es6的语法转换成浏览器认识的语法 */
      {
        test: /\.js$/,
        loader: 'babel-loader',
        /* 排除模块安装目录的文件 */
        exclude: /node_modules/
      }
    ]
  }
}

这样变准备好了开发用的基本环境,接下来我们来实现具体的核心代码,为了方便起见,我的代码全写在了inde.js

2.代码实现

首选我们需要新建一个对象:

/**
 * @author monkeywang
 * Date: 17/3/30
 */
class Matting {

}

然后我们需要接受用户上传的图片文件:

class Matting {
/**
   * 构造函数
   * @param file
   */
  constructor(file) {
    this.file = file
  }
}

再接着把图片文件转成base64格式,所以我们在类中建了一个createStream方法:

createStream() {
let reader = new FileReader()
let ext = this.file.name.substring(this.file.name.lastIndexOf(".") + 1).toLowerCase()
if (ext != 'png' && ext != 'jpg' && ext != 'jpeg') {
alert("图片的格式必须为png或者jpg或者jpeg格式!")
return
  }
reader.onload = (e) => {
let src = e.target.result
    let img = new Image()
img.src = src
    let w = img.width
    let h = img.height
    this.fitch(w, h, img)
  }
reader.readAsDataURL(this.file)
}

然后,开始我们的抠图逻辑代码之前,先描述一下我的思想:颜色属性其实是由RGBA四个元素组成的,RGB,代表三基色,A代表透明度,当A的值为0,则表示这个颜色是纯透明的,所以我们的主要逻辑就是把背景色设置为透明就好了。

创建一个canvas画布,然后把图片放到这个画布中,接着取这个图片上下左右四个点的像素,接下来要扣除这个图片的背景,那么,就需要去对整个图片的像素点颜色和背景色之前的区别,如果颜色相同,则把这个颜色的透明度设置成0

fitch(width, height, img) {
    let dataUrl
    let c = document.createElement("canvas")
    c.width = width
    c.height = height
    let ctx = c.getContext("2d")
    ctx.drawImage(img, 0, 0)
    /**
     * 取图片四个脚边的像素点rgba
     * @type {*}
     */
    let tl = Array.prototype.slice.call(ctx.getImageData(0, 0, 1, 1).data).join(',')
    let tr = Array.prototype.slice.call(ctx.getImageData(width - 1, 0, 1, 1).data).join(',')
    let br = Array.prototype.slice.call(ctx.getImageData(width - 1, height - 1, 1, 1).data).join(',')
    let bl = Array.prototype.slice.call(ctx.getImageData(0, height - 1, 1, 1).data).join(',')
    let imgdata = [tl, tr, bl, br] // 四个取色点
    let selfImageData = [] // 当前rgba
    imgdata.sort()
    // 目前只支持纯色背景抠图,简单的判断是否为纯色
    let deferNum = this.unique(imgdata).length
    if (deferNum <= 1) {
      {
        selfImageData = imgdata[1].split(",") // 设置要扣除的主题色
        let isPNG = true // 判断是否已经扣过
        let imgDataUrl = ctx.getImageData(0, 0, width, height) //获取像素点
        let data = imgDataUrl.data
        for (let i = 0; i < data.length; i += 4) {
          // 得到 RGBA 通道的值
          let r = data[i]
          let g = data[i + 1]
          let b = data[i + 2]

          /**
           * function 判断颜色是不是属于背景色
           * @param numerical
           * @param index
           * @returns {boolean}
           */
          let isIn = (numerical, index) => {
            if (selfImageData[3] == 0) {
              isPNG = false
              return false
            }
            return numerical > parseInt(selfImageData[index]) && numerical < parseInt(selfImageData[index])// 去掉边缘色
          }

          if ([r, g, b].every(isIn)) {
            data[i + 3] = 0 // 设置背景透明
          }
        }
        // 将修改后的代码复制回画布中
        ctx.putImageData(imgDataUrl, 0, 0)
        dataUrl = c.toDataURL("image/png")
        if (isPNG) {
          /**
           * 创建下载链接 进行图片下载
           * @type {Element}
           */
          let a = document.createElement('a')
          a.href = dataUrl //下载图片
          a.download = '未命名.png'
          a.click()
        }
        else {
          alert('背景已抠除!')
        }
      }
    }
    else {
      alert('只支持纯色背景抠图!')
    }

  }

然后我们测试一下效果:创建index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
 <input type="file" id="file">
 <button onclick="matting()">开始抠图</button>
</body>
<script src="./dist/matting.js"></script>
<script>
  function matting() {
    var file = document.getElementById('file').files[0];
    var mat = new Matting(file);
    mat.createStream();
  }
</script>
</html>

然后我们选择一个纯色背景图
canvas简单实现纯色背景图片抠图

抠图后,我们看看:

canvas简单实现纯色背景图片抠图

确实完成了抠图。

当然实现算法还不是很完善,主要是为了给大家展示canvas的无限可能,当然我也在逐步优化算法中,使得图片抠图更加完美,更加智能,也欢迎大家的star, pullrequest

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Karen110 Karen110
4年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Python进阶者 Python进阶者
4年前
手把手教你使用CanvasAPI打造一款拼图游戏
一、canvas简介1.canvas是HTML5提供的一种新标签,双标签;2.HTML5 canvas标签元素用于图形的绘制,通过脚本(通常是JavaScript)来完成;3.canvas标签只是图形容器,必须使用脚本来绘制图形;Canvas是一个矩形区域的画布,可以用JavaScript在上面绘画;二、案例目标我们今天的目标是使用HTML5
Python进阶者 Python进阶者
3年前
Canvas之鼠标滑动特效
大家好,我是皮皮。我们会看到很多网页的粒子特效;如上图所示,这些都是借助HTML新特性,使用新增标签Canvas得到的效果;那么我们来了解下canvas。什么是Canvas在MDN中是这样定义  的: 是HTML5新增的元素,可用于通过使用JavaScript中的脚本来绘制图形。例如,它可以用于绘制图形、制作照片、创建动画,甚至可以进行实时
Stella981 Stella981
4年前
Canvas绘制不规则图形,实现可拖动,编辑
目前的工作在做在线的标注工具,接触canvas一年了,各种绘制,基本上图像的交互canvas都可以完成,也写了几篇关于canvas的文章,遇到的问题也写博客上了,对于canvas有问题的朋友可以去看看。一直想写一个关于canvas系列的东西,也没时间。正好最近再捣鼓canvas,有时间就写一点,一个功能一个功能的写,争取写一个系列。以前都是绘制矩形,
Stella981 Stella981
4年前
H5中canvas和svg绘图方式介绍
在HTML5中包括了两种绘图方式,canvas和svg(矢量呈现),而与canvas不同的是,svg是一种XML标记语言,它既可以单独保存以“.svg”为后缀的文件在浏览器中打开显示,也支持建立svg标签直接嵌入在网页中显示,还可以通过<embedsrc"文件.svg"name"name自命"type"image/svgxml"height
Stella981 Stella981
4年前
HTML5 & CSS3 初学者指南(4) – Canvas使用
介绍传统的HTML主要用于文本的创建,可以通过<img标签插入图像,动画的实现则需要第三方插件。在这方面,传统的HTML极其缺乏满足现代网页多媒体需求的能力。HTML5的到来,带来了新的成员<canvas标签。什么是Canvas?HTML5的Canvas 元素使用JavaScript在网页上绘制图像。
Wesley13 Wesley13
4年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这