GEGL 0.3.0 et babl 0.1.12 sont de sortie

Posté par  (site web personnel, Mastodon) . Édité par BAud, bubar🦥, palm123, Benoît Sibaud, patrick_g et RyDroid. Modéré par patrick_g. Licence CC By‑SA.
Étiquettes :
58
16
juin
2015
Graphisme/photo

Babl est une bibliothèque de conversion de formats de pixels. GEGL est un moteur de traitement d'images en graphe, construit autour de babl.

Ce sont deux bibliothèques nées du projet GIMP, qui constitueront son nouveau moteur interne d'édition d'image dès la version majeure suivante (GIMP 2.10, date de sortie non connue). Mais ce sont aussi bien plus que les moteurs de GIMP, ce sont surtout des projets indépendants qui commencent à être utilisés par d'autres projets, que ce soient des logiciels Libres, comme des services propriétaires.

De nouvelles versions viennent de sortir (GEGL 0.3.0 et babl 0.1.12), et je profite donc de l'occasion pour présenter ces logiciels, leurs spécificités, leurs historiques et leurs utilisations dans la seconde partie de la dépêche.

Sommaire

babl

babl

Babl est une bibliothèque de conversion de formats de pixels. Quand on parle de formats de pixels, on entend la représentation du pixel en mémoire comme par exemple l'espace de couleur (RVB, CIE Lab…), une profondeur de couleur (nombre de bits utilisés par canal…) ou le type de données (entiers, flottants…), utilisés pour la représentation et non un format de fichier d'image pour enregistrer sur un espace de stockage.

Ainsi, cela permet par exemple de traduire des représentations de pixels RVB, RVBA, CMJN, CIE Lab, etc. Babl ne fait pas de traitement d'image dans le but de la modifier, mais uniquement des conversions (les seules modifications étant par exemple de la perte éventuelle de données si on passe à un format moins précis, ou de l'approximation entre deux formats qui ne sont pas parfaitement équivalents bijectivement).

Cette bibliothèque fournit une API extrêmement petite (le dépôt de source fait 4 Mo en tout, pour moins de 900 ko de code), simple et stable par conception.

GEGL

GEGL logo

GEGL est une bibliothèque permettant de traiter des images sous la formes de graphes de nœuds, chaque nœud pouvant avoir des entrées, sorties et une configuration.

Un nœud représente ce qu'on appelle une "opération". Typiquement, il y a des opérations servant principalement d'entrée à partir d'image stockées en mémoire ou sur disque, des opérations servant de sortie pour enregistrer ou afficher de nouvelles images, et des opérations intermédiaires servant de filtres mathématiques pour modifier l'image entre le nœud d'entrée et de sortie. Ces opérations intermédiaires sont par exemple:

  • les filtres classiques que l'on aurait dans le menu "Filtres" de GIMP (flou, netteté, filtres artistiques, etc.);
  • des modifications des couleurs (balance, saturation, etc.);
  • de la composition (avec 2 images en entrée pour 1 en sortie, ce qui correspond notamment aux modes de composition des calques de GIMP : normal, multiply, add, substract, etc.):
  • des traitements plus utilitaires (découpe, redimensionnement, rotation, etc.):

Formats d'images

GEGL sait de base ouvrir et sauver de nombreux formats d'images, des plus classiques (PNG, JPEG, BMP, tiff…) aux formats plus spécialisés (RAW, OpenEXR…), et même des formats vectoriels (SVG…). En outre, il sait tirer profit d'autres sources, comme des fichiers vidéos (avec ffmpeg) ou de l'acquisition vidéo directe (v4l).

GEGL sait aussi tirer profit de très hautes précisions de couleur (high bit precision depth), permettant par là de travailler sur des images à 8, 16, 32 ou même 64 bits par canal (et en nombre entier ou flottant, avec codage linéaire ou correction gamma), ainsi que de charger et sauver les données en haute précision lorsqu'on utilise un format l'autorisant (comme ce sera le cas des fichiers d'image de GIMP, XCF, à la sortie de GIMP 2.10).
Cela permet des traitements intermédiaires complexes avec beaucoup moins de perte de données et de qualité.

Traitement d'image en graphe

Si un traitement simple (toute action basique sur une image) peut être représenté en un nœud unique avec l'image en entrée et le résultat en sortie, un traitement complexe est un graphe liant de multiples nœuds.

Schématiquement, travailler sur une image ressemble à l'éditeur de nœuds de Blender.
Éditeur de Nœuds de Blender

Le traitement par graphe a deux intérêts principaux:

  1. Du traitement "non destructif" si l'interface logicielle le permet. Cela implique un flot de travail où on ne touche jamais l'image originelle. Elle est en entrée, et toute modification du graphe influe sur les pixels dans le nœud de sortie (qui peut être théoriquement le même fichier que celui chargé en entrée, mais alors cela rend le concept moins intéressant). On peut à tout moment supprimer ou modifier les paramètres d'une opération sans craindre pour l'image de base (plutôt qu'appliquer, voir, annuler, refaire avec d'autres paramètres, revoir, et ainsi de suite). Le concept de traitement non destructif rend donc presque caduque le concept d'historique et d'annulation des opérations.
  2. La limitation de la dégradation d'une image qui se produit lors de l'application de filtres. Un exemple explicite: une simple rotation d'image à 45° dégrade une image. Si on se rend compte qu'on préfère une rotation à 90° et qu'on applique donc une nouvelle rotation à 45° : on a alors dégradé 2 fois l'image et on se retrouve avec une qualité très amoindrie par rapport à l'original. Or une rotation à 90° directement se fait entièrement sans perte (puisqu'on travaille dans des matrices de pixels). Si on avait sauvé un graphe et qu'au lieu d'appliquer 2 rotations successives, on modifiait seulement les paramètres de la première rotation, on se retrouve avec un traitement sans perte. Bien sûr dans le cas général, on aura de la dégradation, mais celle-ci peut être limitée par la création d'un graphe optimal qui ne sera appliqué qu'une seule fois.

Développement d'opérations maison

