对标题不是很理解?先看效果(请在 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 标签创建一个画布。

这两个标签跟其他标签一样,可以通过 document.getElementById 获取到当前元素,对于 canvas 元素,我们的操作都是在它的 context 上进行操作,它的 context 可以通过 getContext(‘2d’) 进行获取。

然后在 context 提供了各种各样的方法,可以画线,圆,矩形,图片,文字等等,关于canvas的更多知识点,这里不一一讲解,我们只讲几个必须用到的。

1. context.clearRect(x, y, width, height);   用于清除画布,类似于用黑板擦擦黑板,x,y 表示起点坐标,width 和 height 为宽和高,表示擦除的区域大小。如果只是想画一张图在画布上,用不到这个方法。
 
2. context.drawImage(img, x, y, width, height); 把一张图画在画布的某个区域,img 是图片对象,其他几个参数跟上面的意思一样。
 
3. context.getImageData(0, 0, width, height);获取画布上的图片信息,参数意思跟1相同,获取到的是一个对象,对象的结构是这个样子 {data:[222,122,133,1,…], width:240, height:360 },width 和 height 就不用说了,data 是一个很长的数组,包含了所有点的像素值,我们看的时候,从0 开始,每四个一组,每一组代表一个点,从前往后分别为当前像素的r,g,b,a。
 
说完了API,再说一下图片转字符来呈现的原理。我相信大家都看过那种标题的电子屏,类似下面这样:
 
 
我们可以很清楚的看见,这些字其实不是一笔一划的写出来的,而是由一个个亮色的点组成的。电子屏上除了亮色的点,还有暗色的点,那它是怎么知道哪些点该亮,哪些点不该亮的呢。其实我们可以这样想,我们先拿一张和电子屏大小一样的纸,在纸上同样的位置写上同样大小的字(我们以ps两个字为例说明)
 
 
然后把我们的纸横向和纵向都均匀的分成若干份,每一份看做一个“点”,
 
 
然后用一个二维数组表示平面上的所有点,以上图为例,假如这个点有红色,我们就让它的值为true,没有红色就为false,然后电子屏读取我们的数组,遇到是true的就亮,否则不亮,自然就有了上面的那种点组成字的效果。

 

有了上面这些理论知识后,就开始实践吧。我们以一个视频为演示讲解,图片和视频唯一的区别在于,图片只渲染一帧,而视频要一直渲染,每个一定时间就重新渲染一帧。

第一步,简单创建一个 html ,内容大概如下:

video 用来在页面插入视频,div[id=”TO”]元素用来放我们转换后的文字,canvas用来画图,我们这里是画视频的某一帧。

注意,我们的html里面定义了字体为font-family: simsun; 用这个字体的原因是为了保证每个字母的宽度和高度一样,假如你用微软雅黑,由于有些字体的宽度不一致,导致最后生成的字母不能对齐,不能达到理想的效果。比如,如果我们把字体改成微软雅黑,一个 “.” 和一个符号 “#” 所占的宽度是不一样的,这样就会造成同样多的字符显示出来的长短却不是一样的,根本没法对齐。

 

然后,我们写两个方法,一个方法用于根据 rgb 生成 灰度值,另一个方法用于根据灰度值生成对应的字符。

对于第二个方法,值的区间和在某个区间要用什么字母由你自己决定,你也可以根据自己的视频来适当的划分更多的区间,但按常理来说,灰度值越小,代表我们图片转成黑白色之后颜色越黑,所以建议用像素比较多一点的字母,比如“#”,反之就用少一点的,比如“.”。

 

然后,我们再来实现一个把 canvas 上的一张图片转成字符的方法:

 

最关键的几步都已经搞定了,就剩动画部分了,来一个  requestAnimationFrame 即可,最后,附上完整的js代码:

 

直接把 js 嵌入 html 即可。完整代码见这里:https://github.com/lilaobiao/video-to-text

效果请直接看这里(请在 PC 端看效果):http://www.liguixing.com/lgxpc/video-to-text.html