IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python生成截图选餐GIF动画 -> 正文阅读

[Python知识库]Python生成截图选餐GIF动画

大家好,我是小小明。

之前群里有小伙伴问今天中午该吃什么,然后另一位小伙伴发了一张下面的动图:

截图吃饭

我个人觉得还挺有意思的,截图还真像抽奖一样随机选一个菜名。考虑到这张动图中的菜名候选并不见得都是我们能够吃的菜。我们可以用python根据菜名列表生成这样的动图玩玩。

之前还看到什么截图选头像之类的动图,那类通过图片生成的动图都比较简单,通过文中提到的Imagine的动画作坊工具就可以做。所以本文只演示如何生成文字动图。

python生成文字动图

下面我们一步步来完成这个操作:

下载表情图片到本地

为了分析这种表情图片,第一步需要先下载下来,但是对于微信的表情动图,经过测试还真没法直接下载下来。

虽然通过文件监控工具分析出,gif表情动图存储位置在C:\Users\ASUS\Documents\WeChat Files\你的微信ID\FileStorage\CustomEmotion\xx\xxxx位置,但是却无法用图片工具查看。用winhex分析二进制得到了V1MMWX这样的文件头,说明微信对表情都进行了一定程度的加密。虽然可以解密,但这样大动干戈未免过于麻烦。

后面终于想到了一个简单的方案,那就是把向你有权限登录后台的公众号发送这个表情,再去公众号后台下载:

image-20210726163537948

微信发送的动图都是存储为自己特有V1MMWX加密格式,可能是为了使用自己独创的压缩算法有更大的压缩比吧。那说明我们想直接看本地微信存储的gif动图,只能自行开发专门针对这种微信格式的解码器了。

分析动图

下面我使用小工具Imagine,并使用动画作坊打开:

image-20210726163518497

可以看到这张动图由22张文字图片组成,帧切换时间为20毫秒。

生成单张图片

分析完成我们考虑用PIL库来生成单张图片,如果还没有安装该库的童鞋,使用以下命令安装该库:

pip install pillow

下面选择了用蓝底做背景。我们先来绘制中间的菜名文字:

from PIL import Image, ImageFont, ImageDraw


text = "珍珠土豆焖牛腩"
size = 320
fontsize = (size-20)//len(text)
im = Image.new(mode='RGB', size=(size, size), color="lightblue")

draw = ImageDraw.Draw(im=im)
draw.text(xy=(10, (size-fontsize*1.5)/2),
          text=text, fill=0,
          font=ImageFont.truetype('msyh.ttc', size=fontsize))
im

image-20210726172326328

由于菜品的名字文字个数不一致,为了都能填满整图,作了自动文字大小调整处理。

字体我选择了微软雅黑,当然微软雅黑也有三种子字体,可以通过系统字体安装目录查看字体文件的属性从而知道字体对应的文件名:

image-20210726164518133

下方带阴影的的文字生成起来会麻烦一些,我的思路是先绘制纯黑的文字,在绘制带黑色边缘白色填充的文字向上偏移几个单位:

def text_border(text, x, y, font, shadowcolor, fillcolor):
    draw.text((x - 1, y), text, font=font, fill=shadowcolor)
    draw.text((x + 1, y), text, font=font, fill=shadowcolor)
    draw.text((x, y - 1), text, font=font, fill=shadowcolor)
    draw.text((x, y + 1), text, font=font, fill=shadowcolor)

    draw.text((x - 1, y - 1), text, font=font, fill=shadowcolor)
    draw.text((x + 1, y - 1), text, font=font, fill=shadowcolor)
    draw.text((x - 1, y + 1), text, font=font, fill=shadowcolor)
    draw.text((x + 1, y + 1), text, font=font, fill=shadowcolor)

    draw.text((x, y), text, font=font, fill=fillcolor)


bottomtext = "不知道吃什么?截图吃饭"
bottom_fontsize = 27
bottom_font = ImageFont.truetype('STHUPO.TTF', size=bottom_fontsize)
x, y = (size-bottom_fontsize*len(bottomtext))/2, size-bottom_fontsize*1.2
draw.text(xy=(x, y), text=bottomtext,
          fill=0, font=bottom_font)
text_border(bottomtext, x, y-4,
            bottom_font, 0, (255, 255, 255))
im

image-20210726172847077

上述代码选择了华文琥珀作为字体,个人用来绘制文字边框的方法比较简单粗暴,如果有更好的办法,欢迎留言交流。

