前言
近期模拟了一个百度AI——图片多主体识别的demo,使用了canvas实现这个功能。相信前端的同事对canvas都不算陌生,那我们就用canvas来看看这个功能是怎么实现的吧。
什么是canvas。
- canvas是html5的一个新标签,属于h5的新特性
- canvas标签是一个图形的容器,简单点说就是一块画布,你可以在上画矩形,圆形,三角形,折线等等,也可以用来画logo
- 它是通过javascript来画的,即脚本绘制图形
canvas可以用来做什么
- 制作web网页游戏(但是如果代码写的不咋的游戏可能会非常卡)
- 数据可视化(这么说你可能不明白,但我告诉你echarts就是基于canvas)
- 广告banner的动态效果非常适合用canvas制作
- canvas还可以用来内嵌一些网页
介绍完canvas之后,我们就开始模拟图片多主体识别的效果吧( 注意:本片文章重点不是demo实现的过程,重点是canvas的用法
)
期望效果:
首先我们先在节点上创建一个canvas的节点,再添加一个上传文件的节点。
<div class="canvasBox">
<canvas id="canvas" width="900" height="500"></canvas>
</div>
<div class="button">
<label for="file" class="file_load">本地上传</label>
<input id="file" onclick="getFiles()" type="file" style="display:none">
</div>
然后点击上传文件解析图片文件(注:因接口需要,需把图片文件转换成base64格式)
/**
* 获取上传的文件
*/
var imgUrl = '' // 定义图片链接
function getFiles() {
var file = document.getElementById('file');
var image = document.querySelector("img");
file.onchange = function (e) {
var fileData = this.files[0];//这是我们上传的文件
if (fileData) {
imgUrl = window.URL.createObjectURL(fileData)
getBase64(imgUrl, dataURL => {
const imgBase64 = encodeURIComponent(dataURL.split(',')[1])
postImgUrl(accessToken, imgBase64)
});
}
var reader = new FileReader();
reader.readAsDataURL(fileData);//异步读取文件内容,结果用data:url的字符串形式表示
}
}
var imgX = ''
var imgY = ''
// 文件转base64
function getBase64(imgUrl, callback) {
var canvasDom = document.getElementById('canvas')
var ctx = canvas.getContext("2d")
// ctx.clearRect()
let image = new Image();
// 解决跨域问题
image.setAttribute("crossOrigin", "anonymous");
let imageUrl = imgUrl;
image.src = imageUrl;
// image.onload为异步加载
// eslint-disable-next-line no-undef
image.onload = function () {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 1, 1, canvas.width, canvas.height);
// 这里的dataurl就是base64类型
// 使用toDataUrl将图片转换成jpeg的格式,不要把图片压缩成png,因为压缩成png后base64的字符串可能比不转换前的长!
let dataurl = canvas.toDataURL("image/jpeg", quality);
callback ? callback(dataurl) : null; //调用回调函数
};
}
base64转换成功后,就需要使用转换后的值作为参数请求接口获取数据(此处接口后端有做跨域调整,故使用时不可直接复制使用)
/**
* 上传图片获取图片识别数据
*/
var moduelUrl = ''
function postImgUrl(accessToken, imgBase64) {
moduelUrl = '/bdapi/rest/2.0/image-classify/v1/multi_object_detect'
const xhr = new XMLHttpRequest()
const testUrl = `${moduelUrl}?access_token="${accessToken}"`
xhr.open('POST', `${moduelUrl}?access_token="${accessToken}"`)
xhr.send(`image=${imgBase64}`);//发送请求 将情头体写在send中
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status === 200) {
const res = JSON.parse(xhr.responseText)
if(res){
showImg(imgUrl, res)
}
}
}
}
请求数据成功后就是渲染图片跟画图
/**
* canvas显示图片
*/
var imgMultiple = 0.5859375 // 按比例缩放图片,后续需要使用到
function showImg(imgUrl, data){
//获得canvas画布
var canvas = document.getElementById("canvas");
//得到画布的上下文
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, 1000, 600)
//创建一个image元素
let image = new Image();
//onload一下
image.onload = function (res) {
imgWigth = imgMultiple * image.width
imgHeight = imgMultiple * image.height
imgX = (900 - imgWigth) / 2
imgY = (500 - imgHeight) / 2
ctx.drawImage(image, imgX, imgY, imgWigth, imgHeight);
drawSquare(data)
}
//用src设置图片的地址
image.src = `${imgUrl}`;
}
/**
* canvas画正方形
*/
function drawSquare(data){
//获得canvas画布
var canvas = document.getElementById("canvas");
//得到画布的上下文
var cxt = canvas.getContext("2d");
cxt.beginPath()
var array
if (data.result) {
array = data.result
} else {
array = data.results
}
console.log('draw square', imgMultiple);
for (let i = 0; i < array.length; i++) {
const item = array[i];
const x = item.location.left * imgMultiple + imgX
const y = item.location.top * imgMultiple + imgY
const width = item.location.width * imgMultiple
const height = item.location.height * imgMultiple
// 设置字体
cxt.font = "12px 微软雅黑 bolder"
// 设置字体颜色
cxt.fillStyle = "#000"
cxt.textAlign = "left"
// 添加文字和位置
cxt.fillText(item.name, x, y - 6);
cxt.moveTo(x, y)
cxt.lineTo(x + width, y)
cxt.lineTo(x + width, y + height)
cxt.lineTo(x, y + height)
cxt.lineTo(x, y)
cxt.strokeStyle = 'red' // 设置或返回用于描边的颜色
// cxt.fillStyle = '#008833' // 设置或返回用于填充绘画的颜色
// cxt.globalCompositeOperation="destination-over"; // 绘制的图
cxt.stroke()
// cxt.salce(1 / imgMultiple, 1 / imgMultiple)
}
}
实际实现效果:
当然,前面一开始就已经说明,本次文章的重点不是如何实现图片多主体识别的效果,重点是讲解一下canvas的用法,想更深入了解canvas在图片上画图的朋友接着看下面的文章。