Créer et optimiser un GIF avec Python

Temps de lecture : 5 minutes

Au menu de cet article, un truc fun et cool ! Plus précisément, un GIF que tu pourras envoyer à quelqu’un dans ton prochain mail. Et comme on est de grands romantiques, amusons-nous à animer un cœur 😊. Un peu comme ceci :

Pour créer et optimiser ce GIF sur Python, nous allons utiliser une librairie dédiée au traitement d’images : imagio.

[Code complet (Jupyter Notebook) et ressources sur Github]

Quelques mots sur imagio

Imagio offre une interface facile d’utilisation pour la lecture et l’écriture d’images sur Python. Son gros avantage, c’est probablement la variété des formats qu’elle permet de traiter : images, images animées, données volumétriques… etc. Donc sauf si tu travailles sur des formats d’images « très sombres », tu devrais y trouver ton bonheur 😉.

Si tu es sur une version de Python 3.5+, elle est installée par défaut. Sinon tu peux toujours l’installer rapidement. Il suffit d’ouvrir ton terminal et de taper pip install imageio. Bon maintenant que les introductions sont faites, revenons aux affaires de cœur !

Génération des images pour le GIF

Tu le sais peut-être déjà, le GIF n’est qu’une succession d’images qui se suivent en boucle ! Donc pour générer notre cœur, il faut commencer par générer et enregistrer les images successives. Un peu comme ceci :

Comment fait-on ? Bonne question ! On utilisera une équation paramétrique d’un cœur. Pour faire simple, c’est une fonction à plusieurs variables qui permet de retrouver la bonne succession des points pour représenter notre cœur (on ne fera pas plus de géométrie que ça 😅) . Si tu tapes sur Google, tu trouveras plusieurs propositions, moi j’ai choisi de partir sur celle-ci :

x = 16\sin^3(t) \\ y = 13\cos(t) - 5\cos(2t) - 2\cos(3t) - \cos(4t) \\ avec~t \in [0,2\pi]

Avec cette équation, on peut commencer à tracer nos figures. A chaque fois qu’on rajoute un point (x,y), on enregistre le rendu. Sur Python, ça donne ceci :

# Implémentation de l'équation paramétrique
t = np.linspace(0,2*np.pi, 50) # Je choisis 50 points entre 0 et 2pi
x = 16*(np.sin(t))**3
y = 13*np.cos(t) - 5*np.cos(2*t) - 2*np.cos(3*t) - np.cos(4*t)

# Enregistrement des images
for i in range(len(t)):
    plt.scatter(x[i], y[i]) # Tracé
    plt.savefig(f"images/{i}.jpg") # Enregistrement dans le dossier "images"

Au cas où tu te poses des questions sur la syntaxe de la dernière ligne i.e. f"images/{i}.jpg", j’utilise tout simplement le concept de f-strings pour enregistrer les images sous forme 0.jpg, 1.jpg … etc.

Création du GIF

Nous avons désormais nos images 😀 ! Prochaine étape, générer le GIF. Pour faire ça, il faut commencer par créer une pile (« stack » sur Python) avec nos 50 images.

import imageio as iio
frames = np.stack([iio.imread(f"images/{i}.jpg") for i in range(len(t))], axis = 0)

En gros, on lit les images avec iio.imread et on les empile avec np.stack. La suite ? Utiliser tranquillement la fonction mimwrite de imagio pour générer le GIF à partir de notre pile d’images fraichement créée. C’est une fonction, qui dans son usage le plus simple, prend en entrée le nom qu’on veut donner à notre GIF (chemin) et la pile d’images. Plutôt direct non ? Sur Python, ça donne ceci :

iio.mimwrite('coeur.gif', frames)

Et tada, notre GIF est prêt à être expédié 🙂 !

Coeur ballant (Créer et optimiser un GIF avec Python)