考虑到后续图片发送到微信上显示都很小,干脆现在就压缩一下像素大小:

im.thumbnail((128, 128))
im

image-20210726172948959

下面我们封装一下生成代码,方便后续调用:

from PIL import Image, ImageFont, ImageDraw


def text_img(text, bgcolor="lightblue", bottomtext="不知道吃什么?截图吃饭", size=360, result_size=(128, 128)):
    def text_border(text, x, y, font, shadowcolor, fillcolor):
        draw.text((x - 1, y), text, font=font, fill=shadowcolor)
        draw.text((x + 1, y), text, font=font, fill=shadowcolor)
        draw.text((x, y - 1), text, font=font, fill=shadowcolor)
        draw.text((x, y + 1), text, font=font, fill=shadowcolor)

        draw.text((x - 1, y - 1), text, font=font, fill=shadowcolor)
        draw.text((x + 1, y - 1), text, font=font, fill=shadowcolor)
        draw.text((x - 1, y + 1), text, font=font, fill=shadowcolor)
        draw.text((x + 1, y + 1), text, font=font, fill=shadowcolor)

        draw.text((x, y), text, font=font, fill=fillcolor)

    im = Image.new(mode='RGB', size=(size, size), color=bgcolor)
    draw = ImageDraw.Draw(im=im)
    fontsize = (size-20)//len(text)
    draw.text(xy=(10, (size-fontsize*1.5)/2),
              text=text, fill=0,
              font=ImageFont.truetype('msyh.ttc', size=fontsize))
    bottom_fontsize = (size-20)//len(bottomtext)
    bottom_font = ImageFont.truetype('STHUPO.TTF', size=bottom_fontsize)
    x, y = (size-bottom_fontsize*len(bottomtext))/2, size-bottom_fontsize*1.2
    draw.text(xy=(x, y), text=bottomtext,
              fill=0, font=bottom_font)
    text_border(bottomtext, x, y-4,
                bottom_font, 0, (255, 255, 255))
    im.thumbnail(result_size)
    return im

测试一下:

text_img("鱼香茄子")

image-20210726174000710

ok,现在我们就已经能够给任何菜品生成图片了。但是菜品的名字哪里来呢?我找到了一个网站,下面考虑爬一下它:

爬取菜品数据

网址是:https://m.meishij.net/caipu/

这个网站结果非常简单,一个简单的xpath即可获取到所有的菜品名称:

image-20210726174726258

下面开始下载:

from lxml import etree
import requests

req = requests.get("https://m.meishij.net/caipu/")

html = etree.HTML(req.text)
menu = html.xpath("//dl[@class='recipe_list']//a/text()")
menu = list(set([_.strip(".") for _ in menu]))
print(len(menu), menu[:10], menu[-10:])
3744 ['排骨藕汤', '芋圆', '海鲜汤', '凉拌杏鲍菇', '三汁焖锅', '奶香玉米汁', '炒豆角', '茄子酱', '芒果糯米糍', '馒头'] ['清蒸茄子', '西兰花炒鸡', '老式蛋糕', '排骨年糕', '清炒丝瓜', '芋头蒸排骨', '木耳炒肉', '蚝油油麦菜', '麻辣鸡块', '荷叶饼']

有了这些菜名,我们已经可以用来生成动图了。不过为了以后还能够学做菜,我们可以将菜名保存起来,要学做菜的时候呢打开网页:https://so.meishi.cc/?q=菜名,进行搜索。

保存菜名:

with open("meau.csv", "w", encoding="u8") as f:
    f.write("菜名\n")
    for row in menu:
        f.write(row)
        f.write("\n")

下面我们开始生成菜名动图:

生成菜名动图

3767多个菜名毕竟是太多,我们可以随意取30个菜名来生成动图:

import random

gif_list = random.choices(menu, k=30)
print(gif_list)
['蒸水蛋', '肉桂卷', '凉瓜炒蛋', '芝士焗红薯', '香蕉酥', '酸奶慕斯', '鸡蛋肠粉', '红油肚丝', '玉米鸡蛋饼', '酸辣豆腐汤', '萝卜炖牛腩', '苦瓜排骨汤', '腐竹拌芹菜', '西红柿炒土', '蒜蓉蒸茄子', '豆沙面包', '蘑菇炒肉', '清炒莲藕', '黑椒牛肉粒', '南瓜煎饼', '炒黄瓜', '杂粮馒头', '桃山皮月饼', '葱爆肉', '小炒牛肉', '豆瓣鲫鱼', '虾仁烩豆腐', '素馅饺子', '凉拌黄瓜', '砂锅鱼头']