GEGL est livré avec de nombreuses opérations par défaut, mais cela ne le rend pas exhaustif pour autant. La bibliothèque est conçue pour être étendue à volonté par les développeurs.

Vous avez besoin d'ajouter la prise en charge d'un format d'image maison ou peu répandu ? Vous avez besoin d'un filtre qui n'a pas été déjà développé, ou vous avez développé une version algorithmique bien plus efficace ? Vous avez besoin de traitements un peu particuliers qui servent spécifiquement à votre application et n'auraient aucun sens en upstream ? Il n'y a pas besoin d'attendre que vos opérations soient intégrées dans GEGL ou de le patcher. Vous pouvez créer et étendre GEGL à l'infini.

En ce sens, GEGL est un "cadriciel" (framework) pour la création d'opérations de traitement d'image.

Scriptable

Grâce à l'introspection GObject, cette bibliothèque est accessible à de nombreux langages tiers, notamment des langages de scripts (Python, Ruby, JavaScript, PHP…). Cela signifie notamment qu'il est possible de l'utiliser pour des petits scripts rapides ou en ligne de commande, et en augmente considérablement l'accès.

Bien sûr, certains wrappers existent déjà, permettant une encore meilleure intégration aux spécificités d'un langage. Le wrapper Python est un bon exemple de bibliothèque maintenue.

Voici par exemple un court script Python pour inverser une image avec GEGL :

import gegl

x = gegl.Graph("png-load", "invert", "png-save")
x[0].path = "/home/user/input.png"
x[2].path = "/home/user/output.png"
x()

Enfin, il est possible, avec l'outil console gegl fourni par défaut de lui passer des graphes d'opérations GEGL en XML, pour un traitement d'image scripté.

Performance : OpenCL et multi-threading

GEGL prend en charge OpenCL, langage de programmation et bibliothèque « conçu pour programmer des systèmes parallèles hétérogènes comprenant par exemple à la fois un CPU multi-cœur et un GPU » (cf. OpenCL sur Wikipédia). En d'autres termes, cela permet de faire travailler votre carte graphique, en plus de votre processeur, en parallèle, pour accélérer les calculs.
Cela couplé avec du traitement "thread-safe" (pour vous autoriser à utiliser du multi-thread dans votre propre code), et un début de multi-thread dans GEGL même, nous essayons d'obtenir une utilisation aussi performante que possible.

Traitement de très grandes images

