资讯专栏INFORMATION COLUMN

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

Cheng_Gang / 3055人阅读

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

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

这个就是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




  
  Title


 
 




然后我们选择一个纯色背景图

抠图后,我们看看:

确实完成了抠图。

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82287.html

相关文章

  • 【小案例】基于色键技术的纯客户端实时蒙版弹幕

    摘要:组件提供了一系列的操作接口以方便用户对弹幕的相关特性进行定制。对于这种类型的图像,我们可以使用色键的方式进行抠图生成蒙版。其中,用于更新蒙版的接口为。 导读:本文内容是笔者最近实现的 web 端弹幕组件—— Barrage UI 的一个延伸。在阅读本文的实例和相关代码之前,不妨先浏览项目文档,对组件的使用方式和相关接口进行了解。 各位童鞋如果经常上 B 站(bilibili.com) ...

    muzhuyu 评论0 收藏0
  • canvas 绘制双线技巧

    摘要:只有源图像外的目标图像部分会被显示,源图像是透明的。绘制了线路的图像是目标图像,线路是源图像。 楔子 最近一个项目,需要绘制双线的效果,双线效果表示的是轨道(类似铁轨之类的),如下图所示: 负责这块功能开发的小伙,姑且称之为L吧,最开始是通过数学计算的方式来实现这种双线,也就是在原来的路径的基础上,计算出两条路径。但是这个过程的计算算挺复杂,而是最终实现的效果很耗性能,性能损耗估计主要...

    王军 评论0 收藏0
  • canvas之人脸美白

    摘要:下面就讲解一下,移动端上传照片,旋转,抠图以及图片美白效果原理。 下面就讲解一下,移动端上传照片,旋转,抠图,以及图片美白效果原理。 一、上传照片 下面是两种上传照片的方法 1、此方法被废弃,希望能给大家一点提示,和思考的空间 a、通过改变file的值获取图片路径,并把路径添加到img元素中,在页面中展示 b、图片上传,可以用form表单上传,但是获取不到返回值,可以用ajaxfil...

    lbool 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<