Couverture de l'article YOGA, un outil pour optimiser vos images et vos modèles 3D !
Retour aux articles

L'agence

WanadevStudio

YOGA, un outil pour optimiser vos images et vos modèles 3D !

Comme vous le savez peut-être, chez Wanadev on travaille sur beaucoup de projets WebGL ; la conversion et l'optimisation d'assets sont donc des problématiques récurrentes. Il existe bien sûr de nombreux outils et bibliothèques pour cela, mais aucun ne répondait à l'ensemble de nos besoins et ne permettait d'automatiser simplement cette tâche. C'est ainsi que commença le projet Yummy Optimizer for Gorgeous Assets, plus couramment nommé YOGA.

Logo de YOGA

Qu'est-ce que YOGA exactement ?

YOGA est à la fois un utilitaire en ligne de commande et une bibliothèque Python permettant :

  • De convertir, redimensionner et optimiser des images. Il supporte une large variété de formats en entrée, et prend actuellement en charge les formats JPEG et PNG en sortie.

  • De convertir et d'optimiser des modèles 3D. Là encore, il supporte une large variété de formats en entrée, et en sortie il propose les formats glTF et GLB.

YOGA est open source, vous pouvez donc l'utiliser librement dans vos projets. N'hésitez pas à jeter un coup d'œil à la page Github du projet si vous souhaitez l'utiliser ou contribuer :

Développement du projet

Notre histoire commence fin 2017. Depuis quelque temps nous commencions à utiliser le tout récent format glTF 2.0 pour nos modèles 3D. Il s'agit d'un format standardisé par le Khronos Group, les mêmes qui développent les normes OpenGL et WebGL. Il a pour avantage d'être normalisé, relativement simple et adapté au Web.

Pour être un peu plus spécifiques, nous nous intéressions surtout au format GLB, la version binaire du glTF 2.0, qui offre pas mal d'avantages :

  • comme il s'agit d'un format binaire, il produit des fichiers moins volumineux que le glTF qui est basé sur du JSON,

  • il permet d'intégrer des textures directement en binaire à l'intérieur du même fichier plutôt que de référencer d'autres fichiers (qu'il faudra alors télécharger par la suite) ou d'intégrer les images en base64 dans le JSON (ce qui est assez peu compatible avec une taille de fichier réduite).

Nous avions donc besoin d'un outil avec le cahier des charges suivant :

  • Permettre de convertir divers formats 3D vers GLB,
  • Si possible, permettre d'optimiser certaines choses sur les modèles (comme supprimer les maillages inutiles),
  • Permettre de convertir les formats d'images utilisés pour les textures (on est sur le Web, il faut donc utiliser des formats d'images supportés par les navigateurs, comme JPEG et PNG),
  • Permettre de redimensionner les textures (on a rarement besoin de textures 4K sur le Web, elles ne seraient de toute façon pas supportées par le PC de la plupart des utilisateurs),
  • Optimiser la compression des images en sortie (donc nos JPEGs et nos PNGs).

Nous n'avons, à l'époque, trouvé aucun outil permettant de faire tout cela facilement et de manière automatisable... Et à vrai dire, on avait trouvé assez peu d'outils prenant en charge le GLB... C'est pourquoi Alexis et moi-même nous sommes lancés dans le développement de YOGA.

Il a été décidé dès le départ que ce projet serait open source : on s'appuie en effet sur le travail d'énormément de monde, que ce soit à travers des spécifications ou des bibliothèques elles-mêmes open source ; ça nous semblait donc évident de partager notre travail avec la communauté et bien sûr de contribuer aux projets que nous allions utiliser.

Choix du langage de programmation

Pour développer YOGA, nous avons choisi d'utiliser le langage Python. Plusieurs raisons à cela :

  • Python est un langage très pratique lorsqu'il s'agit de manipuler des données,
  • il dispose d'une bonne communauté et de nombreuses bibliothèques de qualité,
  • il s'interface très facilement avec du C et du C++ (ce qui tombe bien puisque sous le capot, nous allons faire appel à des bibliothèques écrites dans ces langages-là),
  • pour l'un de nos projets, nous voulions pouvoir appeler les fonctionnalités de YOGA directement depuis des workers, eux-mêmes codés en Python. Ces workers prennent en charge la conversion et l'optimisation automatique des modèles 3D importés sur ce projet.

