android 字符画,抖音上很火的字符画 Android 实现 | 视频转换实现

作者:Line_cut_feng

链接:https://www.jianshu.com/p/a14f1ac558e1

之前我推送过抖音上很火的字符画 Android 实现,当时文末:按说拿到ascii图后,想要把整个视频转换成ascii字符视频就很简单了。只要把视频逐帧抽成图片,图片转换后,再合成为视频播放出来,但我视频库用的不多,希望有能力的朋友可以帮助完成最后一步。

我群里的同学实现啦,看正文吧~

1、概述

前一阵看鸿洋日推,看到一个几年前就感觉有意思的一个技术,那就是图片转Ascii码,记得上大学时玩过windows的图片或视频转ascii码,可惜那个软件不好用,有bug,转视频的时候动不动就卡死,5分钟的视频,转码百分之7,8十的时候有一半概率卡死- -,总有意犹未尽的感觉。

去年的时候,自己从java移植过一种算法到android,大概思路如下:

首先固定字号,然后计算这个字号下绘制出一个字母需要的像素(长x宽),然后对于图片:取出同等大小的图片碎片,然后列出每一个备选的字母绘制出来以后的像素rgb值(一般是ascii码,当然也可以是汉字,不过肯定效果不好),计算每个替换字的rgb转灰色像素数组 相对 图片碎片像素数组的标准差(还有几个备选算法不记得了,这不是重点~),标准差最小的,作为图片碎片的替换字。

最后像国际象棋格子一样,一块一块的替换掉,由于计算相对比较复杂,所以耗时比较长,因此当时那个demo也让我搁置了。

最近看到这篇日推,不由得眼前一亮,因为很少有人在android端做这种东西,因为算法方案是一大堆,不过很少有感兴趣的人去移植到android- -,我就参考了这篇文章的方案,不由得赞叹这个方法的巧妙,避免了大量的计算,图片转化率大大提高了,可以看看效果图 :

a3c5119403de793a6c29db616079701b.png

哈哈哈,是不是很酷炫?

为了看清每一个字母,特意上传了一个大图(ps:抖音上竟然有人手动敲的ascii码,而且敲了几天,真是丧心病狂)。好了,下面进入正题~

1、图片转ascii

巧妇难为无米之炊,既然要图片/视频转化 ascii码,要有对应的媒体文件,选择一个图片,相信每一个android开发者都或多或少有个趁手的图片选择库,这里使用了 "com.github.LuckSiege.PictureSelector:picture_library:v2.2.3",持续更新的库,比较好用。

用法大概如下~

bb3779250f8e8f3fbf0b015073dba5a3.png

接着进行下一步操作,上代码:

7b94ee3652c6e0ad3b38b169e530beea.png

我来说下代码的意义~

首先会得到屏幕宽高,接着正规操作,对图片进行缩放,如果图片大小过大,就对图片进行缩放,最大是屏幕的1/7,接着就是for循环嵌套长宽,这里为什么y是y+=2呢?

因为ascii码一般都比较长吧~,按照android的标准来看ascii码绘制出来的效果比较长。

我们看for循环里面做了什么:

对拿到的每个像素点进行灰度转化,这里就用到图像学的知识了,为什么是0.229:0.578:0.114呢?

因为据研究(不是我研究的~),按照这样的配比rgb转化以后,人眼看到的是灰度图像。。。。。开个玩笑,这就是rgb转灰度的公式之一。

然后根据灰度值,在0到255之间的位置,来配对应的ascii码,这里 final String base = "#8XOHLTI)i=+;:,.";(字符串由复杂到简单) 所谓的简单到复杂其实想的不用那么复杂,就是相同体积内,绘制出这些字母,哪一个黑色像素更多,仅此而已。

直到遍历所有的像素点以后,拼成一个Stringbuffer,这里每次读取一个width的像素以后都要加上一个换行以区分一行。

接着放到一个text转bitmap的方法里:

162fe7c5d25aeabcf5f11a9805e756b6.png

这里用到了StaticLayout去绘制文字,textpaint 设置单间隔的文字,设置好参数以后,在canvas上绘制,通过bitmap初始化的canvas,其实也会反应在bitmap上。

(我一年前应该是没设置好这样的参数,所以当时画出来的ascii码图片,文字间隔比较大,当时就弃坑了)得到bitmap以后,可以显示在界面上了,也可以输出到文字里,对于图片转ascii码的步骤就到此为止了。

2、视频转ascii 码

其实视频可看做是一帧一帧的图片,那么接下来的思路就清晰了吧~

首先将视频抓帧,可以按照你设定好的每秒抓多少帧,这样得到一堆图像序列。