Un des points forts de GEGL est sa gestion intelligente des buffers (objet gérant les données bitmap à l'aide des formats de pixel babl), permettant à l'utilisateur d'allouer une partie seulement de sa mémoire, mais également de travailler sur des images plus grandes que la mémoire disponible. Un buffer GEGL est une matrice de dalles, chacune pouvant avoir un backend de stockage différent, en particulier en mémoire RAM, ou en swap lorsque l'on dépasse la mémoire allouée.
Dans l'avenir, on pourrait avoir un backend en mémoire GPU, en réseau, etc.

Cela permet ainsi d'avoir une majeure partie d'une image en mémoire, ainsi qu'une autre partie en swap disque, lors du travail sur de très grandes images. Le travail en dalle autorise aussi un travail plus efficace lorsqu'il s'applique sur de petites parties d'une image.

Utilisation actuelle de GEGL

Historique et relation à GIMP

GEGL est un projet très ancien avec pour raison initiale son intégration dans GIMP. Les plus anciennes annonces officielles de GIMP datent de 1995, et le plus vieux commit daté du dépôt de source est du 1er janvier 1997. De son côté, le plus vieux commit daté du dépôt de source de GEGL est du… 2 janvier 1997, avec comme auteur "People doing a 16 bpc version of gimp" (traduisible en « personnes faisant une version de gimp à 16 bits par canal »). À un jour près, les dépôts de source des deux projets ont donc été créés quasiment en même temps. GEGL n'est donc pas seulement né de GIMP, c'est presque son frère ! Certains n'ont cependant pas cru en GEGL (du moins pas suffisamment pour y consacrer du temps, ce qui a même engendré un fork célèbre de GIMP), et il faut avouer que cela a pris son temps (presque 20 ans !).
La première tentative d'intégration n'est apparue qu'en 2008 dans GIMP 2.6, soit 11 ans après la naissance du projet GEGL. Cette version comprenait seulement un port des opérations de couleur sur GEGL, optionnel (par défaut les opérations de couleur utilisaient toujours le code non GEGL) et un outil « GEGL » expérimental (donc non disponible par défaut dans la boîte à outils). Ainsi, cela initia un balbutiement du port où rien n'était activé par défaut, mais pouvait l'être par l'utilisateur aventureux.

GIMP 2.8 a vu plusieurs autres ports de fonctionnalités sur GEGL, notamment la projection des calques, les modes de calques, les sélections flottantes et le redimensionnement des calques. Néanmoins là encore, tous ces ports restaient désactivés par défaut dans la version stable.

Ainsi, bien que GEGL soit présent dans GIMP depuis 7 ans, cette partie du code est techniquement inexistante pour les utilisateurs, du code mort, inutilisé, dans le reste du programme. Dans les faits, GIMP ne contient donc pas encore de code GEGL du point de vue de l'utilisateur de base. C'est en 2012 que, finalement, deux contributeurs majeurs de GIMP (Mitch, mainteneur de GIMP et Pippin, mainteneur de GEGL) se sont un jour enfermés chez Mitch sans vraiment le planifier, et ne sont ressortis à la lumière du jour que 3 semaines plus tard, après avoir porté 90 % du cœur applicatif de GIMP vers GEGL.
Invasion des chèvres

Ces 90 % n'étaient en fait que le début. Car en plus du cœur, il restait toute la plateforme applicative de GIMP (les greffons, filtres, etc.) à porter, l'interface graphique à mettre à jour pour profiter du nouveau cœur, les détails à fignoler et la bibliothèque à optimiser.

D'ailleurs, c'est seulement quelques mois après cet évènement que je suis arrivé moi-même dans ce projet : un peu par hasard aussi et sans le vouloir, un jour j'ai donné un patch. À l'époque, je n'avais même jamais entendu parler de GEGL. Eh bien, laissez-moi vous dire que la version de développement de GIMP, à ce moment là, était totalement inutilisable, et ce, encore jusqu'à il y a à peine deux ans, car si lente que l'on pouvait donner un coup de pinceau puis aller préparer un café en regardant le trait s'afficher (ou presque !). C'est seulement très récemment que la bibliothèque a été massivement optimisée et commence à peine à atteindre la vitesse de la version précédente du cœur de GIMP pour les opérations de base (voire est plus rapide, dans pas mal de cas).

Un avant-goût de GIMP 2.10

GIMP 2.10 sera donc la première version où GEGL sera utilisée, remplaçant le code originel de traitement d'image, ce qui est une étape majeure de l'histoire de GIMP. En fait, ce ne sera même pas une alternative. Tout traitement d'image utilisera désormais GEGL.

Cela donnera à GIMP la prise en charge de nombreux nouveaux formats de fichiers, de la haute précision de couleur, une prévisualisation (partielle ou totale) des effets directement dans le canvas pour peaufiner les paramètres, et surtout à terme du traitement d'image non destructif. Mais… ceci est une histoire pour une prochaine news… :-)

Intégration dans les bibliothèqes graphiques

Il existe plusieurs bibliothèques intermédiaires pour simplifier l'utilisation de GEGL :

Autres projets

Bien que GEGL soit né de GIMP, c'est avant tout dorénavant un projet à considérer comme une bibliothèque indépendante. Et nous espérons que de plus en plus de projets l'intégreront dans leur code.

Jon Nordby est l'un des grands implémenteurs de GEGL. Il a ainsi intégré GEGL comme backend supplémentaire dans MyPaint.
Il a également expérimenté le traitement vidéo par GEGL en implémentant un filtre GEGL pour GStreamer permettant d'intégrer un graphe GEGL dans son traitement vidéo, bien que celui-ci n'ait finalement pas été intégré upstream, non par intérêt des développeurs GStreamer, mais simplement par manque de temps pour le porter sur l'interface changeante du projet (laissant ouverte la possibilité).
Son dernier gros projet, imgflo, est un serveur web pour le traitement d'image, avec une interface graphique orientée graphes.
imgflo
Jon Nordby étant désormais employé par The Grid, plateforme commerciale de création de sites web générés par une intelligence artificielle, imgflo, et par conséquent GEGL est utilisé comme composant majeur pour toute l'automatisation des traitements d'image (ajustements de couleurs pour que les images utilisateurs s'adaptent au jeu de couleur du site, par exemple). Ce traitement passe par imgflo-server.
Enfin le travail de Jon Nordby sur imgflo montre un des grands intérêts de GEGL : Jon a développé un format JSON pour déclarer des graphes GEGL qui peuvent alors être sauvés dans imgflo puis chargés dans GIMP, par exemple. Il est ainsi très simple de partager des processus de travail complets entre applications pour automatiser des traitements sur de multiples images.
Bien entendu, cela a permis plusieurs améliorations de GEGL, sponsorisées par The Grid.

En dehors de ce développeur très prolifique, GNOME-photos dépend désormais de GEGL-gtk.
Certains autres projets bien établis ont aussi montré leur intérêt pour GEGL. Darktable par exemple avait entamé un port GEGL, en 2011 déjà, et annoncé qu'ils le termineraient quand GIMP prendrait en charge la haute précision des couleurs et l'édition non destructrice, ceci afin de pouvoir partager une image entre les deux applications. On est donc à la moitié du chemin.
Blender a aussi montré son intérêt pour GEGL, dans le but de mettre au niveau leur compositeur (« node editor », ou éditeur de nœuds en français), vieux et souffrant d'un design inadéquat pour de la composition efficace, comme le montre cette page de wiki proposant l'usage de GEGL comme alternative à une ré-implémentation maison. Bien sûr, rien ne dit s'ils agiront dans ce sens au final, mais ce serait une très bonne nouvelle, bénéfique pour tous les projets.

Nous découvrons aussi des plus petits projets qui utilisent GEGL. Par exemple lors des Libre Graphics Meeting 2014, Manuel Quiñones a annoncé son début d'implémentation d'un logiciel d'animation 2D, xsheet. On ne sait pas vraiment ce que cela va donner, mais il est toujours intéressant de voir apparaître des développeurs extérieurs qui commencent à utiliser GEGL.

Ces divers exemples montrent bien que le projet atteint une certaine maturité, et il faut maintenant continuer dans cette voie.

Note de sortie

Nous terminerons par un survol de la liste des nouveautés dans GEGL 0.3.0 et babl 0.1.12, qui comptent 92 contributeurs.

  • Amélioration du parallélisme et de la sécurité du multi-tâche.
  • OpenCL est désormais activé par défaut si détecté.
  • Multi-tâche expérimental, activé avec la variable d'environnement GEGL_THREADS=<nombre de tâches>.
  • Rendu mipmap expérimental permettant la prévisualisation sur des versions de petite taille de manière transparente, avec la variable d'environnement GEGL_MIPMAP_RENDERING=true.
  • Nouveau site web, galerie d'opérations et navigateur d'API.
  • Découverte et documentation des opérations en ligne de commande, avec les options suivantes sur la commande gegl: --list-all (liste de toutes les opérations disponibles), --properties (imprime les détails d'une opération) et --exists (teste l'existence d'une opération).
  • Prise en charge des URI pour le chargement d'image.
  • Chargement de meta-opérations en format JSON.
  • Nouvelles opérations: alien-map, antialias, apply-lens, bilateral-filter, bump.map, cartoon, channel-mixer, color-enhance, color-exchange, color-reduction, color-rotate, convolution-matrix, copy-buffer, cubism, deinterlace, diffraction-patterns, distance-transform, displace, edge, emboss, engrave, exposure, fractal-trace, high-pass, image-compare, illusion, invert-gamma, lens-flare, linear, linear-gradient, mosaic, motion-blur-circular, motion-blur-zoom, noise-cell noise-cie-lch, noise-hsv, noise-hurl, noise-pick, noise-rgb, noise-simplex, noise-spread, n-point deformation ops, oilify, panorama-projection, photocopy, plasma, radial-gradient, red-eye-removal, scale-size-keep-aspect, softglow, stretch-contrast, texturize-canvas, tile-glass, tile-seamless, tile-paper, tile, warp, whirl-pinch, wind, cache, cast-format, lcms-from-profile, npy-save, webp-load, webp-save, scale-ratio, scale-size, seamless-clone, sinus, supernova, value-propagate, video-degradation
  • Réimplémentation de gaussian-blur (plus rapide et précis).
  • Nouveau backend de tuiles par défaut, écrivant sur le disque en tâche séparée.

Aller plus loin

  • # perf ?

    Posté par  (site web personnel) . Évalué à 7.

    J'imagine que gegl ne fait pas d'optimisation, il n'est pas capable d'éviter la création d'un buffer lorsque l'on passe d'un noeud d'un graph à un autre ? Mais est-ce qu'il pourra le faire à terme ?

    Une correction gamma suivi d'une augmentation de luminosité est une simple opération à faire sur chaque pixel, elles peuvent être fusionné, et il n'y a pas besoin de passer par un buffer intermédiaire.

    Je pause cette question, car pour avoir utiliser darktable, qui ne fait pas de destructif non plus, il arrive vite à manger toute la mémoire, avec seulement quelques opérations.

    "La première sécurité est la liberté"

    • [^] # Re: perf ?

      Posté par  (site web personnel, Mastodon) . Évalué à 7.

      J'imagine que gegl ne fait pas d'optimisation, il n'est pas capable d'éviter la création d'un buffer lorsque l'on passe d'un noeud d'un graph à un autre ?

      Je ne suis pas le mieux calé pour parler de GEGL. Aucun de mes patchs là-bas ne sont algorithmiques. J'y touche plus dans le cadre de problèmes rencontrés dans GIMP que pour GEGL même. Donc ma première réponse, c'est que je ne sais pas s'il fait la moindre optimisation pour réduire des opérations.

      Mais est-ce qu'il pourra le faire à terme ?

      S'il ne fait pas déjà, je ne vois rien qui empêche de le faire à l'avenir. Ensuite il y a des cas plus simples que d'autres. Par exemple, il y a toute une série d'opérations qui sont matricielles (les transformations mathématiques: rotation, translation, réflexion, changement d'échelle…), et bon ça la multiplication de matrice, c'est pas compliqué! Si on a plusieurs opérations matricielles de ce style qui se suivent, on devrait être capable très simplement de les combiner en une seule de façon transparente (tout en gardant une visualisation multi-nœuds).
      D'ailleurs c'est peut-être déjà fait, aucune idée!

      Pour d'autres types d'opérations, j'imagine que ça dépend. Beaucoup de choses simples à faire pour un humain ne le sont pas pour un ordi (et vice-versa). Donc à part programmer des cas particuliers partout (possible aussi, et cela reste intéressant si certains cas particuliers sont des optimisations connus pour faire gagner énormément et difficilement automatisables autrement), ce qu'il faut, c'est réorganiser le code pour créer des schémas.
      Par exemple, une opération qui traite chaque pixel l'un après l'autre, ce dont tu parles, si c'est entièrement codé comme une boucle de programmation, c'est dur à réduire (car un programme ne "comprend" pas son propre code!). Par contre, on devrait être capable de faire une sous-classe d'opération pour les opérations "pareil sur tous les pixels" où — au lieu de faire des boucles sur les pixels — on donne juste l'opération mathématique à appliquer sur le contenu d'un pixel. À ce moment là, le programme sera capable d'enchaîner les opérations dans la même boucle, ce qui est déjà une bonne optimisation.

      Ensuite il faut faire attention à ne pas introduire d'erreurs. Dans notre cas précis, cela fonctionne si les seules données extérieures sont indépendantes de l'image. Si par exemple, la modification d'un pixel utilise les données d'autres pixels, alors ce n'est plus possible (du moins plus aussi facile). En effet, la seconde opération utiliserait le pixel après modification par la première opération. Or si on exécute l'optimisation "2 opérations en une", elle n'aurait accès qu'au pixel avant modification par la première opération! On introduirait donc une erreur puisque le résultat serait différent.

      Enfin voilà, tout est possible, mais faut bien y réfléchir et trouver l'architecture idéale.
      Et dans tous les cas, il est conseillé de faire un programme qui marche bien avant de l'optimiser. Règle de base du bon programmeur. :-)

      Je pause cette question, car pour avoir utiliser darktable, qui ne fait pas de destructif non plus, il arrive vite à manger toute la mémoire, avec seulement quelques opérations.

      Ensuite y a pas de miracle. Le non-destructif a de gros avantages, mais ce n'est pas magique. Ça a son prix. La qualité a toujours son prix, c'est la raison pour laquelle dans beaucoup de workflow, il ne sert à rien d'en faire. C'est comme utiliser du jpg pour le web. Utiliser du png est théoriquement bien meilleur, mais dans 99% des cas, si l'usage est "web", alors on va exporter en jpg et de toutes façons personne (pas même soi-même) ne verra la différence. Par contre, quand on exporte pour travailler ailleurs, ou intégrer dans une vidéo, c'est du PNG.
      Il faut savoir choisir les pratiques en fonction des besoins.

      De manière générale travailler sur des données médias (son, et encore plus image), ça prend une place de dingue.
      Pour ZeMarmot, nos ressources pour le teaser de 1 min font plusieurs GO de données! Notamment on travaille en HD avec des fichiers XCF qui peuvent dépasser 100 MB. Et encore on a des retours de gens qui travaillent dans des secteurs où leurs fichiers XCF font plusieurs GB! (ceux qui scannent des images à extrêmement haute résolution par exemple, ou des secteurs médicaux, scientifiques, etc.) Donc oui bien sûr, dans leur cas, rien qu'ouvrir l'image peut prendre toute la mémoire sur un ordi de base du supermarché.
      Bon ces tailles de fous ne sont pas la majorité. Mais plusieurs centaines de MB par contre, c'est courant. Donc de nos jours, si tu travailles des images (photos, dessins…), avoir 8GB de RAM, c'est pas grand chose. Être à 16 ou même 32 GB, c'est pas du luxe. C'est inhérent même aux qualités et résolutions qu'on atteint maintenant avec l'imagerie (même amateur).

      En conclusion: oui on peut probablement optimiser, et on le fera sûrement (développeurs recherchés! Si tu développes et souhaites ajouter le type d'optimisation dont on a parlé ici, tu es le bienvenu!). Mais faut pas non plus croire au miracle. Si tu charges des images de dizaines de Mégas par défaut (cas des images DSLR de nos jours), et que tu fais des dizaines d'opérations dessus, oui ça monte vite.
      Faire du destructif est une possibilité et peut très bien être un choix valide. Tu perds en flexibilité et potentiellement en qualité du résultat final par contre.

      Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

      • [^] # Re: perf ?

        Posté par  (site web personnel) . Évalué à 2.

        Disons que je serais capable de faire ce genre d'optim, mais pas en C. Cela serait trop douloureux.

        Peut-être qu'il est possible de faire une "optimisation" du graph qui fusionnerait des opérations, mais de façon externe à la lib ?

        "La première sécurité est la liberté"

        • [^] # Re: perf ?

          Posté par  (site web personnel, Mastodon) . Évalué à 2. Dernière modification le 16 juin 2015 à 17:37.

          Peut-être qu'il est possible de faire une "optimisation" du graph qui fusionnerait des opérations, mais de façon externe à la lib ?

          Je ne comprends pas ce que tu veux dire… Comment est faite cette optimisation? Qui l'a fait si ce n'est pas GEGL? Quelle information utilise-t-il pour déterminer comment optimiser des nœuds sans en connaître le contenu?

          Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

          • [^] # Re: perf ?

            Posté par  (site web personnel) . Évalué à 2.

            J'ai lu un peu vite, mais la boite qui fait des site web, elle génère bien des graphes à la volé, donc il y a bien un moyen de les voir sous forme de data et non de code.

            "La première sécurité est la liberté"

            • [^] # Re: perf ?

              Posté par  (site web personnel, Mastodon) . Évalué à 1.

              Oui, GEGL est fait pour être extensible, et les graphes visibles de l'extérieur, avec beaucoup d'introspection. Donc oui, on voit les détails du graphe de l'extérieur, on peut même récupérer automatiquement la description des opérations, la liste des paramètres et leur description, etc. Tout est fait pour l'introspection et la visibilité du graphe.

              Mais pour faire de l'optimisation, faut connaître l'algorithme exact. Sinon tu te remets à réimplémenter tous les algorithmes par du code extérieur (et dans ce cas, pourquoi utiliser GEGL?) et surtout c'est de l'approximation. Optimiser, oui, mais uniquement si tu as exactement le même résultat (au pixel près) avec et sans l'approximation.

              Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

              • [^] # Re: perf ?

                Posté par  . Évalué à 3.

                Faut faire une passe d'optimisation qui te transforme le graphe initial en graphe optimisé avant d'exécuter.

                Et garder quand même le graphe initial pour pouvoir le modifier et re-générer l'optimisé dans ce cas.

      • [^] # Re: perf ?

        Posté par  . Évalué à 4.

        Par exemple, une opération qui traite chaque pixel l'un après l'autre, ce dont tu parles, si c'est entièrement codé comme une boucle de programmation, c'est dur à réduire (car un programme ne "comprend" pas son propre code!). Par contre, on devrait être capable de faire une sous-classe d'opération pour les opérations "pareil sur tous les pixels"

        On en est déjà capable ! C'est assez facile dans certains langages, et intégré dans d'autres. Typiquement tu cherches une fonction map : (Pixel -> Pixel) -> (Image -> Image), et la représenter sous forme d'une variable concrète v = Map (traite_pixel) avec une fonction applique_map qui permet d'exécuter cette commande.

        Ensuite, sous réserve d'absence d'effets de bord1, tu peux affirmer que:

        map f . map g = map (f . g)
        

        Ce qui évite de construire des structures intermédiaires pour le calcul. De manière plus concrète, c'est simplement faire une réduction du graphe avant de lancer le traitement (puisque toutes les actions sont déjà sous forme de nœud dans un graphe, l'étape « représentation » serait simplement l'ajout d'une indication du type : « sujet à fusion »)

        [1] : Ce qui peut être vraiment restrictif sur les fonctions qui peuvent bénéficier de cette optimisation …

        • [^] # Re: perf ?

          Posté par  (site web personnel, Mastodon) . Évalué à 4.

          On en est déjà capable !

          Bien sûr qu'on en est capable techniquement (comme un peu tout). Je parle de GIMP en particulier et j'entendais "ce qu'on pourrait faire", c'était pas une question de savoir si les langages ont cette capacité (quand même, heureusement qu'ils l'ont!).

          Aussi ce que je disais, c'est surtout qu'il faut adapter l'architecture du code. À lire certains commentaires, on a l'impression que c'est trop facile, "il suffit d'optimiser" (yakafokon). Alors que pour le permettre, il faut identifier les cas possibles d'optimisation (comme je disais plus haut, des suites d'opérations matricielles, des ops sur des pixels si et seulement si elles ne sont pas dépendantes d'autres pixels de l'image, etc.), puis éventuellement adapter l'architecture du programme pour rendre ces optimisations faisables. Et ainsi de suite.

          Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

          • [^] # Re: perf ?

            Posté par  . Évalué à 2.

            il faut identifier les cas possibles d'optimisation

            En effet, mais étant donné que tout le code qui génère l'image est déjà sous forme d'un graphe (donc sous forme de données manipulables), il suffit d'ajouter des types de nœuds spécifiques pour des opérations génériques:

            • map : modification de chaque pixel de la structure par une fonction
            • convolution par une autre matrice
            • move : construire une nouvelle matrice en utilisant (sans modification) des pixels d'une autre (possible changement de dimensions)

            Ça c'est la partie « facile », puisqu'à priori, elle ne nécessite que l'ajout de nœuds, et la conversion (progressive) du code existant vers ce modèle. Ensuite il « suffit » de coder les réduction (qui, pour le coup, découlent directement d'une propriété mathématique) :

            • map f . map g = map (f . g)
            • Si on écrit la signature de move comme suit move :: (Int,Int) -> (Position -> Position) -> (Image -> Image) alors move (a,b) f . move (c,d) g = move (a,b) (f . g)
            • Le produit de convolution est associatif, ce qui permet de calculer, pour un produit de type A * B * C le meilleur parenthèsage au niveau temps de calcul (c'est déjà fait pour le produit matriciel).
            • On remarque (faire un dessin) que map f et move (a,b) g commutent ! Donc on peut se débrouiller pour réordonner les enchaînements « move/map/move/map » et avoir seulement deux nœuds.

            Contrairement à ton exemple, les optimisation ici sont génériques, et s'appliquent sur dans des cas spécifiques : les rotations de 45° sont (à priori si on ne fait pas de lissage) des opérations de type « move », et donc l'optimisation sera bien faite …

            La partie la plus difficile semble donc ni la prise en charge de nouveaux nœuds, ni la réduction du graphe (même si c'est déjà plus compliqué), mais trouver des actions assez générales pour que les optimisations touchent beaucoup de code et soient pertinentes ! Je ne dis pas que c'est « rapide à faire », mais que sur une assez longue période de temps, c'est plutôt « simple » à mettre en place.

            Enfin, rien ne peut être optimisé de manière aussi naïve si on a pas l'assurance d'avoir du code sans effet de bord, et je ne sais pas si c'est pertinent pour du traitement d'images …

            D'ailleurs, après réflexion … Pourquoi est-ce un graphe ? Une liste d'opérations à faire ne serait pas aussi bien ?

            • [^] # Re: perf ?

              Posté par  (site web personnel, Mastodon) . Évalué à 6. Dernière modification le 18 juin 2015 à 18:13.

              il suffit d'ajouter des types de nœuds spécifiques pour des opérations génériques:

              C'est bien ce que je dis. Il faut créer de nouvelles sous-classes de nœud et y mettre tous les nœuds qu'on arrive à identifier d'une certaine catégorie, puis réarchitecturer une bonne partie de la lib pour mettre à profit les propriétés de ces types de nœuds, etc. C'est du boulot, et ce il suffit me chagrine beaucoup. Pourquoi quelqu'un d'extérieur à un projet a souvent cette idée que les choses sont si simples à faire?

              Ensuite je le redis: on accueille les bonnes volontés! Si tu penses avoir les idées et les capacités pour optimiser GEGL, on sera tous très heureux.

              On remarque (faire un dessin) que map f et move (a,b) g commutent ! Donc on peut se débrouiller pour réordonner les enchaînements « move/map/move/map » et avoir seulement deux nœuds.

              La commutation sur pas mal de type de nœud est vraie théoriquement, pas dans la pratique.
              Imagine une fonction f() qui transforme un pixel gris entre [0; 0.5[ en noir, et un pixel gris entre [0.5; 1] en blanc (on va travailler sur un canal unique).

              Tu as au départ une image de 4 pixels (2x2) avec un pixel sur 2 noir puis blanc:
              N B
              B N

              Applique maintenant f(). L'image est inchangée. Applique maintenant une rotation de 45°. Bon il existe divers algorithmes de rotation, donc on va juste supposer qu'on utilise un algorithme qui fait une moyenne entre les pixels adjacents.
              Résultat final: une image totalement grise uniformément.

              Commute maintenant les opérations. Fait d'abord la rotation (tous les pixels deviennent gris), puis applique f().
              Résultat: tous les pixels sont blancs!

              Nous avons 2 images totalement différentes au final. Théoriquement (mathématiquement) tu as raison, on peut réordonner ces opérations. Dans la réalité de l'imagerie informatique, ce n'est plus vrai.

              En plus, je parle même pas du cas où f() utiliserait la position du pixel comme donnée d'entrée (par exemple pour effectuer un traitement en mixant avec une image de pattern…), et déplacer les pixels avant ou après modifierait donc complètement le rendu aussi.

              Enfin, rien ne peut être optimisé de manière aussi naïve si on a pas l'assurance d'avoir du code sans effet de bord, et je ne sais pas si c'est pertinent pour du traitement d'images …

              Ben voilà, tu as compris. On ne peut pas juste optimiser naïvement comme les exemples proposés plus haut. C'est beaucoup plus compliqués que juste des mathématiques (celles ci sont la base, mais il faut ensuite pouvoir les appliquer dans la réalité des représentations informatiques).

              Pourquoi est-ce un graphe ? Une liste d'opérations à faire ne serait pas aussi bien ?

              Comment tu fais pour du compositing sans graphe? Une liste d'opérations ne permet que de modifier une image, mais dans la réalité, on (= tous ceux qui utilisent ce genre de logiciels de dessin et traitement d'image) mélange des dizaines d'images les unes aux autres. On pourrait même avoir des opérations qui rentre dans un noeud et re-rentre dans un autre nœud, pourquoi pas.
              Enfin bon, de toutes façons, même sans aller plus loin, GIMP serait bien triste si on pouvait plus avoir de compositing (= adieu les calques!).

              Je sais pas si tu as déjà testé l'éditeur de Node de Blender, mais tu pourrais presque plus rien y faire s'il s'agissait juste d'un système linéaire, et il perdrait soudainement beaucoup de son intérêt (alors qu'à l'inverse, à l'heure actuelle, c'est un des gros points forts de Blender).

              Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

              • [^] # Re: perf ?

                Posté par  . Évalué à 2.

                En effet, je n'avais pas pris le temps de tester sur un exemple ! Merci beaucoup d'avoir pris le temps d'expliquer.

                Sinon, pourquoi la rotation fait une moyenne ?

                Encore une question (peut-être stupide), mais pour GIMP et les calques (je n'ai jamais vraiment utilisé Blender), le graphe ressemble à plusieurs listes qui se terminent en un même nœud ?

                calque 1
                ----------[]-------[]-----[]-----[]----\
                                                        |_ image finale 
                calque 2                                |
                -----[]------------[]------------------/
                

                Je n'arrive pas à me représenter la signification qu'aurait un graphe quelconque … Déjà il « devrait » être acyclique et connexe, mais du coup ça ressemble fortement à un arbre (si on met de côté l'orientation des flèches dans le graphe).

                • [^] # Re: perf ?

                  Posté par  (site web personnel, Mastodon) . Évalué à 5.

                  Sinon, pourquoi la rotation fait une moyenne ?

                  En imagerie informatique, les données sont discrètes. Dans la "nature", une image est continue. Elle n'est pas une succession de point. Donc une vraie rotation physique n’abîme pas une image (tu as une photo posée sur la table, tu la tournes, elle ne perd pas en qualité! :P).
                  Mais dans une représentation bitmap, une image est une matrice limitée de point. Donc à part si tu fais une rotation qui garde une matrice (90°, 180°, 270°…), tes pixels d'origine se retrouvent entre 2 points de la matrice (les nouveaux pixels).

                  Exemple la matrice de pixels:
                  1 2
                  3 4
                  … après rotation à 45° devient:

                   1
                  3 2
                   4
                  

                  Ce n'est plus une matrice, et il faut donc calculer les nouveaux pixels qui formeront l'image après rotation, à partir des pixels d'origine. Il y a pour cela divers algorithmes dit d'interpolation (regarde les paramètres de l'outil "rotation" de GIMP, tu peux voir que tu peux choisir le type d'interpolation, ce qui est bien sûr plutôt pour les utilisateurs avancés). Une interpolation possible pourrait être tout simplement de faire la moyenne pondérée entre les pixels qui chevauchent le nouveau pixel (c'est en gros l'interpolation linéaire quoi). Il y a d'autres algorithmes moins naïfs (ce qui ne veut pas dire que celui-ci est mauvais pour autant!), c'était juste pour prendre un exemple simple.

                  le graphe ressemble à plusieurs listes qui se terminent en un même nœud ?

                  Dans l'UI, ça ressemble à une liste.
                  Mais c'est en fait un graphe où à chaque nœud, on a rentré 2 calques, ce qui a produit un résultat, que l'on va "compositer" avec un autre calque.
                  Ainsi la liste de calque:
                  "Layer 1" (Normal)
                  "Layer 2" (Multiply)
                  "Layer 3"

                  C'est un graphe:

                    Image Finale
                         ^
                      [Normal]
                      ^      ^
                  "Layer 1"  Résultat intermédiaire
                                      ^
                                  [Multiply]
                                  ^        ^
                              "Layer 2"   "Layer 3"
                  

                  Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

                  • [^] # Re: perf ?

                    Posté par  . Évalué à 1.

                    rotation qui garde une matrice (90°, 180°, 270°…),

                    Ok, j'avais « confondu » 45 et 90°, c'est pour ça que je ne comprenait pas le coup de la moyenne !

                    divers algorithmes dit d'interpolation

                    Oui, oui, ensuite faut interpoler … mais pourquoi le faire à chaque étape ? Parce que chaque opération destructive va faire plus ou moins la même chose : on construit une image, mais pour la faire rentrer dans une matrice on interpole sur les pixels. Est-t-il possible de « construire » virtuellement une image parfaite et ensuite faire une unique interpolation ? (dans la même veine que du SVG, sauf que là on part d'une matrice de pixels).

                    Par exemple, la rotation, bah, tourne réellement la matrice (comme sur ton schéma), ensuite on applique ton filtre, et à la fin on interpole sur une grille 4x4 …

                    Mais c'est en fait un graphe

                    D'accord, mais ta description, c'est carrément un arbre (donc le graphe a bien une forme particulière). Par contre, dans ton modèle, je vois pas comment on peut faire une rotation (puisque c'est pas une opération qui demande deux calques …).


                    Sinon, encore une question, pour les images on préfère une interpolation à une matrice minimisant une distance bien définie ?

                    Par exemple, pour approcher une fonction par des polynômes, on peut interpoler sur n points (Lagrange), ou bien regarder le projeté orthogonal de la fonction sur R_n[X] avec un produit scalaire pertinent … Le problème de l'interpolation c'est qu'augmenter le nombre de points de donne pas une nécessairement une convergence vers la fonction, alors qu'augmenter la dimension de l'espace sur lequel on projette si.

                    Par exemple, pour la compression d'image (je sais que c'est un peu différent) on peut utiliser la Décomposition en valeurs singulières pour approcher une image (matrice) par une autre matrice de rang inférieur (diminuer le rang diminue la quantité d'information utile dans la matrice), et en fait on prend la matrice la plus proche pour la norme de Frœbenius.

                    • [^] # Re: perf ?

                      Posté par  (site web personnel, Mastodon) . Évalué à 3.

                      mais pourquoi le faire à chaque étape ?

                      Tout est possible. Faut ensuite voir le "coût", que ce soit le coût de développement, c'est à dire la complexité, le temps que cela va nous prendre à avoir une architecture convenable, mais aussi le coût additionnel du traitement, et donc notamment le temps additionnel et la mémoire utilisée, etc. Déjà que beaucoup se plaignent du fait que le non-destructif peut être plus lent (car à chaque changement de l'image ou d'opération, on doit potentiellement recalculer toutes les opérations "au dessus"), et qu'il va rapidement bouffer toute la mémoire, voire swapper, et devenir encore plus lent (cf. commentaire plus haut au sujet de Darktable)… Puis faut voir si ce coût vaut le coup en comparaison du gain.
                      Surtout qu'au début on parlait d'optimisation, maintenant on veut rendre le traitement encore plus parfait (donc lent en général). :-)

                      Par exemple, la rotation, bah, tourne réellement la matrice (comme sur ton schéma), ensuite on applique ton filtre, et à la fin on interpole sur une grille 4x4 …

                      Déjà il faut bien noter que ce n'est commutable qu'avec certains types d'opérations. Par exemple si une opération changeait les pixels en fonction de leur position, cela ne serait pas la même chose de l'appliquer avant ou après rotation (exemple typique: utilisation d'une texture que l'on va mapper au dessus de l'image). C'est un travail assez important de bien cataloguer les opérations. Et l'utilisateur a peut-être choisi exprès d'appliquer telle opération sur les pixels avant ou après une rotation car il voulait tel rendu, ou tel autre. Il faut donc bien faire attention quand on réordonne. Pas tout ne peut l'être.

                      Mais oui, il y a des moyens d'optimiser un graphe et c'est un travail qui demande du temps pour le faire bien, en utilisant des connaissances mathématiques puis en les appliquant à la réalité de la représentation algorithmique. Et surtout ne pas changer le rendu désiré par l'artiste.

                      D'accord, mais ta description, c'est carrément un arbre (donc le graphe a bien une forme particulière)

                      Non, mon exemple est un arbre. Pourquoi en faire une généralité?
                      On pourrait avoir une opération qui re-rentre dans un autre nœud. Par exemple ci-dessous, op1 rentrerait dans op2 et re-rentrerait dans op3, ce qui est très pratique car cela évite d'avoir à recréer des nœuds qui recalculeraient des opérations que l'on a déjà calculées.

                           op3
                          ^   ^
                        op2   ^
                        ^     ^
                        op1 >>>
                      

                      On a ce genre de graphes tout le temps dans Blender. Je ne suis pas sûr si GEGL permet plusieurs sorties, mais si ce n'est pas le cas à l'heure actuelle, ce ne serait pas dur à implémenter; ensuite il faut juste adapter l'UI. Dans GIMP, certains semblent ne pas vouloir une UI trop complexe. Ce que je comprends tout à fait pour l'UI de base, du moment qu'on puisse aussi proposer une UI alternative pour gérer des graphes complexes. Enfin on verra cela quand nous commencerons à réfléchir sur une UI pour le non destructif dans GIMP.

                      Par contre, dans ton modèle, je vois pas comment on peut faire une rotation

                      Je reprends donc mon exemple. Supposons que l'on ait une rotation comme opération non destructive sur le Layer 2.

                         Image Finale
                             ^
                          [Normal]
                          ^      ^
                       "Layer 1"  Résultat intermédiaire
                                          ^
                                      [Multiply]
                                      ^        ^
                                  [rotate]  "Layer 3"
                                      ^
                                  "Layer 2"
                      

                      Et voilà!

                      puisque c'est pas une opération qui demande deux calques

                      Une opération n'a pas forcément 2 entrées. Elle peut en avoir 1, 2… et même zéro! (par exemple une opération de chargement d'image n'a pas besoin d'entrée. Elle a juste un paramètre pour indiquer un chemin d'accès au fichier, ou même des opérations qui génèrent de l'aléatoire). De même pour les sorties.

                      Sinon, encore une question […]

                      Je ne peux pas vraiment répondre à ce type de questions. Comme je disais, je ne suis pas du tout expert en algorithmique du traitement d'image. J'ai fait des maths à niveau correct et de l'algorithmique à la fac, mais c'était y a des années et je ne lis pas de livres de maths le soir avant de me coucher. Donc oui je comprends comment marchent les divers algorithmes quand je lis du code. Par contre non je ne pourrai pas comparer les algorithmes. Enfin si, peut-être si je me mettais à lire beaucoup sur le sujet, puis à prendre un crayon et difficilement essayer de me remettre à résoudre des problèmes mathématiques, mais je n'en ressens pas le besoin. :P

                      On a plusieurs dévs qui sont très calés sur ce genre de choses, et je leur fais confiance. De mon côté, je fais d'autres choses peut-être un peu plus terre à terre, mais bon, chacun son truc. :-)

                      Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

    • [^] # Commentaire supprimé

      Posté par  . Évalué à 2.

      Ce commentaire a été supprimé par l’équipe de modération.

      • [^] # Re: perf ?

        Posté par  (site web personnel) . Évalué à 2. Dernière modification le 26 juin 2015 à 14:27.

        "Parcontre, GEGL gère bien les grosses images avec tuilage, swap sur fichier, etc."

        Tu veux dire qu'un traitement ne se fait pas entièrement sur l'image avant de passer au suivant, mais entièrement sur une tuile avant de passer à l'autre ? Si la tuile reste en cache, cela peut être une situation intermédiaire très efficace.

        Est-ce que gegl est capable de gérer aussi les "preview" cad appliquer les filtres sur une réduction de l'image pour avoir le résultat plus vite à l'écran, sans faire le traitement entier puis la réduction ? C'est important pour avoir un feedback rapidement et faire des ajustements.

        "La première sécurité est la liberté"

  • # Merci !

    Posté par  . Évalué à 3.

    Merci pour cette dépêche très instructive.

    C'est toujours intéressant de savoir ce qui ce passe dans les entrailles de logiciels qu'on utilise régulièrement.

  • # Darktable et GEGL

    Posté par  . Évalué à 1.

    Réponse d'un des développeurs de Darktable à propos de GEGL :

    Darktable used to use GEGL in the past and it was unbearably slow. Maybe some day that will be revisited, but don't hold your breath. It could be another year, or five years, or ten. Or maybe never. So while we think it would be cool for workflows where you interchange data between darktable and GIMP it is absolutely no priority and we are not even sure if it's feasible from a technical point.

    • [^] # Re: Darktable et GEGL

      Posté par  (site web personnel, Mastodon) . Évalué à 6.

      Salut,

      Oui c'est bien ce que je dis dans la dépêche:

      la version de développement de GIMP, à ce moment là, était totalement inutilisable, et ce, encore jusqu'à il y a à peine deux ans, car si lente que l'on pouvait donner un coup de pinceau puis aller préparer un café en regardant le trait s'afficher (ou presque !).

      Or comme j'explique, leur tentative de port date de 2011 (cf. leur blog post), c'est à dire y a 4 ans, donc bien avant (2 voire 3 ans) que la bibliothèque GEGL ne commence à devenir vraiment utilisable (cad. pas trop lente). Et en outre, GIMP 2.10 n'est même pas encore sorti et surtout il n'aura même pas encore l'UI adaptée pour travailler en non-destructif. Donc clairement si Darktable décide de reprendre leur port GEGL, ce n'est pas pour demain. :-)
      C'est pas grave, personne n'est pressé. Ce sont des choses qui se font avec le temps.

      Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.