Au-delà du choix de Python, ce projet nécessitera tout de même l'écriture de certaines parties en C pour s'interfacer avec les bibliothèques dont nous allons parler par la suite.

Conversion et optimisation des images

Commençons par parler de la partie « Image » de YOGA.

Pillow : décodage et manipulation des images d'entrées

Avant de vouloir optimiser des images, il faut être en mesure de les ouvrir. Ici on n'a pas cherché bien longtemps : on a utilisé Pillow, la bibliothèque de manipulation d'images de référence en Python.

Pillow supporte énormément de formats d'images en entrée, je vous mets la liste ci-dessous pour que vous puissiez le constater par vous-mêmes :

BMP, EPS, GIF, ICNS, IM, JPEG, JPEG 2000, MSP, PCX, PNG, PPM, SPIDER, TIFF, WebP, XBM, CUR, DCX, FLI, FLC, FPX, GBR, CD, ICO, IMT, IPTC/NAA, MCIDAS, MIC, MPO, PCD, PSD (2.5, 3.0), SGI, TGA, WAL, XPM, PALM, PDF, XV Thumbnails

Ce sont donc les formats que YOGA supportera en entrée.

Optimisation des images

Maintenant qu'on sait ouvrir la plupart des formats d'image courants, parlons de l'optimisation. Pillow sait bien évidemment enregistrer des images dans les formats qui nous intéressent, mais il utilise pour cela des bibliothèques classiques comme libjpeg et libpng, qui ne fournissent pas la compression la plus efficace possible.

Ces bibliothèques sont en effet programmées avec d'autres contraintes que les nôtres : elles doivent certes essayer de sortir des fichiers les plus petits possibles, mais en conservant un temps de traitement et une empreinte mémoire raisonnable.

Comme vous vous en doutez, l'optimisation à l'extrême de la compression des images a un coût : ça prend du temps, et ça consomme beaucoup de CPU et de RAM. Dans notre cas il ne s'agit toutefois pas d'un problème majeur : on effectue l'optimisation qu'une seule fois lors de l'importation de l'asset au projet ; nos contraintes seraient tout autres si nous devions effectuer cette opération de manière répétée.

Guetzli : l'encodeur JPEG optimal

Au tout début du projet, nous pensions partir sur MozJPEG pour compresser les JPEG. Il s'agit d'une bibliothèque développée par Mozilla réduisant la taille des fichiers de 2% à 10%, à qualité visuelle équivalente, par rapport à une image encodée avec libjpeg.

