终极表情包生成器 - 1

终极表情包生成器 - 1

缘起

说起斗图,自己也算身经百战了。初中贴吧方兴未艾之时,我也曾干过天天爬楼收图分类重命名的蠢事儿。现在想来,毫无意义且浪费了大把宝贵的时间。随着年龄增长,对斗图的兴趣变淡了斗不过小伙子了,因而也就慢慢退出了江湖。

然而,微信的兴起再次点燃了斗图魂!瞬息万变的局势,对手难以预测的招数,语义情感分析的烧脑与反攻裉节儿的酣畅淋漓,都让人欲罢不能。为了更好的斗图,在“表情君”等公众号还未出现前,自己就曾制作了个极为简陋的文字表情包生成器。大概是2014年,自从开始大量发文字表情包之后,各类表情公众号也如雨后春笋般接连生根发芽。也不知道我制作的表情是否在其中起到了推波助澜的作用……

grass 🤦‍

之后便是亚洲微笑三巨头和学友熊猫人统治的时代了。随着微信表情平台的开放,斗图的选择多了很多,最初的9GAG和千压绿图熊猫人的风格和质量差强人意,自然退居二线。加之同学多喜爱萌贱类表情,像神经蛙、颜团子、蛋黄猫等表情逐渐占据上风。自从微信可以直接搜索表情后,发自制表情的机会越来越少了。除非是真的在乎,想要摆开架势斗上一斗的好友,否则难得花费时间和精力去做表情了。就像最近正火的skr和diss track一般,无非是看得起才会绞尽脑汁相互调戏。15到17年做过的精品表情屈指可数,每一个表情背后都能讲出故事。

什么时候有了总结斗图生涯的想法呢?大概是换了电脑又不想再装UIead GIF Animator的时候产生的。GIF89a从诞生至今已逾20年,web2.0时代却依旧在依赖如此古拙的格式,实属特别。自从开始写一点代码以来,自己逐渐开始用视频取代GIF,一段h264/mp4或webm无论呈现效果、文件大小还是兼容性都优于古老的GIF,为何还要折腾自己呢?这样的想法愈发强烈,直到17年年底达到最盛:我要用一个终极表情包生成器,来为我的web1.0斗图岁月画上圆满句号。

起手

最初是想花几天时间集中编写,制作为一个作品的。但由于手腕摔断了一下子打乱了计划,便搁置了。直到再一次因为不想面对现实的压力和趣味性的缺乏,才捡起来这个想法。

最初是想开发C#的GUI程序,或是web based服务,可能会用到opencv,输出为可选的各种主流格式,添加apng支持等等。同时,当时看到了某些人脸识别企业搞出来的动态人脸熊猫人,感觉这种骚操作也应该模仿一下,同样加入了开发列表中。

最初设计

这里把最初的想法随便一贴,就当回顾啦。

第一步

将图片/视频文件拖拽到程序图标/主窗口上,获取文件路径

第二步1:若为图片,则自动识别图片格式

  • jpg文件转化为RGB矩阵
  • png转化为RGBA
  • tiff转化为RGBA
  • GIF转化为RGB

第二步2:若为视频,则调用相关解码器进行解码,并

  • 简单模式:输入图片、视频,20容差匹配全序列/第一帧中最大色块,输出默认gif图像
  • 黑白边框模式:先裁剪图片至最小非匹配色度边缘,然后使用非连续色度
  • gif去底色
  • 图像序列转表情包模式:输入图像序列(Windows默认遍历序列/按编号拖拽改变序列排序),转为默认gif动图
  • 高级转换模式:选取选定帧需替换的色度,替换为透明背景,并选择是否裁切至合适范围,转为自定义gif动图,可更改色阶和抖动以减小大小(输出文件大小估计:关键帧预渲染平均估计文件大小)
  • 高级转换模式2:涂抹选定中心可匹配区域,非匹配区域内的直接置空,匹配区域内使用简单模式
  • 文字表情包模式:输入文字序列,转化为默认黑体文字表情包gif静态图
  • 高级文字模式:卡通文字模板/可选字体/可选特效(视频/flash/gif素材),覆叠在文字图层上;文字运动特效 - 加入正弦波动,下落,浮现等类似ppt特效的预置选项,或按帧数的函数运动轨迹
  • 面部识别模式:停止你的性幻想

第三步子模块

色度键处理,快速选择背景色并去除

动态图加2px白边抑制抖动

第四步 保存文件

图片直接保存成gif透明度文件,后续可以选择GIF优化算法 视频保存成png然后FFmpeg合成为webm/webp/apng/gif/MP4 h264

其他

限制gif输出帧数/限制gif文件大小,后期加抖动和劣化为可发送的表情/768kb/限制图片尺寸:1000 * 1000最大/自动裁切至最小边框宽度/加文字/加动态文字/拖拽上传 实现/svg一键制作压缩功能/

实现

参考repo中的描述。基本是按照上面的想法目录实现的,只不过为了快点得到结果简化了一些步骤。得到的一些结果可以参考results和outputs中的图像。

关于各种动态图像格式

移动端图片格式调研

本想自己写一个动态图像对比的情况的,但看了上面的链接,自觉没有博主了解那么深入,索性贴上链接再补充一两句。

GIF89a

GIF的最大优势,在于需要极小文件体积的时候它真的能做到很小。譬如各种计算机算法的图形化展示——排序算法、涂色算法、二叉树、迷宫算法等等。它们的特点是仅需要少于8个颜色,甚至4个颜色即可说明问题,而动态本身的意义是体现变化趋势而非呈现图像效果。正如本repo中迷宫生成的子repo,GIF完全可以自己控制输出流从而达到极小的输出体积。由于GIF标准里没有冗长的元数据头部或是复杂的映射关系,在小尺寸动画中存在一定优势。同样地,APNG在这些方面也具有与GIF相似的优势。

而webp则更像是webm,或是h264/mp4的简化版。NBA动图、明星表情包、搞笑短视频才是它们的天下。实际上,我觉得动态图本该归为两个阵营:animated graph/diagram 和 short video clip.前者用静态图像压缩算法辅以各种像素映射关系的优化,后者用视频压缩算法辅以关键帧的优化。通用编码一般都会造成无法达到最优的质量体积比。

对于简笔画型的表情包而言,自然是偏向静态图片的一类编码格式更为适宜。当然,凭借谷歌的实力,未来图像格式究竟鹿死谁手还不好说。就算有一天GIF寿终正寝,在互联网的当下,大概我们仍旧看不到1995年的PNG格式走向终结了。

论熊猫人为何在web2.0时代凭借GIF成功

  • 颜色少:体积小,与GIF相辅相成

  • 对比强:抓人眼球,毋需思考

  • 客制化自由度高:甚至用画图都能随便撸出一个表达自己心情的表情

总结

有时候编程会沉浸在自己的想象中,充耳不闻天下事。像苹果,早已抛弃GIF转投APNG的怀抱,而腾讯阿里等则多管齐下webp,BGP,GIF一把抓。或许只有如我这样裹足不前的古董才会偏执地纠结于那个属于黄金时代的过往辉煌吧。

开发随记

以下为开发过程中简单记录内容

PyQt 安装与配置

  1. 打开PyCharm,设置,解释器,添加新的依赖库

    pip install pyqt5 pip install pyqt5-tools

其中第一个是PyQt的总库,第二个包含了对QtDesigner的配置。

  1. 在PyCharm中配置external tools以实现界面编辑,程序路径为

    Program: C:\Program Files\Python36\Lib\site-packages\pyqt5-tools\designer.exe Working Directory: $ProjectFileDir$

  2. 设置Qt .ui界面文件转换为.py文件的命令 PyUICon

    Program: C:\Program Files\Python36\python.exe Parameters: -m PyQt5.uic.pyuic $ProjectFileDir$$FileName$ -o $ProjectFileDir$$FileNameWithoutExtension$.py

    注:我的习惯是把文件全扔在项目文件夹的根目录下。可根据个人喜好自行定制上面的命令。

每次绘制界面并保存为.ui文件后手动执行PyUICon即可,并在.py文件中import对应界面

tqdm

一个轻量化的命令行进度条,可以插入运行较慢的程序

grayconnected/floodfill

def color_diff(rgba1, rgba2):
    if len(rgba1) == 3 and len(rgba2) == 3:
        return abs(rgba1[0] - rgba2[0]) + abs(rgba1[1] - rgba2[1]) + abs(rgba1[2] - rgba2[2])
    try:
        euc_length = abs(rgba1[0]-rgba2[0]) + abs(rgba1[1]-rgba2[1]) + abs(rgba1[2]-rgba2[2]) + abs(rgba1[3]-rgba2[3])
    except IndexError:
        return
    else:
        return euc_length

def flood_fill(image, xy, value, thresh=0):
    pixel = image.load()
    x, y = xy
    try:
        background = pixel[x, y]
        if color_diff(value, background) <= thresh:
            return  # seed point already has fill color
        pixel[x, y] = value
    except (ValueError, IndexError):
        return  # seed point outside image
    edge = {(x, y)}
    full_edge = set()
    while edge:
        new_edge = set()
        for (x, y) in edge:  # 4 adjacent method
            for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
                if (s,t) in full_edge:
                    continue
                try:
                    p = pixel[s, t]
                except IndexError:
                    pass
                else:
                    if color_diff(p, background) <= thresh:
                        pixel[s, t] = value
                        new_edge.add((s, t))
                        full_edge.add((s, t))
        edge = new_edge

实现以后发现效率还是有些低,或许是Python本身的限制吧。本想用Cython,但觉得编译的开销抵不上优化出的一点点时间,放弃了。

what’s in a GIF

http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp

https://www.jayxon.com/gif-apng-webp/

optimize transparency

默认支持256色。对于本身色彩数不到256的动态图,如果需要缩减颜色,则需要提供关于透明度下标的设置。

http://www.pythonclub.org/modules/pil/convert-png-gif

use single palette

Windows复制路径小贴士

  1. 按住 SHIFT + 鼠标右键,可以看到多了一项 “复制到路径” 选项。

  2. 只要点击 “复制到路径”,路径就复制到剪贴板中了, 然后就可以粘贴了。

Issues about PIL GIF dither RGB RGBA P and convert

每一帧用local palette,如果转为通用的palette,那么有的index原来对应有颜色,现在变成没有颜色了

三步走:

视频

palette相同的GIF

palette变化的GIF

人脸识别:CLM

人脸三维建模的难点:如何控制模型各个部位随关键点改变。

三维建模:Unity和特征点的映射

三维的缺失:噘嘴

输出成流媒体还是坐标点序列,后期渲染?

Animoji 的鼻子也能动……

除了面部识别还有物理模型……耳朵还能动……这个真的学不来……眼眶肌肉还能动……阿西吧不干了爱谁谁吧!

Chen Ting

Chen Ting

The page aimed to exhibit activities & achievements during Ting's undergraduate & graduate period. Meanwhile, other aspects such as lifestyles, literary work, travel notes, etc. would interweave in the narration.

Leave a Comment

Disqus might be GFW-ed. Excited!