而这里得到视频帧用到了android原生的api,需要android5.0以上:MediaMetadataRetriever 这个类可以得到视频的时长,以及第多少毫秒的图片预览帧。

于是我先拿到视频的时长,比如10000毫秒,也就是10秒,那么接下来如果我每秒要取15张图片,那么就每(1000/15)毫秒取一张预览帧,直到10000毫秒为止,首先需要强调下,这个操作是十分耗时的,因此必须将这个操作放到线程里将这些图片保存到一个路径下,具体代码如下(MediaDecoder是对于MediaMetadataRetriever 稍微封装了一下)

877b86c68208f6ef90185e1db7258dee.png

b5711516942130cd6c8615d6a4376bd1.png

这里我直接保存的转换成ascii码图片之后的文件了,图片转ascii码的步骤见文章上半部分。

接下来就是最后一步了,将分割转换的图片再合成成视频,合成视频的方法我网上也找了很多,不过基本都是2个方式:

第一个就是javacodec这个库,可是这个库发现控制不了帧率,也就是说一个视频如果你转化成图片设置的fps比较少的话,比如fps=5,那么合成视频的时候,他会按照fps = 25默认的去合成视频,那么会出现的问题就是合成的视频的播放速度会是原先的5倍- -,当然也可以改这个库的源码,不过因为这个项目以后还有可能加其他的好玩的功能,于是选择了第二种方案。

第二种方案:用ffmpeg进行合成,ffmpeg是一个用c写的跨平台的视频处理库,里面包含了强大的,视频编解码,推流,加水印,滤镜等强大的功能,这也是我选择它的原因,由于编译ffmpeg也是个大坑,所以直接拿来了别人编好的移植过来了。

这里使用了ffmpeg库里ffmpeg.c的run方法去跑你拼接的命令,他也是通过java层传递过来一个数组,这个数组装有ffmpeg的要执行的命令,再传到jni里,在这里面变成一个char数组传递到ffmpeg的run方法,,jni文件如下:

d103ac9a69c9a2f2907c1ccecb8dfbe3.png

而java拼成ffmpeg的命令的方法如下:

a59af7b8ffd485dae148ff54b5f3b27c.png

简略的说下各种参数 -f是他规定的图片格式,-framerate就是帧率啦,fps就是一个int值,一般5到25都行,太少会影响视频的流畅,太多会导致视频播放过快,当然这个fps一定要和当时分割成图片的fps是一模一样的,当时分割的如果太细,会导致后来合成视频的文件过大,因为按照视觉残留原理,15fps就会看做是连续的画面了,无停顿感。

这里我默认选择5fps是因为200毫秒取一帧省时间,帧数少,一会转化视频耗时时间少啊。-i表示输入的媒体文件,一般是avi或mp4的视频.-b是码率,这个可以设置小一点,就是1秒的媒体所占的大小限制,-ss是开始的时间,-r是输出的帧率控制,这里是硬控制,这里我设置个大于framerate的数就行了,拼好命令以后,就可以传给ffmpeg进行合成了。

合成过程比较慢,因为一涉及到视频处理一般都会慢,静静等待执行完之后就行了,到对应目录上查看合成之后的文件。

效果图如下:

ab5cefaf091c8bfeac49810171090ee1.gif

3、不足与改进

这个demo的不足以及以后将会改进的地方:

视频分割成图片使用的是系统的api,并没有,相当于重复调用android native的接口,反复的创建,销毁资源,耗时比较多。过一阵将会改成使用ffmpeg来进行帧分解,我已经跑过单独的测试demo,效率是目前的10倍 - -。

以后会增加彩色ascii码的功能,现在是黑白的ascii码,其实在图片成ascii码图片之后,再增加一步就行了,和原先的图片进行相交处理,如果是黑色的,就取原先图片的彩色rgb,如果是白色的,就不做处理。

目前支持视频avi,mp4等常见格式转化成avi,mp4,gif。后续会支持gif转ascii 的gif或视频。

项目地址:https://github.com/GodFengShen/PicOrVideoToAscii

欢迎star,你的收藏是我更新的动力。

推荐阅读

抖音上很火的字符画 Android 实现

Android 沉浸式状态栏的实现

Android 图文混排富文本编辑器实现详解

OPPO Android 开发技术面总结

Python|面试官问你:连接字符串用join还是+,怎么回答?

免责申明:本栏目所发资料信息部分来自网络,仅供大家学习、交流。我们尊重原创作者和单位,支持正版。若本文侵犯了您的权益,请直接点击提交联系我们,立刻删除!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值