SoFunction
Updated on 2024-10-29

Python Generate Screenshot Meal Selection GIF Animation

Earlier in the group, one of the guys asked what they should eat for lunch today, and then another guy posted a moving picture of the following:

截图吃饭

Personally, I find it kinda interesting that the screenshot actually picks a random dish name like a lottery. Considering that not all of the dish name candidates in this motion picture are necessarily dishes that we can eat. We can play around with python to generate such a motion picture based on a list of dish names.

Before I also saw what screenshots to choose avatars and other moving pictures, that type of moving pictures generated through the picture are relatively simple, through the text mentioned in the animation workshop tool of Imagine can do. So this article only demonstrates how to generate text animation.

python generate text motion graphic

Let's go through this step by step:

Download emoji images locally

In order to analyze this kind of emoticon pictures, the first step needs to be downloaded first, but for WeChat's emoticon moving pictures, after testing it is not really possible to download them directly.

Although analyzed by a file monitoring tool, the gif emoji motion picture is stored in theC:\Users\ASUS\Documents\WeChat Files\Your WeChat.ID\FileStorage\CustomEmotion\xx\xxxxThe location, but it is not viewable with the image tool. Analyzing the binary with winhex got theV1MMWXSuch a file header suggests that WeChat encrypts all emojis to some degree. It's possible to decrypt them, but it's too much of a hassle to make such a big deal out of it.

I finally came up with a simple solution later, which is to send this emoji to the public number you have access to log into the backend, and then go to the backend of the public number to download it:

image-20210726163537948

WeChat sends motion pictures that are stored as unique to itselfV1MMWXEncrypted format, probably in order to use their own original compression algorithm has a greater compression ratio it. That means we want to directly look at the local WeChat stored gif animation, can only develop their own decoder specifically for this WeChat format.

Analyze the motion picture

Below I use the widget Imagine and open it using the animation workshop:

image-20210726163518497

You can see that this motion picture consists of 22 text images with a frame switching time of 20 milliseconds.

Generate single images

Analysis is complete we consider the PIL library to generate a single image, if you have not yet installed the library of the children's shoes, use the following command to install the library:

pip install pillow

Below, I chose to use a blue background for the background. Let's start by drawing the dish name text in the center:

from PIL import Image, ImageFont, ImageDraw


text = "Beef brisket casserole with pearl potatoes."
size = 320
fontsize = (size-20)//len(text)
im = (mode='RGB', size=(size, size), color="lightblue")

draw = (im=im)
(xy=(10, (size-fontsize*1.5)/2),
          text=text, fill=0,
          font=('', size=fontsize))
im

image-20210726172326328

Due to the inconsistency in the number of words in the names of the dishes, automatic text resizing was done to fill the whole picture.

Font I chose Microsoft Black, of course, Microsoft Black also has three sub-fonts, you can check the properties of the font file through the system font installation directory so as to know the corresponding file name of the font:

image-20210726164518133

The text with shadows on the bottom will be a little tricky to generate, my thinking is to draw solid black text first, and then offset the text with black edges white fill upwards by a few units when drawing the text:

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

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

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


bottomtext = "Don't know what to eat? Screenshot for dinner."
bottom_fontsize = 27
bottom_font = ('', size=bottom_fontsize)
x, y = (size-bottom_fontsize*len(bottomtext))/2, size-bottom_fontsize*1.2
(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

The above code selected Huawen Amber as the font, personally used to draw the text border method is relatively simple and brutal, if there is a better way, welcome to leave a message to exchange.

Considering that subsequent images sent to WeChat display small, simply compress the pixel size now:

((128, 128))
im

image-20210726172948959

Below we encapsulate the generated code for easy subsequent calls:

from PIL import Image, ImageFont, ImageDraw


def text_img(text, bgcolor="lightblue", bottomtext="Don't know what to eat? Screenshot for dinner.", size=360, result_size=(128, 128)):
    def text_border(text, x, y, font, shadowcolor, fillcolor):
        ((x - 1, y), text, font=font, fill=shadowcolor)
        ((x + 1, y), text, font=font, fill=shadowcolor)
        ((x, y - 1), text, font=font, fill=shadowcolor)
        ((x, y + 1), text, font=font, fill=shadowcolor)

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

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

    im = (mode='RGB', size=(size, size), color=bgcolor)
    draw = (im=im)
    fontsize = (size-20)//len(text)
    (xy=(10, (size-fontsize*1.5)/2),
              text=text, fill=0,
              font=('', size=fontsize))
    bottom_fontsize = (size-20)//len(bottomtext)
    bottom_font = ('', size=bottom_fontsize)
    x, y = (size-bottom_fontsize*len(bottomtext))/2, size-bottom_fontsize*1.2
    (xy=(x, y), text=bottomtext,
              fill=0, font=bottom_font)
    text_border(bottomtext, x, y-4,
                bottom_font, 0, (255, 255, 255))
    (result_size)
    return im

Test it:

text_img("Fish-flavored eggplant.")

image-20210726174000710

OK, now we'll be able to generate an image for any dish. But where to get the name of the dish? I found a website, consider crawling it below:

Crawling dish data

The URL is:/caipu/

This site turned out to be very simple, a simple xpath to get all the names of the dishes:

image-20210726174726258

Start downloading below:

from lxml import etree
import requests

req = ("/caipu/")

html = ()
menu = ("//dl[@class='recipe_list']//a/text()")
menu = list(set([_.strip(".") for _ in menu]))
print(len(menu), menu[:10], menu[-10:])

3744 ['Lotus Root Soup with Spare Ribs', 'taro ball (* dessert)', 'seafood soup', 'Cold Almond Abalone Mushroom', 'casserole', 'Creamy Corn Juice', 'stir-fried string bean', 'eggplant sauce', 'Mango mochi cake', 'steamed bread'] ['steamed eggplant', 'Stir Fried Chicken with Broccoli', 'Old Fashioned Cake', 'Spare ribs and rice cake', 'stir-fried loofah', 'Steamed Spare Ribs with Taro', 'stir-fried pork with fungus', 'Oyster Sauce Oatmeal' , 'Spicy Chicken Nuggets', 'Lotus Leaf Cake']

With these dish names, we can already use them to generate moving pictures. However, in order to still be able to learn how to cook in the future, we can save the names of the dishes, and when we want to learn how to cook it open the page:/?q= dish name to search.

Save the name of the dish:

with open("", "w", encoding="u8") as f:
    ("Name of the dish\n")
    for row in menu:
        (row)
        ("\n")

Here we start generating the dish name motion picture:

Generate menu name animation

More than 3767 dish names are too many after all, we can take 30 dish names at random to generate moving pictures:

import random

gif_list = (menu, k=30)
print(gif_list)

['steamed egg', 'cinnamon roll', 'Scrambled Eggs with Cold Melon', 'Baked Sweet Potato with Cheese', 'banana split', 'yogurt mousse', 'egg rice noodle roll, a roll made from sheets of rice flour dough, steamed and stuffed with egg', 'shredded tripe in red oil', 'corn and egg pancake', 'hot and sour tofu soup', 'Stewed Beef Brisket with Turnip', 'Bitter Melon and Spare Ribs Soup', 'Celery with Bean Curd', 'stir-fried potato with tomato', 'Steamed Eggplant with Garlic', 'bean paste bread', 'stir-fried pork with mushrooms', 'stir-fry lotus root ', 'Diced Beef with Black Pepper', 'Pumpkin Pancakes', 'Fried Cucumber', 'Mixed Grain Steamed Buns', 'Peach Hill Skin Mooncake', 'Sautéed Pork with Scallions', 'Stir-Fried Beef', 'Crucian Carp with Bean Paste', 'Braised Bean Curd with Shrimp', 'Vegetarian Dumplings', 'Cold Cucumber', 'Fish Head in Casserole']

PS: It's better to pick your own dish name and write the list to death 😅

import imageio

frames = [text_img(text) for text in gif_list]
("", frames, 'GIF', duration=0.02)

Generate results:

meau-1627295603332

Complete code for generating motion pictures based on a list of dish names

import imageio
from PIL import Image, ImageFont, ImageDraw


def text_img(text, bgcolor="lightblue", bottomtext="Don't know what to eat? Screenshot for dinner.", size=360, result_size=(128, 128)):
    def text_border(text, x, y, font, shadowcolor, fillcolor):
        ((x - 1, y), text, font=font, fill=shadowcolor)
        ((x + 1, y), text, font=font, fill=shadowcolor)
        ((x, y - 1), text, font=font, fill=shadowcolor)
        ((x, y + 1), text, font=font, fill=shadowcolor)

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

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

    im = (mode='RGB', size=(size, size), color=bgcolor)
    draw = (im=im)
    fontsize = (size-20)//len(text)
    (xy=(10, (size-fontsize*1.5)/2),
              text=text, fill=0,
              font=('', size=fontsize))
    bottom_fontsize = (size-20)//len(bottomtext)
    bottom_font = ('', size=bottom_fontsize)
    x, y = (size-bottom_fontsize*len(bottomtext))/2, size-bottom_fontsize*1.2
    (xy=(x, y), text=bottomtext,
              fill=0, font=bottom_font)
    text_border(bottomtext, x, y-4,
                bottom_font, 0, (255, 255, 255))
    (result_size)
    return im


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

Example of use:

meau = [
    "Glutinous Rice Chicken in Lotus Leaf.", "Roast Lamb", "Black Pepper Steak.", "Home-style chicken.", "Garlic bean curd.",
    "Beef with onions.", "Scrambled eggs with loofah.", "Scrambled eggs with mushrooms.", "Tofu with chicken.", "Hibiscus Vegetable Soup.",
    "Fried zucchini.", "Eggplant and beans.", "Beef with slippery eggs.", "Mushroom and bok choy.", "The Earth's Three Freshmen.",
    "Roasted apricot mushrooms in sauce.", "Chicken Wings with Bean Curd", "Sliced lotus root in vinegar.", "Coconut Chicken Stew.", "Braised tofu with shiitake mushrooms.",
    "Curry Chicken Legs and Rice.", "Chicken mashed potatoes.", "Eggplant and potato stew.", "Fried Udon Noodles", "Curry Potato Chicken.",
    "Baby vegetables in soup.", "Steamed eggplant with garlic.", "Baked Sweet Potato with Cheese.", "Chestnut cassoulet.", "Loofah and tofu soup.",
]
save_meau_gif("", meau)

Generate results:

meau

Since our moving pictures have been generated! Whenever you don't know what to eat, you can take a screenshot and play with it~🐶!

😆 Have fun choosing your meals~!

Other operations of the PIL operation gif

In fact, with specialized motion picture processing software can be operated, the following is still added, python operation API record:

Gif Splitting

For example, let's break down this chart:

功夫熊

from PIL import Image, ImageSequence

img = ('Kung Fu Bear.gif')
for i, f in enumerate((img), 1):
    (f'broken up inseparate items/Kung Fu Bear-{i}.png')

Split results:

image-20210726191539826

GIF Rewind

Let's rewind the above motion picture a bit below:

from PIL import Image, ImageSequence
import imageio

im = ('Kung Fu Bear.gif')
sequence = [() for f in (im)]
()  # Put the frames in the list in reverse order with the reverse() function
sequence[0].save('Rewind Kung Fu Bear.gif', save_all=True, append_images=sequence[1:])

倒放功夫熊

To this article on Python to generate screenshots of selected meals GIF animation is introduced to this article, more related Python to generate screenshots of GIF animation content, please search for my previous articles or continue to browse the following related articles I hope you will support me in the future more!