Mais en creusant davantage le sujet, nous sommes tombés sur Guetzli (oui, c'est imprononçable, et au cas où vous vous le demanderiez, ça signifie « biscuit » en Suisse allemand... 😅️), un tout nouvel encodeur JPEG développé par Google. Cette bibliothèque promet une réduction moyenne de 35% du poids des JPEGs par rapport à un encodage effectué par libjpeg, et ce pour une qualité visuelle souvent meilleure !

Voici un exemple tiré de l'article de blog de recherche sur l'intelligence artificielle de Google :

Exemple de compression avec Guetzli #01

À gauche, l'image originale, sans aucune compression à perte, au centre l'image compressée avec un algorithme classique (tel que celui de la libjpeg), et à droite l'image compressée avec Guetzli.

On peut constater que cette dernière affiche des artefacts de compression moins marqués, alors que dans le même temps le fichier généré est plus petit (Google ne fournit cependant aucun chiffre pour cet exemple en particulier).

Nous avons donc décidé de tester cette bibliothèque et finalement de l'intégrer à YOGA. Il y avait cependant un petit souci : Guetzli est développée en C++, et comme ce projet était tout récent, personne n'avait encore effectué le travail nécessaire pour permettre son utilisation en Python...

Nous avons donc développé PyGuetzli, un binding Python pour la bibliothèque Guetzli. Vous retrouverez bien sûr ce projet sur Github et sur PyPI.

ZopfliPNG : optimisation des PNG sur plusieurs aspects

Pour bien saisir les leviers d'optimisation dont nous disposons pour le format PNG, il faut nous y intéresser d'un peu plus près ainsi qu'aux différentes étapes nécessaires à l'encodage d'une image dans ce format.

Le format PNG est un format de compression d'image sans perte. Il supporte divers modes de couleurs, de 1 bit par pixel (image monochrome) à 48 bits par pixel, avec ou sans transparence (canal alpha), et peut également utiliser un mode de couleurs indexées, c'est-à-dire basées sur une palette allant jusqu'à 256 couleurs.

L'encodage d'une image en PNG se fait en 2 temps :

  • le filtrage
  • et la compression.

L'étape de filtrage va tenter de définir la meilleure façon de décrire l'image tout en prenant le moins de place possible. Pour imager un peu tout ça (#budumTsss 🥁️), plutôt que d'écrire « là il y a un pixel vert, suivit d'un autre pixel vert, et d'encore un autre pixel vert » on pourrait écrire directement « là il y a 3 pixels verts ». Plutôt que de répéter les informations de 2 lignes identiques, on pourrait juste se contenter d'écrire que la ligne courante est identique à la précédente. Bon c'est évidemment un peu plus compliqué que ça en pratique mais vous voyez le principe 😉️... Cette étape est celle qui aura le plus gros impact sur la taille du fichier final.

Une fois l'étape de filtrage achevée, les données qui en sortent sont compressées avec l'algorithme deflate, le même que celui utilisé par gzip et par zlib.

Zopfli (oui, c'est encore un nom un peu étrange, qui dérive de Butterzopf qui est une brioche tressée suisse... décidément... 😂️) s'attaque à cette seconde étape, et permet d'augmenter la compression deflate de 3,7% à 8,3%. C'est déjà un bon début, mais comme je l'ai dit un peu plus tôt, c'est sur l'étape de filtrage qu'il y a le plus à gagner... Et c'est là qu'entre en jeu ZopfliPNG.

ZopfliPNG va commencer sélectionner le bon mode de couleur en fonction de l'image, supprimer des informations inutiles (typiquement les métadonnées et le canal alpha si aucune transparence n'est utilisée) et travailler à fournir le résultat le plus optimal possible lors de l'étape de filtrage grâce à divers algorithmes. Une fois tout ceci fait, il terminera en compressant les données obtenues à l'aide de Zopfli.

C'est grâce à la prise en compte de tous ces aspects (choix du bon mode de couleur, suppressions de données inutiles, optimisation du filtrage et de la compression) que ZopfliPNG est extrêmement efficace dans sa tâche. D'après mon expérience personnelle, il n'est pas rare de réduire le poids d'une image PNG de plus de 50 % avec cette bibliothèque !

Zopfli étant un peu plus ancienne que Guetzli, elle était cette fois-ci déjà disponible en Python à travers le package Zopflipy... Ça, c'était la cerise sur le gâteau la brioche ! 🍒️

Conversion des modèles 3D

Dernier élément nécessaire à notre outil : la conversion des modèles 3D. Cette fois-ci c'est vers la bibliothèque Assimp que l'on s'est tournés.

Cette bibliothèque supporte un grand nombre de formats 3D et permet de les convertir des uns vers les autres. Sur le papier ça sonne bien... Le problème, c'est que Assimp ne prenait pas totalement en charge le format glTF 2.0, et ne supportait pas du tout le format GLB... Pas de chance... Alexis s'est donc penché sur le sujet : il a implémenté la prise en charge du GLB, et effectué quelques autres contributions et corrections de bugs.

À travers Assimp, YOGA supporte aujourd'hui les formats 3D suivants en entrée :

3D, 3DS, 3MF, AC, AC3D, ACC, AMJ, ASE, ASK, B3D, BLEND, BVH, COB, CMS, DAE, DXF, ENFF, FBX, glTF 1.0 + GLB, glTF 2.0, HMB, IFC-STEP, IRR, IRRMESH, LWO, LWS, LXO, MD2, MD3, MD5, MDC, MDL, MESH, MESH.XML, MOT, MS3D, NDO, NFF, OBJ, OFF, OGEX, PLY, PMX, PRJ, Q3O, Q3S, RAW, SCN, SIB, SMD, STL, STP, TER, UC, VTA, X, X3D, XGL, ZGL

Et permet donc de générer des glTF et des GLB en sortie.

C'est cool tout ça, mais comment ça s'utilise ?

Pour utiliser YOGA il faut commencer par l'installer (sans blague 😜️), soit en passant par pip, le gestionnaire de paquets Python, soit en utilisant le binaire standalone si vous êtes sous Windows.

Ensuite c'est assez simple, YOGA fournit une ligne de commande permettant de faire tout ce dont vous avez besoin.

Par exemple pour optimiser une image PNG, vous pourrez utiliser la commande suivante :

yoga  image  input.png  output.png

Si vous souhaitez convertir une image en JPEG tout en contrôlant la qualité du résultat final, il vous faudra alors utiliser cette commande :

yoga  image  --ouput-format=jpeg  --jpeg-quality=84  input.png  output.jpg

Et pour convertir un modèle 3D, ça n'est pas plus compliqué :

yoga  model  input.fbx  output.glb

Vous retrouverez bien sûr toutes les informations utiles et divers exemples dans la documentation de YOGA.

Aujourd'hui, YOGA est utilisé sur pas mal de projets 3D / WebGL chez Wanadev, et personnellement je l'utilise régulièrement pour optimiser les images des sites sur lesquels je travaille ainsi que pour mon blog.

Nous espérons que ce projet pourra vous être utile pour optimiser vos assets, que vous travailliez sur de la 3D ou sur des sites Web. 😁️

Commentaires

Intéressant comme outil. Ça serait compliqué de l'intégrer à des softs tels que Blender par exemple ? Les graphistes n'aiment généralement point trop tâter de la ligne de commande, avoir un bouton à cliquer et des options à cocher (enfin une UI quoi ^^) faciliterait l'adoption. Enfin c'est déjà super de pratiquer l'open-source 👍🏾
Étant donné que YOGA est également une bibliothèque Python et que Blender se script en Python, il ne devrait pas être trop compliqué d'écrire un plugin Blender qui rajoute un bouton dans l'interface :)
  • Couverture de l'article Event Bus : Le secret d'une architecture Symfony réellement découplée
    Event Bus : Le secret d'une architecture Symfony réellement découplée

    Il y a 3 semaines

    Imaginez : votre utilisateur clique sur "Commander". En coulisses, le domaine Stock doit décrémenter les quantités, le domaine Facturation doit générer une facture, et le domaine Notification doit envoyer un email de confirmation. Trois domaines, une seule action... et un spaghetti de dépendances en perspective. 🍝

    Et si ces domaines pouvaient collaborer sans jamais se connaître ?

    C'est exactement ce que permet l'Event Bus. Mais avant de foncer tête baissée, une question se pose : Symfony propose déjà l'EventDispatcher pour gérer les événements. Alors pourquoi introduire un nouveau concept ?

    Spoiler : ce ne sont pas les mêmes outils, et les confondre peut vous coûter cher.

    Dans cet article, nous allons démystifier leurs différences et découvrir comment l'Event Bus de Symfony Messenger vous permet de construire une architecture réellement découplée.

    Ce que vous allez apprendre :

    • Les différences fondamentales entre EventDispatcher et Event Bus
    • Quand utiliser l'un plutôt que l'autre
    • Comment configurer un Event Bus avec Symfony Messenger
    • Créer une architecture événementielle découplée
  • Couverture de l'article CQRS avec Symfony Messenger : Domptez la complexité de vos applications
    CQRS avec Symfony Messenger : Domptez la complexité de vos applications

    Il y a 1 mois

    Vous êtes-vous déjà retrouvé face à un controller Symfony surchargé qui gère à la fois la validation, la logique métier, la persistence et les réponses HTTP ? Si oui, le CQRS est fait pour vous !

    Le CQRS (Command Query Responsibility Segregation) est un pattern architectural qui sépare clairement les opérations d'écriture (Commands) et de lecture (Queries). Combiné avec Symfony Messenger, il vous permet de :

    • Organiser votre code de manière claire et maintenable
    • Séparer les responsabilités pour respecter les principes SOLID
    • Valider vos données avant même qu'elles n'atteignent votre logique métier
    • Gérer les transactions de base de données de manière élégante
    • Préparer votre application pour l'asynchrone sans effort

    Dans cet article, nous allons explorer les Commands (écriture) et les Queries (lecture) à travers un exemple concret de gestion de bibliothèque.

  • Couverture de l'article LockPass : automatiser la sauvegarde des mots de passe
    LockPass : automatiser la sauvegarde des mots de passe
    Développement

    Il y a 3 mois

    Chez Wanadev, on a récemment changé de gestionnaire de mot de passe. On est passés de la solution états-unienne Zoho Vault à LockPass, édité par l'entreprise française LockSelf.

    Aussi fiable que puisse être le prestataire choisi, il est essentiel pour nous d'avoir une sauvegarde de nos mots de passe en dehors de chez lui pour ne pas nous retrouver dans la panade le jour où il y a un souci.

  • Couverture de l'article Figma Make : enfin une passerelle prometteuse entre design et code grâce à l'IA
    Figma Make : enfin une passerelle prometteuse entre design et code grâce à l'IA

    Il y a 8 mois

    Depuis quelques années, les outils d'IA pour générer des intégrations d'interfaces à partir de maquettes fleurissent. On en a testé plusieurs chez WanadevDigital : de Locofy à Uizard, en passant par Framer AI. Tous ont leurs qualités, mais jusqu’ici, il manquait un vrai pont stable entre les intentions du designer et la réalité du code front.

    L’arrivée de Figma Make change la donne. Et si je devais résumer son impact en une phrase : ça fonctionne, et ça fonctionne pour tout le monde, designers, développeurs et intégrateurs !

  • Couverture de l'article Maîtriser la traduction (i18n) dans un projet web - Partie 1 : Configurer proprement
    Maîtriser la traduction (i18n) dans un projet web - Partie 1 : Configurer proprement

    Il y a 11 mois

    Mettre en place l'internationalisation (i18n) dans un projet web peut sembler simple. Cependant, de nombreux projets se retrouvent avec des configurations de traduction mal gérées, difficiles à maintenir ou à faire évoluer à mesure que l'application grandit. Une stratégie i18n robuste est essentielle pour offrir une expérience utilisateur fluide dans plusieurs langues.

    Je vous décris ici, les pratiques que nous avons établies chez Wanadev au fil des années d'expérience pour mettre en œuvre et gérer les traductions dans les projets Vue. Bien que les exemples soient spécifiques à Vue, la plupart de ces pratiques peuvent être appliquées à n'importe quel framework.

  • Couverture de l'article Maîtriser la traduction (i18n) dans un projet web - Partie 2 : Conseils pour une localisation gérable et évolutive
    Maîtriser la traduction (i18n) dans un projet web - Partie 2 : Conseils pour une localisation gérable et évolutive

    Il y a 11 mois

    Dans la partie 1, nous nous sommes concentrés sur la mise en place d'une base solide pour la gestion des traductions dans un projet Vue. Maintenant que votre système de traduction est opérationnel, il est temps d'examiner de plus près comment structurer, gérer et faire évoluer vos fichiers de traduction de manière efficace.

    Cette partie couvrira les bonnes pratiques que nous utilisons chez Wanadev pour créer des clés de traduction maintenables, éviter les pièges courants et garantir que vos fichiers de traduction restent propres et évolutifs au fur et à mesure que votre projet grandit.