Et bien sûr, pour aller plus loin mimwrite nous donne la possibilité de fixer d’autres paramètres comme la durée du GIF, le nombre d’images par seconde… etc. Mais je te laisse t’amuser avec ça 😉.

De « coeur ballant » à « coeur fixe »

Si tu as été attentif, tu as aussi dû remarquer que les limites des axes de notre GIF n’étaient pas fixes dans le temps. Je n’ai pas changé ce comportement parce que je trouvais que ça donnait un effet dynamique de plus au GIF. Mais on peut changer cet effet « coeur ballant » pour avoir un « coeur fixe » (parait-il que c’est mieux 😜). Un peu comme ceci :

Coeur fixe (Créer et optimiser un GIF avec Python)
« Cœur fixe »

Tu l’as peut-être deviné… Pour réaliser ce résultat, il faut simplement fixer les limites des axes pour toutes les images individuelles. Le reste du traitement reste exactement le même.

# Coeur fixe
plt.close()

t = np.linspace(0,2*np.pi, 50)
x = 16*(np.sin(t))**3
y = 13*np.cos(t) - 5*np.cos(2*t) - 2*np.cos(3*t) - np.cos(4*t)


for i in range(len(t)):
    plt.scatter(x[i], y[i])
    # On rajoute des limites fixes aux axes des images individuelles
    plt.xlim([-17,17])
    plt.ylim([-18,13])
    plt.savefig(f"images2/{i}.jpg")
Optimisation du GIF (avec « subrectangles »)

On est d’accord, les GIFs c’est joli mais leur gros problème c’est probablement la taille ! Celle du « cœur fixe  » est par exemple de 2.31 MB sur mon ordinateur (tu devrais avoir le même ordre de grandeur chez toi). Une telle taille devient un peu embêtante quand on veut insérer notre joli GIF dans un mail par exemple. Comme quoi, un grand cœur n’est pas toujours une bénédiction (pardon, il fallait que je la fasse, cette blague 😅). Pour la petite anecdote, j’ai déjà envoyé un mail de groupe avec un GIF de 11MB ! Résultat, j’ai saturé la boite mail de certains destinataires, oups ! Heureusement, il existe des solutions pour optimiser la taille.

Pour le cœur fixe par exemple, tu peux remarquer que les images consécutives ont beaucoup de points (x,y)en commun. On n’est donc pas obligé de stocker toutes les images dans le GIF mais on peut se contenter d’enregistrer uniquement les différences d’une image à une autre. mimwrite possède une option pour faire ça pendant la création du GIF : subrectangles. Il suffit de fixer sa valeur à True pour obtenir le résultat. Une création optimisée ressemble donc plutôt à ceci sur Python :

iio.mimwrite('coeur_fixe_optimise.gif', frames, subrectangles = True)

Et si tu checkes la taille du nouveau GIF (coeur_fixe_optimise), on a désormais 95 Ko contre 2,31 MB initialement. Soit qu’on a divisé la taille initiale par 25 – pour le même rendu final !!! (j’aurais pu sauver certaines boites mails si j’avais connu cette option plus tôt 😑). Bon ok, sur notre cas d’étude, cette optimisation marche super bien. Sur d’autres cas, ça marcherait peut-être moins bien mais cela vaut toujours le coup de la tenter.

Autres méthodes d’optimisation

Ici, nous avons optimisé le GIF pendant sa création. Il existe aussi des méthodes permettant d’optimiser le GIF après sa création. L’une des plus populaires utilise la librairie pygifsicle. Le principe d’optimisation est le même mais l’installation sur Windows, quoique pas très compliquée, nécessite une étape supplémentaire en plus du pip install (je pourrais écrire un petit billet sur ça au besoin).

Mais désormais, tu sais créer et optimiser des GIFs ! Va donc envoyer des cœurs ! (oui je sais, mes blagues et mes jeux de mots, c’est de pire en pire avec le temps mais bon…😑).

Partager

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *