对标题不是很理解?先看效果(请在 PC 端看效果),地址:http://www.liguixing.com/lgxpc/video-to-text.html
之前在抖音上看到有人发的视频,是一个极乐净土的舞蹈视频,被做成了一堆字母在跳舞的效果,感觉新鲜。因为自己有一些 PS 和 canvas 的基础,大概明白其中的实现原理,所以打算自己也尝试一下,果然如我所想,能行得通。先看一个实现后的效果图吧(图片为例,右边是canvas,左边是字符化后的图片)。
首先,我们先讲一下这个的实现原理。
在我们的Ps中,有一个效果叫”去色”,它的功能就是把我们的图片从彩色的变成黑白的。就拿上面的图片举例,”去色”后的效果是这样的:
你可能会问,这个去色有什么用呢?因为我们是代码实现,不可能用到PS,但我们得明白去色的原理。我们应该都知道,每一个颜色都是由三原色(红绿蓝)混合而成的,对应到我们前端的代码就是R(red)G(green)B(blue)。去色就是把我们图片的每一个像素的颜色rgb经过重新计算,得到一个新的值而做到的。那我们如何通过代码实现去色呢,这得益于我们的canvas,在canvas中,我们可以绘制一张图片,也可以从绘制好的图片中进行rgb读取,有了这两个功能,去色就搞定了。然后我们把图片看成一个个像素的组成,每一个点读取它的rgb,计算出灰度值,根据灰度值的大小,我们用不同的字母代替,就得到了我们的效果图。
接下来,在我们实现之前,先讲一下需要用到的几个API。在html5中,我们可以通过video标签引入一个视频,用 canvas 标签创建一个画布。
1 2 3 |
<video src="./123.mp4" controls id="video" width="200"></video> <canvas id="c1" width="360" height="640"></canvas> |
这两个标签跟其他标签一样,可以通过 document.getElementById 获取到当前元素,对于 canvas 元素,我们的操作都是在它的 context 上进行操作,它的 context 可以通过 getContext(‘2d’) 进行获取。
1 2 |
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); |
然后在 context 提供了各种各样的方法,可以画线,圆,矩形,图片,文字等等,关于canvas的更多知识点,这里不一一讲解,我们只讲几个必须用到的。
有了上面这些理论知识后,就开始实践吧。我们以一个视频为演示讲解,图片和视频唯一的区别在于,图片只渲染一帧,而视频要一直渲染,每个一定时间就重新渲染一帧。
第一步,简单创建一个 html ,内容大概如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>视频转为字符串</title> <style> *{margin:0;padding:0;box-sizing: border-box;} body {font-size: 12px; margin: 10px; font-family: simsun; background: #fff;} p { height: 12px;margin:0;padding:0;} .text{ width:360px;height:640px;float:left; font-size:12px; } .text p{ line-height:12px; color:#000; } .clearfix{zoom:1} .clearfix::after{content:'';display:block;width:0;height:0;visibility:hidden;clear: both;} </style> </head> <body> <div class="clearfix"> <div style="float:left;width:240px;"> <video src="./123.mp4" controls id="video" width="200"></video> </div> <div class="text" id="TO"></div> <div style="width:360px;height:640px;float:left;"> <canvas id="c1" width="360" height="640"></canvas> </div> </div> </body> </html> |
video 用来在页面插入视频,div[id=”TO”]元素用来放我们转换后的文字,canvas用来画图,我们这里是画视频的某一帧。
注意,我们的html里面定义了字体为font-family: simsun; 用这个字体的原因是为了保证每个字母的宽度和高度一样,假如你用微软雅黑,由于有些字体的宽度不一致,导致最后生成的字母不能对齐,不能达到理想的效果。比如,如果我们把字体改成微软雅黑,一个 “.” 和一个符号 “#” 所占的宽度是不一样的,这样就会造成同样多的字符显示出来的长短却不是一样的,根本没法对齐。
然后,我们写两个方法,一个方法用于根据 rgb 生成 灰度值,另一个方法用于根据灰度值生成对应的字符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// 根据rgb值计算灰度 function getGray(r, g, b) { return 0.299 * r + 0.578 * g + 0.114 * b; // 这里的几个参数你可以适当进行修改 } // 根据灰度生成相应字符 function toText(g) { if (g <= 30) { return '#'; } else if (g > 30 && g <= 60) { return '%'; } else if (g > 60 && g <= 90) { return '&'; } else if (g > 90 && g <= 90) { return 'm'; } else if (g > 120 && g <= 150) { return '.';//h } else if (g > 150 && g <= 180) { return '.';//n } else if (g > 180 && g <= 210) { return '.';//! } else if (g > 210 && g <= 240) { return '.';//: } else { return '.'; } } |
对于第二个方法,值的区间和在某个区间要用什么字母由你自己决定,你也可以根据自己的视频来适当的划分更多的区间,但按常理来说,灰度值越小,代表我们图片转成黑白色之后颜色越黑,所以建议用像素比较多一点的字母,比如“#”,反之就用少一点的,比如“.”。
然后,我们再来实现一个把 canvas 上的一张图片转成字符的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// 转换 var toDiv = document.getElementById('TO'); function init(ctx,width,height) { var imgData = ctx.getImageData(0, 0, width, height); // console.log(imgData) var imgDataArr = imgData.data; var imgDataWidth = imgData.width; var imgDataHeight = imgData.height; var html = ''; // 这里的 h 和 w 就相当于一个点的高和宽 // 竖向上每 12 个像素取一点,正好和我们的字体大小对应 for (h = 0; h < imgDataHeight; h += 12) { // h 每增加 12,就是新的一行开始 var p = '<p>'; // 横向上每 6 个像素取一点 for (w = 0; w < imgDataWidth; w += 6) { // 计算出点的索引起始位置 var index = (w + imgDataWidth * h) * 4; var r = imgDataArr[index + 0]; // r 值 var g = imgDataArr[index + 1]; // g 值 var b = imgDataArr[index + 2]; // b 值 var gray = getGray(r, g, b); p += toText(gray); } p += '</p>'; html += p; } toDiv.innerHTML = html; } |
最关键的几步都已经搞定了,就剩动画部分了,来一个 requestAnimationFrame 即可,最后,附上完整的js代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
// 根据灰度生成相应字符 function toText(g) { if (g <= 30) { return '#'; } else if (g > 30 && g <= 60) { return '%'; } else if (g > 60 && g <= 90) { return '&'; } else if (g > 90 && g <= 90) { return 'm'; } else if (g > 120 && g <= 150) { return '.';//h } else if (g > 150 && g <= 180) { return '.';//n } else if (g > 180 && g <= 210) { return '.';//! } else if (g > 210 && g <= 240) { return '.';//: } else { return '.'; } } // 根据rgb值计算灰度 function getGray(r, g, b) { return 0.299 * r + 0.578 * g + 0.114 * b; } // 转换 var toDiv = document.getElementById('TO'); function init(ctx,width,height) { var imgData = ctx.getImageData(0, 0, width, height); // console.log(imgData) var imgDataArr = imgData.data; var imgDataWidth = imgData.width; var imgDataHeight = imgData.height; var html = ''; for (h = 0; h < imgDataHeight; h += 12) { var p = '<p>'; for (w = 0; w < imgDataWidth; w += 6) { var index = (w + imgDataWidth * h) * 4; var r = imgDataArr[index + 0]; var g = imgDataArr[index + 1]; var b = imgDataArr[index + 2]; var gray = getGray(r, g, b); p += toText(gray); } p += '</p>'; html += p; } toDiv.innerHTML = html; } var canvasDrawVideo = { onLoad: function (canvas) { this.canvas = document.getElementById(canvas); this.video = document.getElementById('video'); this.ctx = this.canvas.getContext('2d'); this.render(); this.video.play(); }, render: function() { function renderWord() { // 这个地方要注意,每次绘画之前一定要清除上一帧的内容 this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.ctx.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height); init(this.ctx,this.canvas.width, this.canvas.height); // 该方法每秒会重复执行60次 requestAnimationFrame(renderWord.bind(this)); } setTimeout(renderWord.bind(this), 0); } } window.onload = function(){ canvasDrawVideo.onLoad('c1'); } |
直接把 js 嵌入 html 即可。完整代码见这里:https://github.com/lilaobiao/video-to-text
效果请直接看这里(请在 PC 端看效果):http://www.liguixing.com/lgxpc/video-to-text.html
发表评论