Python生成截圖選餐GIF動(dòng)畫
之前群里有小伙伴問今天中午該吃什么,然后另一位小伙伴發(fā)了一張下面的動(dòng)圖:
我個(gè)人覺得還挺有意思的,截圖還真像抽獎(jiǎng)一樣隨機(jī)選一個(gè)菜名??紤]到這張動(dòng)圖中的菜名候選并不見得都是我們能夠吃的菜。我們可以用python根據(jù)菜名列表生成這樣的動(dòng)圖玩玩。
之前還看到什么截圖選頭像之類的動(dòng)圖,那類通過圖片生成的動(dòng)圖都比較簡單,通過文中提到的Imagine的動(dòng)畫作坊工具就可以做。所以本文只演示如何生成文字動(dòng)圖。
python生成文字動(dòng)圖
下面我們一步步來完成這個(gè)操作:
下載表情圖片到本地
為了分析這種表情圖片,第一步需要先下載下來,但是對于微信的表情動(dòng)圖,經(jīng)過測試還真沒法直接下載下來。
雖然通過文件監(jiān)控工具分析出,gif表情動(dòng)圖存儲位置在C:\Users\ASUS\Documents\WeChat Files\你的微信ID\FileStorage\CustomEmotion\xx\xxxx
位置,但是卻無法用圖片工具查看。用winhex分析二進(jìn)制得到了V1MMWX
這樣的文件頭,說明微信對表情都進(jìn)行了一定程度的加密。雖然可以解密,但這樣大動(dòng)干戈未免過于麻煩。
后面終于想到了一個(gè)簡單的方案,那就是把向你有權(quán)限登錄后臺的公眾號發(fā)送這個(gè)表情,再去公眾號后臺下載:
微信發(fā)送的動(dòng)圖都是存儲為自己特有V1MMWX
加密格式,可能是為了使用自己獨(dú)創(chuàng)的壓縮算法有更大的壓縮比吧。那說明我們想直接看本地微信存儲的gif動(dòng)圖,只能自行開發(fā)專門針對這種微信格式的解碼器了。
分析動(dòng)圖
下面我使用小工具Imagine,并使用動(dòng)畫作坊打開:
可以看到這張動(dòng)圖由22張文字圖片組成,幀切換時(shí)間為20毫秒。
生成單張圖片
分析完成我們考慮用PIL庫來生成單張圖片,如果還沒有安裝該庫的童鞋,使用以下命令安裝該庫:
pip install pillow
下面選擇了用藍(lán)底做背景。我們先來繪制中間的菜名文字:
from PIL import Image, ImageFont, ImageDraw text = "珍珠土豆?fàn)F牛腩" 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
由于菜品的名字文字個(gè)數(shù)不一致,為了都能填滿整圖,作了自動(dòng)文字大小調(diào)整處理。
字體我選擇了微軟雅黑,當(dāng)然微軟雅黑也有三種子字體,可以通過系統(tǒng)字體安裝目錄查看字體文件的屬性從而知道字體對應(yīng)的文件名:
下方帶陰影的的文字生成起來會麻煩一些,我的思路是先繪制純黑的文字,在繪制帶黑色邊緣白色填充的文字向上偏移幾個(gè)單位:
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
上述代碼選擇了華文琥珀作為字體,個(gè)人用來繪制文字邊框的方法比較簡單粗暴,如果有更好的辦法,歡迎留言交流。
考慮到后續(xù)圖片發(fā)送到微信上顯示都很小,干脆現(xiàn)在就壓縮一下像素大?。?/p>
im.thumbnail((128, 128)) im
下面我們封裝一下生成代碼,方便后續(xù)調(diào)用:
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("魚香茄子")
ok,現(xiàn)在我們就已經(jīng)能夠給任何菜品生成圖片了。但是菜品的名字哪里來呢?我找到了一個(gè)網(wǎng)站,下面考慮爬一下它:
爬取菜品數(shù)據(jù)
網(wǎng)址是:https://m.meishij.net/caipu/
這個(gè)網(wǎng)站結(jié)果非常簡單,一個(gè)簡單的xpath即可獲取到所有的菜品名稱:
下面開始下載:
from lxml import etree import requests req = requests.get("https://m.meishij.net/caipu/") html = etree.HTML(req.text) menu = html.xpath("http://dl[@class='recipe_list']//a/text()") menu = list(set([_.strip(".") for _ in menu])) print(len(menu), menu[:10], menu[-10:])
3744 ['排骨藕湯', '芋圓', '海鮮湯', '涼拌杏鮑菇', '三汁燜鍋', '奶香玉米汁', '炒豆角', '茄子醬', '芒果糯米糍', '饅頭'] ['清蒸茄子', '西蘭花炒雞', '老式蛋糕', '排骨年糕', '清炒絲瓜', '芋頭蒸排骨', '木耳炒肉', '蠔油油麥菜', '麻辣雞塊', '荷葉餅']
有了這些菜名,我們已經(jīng)可以用來生成動(dòng)圖了。不過為了以后還能夠?qū)W做菜,我們可以將菜名保存起來,要學(xué)做菜的時(shí)候呢打開網(wǎng)頁:https://so.meishi.cc/?q=菜名,進(jìn)行搜索。
保存菜名:
with open("meau.csv", "w", encoding="u8") as f: f.write("菜名\n") for row in menu: f.write(row) f.write("\n")
下面我們開始生成菜名動(dòng)圖:
生成菜名動(dòng)圖
3767多個(gè)菜名畢竟是太多,我們可以隨意取30個(gè)菜名來生成動(dòng)圖:
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)
生成結(jié)果:
根據(jù)菜名列表生成動(dòng)圖的完整代碼
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)
生成結(jié)果:
自從我們的動(dòng)圖就生成完畢啦!不知道吃啥的時(shí)候都可以拿出來截圖玩玩~🐶
😆祝大家選餐愉快~
PIL操作gif的其他操作
其實(shí)用專門動(dòng)圖處理軟件就可以操作,下面還是補(bǔ)充一下,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')
拆分結(jié)果:
GIF倒放
下面我們再將上面這張動(dòng)圖倒放一下:
from PIL import Image, ImageSequence import imageio im = Image.open('功夫熊.gif') sequence = [f.copy() for f in ImageSequence.Iterator(im)] sequence.reverse() # 將列表中的幀通過reverse()函數(shù)進(jìn)行倒序 sequence[0].save('倒放功夫熊.gif', save_all=True, append_images=sequence[1:])
到此這篇關(guān)于Python生成截圖選餐GIF動(dòng)畫的文章就介紹到這了,更多相關(guān)Python生成截圖GIF動(dòng)畫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用Python實(shí)現(xiàn)QQ游戲大家來找茬輔助工具
這是一個(gè)用于QQ大家來找茬(美女找茬)的輔助外掛,開發(fā)的原因是看到老爸天天在玩這個(gè)游戲,分?jǐn)?shù)是慘不忍睹的負(fù)4000多。本來是想寫個(gè)很簡單的東西,但由于過程中老爸的多次嘲諷,逼得我不得不盡力完善,最后形成了一個(gè)小小的產(chǎn)品。2014-09-09Python將主機(jī)名轉(zhuǎn)換為IP地址的方法
今天小編就為大家分享一篇Python將主機(jī)名轉(zhuǎn)換為IP地址的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08關(guān)于pytorch中網(wǎng)絡(luò)loss傳播和參數(shù)更新的理解
今天小編就為大家分享一篇關(guān)于pytorch中網(wǎng)絡(luò)loss傳播和參數(shù)更新的理解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08python實(shí)現(xiàn)應(yīng)用程序在右鍵菜單中添加打開方式功能
這篇文章主要介紹了python實(shí)現(xiàn)應(yīng)用程序在右鍵菜單中添加打開方式功能,本文分步驟給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-01-01python科學(xué)計(jì)算之narray對象用法
今天小編就為大家分享一篇python科學(xué)計(jì)算之narray對象用法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11