PS:还是自己选好菜名,写死列表更好😅

import imageio

frames = [text_img(text) for text in gif_list]
imageio.mimsave("meau.gif", frames, 'GIF', duration=0.02)

生成结果:

meau-1627295603332

根据菜名列表生成动图的完整代码

import imageio
from PIL import Image, ImageFont, ImageDraw


def text_img(text, bgcolor="lightblue", bottomtext="不知道吃什么?截图吃饭", size=360, result_size=(128, 128)):
    def text_border(text, x, y, font, shadowcolor, fillcolor):
        draw.text((x - 1, y), text, font=font, fill=shadowcolor)
        draw.text((x + 1, y), text, font=font, fill=shadowcolor)
        draw.text((x, y - 1), text, font=font, fill=shadowcolor)
        draw.text((x, y + 1), text, font=font, fill=shadowcolor)

        draw.text((x - 1, y - 1), text, font=font, fill=shadowcolor)
        draw.text((x + 1, y - 1), text, font=font, fill=shadowcolor)
        draw.text((x - 1, y + 1), text, font=font, fill=shadowcolor)
        draw.text((x + 1, y + 1), text, font=font, fill=shadowcolor)

        draw.text((x, y), text, font=font, fill=fillcolor)

    im = Image.new(mode='RGB', size=(size, size), color=bgcolor)
    draw = ImageDraw.Draw(im=im)
    fontsize = (size-20)//len(text)
    draw.text(xy=(10, (size-fontsize*1.5)/2),
              text=text, fill=0,
              font=ImageFont.truetype('msyh.ttc', size=fontsize))
    bottom_fontsize = (size-20)//len(bottomtext)
    bottom_font = ImageFont.truetype('STHUPO.TTF', size=bottom_fontsize)
    x, y = (size-bottom_fontsize*len(bottomtext))/2, size-bottom_fontsize*1.2
    draw.text(xy=(x, y), text=bottomtext,
              fill=0, font=bottom_font)
    text_border(bottomtext, x, y-4,
                bottom_font, 0, (255, 255, 255))
    im.thumbnail(result_size)
    return im


def save_meau_gif(savename, meau):
    frames = [text_img(text) for text in meau]
    imageio.mimsave(savename, frames, 'GIF', duration=0.02)

使用示例:

meau = [
    "荷叶糯米鸡", "烤羊肉", "黑椒牛排", "家常大盘鸡", "蒜泥豆角",
    "洋葱炒牛肉", "丝瓜炒鸡蛋", "平菇炒鸡蛋", "鸡刨豆腐", "芙蓉鲜蔬汤",
    "炒西葫芦", "茄子豆角", "滑蛋牛肉", "香菇青菜", "地三鲜",
    "酱烧杏鲍菇", "腐乳鸡翅", "醋溜藕片", "椰子炖鸡", "香菇烧豆腐",
    "咖喱鸡腿饭", "鸡汁土豆泥", "茄子炖土豆", "炒乌冬面", "咖喱土豆鸡",
    "上汤娃娃菜", "蒜蓉蒸茄子", "芝士焗红薯", "栗子黄焖鸡", "丝瓜豆腐汤",
]
save_meau_gif("meau.gif", meau)

生成结果:

meau

自从我们的动图就生成完毕啦!不知道吃啥的时候都可以拿出来截图玩玩~🐶

😆祝大家选餐愉快~

PIL操作gif的其他操作

其实用专门动图处理软件就可以操作,下面还是补充一下,python的操作API记录一下:

Gif拆分

比如我们拆分一下这张图:

功夫熊

from PIL import Image, ImageSequence

img = Image.open('功夫熊.gif')
for i, f in enumerate(ImageSequence.Iterator(img), 1):
    f.save(f'拆分/功夫熊-{i}.png')

拆分结果:

image-20210726191539826

GIF倒放

下面我们再将上面这张动图倒放一下:

from PIL import Image, ImageSequence
import imageio

im = Image.open('功夫熊.gif')
sequence = [f.copy() for f in ImageSequence.Iterator(im)]
sequence.reverse()  # 将列表中的帧通过reverse()函数进行倒序
sequence[0].save('倒放功夫熊.gif', save_all=True, append_images=sequence[1:])

倒放功夫熊

动作比较滑稽😆~

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-07-31 16:35:50  更:2021-07-31 16:35:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 9:22:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码