最近在研究html5 canvas的过程当中,发现,canvas为前端对图像的处理开辟了一条新的道路,canvas能够作到不少事情,甚至能够作个相似于PhotoShop的东西,曾经本人在一家软件工做就作相似的工做,能够看一下我以前开发的软件:html
这个就是canvas实现的相似于Adobe Photoshop,足以见得canvas的强大之处!前端
本文就是抽出其中一个小的功能点,来简单聊聊canvas强大之处:咱们来一步步实现一个简单的智能抠图功能:(具体的代码见个人github:monkeyWangs/Matting)html5
1.环境准备node
本人采用ES6语法做为开发环境,选用webpack做为构建工具,因而乎,咱们有了webpack.config.jswebpack
/** * @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.jsgit
2.代码实现es6
首选咱们须要新建一个对象:github
/** * @author monkeywang * Date: 17/3/30 */ class Matting { }
而后咱们须要接受用户上传的图片文件:web
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的无限可能,固然我也在逐步优化算法中,使得图片抠图更加完美,更加智能,也欢迎你们的star, pullrequest