⚠️ Ceci est un site de traduction non officiel, sans lien avec ImageMagick Studio LLC. Pour des informations officielles, consultez la page originale (https://usage.imagemagick.org/api/index.html).

Exemples ImageMagick -- API et écriture de scripts

Exemples ImageMagick — préface et index
L'API et les autres méthodes d'utilisation d'IM

L'interface en ligne de commande (CLI) d'ImageMagick dont traitent ces exemples n'est qu'une des méthodes permettant d'utiliser, de modifier et de contrôler des images à l'aide de la bibliothèque de fonctions centrale d'ImageMagick (MagickCore). Il s'agit essentiellement de l'interface API « shell ». Il existe quantité d'autres interfaces de programmation applicative (API) que vous pouvez utiliser plus directement depuis de nombreux langages de programmation ; voir les API d'ImageMagick. Ici, j'examine les façons d'améliorer vos scripts et vos programmes IM, les différences entre l'écriture de scripts sous Windows et sous Unix, et les bases de l'utilisation d'IM depuis d'autres API et langages de programmation.


L'API et les autres méthodes d'utilisation d'IM

Les API (Application Programming Interface, interface de programmation applicative) destinées au traitement d'image proprement dit ne sont en réalité pas plus rapides que l'emploi des commandes CLI (telles que "magick", qui représente elle-même un type d'API shell). La même bibliothèque « centrale » sert à tous les traitements d'image dans IM. Ainsi, pour une tâche complexe comme la distorsion d'image, utiliser une API plutôt que le "magick" du shell ne fait pratiquement aucune différence en termes de « vitesse » globale de traitement. Alors pourquoi utiliser une API plutôt que la ligne de commande ? Un shell « forke » sans cesse de nombreuses commandes différentes (pas seulement les commandes "magick" d'IM, même si les shells disposent de quelques « primitives »), chacune devant être chargée et initialisée à chaque exécution. Chaque commande IM doit elle aussi réinitialiser ses fichiers de configuration, analyser les arguments de la ligne de commande, relire les images sur lesquelles vous travaillez et, souvent, réenregistrer les résultats sur le disque. Tout cela prend du temps et les rend plus lentes. Autrement dit, toutes ces étapes supplémentaires consomment du temps et de la puissance de calcul ; si vous les répétez souvent, une API commence à se justifier. Une API permet aussi de faire d'autres choses que la ligne de commande ne peut pas.

  • Si l'API tourne déjà, le temps de préparation est quasi nul, voire inexistant.
  • Conserver en mémoire un tableau de nombreuses listes d'images, traitables dans l'ordre de votre choix. Par exemple, j'ai un programme qui lit des centaines d'images en les transformant en vignettes de 32x32 pixels au fur et à mesure de la lecture, puis les compare toutes deux à deux avec magick, une paire à la fois (9 900 comparaisons pour 100 images). On pourrait aussi, par exemple, trier les images par couleur générale directement en mémoire !
  • Vous pouvez mener de front de nombreux fils de traitement d'image différents, dans n'importe quel ordre. Nul besoin de « terminer » une séquence de traitement avant de passer à l'image ou à l'étape suivante. Imaginez par exemple un programme qui résout un puzzle !
  • Vous pouvez récupérer des informations sur les images et les exploiter de façons complexes pour modifier le traitement sans avoir à le réinitialiser (relire les configurations et les images) encore et encore. Par exemple, obtenir la taille de l'image avant de calculer les besoins de cadrage.
  • Boucler sur les images en faisant quelque chose de radicalement différent pour chacune ! Par exemple, générer une séquence d'animation où chaque image est déformée d'une manière légèrement différente.
  • Un accès direct et totalement aléatoire aux données de l'image. Par exemple, rechercher un « visage » dans une image.

Mais pour la plupart des tâches ne mettant pas en jeu de grands nombres d'images, ou pour un traitement d'images générique et bien défini, la ligne de commande est à peu près tout aussi rapide. Vous pouvez néanmoins économiser beaucoup de temps et de retraitement en... * Recourant à l'enregistrement d'images MPC (pour des relectures plus rapides) des images intermédiaires. Voir mon script divide_vert. * Recourant au traitement parallèle par pipeline (en utilisant même des machines différentes pour des étapes différentes !), afin de mieux exploiter le processeur et d'éviter d'enregistrer les images intermédiaires sur le disque. Voir enlarge_image pour un exemple de script en pipeline où une étape envoie l'image (ou les images) par tube vers l'étape suivante (parfois facultative) de la séquence. * Utilisant une boucle pour traiter chaque image individuellement, avant d'envoyer par tube un flux de résultats vers une étape finale « qui fusionne le tout ». J'ai beaucoup de scripts de ce genre... Voir Placement programmé d'images en couches pour un tel script.

Bien sûr, dans une API, on aurait aussi pu employer, pour une tâche de traitement particulière, des techniques différentes et plus rapides d'accès à l'image. Et, au fil du temps et des découvertes, nous programmons souvent de telles techniques dans la bibliothèque centrale. Les distorsions d'image et les diverses expressions FX en sont un exemple.

Windows et DOS

Les exemples d'écriture de scripts Windows et DOS avec l'API CLI ont été déplacés vers Utilisation sous Windows.

PHP (commandes IM depuis les fonctions "system()")

Les utilisateurs de PHP disposent de trois façons d'utiliser ImageMagick.

  • l'interface PECL "imagick"
  • l'interface "MagicWand"
  • l'utilisation des fonctions "system()" et "exec()" pour exécuter la commande CLI "magick"

En raison de l'existence des Exemples IM, cette dernière méthode (ainsi que la première que nous examinerons ensuite) est devenue ces dernières années la plus courante pour utiliser IM depuis PHP. Bien entendu, pour certaines choses, ce n'est peut-être pas la meilleure méthode (voir plus haut) ; dans ce cas, les interfaces API sont disponibles, même si un administrateur système devra peut-être les rendre accessibles à votre environnement PHP.

PHP à l'aide de commandes shell

La meilleure source d'informations précises sur cette technique est l'utilisateur du forum IM Bonzo et son site web RubbleWebs. Notez que PHP exécute "magick" dans un environnement différent, et probablement même sous un utilisateur différent, de ce que vous obtiendriez en ligne de commande. Ainsi, ce qui fonctionne en ligne de commande peut demander quelques ajustements pour fonctionner depuis un script PHP piloté par le web. Voici la procédure recommandée pour les premiers tests de l'interface IM en ligne de commande d'un FAI, en supposant que vous n'avez pas d'accès « shell » direct en ligne de commande sur le système distant. Autrement dit, vous ne pouvez téléverser que des fichiers web à exécuter. La première chose à faire est donc d'essayer de localiser la commande "magick" sur le système, de connaître la version installée et l'environnement dans lequel PHP s'exécute. Sur un service web Linux, téléversez et exécutez ce script PHP depuis le serveur web du FAI...

<?php
  header('Content-Type: text/plain');
  system("exec 2>&1; pwd");
  system("exec 2>&1; type magick");
  system("exec 2>&1; locate */magick");
  system("exec 2>&1; magick -version");
  system("exec 2>&1; magick -list type"); <!-- before IM v6.3.5-7 -->
  system("exec 2>&1; magick -list font");
  print("------ENVIRONMENT VARIABLES-------\n");
  system("exec 2>&1; env");
?>

Cela exécute un bon nombre de commandes pour découvrir à quoi ressemble votre environnement. Le premier "pwd" vous indique le répertoire courant dans lequel le script PHP a été exécuté. Il peut correspondre ou non au répertoire où se trouve le script PHP, et il se peut que vous ne puissiez pas y écrire depuis le script PHP. Les deux commandes suivantes vous indiquent, à l'aide de "type", si "magick" est disponible sur le PATH de commandes fourni par défaut et, le cas échéant, où il se trouve. La commande "locate" devrait trouver toutes les commandes "magick" présentes sur le serveur (en supposant qu'il s'agisse d'un serveur linux), mais elle peut aussi trouver d'autres commandes, fichiers et répertoires "magick" étrangers à ImageMagick. Vous devrez interpréter les résultats. Les trois commandes suivantes supposent que "magick" est sur le PATH et lui demandent d'indiquer son numéro de version ainsi que les polices auxquelles IM pense avoir accès. La commande qui rapporte les polices dépend de l'ancienneté de la version d'IM installée. Si vous ne voyez que des erreurs, c'est que "magick" n'est pas sur le chemin de commande, et que votre FAI n'a PAS initialisé correctement les variables "PATH" et "LD_LIBRARY_PATH" du serveur web pour l'y inclure. Reportez-vous à la sortie de la commande "env" pour voir ce qu'ils ont défini. Dans ce cas, vous devrez trouver son emplacement exact et utiliser un script PHP semblable au suivant. Cela rendra votre script moins portable, car il est codé en dur pour ce FAI précis. Supposons par exemple que la commande "magick" se trouve dans "/opt/php5extras/ImageMagick/bin" : vous pouvez alors définir une variable pour indiquer son emplacement. Cela se fait souvent dans le cadre du processus de configuration et d'installation de l'application, pour des scripts PHP utilisés sur différents hébergeurs FAI),

<?php
  $im_path="/opt/php5extras/ImageMagick/bin"
  header('Content-Type: text/plain');
  system("exec 2>&1; $im_path/magick -version");
  system("exec 2>&1; $im_path/magick -list type");
  system("exec 2>&1; $im_path/magick -list font");
?>

Si vous obtenez des erreurs de bibliothèque "ldd", c'est que "LD_LIBRARY_PATH" est erroné : le FAI a assurément mal fait son travail lors de l'installation, et vous devez signaler l'erreur pour qu'il corrige le réglage de la variable d'environnement "LD_LIBRARY_PATH" du serveur web, ou réinstalle ImageMagick. Plutôt que de fixer l'emplacement de la commande magick, vous pouvez aussi ajuster la variable d'environnement PATH par une ligne de ce type placée en tête. Cette méthode est toutefois souvent « refusée » par défaut par la configuration système PHP habituelle...

  putenv("PATH=" . $_ENV["PATH"] . ":/opt/php5extras/ImageMagick/bin");
  putenv("LD_LIBRARY_PATH=" . $_ENV["LD_LIBRARY_PATH"] .
                                   ":/opt/php5extras/ImageMagick/lib");

Ensuite, essayez de faire fonctionner quelques-uns des exemples les plus simples des Exemples IM. Par exemple, pour renvoyer l'image « rose » d'IM à l'utilisateur web sous forme de fichier JPEG...

<?php
  header( 'Content-Type: image/jpeg' );
  passthru("magick rose: jpg:-");
?>

Si vous devez indiquer l'emplacement de la commande magick, vous pouvez utiliser...

<?php
  header( 'Content-Type: image/jpeg' );
  $magick="/opt/php5extras/ImageMagick/bin/magick"
  passthru("$magick rose: jpg:-");
?>

ou, en cas de problèmes de bibliothèque, vous pouvez essayer quelque chose comme...

<?php
  header( 'Content-Type: image/jpeg' );
  $magick="/opt/php5extras/ImageMagick/bin/magick";
  $libs="LD_LIBRARY_PATH=\'" .  $_ENV["LD_LIBRARY_PATH"] .
               ":/opt/php5extras/ImageMagick/bin/magick\'";
  system("$libs $magick rose: jpg:-");
?>

Si vous ne voyez toujours rien, consultez mon fichier brut de conseils et astuces PHP pour des informations sur les techniques de redirection des messages d'erreur.
Une fois ce script de base opérationnel, vous pouvez essayer l'une des polices listées par vos scripts de test PHP (adaptez ce qui suit à votre serveur PHP). Par exemple, sur un serveur Solaris dont je disposais à l'époque, j'ai remarqué que le jeu de polices 'Utopia' était disponible ; j'ai donc pu tenter de créer une étiquette avec cette police...

<?php
  header('Content-Type: image/gif');
  passthru("magick -pointsize 72 -font Utopia-Italic label:'Font Test' gif:-");
?>

Exemple de conversion de shell vers PHP

Voici une commande ImageMagick assez typique...

  magick -background none -fill red -gravity center \
          -font Candice -size 140x92 caption:"A Rose by any Name" \
          \( rose: -negate -resize 200% \) +swap -composite    output.gif

Une fois convertie en PHP, elle devient à peu près ceci...

<?php
  header('Content-Type: image/gif');

  $color="red";
  $image="rose:";
  $scale="200%";
  $size="140x96";
  $string="A Rose by any Name";

  passthru( "magick -background none -fill '$color' -gravity center" .
            " -font Candice -size '$size' caption:'$string'" .
            " \\( '$image' -negate -resize '$scale' \\) +swap -composite" .
            " gif:-" );
?>

Remarquez que je continue de découper la longue ligne de commande "magick" pour rendre la séquence de traitement plus facile à suivre et à modifier ensuite. Cela a été fait au moyen de chaînes PHP concaténées plutôt qu'avec la continuation de ligne du shell employée dans les scripts shell. Notez aussi l'espace supplémentaire au début des lignes suivantes, ainsi que le doublement des autres barres obliques inverses présentes dans la commande d'origine. Vous pouvez sinon protéger ces options par des apostrophes plutôt que par des barres obliques inverses. J'ai également utilisé quelques variables PHP pour ajuster plus aisément l'image générée par le script et mieux en contrôler le résultat. Toutefois, lorsque j'insère ces options dans "magick", j'emploie des apostrophes pour les protéger de toute modification supplémentaire par le shell. Mais attention aux apostrophes à l'intérieur de ces chaînes insérées ! Vous pourriez faire de ces options des arguments d'entrée PHP, afin de générer une image pour n'importe quel texte transmis depuis une requête web. Vous pouvez aussi exécuter plusieurs commandes shell au sein d'une même chaîne d'appel system. En fait, un seul appel system peut contenir un script shell complet si vous le souhaitez ! Vous pouvez donc réaliser des boucles shell et plusieurs commandes (avec leur nettoyage) dans un unique appel system. Peu de gens réalisent que c'est possible. En somme, avec un peu de soin, vous pouvez tirer parti des mathématiques offertes par PHP et des capacités de script du shell, le tout en même temps. Surveillez simplement les guillemets. Pour divers exemples d'appel de commandes ImageMagick depuis PHP, voir Rubble Web, écrire du code IM en PHP, qui décrit environ quatre techniques différentes.

Attention aux guillemets supplémentaires

Notez que, typiquement, les commandes IM en PHP sont entourées d'un niveau de guillemets supplémentaire (généralement des guillemets doubles) ; il faut donc veiller à tenir compte de ce niveau de guillemets additionnel. Rappelez-vous ce qui se passe quand PHP exécute une chaîne... PHP effectue ses substitutions de guillemets, de barres obliques inverses et de variables. Le shell découpe ensuite les arguments et effectue ses propres substitutions de variables et de guillemets. Il réalise aussi les redirections de descripteur de fichier du type "2>&1" si elles sont présentes. ImageMagick reçoit un tableau d'arguments, mais effectue également son propre traitement des métacaractères de noms de fichiers, spécifiquement pour DOS (l'environnement dos ne gère pas les métacaractères) et pour des arguments comme coder:*.gif[50x50] que le shell ne parvient pas à développer à cause du préfixe coder: ou du modificateur de lecture [...]. Bref, ÉNORMÉMENT d'analyse d'arguments ! Ce qui peut impliquer beaucoup de gestion de guillemets et de barres obliques inverses. Prudence et anticipation s'imposent. Je vous recommande de lire au moins les manuels PHP sur les fonctions d'exécution de programmes, qui incluent : PHP exec(), system() et passthru(). Regardez aussi l'opérateur backtick. Il est particulièrement important de comprendre ce qui est exactement renvoyé (généralement la dernière ligne seulement) et ce qui est transmis au client appelant (tout le reste).

Sécurité en PHP

Rappelez-vous...

_Sur le net, les seuls utilisateurs dont vous pouvez être sûr qu'ils ne sont pas potentiellement hostiles
   sont ceux qui sont  *activement*  hostiles.
                                     -- Programming Perl - Camel Book, r3_

Vous devez vérifier minutieusement tous les arguments d'entrée transmis par l'utilisateur à une commande IM. Assurez-vous que l'argument est exactement ce que vous attendez. Il vaut bien mieux être trop restrictif que pas assez lorsqu'on a affaire au World Wide Web. Voici quelques points courants auxquels prêter attention.

  • Caractères binaires dans un argument — une technique de pirate courante.
  • Espaces, tabulations, retours à la ligne et retours chariot inattendus dans les arguments.
  • Les barres obliques inverses (séparateurs de répertoires) et les chemins '..'. Sous windows également, '\' et ';' dans les noms de fichiers.
  • Les métacaractères d'expansion de fichiers, dont "~*?[]{}<>", ainsi que le métacaractère spécial "@" propre à ImageMagick.
  • Les autres métacaractères du shell, dont "$#;", et les trois caractères de citation ''', '"', '```'
  • L'argument ne correspond pas à ce qu'ImageMagick attend. Utilisez "[-list](https://imagemagick.org/command-line-options/#list)" pour savoir quels types d'arguments IM comprend pour une option donnée. Par exemple, l'option "[-gravity](https://imagemagick.org/command-line-options/#gravity)" saisie par l'utilisateur n'a que 10 réglages différents.
  • Et ainsi de suite...

Pour tout travail de programmation web, il est vital de comprendre la sécurité et la façon dont les pirates peuvent employer des arguments spécialement conçus pour détourner les commandes appelées. Pas seulement pour PHP, mais aussi pour le shell et pour ImageMagick. IM demande une vigilance particulière car il peut par exemple lire un fichier de mots de passe et le transformer en image renvoyée. Encore une fois... il vaut bien mieux être trop restrictif que d'ouvrir une faille de sécurité imprévue dès lors que le web est en jeu.

Écrire dans le système de fichiers

Comme indiqué plus haut, et si vous avez suivi la procédure initiale ci-dessus, vous saurez avec certitude que PHP s'exécute généralement sous un utilisateur différent et plus restreint sur le serveur. Pour cette raison, il ne pourra en général PAS écrire dans le répertoire contenant le script (ni là où il s'exécute réellement). Pour des raisons de sécurité, vous ne voulez généralement pas écrire dans ce répertoire ! Si vous tenez vraiment à ce que PHP écrive des fichiers, faites-lui enregistrer l'image (ou les données) sous un nom de fichier unique dans "/tmp" et, surtout, nettoyez ensuite, aussi bien à la sortie normale qu'en cas de N'IMPORTE QUELLE erreur. Il est étonnant de voir avec quelle rapidité on peut remplir un disque avec les fichiers temporaires laissés par une application qui ne fait pas le ménage correctement. Si les fichiers enregistrés (images) doivent être visibles par le serveur web, créez pour eux un sous-répertoire spécial « écrit par le programme ». Comment il faudrait procéder. La plupart des applications PHP évitent en réalité d'écrire quoi que ce soit dans le système de fichiers en s'appuyant sur une base de données. Autrement dit, cookies, jetons, données utilisateur, images, etc. sont tous écrits dans une base de données telle que (par ordre de complexité et d'échelle) SQLite, PostgresSQL, MySQL et Oracle. Rien du tout n'est enregistré dans le système de fichiers. Les programmeurs système configurent typiquement l'application PHP avec ces informations lors de son installation. Les images sont typiquement restituées par le même script PHP, ou un script distinct, qui recherche le « blob » de l'image et l'envoie au client. Les images peuvent être envoyées comme images « inline » avec le HTML lui-même (voir le format "[inline:](files.html#inline)", qui propose une démonstration des images HTML inline), ou comme une unique image multiple « tout-en-un », afin que le HTML/JAVA client n'ait qu'une seule requête d'image au lieu de 20 requêtes distinctes. Un dernier point. Une méthode de nettoyage des anciennes données devrait toujours être prévue. Un utilisateur qui ne s'est pas connecté depuis 2 ans devrait probablement voir ses données supprimées.

Obtenir la sortie d'erreur

Essayez l'une de ces méthodes...

<?php
exec("/usr/local/bin/magick -version",$out,$returnval);
print_r($out[0]);
?>

ou

<?php
exec("/usr/local/bin/magick -list",$out,$returnval);
print_r($out);
?>

ou utilisez shell_exec comme suit

<?php
$IM_version=shell_exec("/usr/local/bin/magick -version");
echo $IM_version
?>

Pour inclure la sortie STDERR...

<?php
$array=array();
echo "<pre>";
exec("magick read_test.png write_test.png 2>&1", $array);
echo "<br>".print_r($array)."<br>";
echo "</pre>";
?>

Ce qui précède provient de la discussion du forum des utilisateurs d'IM... Comment afficher les infos d'erreur d'IM en php ?. Voir mes propres notes sur le journal de débogage des erreurs PHP. Elles renvoient au manuel PHP sur la journalisation des erreurs, Gestion et journalisation des erreurs en PHP ; regardez en particulier la section des exemples.

Des commandes ImageMagick plus sûres...

Idéalement, pour des raisons de sécurité, on voudrait éviter d'utiliser le shell pour découper une seule longue chaîne en une commande et ses arguments distincts. Il vaut mieux le faire soi-même ! Cela signifie que vous fournissez les arguments de la commande sous forme d'un tableau de chaînes distinctes, plutôt que comme une unique chaîne analysée par le shell. Ce faisant, vous évitez tout risque d'erreur de syntaxe du shell, la charge supplémentaire de guillemets qu'il exige, et vous empêchez qu'un pirate ne casse la commande shell pour exécuter la sienne (très grave). En contrepartie, vous perdez le script shell, les tubes et la redirection de fichiers, mais ce n'est généralement pas une grande perte quand vous utilisez déjà PHP ou un autre langage d'encapsulation. En PHP, la seule fonction que j'aie trouvée pour appeler une commande directement sans shell est pcntl_exec(). Elle évite en somme le shell et appelle la commande directement. C'est toutefois un véritable appel système 'execl()', qui remplace le processus courant par la commande indiquée. Autrement dit, elle n'effectue pas le 'fork()' ni les liaisons de descripteurs de fichiers nécessaires pour l'exécuter en sous-processus. De ce fait, pcntl_exec() est vraiment de trop bas niveau pour un usage général, et implémenter une commande « sans shell » peut devenir assez complexe. Je suis très surpris qu'un appel de commande « sans shell » plus simple et plus sûr n'ait pas déjà été fourni par l'interface PHP. Mais je ne suis pas programmeur PHP. Perl, en revanche, propose plusieurs méthodes pour appeler des sous-commandes et des processus de façon sûre, ce qui en fait souvent une interface web préférable à PHP. Une personne connaissant la sécurité PHP aurait-elle l'amabilité de m'éclairer ou d'indiquer où trouver plus d'informations ?

L'API PHP 'IMagick'

Pour vérifier si le module PHP PECL Imagick fonctionne réellement, téléversez une simple image de test "image.jpg" et ce script PHP dans le même répertoire accessible par le web.

<?php
  $handle = imagick_readimage( getcwd() . "image.jpg" );
  if ( imagick_iserror( $handle ) ) {
    $reason      = imagick_failedreason( $handle ) ;
    $description = imagick_faileddescription( $handle ) ;

    print "Handle Read failed!<BR>\n";
    print "Reason: $reason<BR>\n";
    print "Description: $description<BR>\n";
    exit ;
  }
  header( "Content-type: " . imagick_getmimetype( $handle ) );
  print imagick_image2blob( $handle );
?>

Un livre PHP IMagick est disponible en ligne, et d'autres exemples d'utilisation d'IMagick se trouvent sur le blog de Mikko. Le seul problème d'IMagick est qu'il n'a pas été maintenu ni mis à jour ; un certain nombre de fonctions peuvent donc ne pas fonctionner ou manquer. Assurez-vous d'utiliser la version 3.x d'IMagick et une version récente d'IM. Cela fonctionne, et pour la plupart des choses cela fonctionne bien, mais si vous devez faire autre chose, d'autres méthodes PHP seront peut-être un meilleur choix.

PHP 'MagickWand'

Vous pouvez vérifier si le module PHP MagickWand fait partie de l'installation PHP en utilisant...

<?php
  if (extension_loaded('magickwand')) {
    echo "PHP MagickWand is available!\n";
  } else {
    echo "PHP MagickWand is NOT available!\n";
  }
?>

Mais pour vérifier qu'il fonctionne réellement correctement, téléversez une image de test "image.png" et ce script...

<?php
  $image = NewMagickWand();
  if( MagickReadImage( $image, 'image.png' ) ) {
    header( 'Content-Type: image/jpeg' );
    MagickSetImageFormat( $image, 'JPEG' );
    MagickEchoImageBlob( $image );
  } else {
    echo "Error in MagickReadImage()";
    echo MagickGetExceptionString($image);
  }
?>

Aucune garantie avec ce qui précède, mais tout retour est le bienvenu. Je ne programme généralement pas en PHP, mais j'ai utilisé ce qui précède pour tester une installation d'essai SunONE-PHP5 (avec les trois méthodes : ligne de commande, magick, MagickWand).

Scripts PHP complexes...

Si vous devez générer et produire à la fois du HTML et des IMAGES, envisagez de concevoir votre script PHP de sorte que des requêtes HTML ou des options d'entrée distinctes génèrent les différentes parties nécessaires à votre document web, à partir du même script PHP ou de scripts différents. Autrement dit, un script PHP de premier niveau peut produire du HTML avec des balises appropriées, qui appellent ce même script (ou un autre script PHP) avec les bonnes options, afin de créer ou de modifier les images affichées par le premier script PHP de premier niveau. C'est ce que font beaucoup de scripts PHP d'albums photo et de tracé de graphiques. Le tout est piloté par les extensions GET et PATH_INFO des appels d'URL. Notez que vous ne pouvez pas utiliser POST dans une balise IMG. En procédant ainsi, vous devriez pouvoir éviter complètement d'avoir à générer, enregistrer puis nettoyer des images temporaires pour les pages web générées en PHP. Une solution truffée de problèmes, tels que les limitations de ressources et le ramasse-miettes, ce qui en fait une très mauvaise technique de programmation. Cette technique est abordée dans les deux livres ImageMagick, même si l'ImageMagick qui y est effectivement utilisé commence à dater un peu.


Scripts Perl Magick

L'API PerlMagick est un bon moyen de convertir des commandes "magick" en un script capable aussi de gérer des bases de données, de grands nombres d'images ou des traitements d'image plus complexes qu'il ne serait autrement possible. La meilleure aide consiste à examiner les scripts 'demo' de PerlMagick, présents à la fois dans les sources et généralement installés dans la zone de documentation de PerlMagick. Sur mon système, cela se trouvait dans "/usr/share/doc/ImageMagick-perl-*/demo/". Ce répertoire contient un nombre croissant d'exemples simples de lecture, d'écriture et de traitement de diverses images. On y trouve aussi le script "demo.pl" qui recense à peu près toutes les options courantes de traitement d'image et la façon de les utiliser. Lorsque vous convertissez une commande "magick" de ligne de commande vers perl, quelques points sont à garder à l'esprit.

  • La première chose à retenir est que PerlMagick ne supprime pas automatiquement les nouvelles images produites lors du traitement. De nombreux opérateurs créent une nouvelle image modifiée à partir de l'ancienne, tandis que d'autres modifient directement une image existante.
  • De plus, beaucoup d'opérateurs n'appliquent pas une opération donnée à toute une liste d'images, mais seulement à la première image de la liste que vous leur passez. Cela signifie que vous devrez boucler vous-même sur la séquence d'images (le tableau perl).
  • Vous pouvez avoir de nombreuses séquences d'images. En pratique, vous lisez typiquement chaque image dans sa propre séquence distincte, au lieu de devoir vous contenter d'une seule séquence comme c'est le cas en ligne de commande.
  • Par ailleurs, une fois l'image en mémoire, vous pouvez facilement extraire la taille de l'image existante. Cela signifie que vous pouvez simplement créer de nouveaux canevas sans avoir à cloner puis vider une image existante comme vous le faites en ligne de commande. Cependant, cloner une image copie aussi ses métadonnées ; pour des images comme les photos numériques, vous voudrez peut-être conserver la trace de ces métadonnées.
  • Vérifiez les erreurs d'image (comme indiqué sur la page PerlMagick) après chaque traitement important, surtout lors de la lecture des images.

Pour convertir une ligne de commande en perl, vous effectuez en gros exactement les mêmes opérations, dans exactement le même ordre. Toutefois, comme les images ne sont généralement pas supprimées et que les séquences d'images multiples sont courantes, l'usage des parenthèses et les opérations de clonage supplémentaires des commandes "magick" ne posent en général pas problème. Le plus difficile, lors de la conversion des scripts, est habituellement de faire correspondre une option de ligne de commande à un appel de fonction PerlMagick. Le moyen le plus rapide que j'aie trouvé est de récupérer le code source d'IM et d'ouvrir le fichier "MagickWand/mogrify.c", puis de rechercher l'option de ligne de commande qui vous pose problème. Par exemple, pour l'option -threshold, recherchez "threshold", guillemets compris. Il y aura deux occurrences : l'une pour une analyse syntaxique rapide qui vérifie que toutes les options sont reconnues, et la seconde avec les appels internes réels de cette option. Vous y trouverez le nom de la fonction de bibliothèque employée, laquelle correspond habituellement directement à la fonction Perl. Dans ce cas... BilevelImageChannel()


Avertissements de sécurité

Lorsque vous écrivez un script destiné à un usage public, en particulier un script PHP web que N'IMPORTE QUI dans le monde pourrait exécuter, il est d'une importance vitale de vérifier tout ce qui pourrait provenir d'un utilisateur inconnu (voire connu). Et je dis bien TOUT : les arguments, les noms de fichiers, les URL, et les images aussi. Tant que vous n'avez pas validé un argument d'entrée, celui-ci peut contenir des lettres, des chiffres, des espaces, de la ponctuation, voire des caractères 'null' et de contrôle. Tant que vous ne l'avez pas vérifié minutieusement, il doit être considéré comme suspect et ne pas être utilisé. Peu importe que vous utilisiez un formulaire de saisie contrôlé par le web. Une personne un tant soit peu compétente peut facilement appeler votre script PHP avec ses propres arguments sans passer du tout par ce formulaire. Et ne croyez pas qu'elle ne le fera pas : des robots sont à l'œuvre, lisant les formulaires de saisie et forgeant leurs propres arguments « piratés » pour tenter de s'introduire dans des scripts au hasard.

Métacaractères dans les noms de fichiers

Du point de vue de la sécurité, méfiez-vous particulièrement des noms de fichiers contenant des espaces, des guillemets, de la ponctuation, des caractères de contrôle ou d'autres métacaractères, car IM comme les shells peuvent tenter de les développer. Le problème est qu'un fichier nommé '*?@${&) .jpg' est en réalité un nom de fichier parfaitement licite sous UNIX, mais que BEAUCOUP de programmes auront du mal à le gérer si ce programme (comme le shell et IM) effectue lui aussi l'expansion des noms de fichiers. N'oubliez pas que, même si vous empêchez le shell de procéder à l'expansion des métacaractères 'glob', IM effectue lui-même cette expansion (pour l'usage sous DOS). Il est donc probablement sage d'interdire tous ces caractères (et de produire une erreur). Par mesure de sécurité, il est souvent judicieux de renvoyer une erreur et d'abandonner si un nom de fichier contient LE MOINDRE caractère inconnu ou inhabituel, c'est-à-dire autre chose que des lettres, des chiffres ou le suffixe attendu. Cela, avant de transmettre un tel nom de fichier à une commande shell ou à IM. Il vaut BEAUCOUP mieux être plus restrictif et empêcher les choses que d'être permissif et laisser passer quelque chose de nuisible.


Conseils pour de meilleurs scripts shell/PHP ImageMagick

Voici quelques points élémentaires de programmation de scripts que j'ai formulés à propos d'un script shell contribué, envoyé à la liste de diffusion IM pour être utilisé par d'autres. Je les avais d'abord transmis en privé à l'auteur (qui restera anonyme), lequel m'en fut reconnaissant. Ils ne sont pas tous propres à IM, mais devraient de toute façon être appliqués en tant que bonne pratique de programmation. Surtout si vous prévoyez que quelqu'un d'autre utilise, examine ou corrige votre programme ou votre script. Cela rendra en retour votre script plus utile.

  • Placez l'« aide » ou la « doc » en tête de vos scripts et programmes. Il est ainsi bien plus facile pour autrui de comprendre ce que fait un programme sans avoir à l'installer ni à l'exécuter. Pour ma part, je jette souvent à la poubelle un programme dépourvu d'un tel commentaire clair sur sa finalité, plutôt que d'essayer de compiler ou d'exécuter un script inconnu. J'ai d'ailleurs vu d'énormes projets dont le premier fichier README ne dit même pas ce que fait ce projet vaste et complexe ! Le programmeur suppose simplement que, si vous l'avez téléchargé, vous devez savoir à quoi il sert ! Veillez également à ce qu'une « mauvaise option » telle que '-?' affiche non seulement un aperçu des options, mais aussi un bref résumé de ce que fait le programme, ou de l'endroit où trouver cette aide. Ne vous contentez pas de renvoyer vers un site web distant, qui pourrait disparaître 10 ans plus tard ! Pour un exemple de script qui affiche ses propres « commentaires d'introduction », voir le script "jigsaw" dans la zone scripts d'IM. Perl peut utiliser POD pour s'autodocumenter (voir le module perl "Pod::Usage"). Voir par exemple le script contribué dpx_timecode.pl. Avoir l'aide sous forme de 'here-document' dans la première sous-routine est également acceptable et fonctionne dans la plupart des langages. Mais placez cette sous-routine EN TÊTE, et non au bas ou au milieu du script ou du programme.
  • Veillez à nettoyer votre code, à supprimer le code et les commentaires obsolètes, et à rendre l'ensemble aussi net et ordonné que possible. Soyez concis, avec des étapes simplement commentées (si possible). Voir à nouveau le script "jigsaw".
  • Assurez-vous que les fichiers temporaires sont nettoyés à la fin. Utilisez une commande shell "trap" pour les supprimer à la sortie ou en cas d'interruption. Vous pouvez bien sûr réutiliser un même fichier temporaire plusieurs fois, de sorte que vous n'en avez pas besoin de beaucoup, surtout avec les commandes magick d'IM v6. Voir à nouveau le script "jigsaw" et recherchez-y "trap".
  • Par ailleurs, tout le monde n'utilise pas le système X, même si cela peut vous sembler être le cas. Ne faites pas référence à des exigences système particulières ni à des méthodes pour résoudre le problème. Ce qui fonctionne pour vous peut être totalement inadapté à leur système et à leur configuration. Ils n'ont peut-être même pas d'accès à internet ! Dites simplement que la commande "magick" d'ImageMagick est introuvable. Si vous souhaitez ajouter des prérequis d'installation ou des suggestions, ajoutez-les dans une documentation distincte plus détaillée.
  • Vérifiez que l'IM utilisé est d'une version suffisamment élevée, ou ajoutez des ajustements rétrocompatibles. À cette fin précise, je place volontairement des notes d'« avertissement de version » tout au long des exemples IM pour indiquer quand telles ou telles fonctionnalités spéciales ont été ajoutées. Cela facilite la création de scripts avec une seule vérification de version minimale. Voici un moyen simple d'obtenir un numéro de version unique à des fins de test dans un script shell. Il extrait les 4 numéros de version et insère le bon nombre de zéros pour porter chaque nombre à 2 chiffres, produisant un simple nombre à 8 chiffres.
    IM_VERSION=`magick -list configure | \
             sed '/^LIB_VERSION_NUMBER /!d;
                  s//,/;  s/,/,0/g;
                  s/,0*\([0-9][0-9]\)/\1/g'`
    

Par exemple, IM v6.3.5-10 produira "06030510" tandis que la version immédiatement suivante, IM v6.3.6.0, produit "06030600". Une version PHP de ce qui précède est disponible sur la page d'exemples RubbleWeb, liste de polices. La chaîne obtenue peut être testée, au moyen d'un simple test numérique ou de chaîne, pour découvrir si la version d'ImageMagick disponible est assez récente pour ce que votre script tente de faire. Par exemple...

    if [ "$IM_VERSION" -lt '06030600' ]; then
          echo >&2 "The perspective distortion operator is not available."
          echo >&2 "Sorry your installed ImageMagick is too old -- ABORTING"
          exit 10
        fi

Remarquez aussi comment j'indique à l'utilisateur la raison exacte de l'abandon, et en particulier la ou les fonctionnalités spéciales visées par la vérification de version. Sans cela, vous pourriez oublier plus tard pourquoi cette version précise (ou supérieure) était nécessaire. Vous pouvez également modifier le comportement d'IM selon les versions. Supposons par exemple que je veuille obtenir la liste des polices disponibles. Avant la version v6.3.5-7 d'IM, le réglage "type" de "[-list](https://imagemagick.org/command-line-options/#list)" renvoyait la liste des 'polices connues'. Avec les versions ultérieures, il faut utiliser le réglage "font" à la place. Je peux donc ici effectuer une vérification de version pour employer le bon réglage et obtenir l'information voulue.

    if [ "$IM_VERSION" -lt '06030507' ];
        then font_list="type";
        else font_list="font";
        fi
        avail_fonts=`magick -list $font_list | cut -d\  -f1 |\
                        egrep -v '^($|----|Path:|Name$)'`

AVERTISSEMENT : en PERL, une chaîne commençant par '0' pourrait être interprétée comme un nombre octal !!! Cependant, comparer deux nombres octaux donnera tout de même un résultat correct, tant que le premier chiffre reste '0'. La prudence et la vérification du test de version sont conseillées. Une autre solution pour tester la version consiste à utiliser "expr" à la place du test "["...

    if  expr + "$im_version" \>= "06030507" >/dev/null; then
           ...

AVERTISSEMENT : le '+' supplémentaire ci-dessus n'est normalement pas nécessaire, du moins pour ce test, mais il l'est si la variable peut contenir le mot-clé spécial 'match", qui poserait des problèmes à "expr", en particulier lorsqu'il sert à un traitement de chaîne ou de sous-chaîne. * Vous pouvez aussi exploiter la sortie d'information de "[-list](https://imagemagick.org/command-line-options/#list)" pour vérifier si une fonctionnalité spéciale a été ajoutée à l'ImageMagick actuellement installé.

    magick -list distort | grep 'Arc' >/dev/null 2>&1
        if [ "$?" -ne 0 ];  then
          echo >&2 "Arc distortion method not available."
          echo >&2 "Sorry your installed ImageMagick is too old -- ABORTING"
          exit 10
        fi

Sachez toutefois qu'une nouvelle méthode, comme "[-distort](https://imagemagick.org/command-line-options/#distort) Arc", apparaît souvent en cours de développement d'IM avant d'être vraiment prête pour un usage général. Une vérification de version reste donc peut-être la meilleure idée. C'est pourquoi, dans les exemples IM, je m'efforce d'indiquer (repérez les symboles) la version d'IM à partir de laquelle une nouvelle fonctionnalité est devenue assez stable pour un usage général. * N'utilisez pas de très très longues lignes uniques. Surtout pour les commandes 'convert' complexes. Découpez-les à l'aide des méthodes de continuation de ligne (montrées plus haut), comme '\' sous UNIX, '^' sous DOS et la concaténation de chaînes '.' en PHP. Je ne veux pas dire pour autant qu'il faille placer CHAQUE réglage et opération sur une ligne distincte. Réalisez une opération ou une étape majeure par ligne : créer une nouvelle image, modifier l'image, la fusionner avec d'autres, etc. Placez tous les réglages nécessaires à un opérateur donné juste avant cet opérateur. Considérez chaque ligne comme une seule étape de traitement. Cela vous permet de séparer les lignes pour faciliter la lecture et la compréhension des différentes étapes. Plus la séparation des opérateurs est nette, plus un traitement d'image complexe est facile à suivre. L'emploi de parenthèses supplémentaires, l'indentation des lignes selon les étapes, ou même l'ajout de lignes vides entre de grands blocs de traitement, peuvent rendre encore plus visibles les grandes étapes d'une opération longue et volumineuse. J'utilise ces techniques partout dans les exemples IM, afin de les rendre plus faciles à suivre et à comprendre ; alors regardez autour de vous ! Enfin, des commentaires supplémentaires sur ce que fait une commande précise peuvent faire une grande différence lorsqu'une autre personne (ou vous-même 2 mois plus tard) lit et cherche à comprendre votre script. Dommage qu'on ne puisse pas actuellement insérer de commentaires dans une longue ligne de commande ! * Essayez de ne pas dépendre de trop de programmes externes, ou ne les utilisez que s'ils sont disponibles (avec d'éventuelles solutions de remplacement). Les autres n'auront probablement pas ce programme, ou préféreront en utiliser un autre. Si l'usage d'un programme peut être facultatif, rendez-le facultatif, soit sous le contrôle de l'utilisateur, soit d'usage automatique s'il est trouvé. Dans la mesure du possible, évitez d'en faire une exigence imposée. Vous pourriez par exemple recourir à "pngcrush", "optipng", "pngnq" pour mieux compresser le PNG qu'IM ne le fait habituellement (IM est conçu pour être général et non spécialisé). De même, "gifsicle", "intergif" ou d'autres optimiseurs de compression LZW pour les animations GIF ont leurs avantages et leurs inconvénients. Simplement, n'en faites pas une exigence absolue pour le fonctionnement d'un script. À titre d'exemple concret, une ancienne version de "gif2anim" n'utilisait pas le "magick identify" d'ImageMagick pour récupérer les métadonnées propres au GIF, mais s'appuyait sur une version corrigée de "[giftrans](http://www.ict.griffith.edu.au/anthony/software/#giftrans)". Cette dépendance est devenue inutile par la suite grâce aux améliorations du "magick identify" d'ImageMagick ; je l'ai donc supprimée pour rendre le script utilisable plus largement. ImageMagick lui-même a BEAUCOUP de dépendances facultatives, comme "ghostscript" pour la lecture des documents postscript et PDF, ou "librsvg" pour le traitement correct des images vectorielles SVG. IM fonctionne sans souci lorsqu'elles sont disponibles. IM traite ces bibliothèques comme facultatives et n'en a besoin que si vous voulez traiter des images de ces formats. Voici un extrait de code que vous pouvez utiliser pour vérifier les dépendances d'un script (particulièrement utile dans un environnement cygwin très minimal)...

    # Check Dependencies to scripts correct working
      DEPENDENCIES="sed awk grep tr bc magick identify"  # adjust to suit

      for i in $DEPENDENCIES; do
        type $i >/dev/null 2>&1 ||
          Usage "Required program dependency \"$i\" missing"
      done

  • Pour aller plus loin sur le point précédent. Si vous avez besoin de calcul en virgule flottante dans un script shell utilisant IM, vous pouvez employer IM lui-même pour ce calcul, au lieu de dépendre d'un autre programme comme 'awk' ou 'bc', qui n'est peut-être pas disponible (surtout sous cygwin sur windows). Par exemple, ici nous calculons le sin() d'un angle précis donné en degrés en faisant faire le travail par 'magick'...
    angle=-20
      sine=`magick xc: -format "%[fx:sin( $angle *pi/180)]" info:`
      echo $sine
    

Ce qui précède produirait la valeur '-0.34202'. Vous pouvez ajuster le nombre de décimales à l'aide du contrôle opérationnel de la précision. Par défaut, il est réglé à 6 chiffres. * Laissez l'utilisateur décider des formats d'image d'entrée/sortie à utiliser. ImageMagick est avant tout un convertisseur d'images capable d'utiliser quantité de formats différents. Il peut produire vers l'écran, le postscript, les imprimantes, ou envoyer l'image par tube vers une autre commande pour un traitement ultérieur. Ne limitez pas l'utilisateur à un format précis. Par exemple, les scripts "jigsaw" et "gif_anim_montage" permettent à l'utilisateur d'indiquer n'importe quelle image d'entrée ou de sortie. Ainsi, les utilisateurs peuvent faire entrer ou sortir des images de ce script par tube, ce qui autorise un traitement complémentaire avec d'autres programmes et scripts. Par exemple, j'utilise souvent une commande comme...

    gif_anim_montage animation.miff show:

pour afficher le résultat du script sur mon écran, plutôt que de l'enregistrer dans un fichier. De fait, dans beaucoup de mes scripts, si la sortie est absente, "show:" est utilisé par défaut. Je n'ai pas limité l'entrée AU SEUL fichier d'animation GIF, ni restreint la sortie aux seuls formats GIF, PNG ou JPEG ; j'ai laissé le script lire et traiter n'importe quel format qu'ImageMagick sait gérer. En effet, IM peut lire depuis des fichiers, des pipelines, l'affichage courant, ou même depuis le World Wide Web au moyen d'un format d'entrée "URL:" ou "HTTP:". Ne limitez pas ces possibilités, sauf si cela devient un problème de sécurité (comme pour un usage web). * Ne lisez les images d'entrée, et ne produisez les images multiples, qu'UNE SEULE fois ! Si l'utilisateur fournit un nom de fichier de pipeline ou une URL, vous ne devez pas tenter de lire ces images plus d'une fois, sous peine de problèmes. Utilisez un fichier temporaire, des images clonées, ou "[MPC:](files.html#mpc)" pour enregistrer une copie de l'image d'entrée lorsque vous devez y faire référence plusieurs fois. Si vous savez gérer plusieurs images issues du pipeline, c'est encore mieux. Voir à nouveau le script "jigsaw" pour un exemple d'enregistrement des images d'entrée dans un fichier temporaire, lorsque vous êtes contraint de traiter l'image d'entrée plusieurs fois, ou dans un ordre différent de celui qu'impliquent les arguments du programme.

Tout cela donne en somme à l'utilisateur de votre programme davantage de liberté pour faire ce que LUI veut, plutôt que ce que VOUS pensez qu'il veut. Ne le limitez pas, et ne vous limitez pas vous-même, en faisant des suppositions sur l'usage qui sera fait du script. PS : mon expertise principale est l'écriture de scripts UNIX, sur quantité d'architectures et de 'saveurs' différentes d'UNIX, de LINUX et d'autres systèmes de type UNIX, avec plus de 25 ans d'expérience derrière moi. Je devrais savoir de quoi je parle concernant ce qui précède.


Pourquoi utiliser plusieurs commandes "magick"

Willem a écrit le mercredi 25 octobre 2006...
Je me demandais : je vois parfois dans vos exemples que vous invoquez Convert plus d'une fois pour obtenir le résultat voulu. En général, je m'attendrais à ce qu'invoquer magick plus d'une fois soit inutile ; tout devrait être possible en 1 seule invocation (mais la commande serait alors plus complexe). Êtes-vous d'accord avec cette affirmation ?

Je suis tout à fait d'accord. Cela dit, avant la version 6 d'IM, c'était en réalité impossible, car IM n'était pas conçu à l'époque pour effectuer plus d'une, voire deux opérations par commande. IMv7 devrait toutefois vous permettre de faire tout votre traitement en une seule commande. Mais, hélas, même cela n'est pas toujours possible. J'utilise plusieurs commandes pour un certain nombre de raisons possibles. Typiquement, dans les pages d'exemples, je le fais pour pouvoir afficher le résultat intermédiaire de l'image et mieux illustrer les étapes de traitement intermédiaires en jeu. Plus loin dans la même section d'exemples, je répète parfois le procédé, mais avec une seule commande, un peu plus complexe peut-être. En principe, donc, oui, une seule commande peut effectuer tout le traitement d'image. Vous êtes tout à fait libre de combiner les techniques de traitement en une seule commande. Je le fais moi-même en permanence. L'exception concerne les cas où je dois extraire une information pour l'insérer ensuite dans la commande suivante. Un exemple en est la technique du rognage flou, qui exige d'extraire le résultat d'un rognage effectué sur une copie floutée de l'image. Ce résultat sert ensuite à rogner l'image d'origine. J'ai fait de même dans la mise à jour de l'exemple des coins arrondis de vignette, où j'ai utilisé IM lui-même pour générer une commande draw à partir de la taille d'une image, en vue de la commande suivante. Il existe toutefois des propositions qui permettront de générer des options directement à partir d'images déjà lues en mémoire. Dans des scripts comme 'jigsaw' (voir les techniques avancées, pièces de puzzle), j'en viens couramment à utiliser plusieurs commandes pour une autre raison : le traitement facultatif. Cela permet aux diverses options d'entrée fournies par l'utilisateur de sélectionner des étapes supplémentaires dans la séquence de traitement. Pour le traitement facultatif, j'utilise donc typiquement des commandes distinctes pour chaque étape. Dans un tel cas, un fichier temporaire est fondamentalement inévitable. Cependant, je n'ai typiquement besoin que d'une ou deux images temporaires au maximum, et chaque étape retraite l'image vers le même nom de fichier temporaire (ou le précédent), pour que l'étape facultative suivante puisse continuer. Par exemple, traiter l'image en remplaçant l'image source.

  magick /tmp/image1.png ..operations..  /tmp/image1.png

Dans ce cas, un fichier MPC peut accélérer la lecture des fichiers intermédiaires jusqu'à la rendre quasi instantanée à l'étape suivante, l'image étant simplement déversée de la mémoire vers le disque, puis « rechargée » par la commande suivante. Cela évite à IM d'avoir à formater et analyser un format de fichier image, même si le fichier temporaire devient plus volumineux puisqu'il s'agit simplement de mémoire non compressée. Une autre solution que j'emploie pour éviter les fichiers temporaires consiste à faire circuler la ou les images de travail par tube d'une commande shell (if-then-else-fi ou boucle while) à une autre. C'est ce qu'on appelle les pipelines d'images, illustré dans plusieurs exemples. On en trouve un exemple dans le streaming d'images MIFF, où plusieurs images sont générées l'une après l'autre dans le même pipeline de sortie, pour être reprises par la commande suivante qui les fusionne toutes en l'image finale. Enfin, vous pourriez avoir à modifier votre façon de traiter selon les résultats des étapes précédentes. Par exemple, dans les comparaisons d'images, j'ai souvent besoin de découvrir une information à utiliser dans les étapes ultérieures, ou de changer la manière dont l'image doit être traitée par la suite. Comparer un schéma ou un dessin peut exiger une technique de comparaison très différente de celle d'une photo réelle. Si l'usage de plusieurs commandes devient un problème, il est peut-être temps de passer à une interface API telle que PerlMagick, où plusieurs séquences d'images peuvent toutes être conservées en mémoire afin d'éviter des entrées/sorties disque inutiles.


Rendre IM plus rapide (en général)

Il existe de nombreuses façons de faire fonctionner IM plus rapidement. Voici les aspects les plus importants à garder à l'esprit. À mesure que vous descendez la liste, le gain de vitesse diminue, ou exige des modifications plus complexes de l'installation d'IM.

  • IM Q8 (soit 8 bits par valeur de couleur, 3 à 4 octets par pixel) est bien plus rapide (3 à 4 fois plus rapide) que le Q16 par défaut d'IM, à la résolution de couleur plus élevée. Si vous n'avez pas besoin de Q16 pour vos images, vous devriez peut-être remplacer votre IM par une version Q8. Sachez toutefois que n'utiliser qu'une qualité interne de 8 bits peut affecter le traitement global, car les images intermédiaires perdent de l'information. Voir HDRI pour l'inverse.
  • Utilisez si possible une seule commande "magick" pour tout le traitement requis par une image. Cela économise l'initialisation, la création de fichiers temporaires ou de pipelines entre commandes, et même l'encodage/décodage vers des formats de fichiers image pour ces pipelines ainsi que les E/S disque. Bien sûr, il faut parfois encore utiliser plusieurs commandes pour permettre des étapes de traitement facultatives, comme des calculs faisant intervenir la taille de l'image, les couleurs, ou même des étapes de traitement optionnelles. L'écriture de scripts IMv7 aidera à cet égard.
  • Les scripts shell sont intrinsèquement lents. Un shell est interprété, exige plusieurs étapes et un surcroît de manipulation de fichiers sur le disque. C'est bien sûr amélioré grâce à la nouvelle gestion des options d'IM v6, qui permet d'effectuer un grand nombre d'opérations de traitement en une seule commande. Malgré tout, vous pouvez rarement tout faire en une seule commande "magick", et devez donc souvent recourir à plusieurs commandes pour parvenir à vos fins. Ainsi, une API comme perl, ruby ou un module magick pour PHP est plus rapide car elle supprime tous les aspects d'interprétation du shell comme de l'API en ligne de commande d'IM. Elle réduit aussi les étapes d'initialisation qu'IM traverse en lisant les définitions de polices et de couleurs.
  • Une API peut aussi conserver toutes les images, voire plusieurs listes d'images, pendant toute la durée de vie du programme (tant que vous disposez d'assez de mémoire). Cela signifie que vous pouvez changer à volonté les images sur lesquelles vous travaillez, sans avoir à les remanier et à jongler avec elles comme en ligne de commande. C'est particulièrement utile lorsque vous devez aussi effectuer des calculs supplémentaires fondés sur les étapes de traitement précédentes.
  • L'écriture du format de fichier image GIF est lente. IM doit travailler dur pour réduire (quantifier) les couleurs d'une image afin de les faire tenir dans les couleurs limitées du format. Et encore, il faut souvent un travail supplémentaire pour y parvenir, surtout pour les animations GIF. PNG et JPEG sont plus rapides, mais au prix de la taille pour PNG et d'une perte de qualité pour JPEG. Cela dit, en termes de qualité, les images GIF sont en réalité pires !
  • Préparer et mettre en cache à l'avance des images telles que fonds, superpositions, cadres, masques, ou pré-générer des tables de correspondance de couleurs, des cartes de distorsion, des gabarits, des masques, etc. Toutes ces choses peuvent faire de grosses différences sur votre temps de traitement. Réfléchissez à ce qui peut être fait au préalable. Une grande bibliothèque d'images pré-générées peut être bien plus rapide que d'essayer de créer les images au besoin. Voir aussi l'usage de "MPC:" pour les images intermédiaires et mises en cache. Ce sont des images mappées en mémoire sur le disque, dont le temps de lecture est essentiellement nul, mais qui sont inutiles pour toute autre chose ou sur d'autres machines. Elles ne devraient être créées qu'au début d'un processus majeur de manipulation d'images, et non conservées un tant soit peu, car la moindre mise à niveau logicielle ou système les invalidera et provoquera probablement des erreurs de segmentation.
  • Évitez d'utiliser FX, l'opérateur d'image à effets spéciaux si vous pouvez employer à la place la composition alpha, ou le plus simple Evaluate, opérations mathématiques simples, ou d'autres techniques. Si vous devez malgré tout y recourir, essayez d'en restreindre l'usage à la plus petite image possible, ou à un seul canal de l'image (pour du niveau de gris).
  • Évitez d'utiliser des flous d'image de grande taille lorsque quelques-uns plus petits peuvent être plus rapides. Faites quelques mesures de temps pour voir ce qui est le plus rapide. Il en va de même pour les autres opérateurs de morphologie et de convolution, comme les opérateurs gaussien et d'ombre.
  • Utilisez des sous-images, ou régions, plus petites pour le traitement complexe de petites zones. Par exemple, repérer et extraire les yeux d'une personne (au moyen de régions) avant de masquer et de recolorer peut apporter un gain de vitesse énorme par rapport au traitement de la grande version complète de l'image. Plus l'écart est grand, plus l'économie l'est aussi.
  • Pour lire de grandes images, ou même un grand nombre d'images, il vaut mieux utiliser un modificateur de lecture afin de les redimensionner ou de les rogner immédiatement après lecture de chaque image, réduisant ainsi le besoin global en mémoire. Pour le JPEG, vous pouvez même utiliser le modificateur de bibliothèque spécial '[jpeg:size](formats.html#jpg_read)' pour éviter l'allocation de la mémoire. Cela empêche à son tour l''écroulement du disque' (qui rend les ordinateurs TRÈS lents). Surtout lorsque beaucoup de grandes images sont en jeu, comme lors de la génération d'index de répertoire en montage ou d'autres collages multi-images.
  • Pour les images vraiment gigantesques qui doivent être traitées depuis le disque, il peut être préférable de les traiter par plus petits morceaux.
  • Toujours pour les grandes images... Si vous disposez d'un OS Windows 64 bits, utilisez la distribution ImageMagick 64 bits. Elle exploite un espace d'adressage plus vaste et peut loger en mémoire de plus grandes images que Windows 32 bits.
  • Par défaut, IM utilise plusieurs threads pour les opérations de traitement individuelles. Cela signifie qu'un ordinateur doté de deux 'cœurs' ou plus traitera généralement les images plus vite qu'une machine à un seul CPU. Pour les grandes images, les capacités multithread d'OpenMP peuvent procurer un net avantage de vitesse, car elles mobilisent davantage de CPU pour mener à bien chaque opération. Notez qu'au sein d'IM, seules les opérations de traitement individuelles sont parallélisées. L'économie est donc plus grande pour le traitement de grandes images, et non lorsqu'on traite un grand nombre d'images (voir ci-après).
  • Pour les petites images, les capacités multithread d'IM ne vous apporteront pas grand avantage. Dans ce cas, exécuter plusieurs convert simultanément sur des images différentes peut accroître le débit. Cela peut aussi se produire lorsque plusieurs requêtes web PHP lancent plusieurs commandes "magick". Dans l'une ou l'autre de ces situations, avoir le multithread activé pourrait s'avérer très nuisible en raison de la contention CPU ; il vaut mieux désactiver OpenMP en fixant la variable d'environnement 'MAGICK_THREAD_LIMIT' à '1'. Voir la discussion du forum IM le multithread ralentit 'convert'. Vous pouvez aussi regarder MAGICK_THROTTLE pour amener ImageMagick à céder plus souvent le contrôle des CPU à des points plus appropriés.
  • Si vous effectuez beaucoup de petites opérations sur les images (comme du dessin), essayez de n'utiliser aucun nom de couleur. Spécifiez les couleurs à l'aide de couleurs hexadécimales, comme "#00AA99", ou de nombres rgb, comme "rgb(0,160,100)", afin d'éviter qu'IM n'ait à charger les tables de noms de couleurs (plutôt volumineuses !). Vous pouvez aussi essayer de supprimer ou d'alléger les fichiers de définition de la liste des 'polices' système (dans "type.xml"). Ou supprimez complètement ces fichiers et indiquez plutôt les polices directement par leur nom de fichier. En somme, réduisez le chargement des informations de configuration supplémentaires qu'IM lit et initialise lorsqu'un traitement précis le requiert. Donc, soit ne les utilisez pas, soit réduisez la taille et l'incidence des fichiers de configuration.
  • Compiler ImageMagick en bibliothèque partagée (le défaut) peut réduire fortement le temps de chargement. Les bibliothèques et les modules de codeurs ne sont chargés qu'au besoin ; une version dynamique d'IM ne chargera donc rien dont elle n'a pas besoin pendant le traitement. De plus, les bibliothèques partagées tendent à rester disponibles et peuvent donc ne pas avoir à être rechargées lors d'une seconde exécution.
  • Si vous appelez ImageMagick au sein d'un module Apache, cela réduira aussi le temps de démarrage, car certaines parties seront chargées une fois et maintenues disponibles pour un usage multiple, au lieu de devoir être rechargées sans cesse. Cela pourrait devenir plus pratique à l'avenir avec un processus IM 'démon' fonctionnant en permanence.

Compiler ImageMagick à partir des sources

Compiler des RPM ImageMagick pour linux à partir de SRPM

Vous n'avez PAS besoin de root pour construire les RPM à proprement parler, mais il vous faut root pour les installer. J'utilise cette méthode pour générer et installer IM sous les systèmes Fedora Linux, mais on a aussi rapporté qu'elle fonctionne pour les systèmes CentOS 5.4 (Enterprise Redhat) Linux (voir des notes plus précises sur IM sous CentOS). Récupérez d'abord la dernière version du RPM source depuis les RPM sources Linux. Assurez-vous d'abord que votre machine dispose de tous les compilateurs et outils nécessaires.

  sudo yum groupinstall "Development Tools"
  sudo yum install rpmdevtool libtool-ltdl-devel

| "sudo" est un programme pour exécuter des commandes en tant que root, si vous y êtes autorisé ; sinon, utilisez un shell root et retirez la partie "sudo" de ce qui précède.
---|---
Pour les systèmes plus anciens comme CentOS 5.5, il semble que vous ayez aussi besoin de ces paquets.

  sudo yum compat-libstdc++ gcc-c++ gcc-objc++ libstdc++ libstdc++-devel

Ensuite, vous devriez aussi installer les paquets de développement des bibliothèques dont IM a besoin pour la compilation. Le moyen simple d'obtenir les plus courants est d'installer d'abord la version de développement d'IM, même si nous en compilerons une nouvelle pour la remplacer plus tard.

  sudo yum install ImageMagick-devel

Vous devriez également vous assurer que ces paquets et leurs dépendances (comme les bibliothèques de développement jpeg et png) sont eux aussi installés :

freetype-devel ghostscript-devel libwmf-devel jasper-devel lcms-devel bzip2-devel librsvg2 librsvg2-devel liblpr-1 liblqr-1-devel libtool-ltdl-devel autotrace-devel

Certains exemples des "ImageMagick examples" peuvent aussi utiliser des programmes fournis par ces paquets et bibliothèques facultatifs, mais ils ne sont pas nécessaires au processus de compilation.

gnuplot autotrace

En règle générale, tous ces paquets sont facultatifs, mais s'ils ne sont pas installés, les 'codeurs' et opérateurs qui utilisent ces bibliothèques risquent de ne pas être intégrés automatiquement à la compilation. Par exemple, le module "liblqr" est nécessaire pour activer l'opérateur de redimensionnement fluide. Téléchargez maintenant un paquet SRPM (RPM source) à partir duquel construire vos RPM binaires. OU construisez un SRPM à partir d'un TAR existant ou d'un téléchargement SVN en utilisant...

  rm config.status config.log
  nice ./configure
  rm *.src.rpm
  make srpm

Notez qu'une fois le SRPM obtenu, vous pouvez construire les RPM proprement dits à installer.

  nice rpmbuild --nodeps --rebuild   ImageMagick*.src.rpm

Cela créera un sous-répertoire "rpmbuild" dans votre répertoire personnel, dans lequel il extraira les sources du SRPM et construira la version RPM du paquet IM compilé. Sur les anciennes versions de Fedora et Redhat, cela se faisait dans "/usr/src", généralement réservé au seul root. Vous pouvez toutefois rendre ce répertoire vôtre ou accessible en écriture afin de pouvoir malgré tout le faire sans nécessiter un accès root complet pour la compilation.
Récupérez maintenant les RPM tout juste construits depuis le répertoire de compilation. Ceci ne prend que les paquets ImageMagick Core et PerlMagick ; vous voudrez peut-être en récupérer davantage, mais cela vous appartient...
  cp -p ~/rpmbuild/RPMS/*/ImageMagick-[6p]*.i[36]86.rpm .

Nettoyez et supprimez les zones de compilation (y compris celles qui ont pu être créées pour vous)...

  rm -rf /var/tmp/rpm-tmp.*  ~/rpmbuild

Vous pouvez maintenant installer les paquets RPM que vous avez construits. Il faut être root pour cela (voir la note sur la commande "sudo" ci-dessus)...

  sudo rpm -ihv --force --nodeps  ImageMagick-*.i[36]86.rpm

Le "--nodeps" est typiquement nécessaire en raison de certaines dépendances inhabituelles qui existent parfois sur les systèmes Linux. Pour mettre à niveau une installation existante, je fais généralement ceci (en tant que root).

  sudo rpm -Uhv --force --nodeps  ImageMagick-*.i[36]86.rpm

Si vous voulez aller plus loin, je vous recommande de consulter le guide avancé d'installation depuis les sources sous Unix sur le site web d'IM.
Pour supprimer IM par la suite, il vous suffit de supprimer le paquet, comme ceci (là encore en tant que root)...

  sudo rpm -e --nodeps  ImageMagick\*

Il m'arrive de vouloir nettoyer et effacer complètement toute trace d'IM du système. Pour cela, j'utilise d'abord la commande précédente pour retirer le paquet du système (une variante est montrée ci-dessous). J'exécute ensuite les commandes de suppression suivantes. NOTE : je ne donne aucune garantie à ce sujet, et je vérifierais soigneusement les commandes au préalable pour m'assurer qu'elles ne suppriment rien d'indu. Si quelque chose a été oublié, ou si elles ont supprimé quelque chose qu'elles n'auraient pas dû, faites-le-moi savoir afin que je puisse les corriger.

  rpm -e --nodeps `rpm -q ImageMagick ImageMagick-perl`
  rpm -e --nodeps `rpm -q ImageMagick-devel`
  rm -rf /usr/lib/ImageMagick-*
  rm -rf /usr/lib/lib{Magick,Wand}*
  rm -rf /usr/share/ImageMagick-*
  rm -rf /usr/share/doc/ImageMagick-*
  rm -rf /usr/include/{ImageMagick,Magick++,magick,wand}
  rm -rf /usr/lib/perl5/site_perl/*/i386-linux-thread-multi/Image/Magick*
  rm -rf /usr/lib/perl5/site_perl/*/i386-linux-thread-multi/auto/Image/Magick*
  rm -rf /usr/share/man/man?/*Magick*
  rm -f /usr/lib/pkgconfig/ImageMagick.pc

Attention, d'autres paquets peuvent nécessiter la présence d'IM ; si vous le supprimez, je vous conseille de mettre immédiatement à jour les paquets de votre système, afin qu'il réinstalle la version d'ImageMagick d'origine par défaut (et généralement assez ancienne) fournie pour votre système Linux. Cela passe en général par un paquet de « mise à jour logicielle graphique » ou la commande "yum upgrade". Profitez-en bien.

ImageMagick à partir des sources sur Ubuntu

Pour obtenir toutes les bibliothèques de développement nécessaires à la compilation d'ImageMagick, utilisez ce qui suit.

  sudo apt-get install imagemagick libmagick++-dev

Une page web de « Shane » décrit comment installer ImageMagick à partir des sources sur Ubuntu 8.04. Je ne l'ai pas essayé, mais cela installait IM directement dans "/usr/local" à l'aide de "make". Cela ne génère pas de paquet d'installation 'DEB', ce qui n'est pas une solution idéale. Si quelqu'un sait créer un paquet 'DEB' pour Ubuntu, qu'il me le fasse savoir. Peut-être à l'aide de l'introduction à l'empaquetage Debian.

Compilation sous MacOSX

La façon la plus simple d'installer ImageMagick sous MacOSX est d'utiliser MacPorts. Mais ce qui suit renvoie à des informations sur la compilation pour MacOSX. Je ne sais pas si cela fonctionne ni si cela sera utile, car je ne l'ai jamais utilisé. Voyez néanmoins Installer ImageMagick sans Fink ni MacPorts et Installer ImageMagick sous Snow Leopard. Ce qui précède est une paraphrase d'une discussion sur le forum des utilisateurs d'IM.

Compiler les versions HDRI d'IM

Pour des informations sur la compilation d'une version HDRI d'IM, voir Activer HDRI dans ImageMagick sur le site principal d'IM ; pour des informations propres à Windows et à Ubuntu Linux, voir aussi la discussion d'annonce des transformées de Fourier sur les forums des utilisateurs.

Créer un ImageMagick personnel

Vous n'avez pas toujours le luxe d'un accès superutilisateur à la machine sur laquelle vous travaillez vos images, et souvent ceux qui possèdent cet accès ne veulent pas mettre à jour leur installation d'ImageMagick. Peut-être pour des questions de gestion de paquets, ou des problèmes de compatibilité. Si vous avez un accès en ligne de commande (par exemple via SSH), tout n'est pas perdu. Vous pouvez installer et utiliser une version personnelle d'ImageMagick. La mauvaise nouvelle, c'est que vous aurez tout de même besoin des administrateurs système pour installer les compilateurs et les paquets de développement (voir plus haut), mais ils sont souvent déjà présents, ce qui n'est pas toujours un problème. Décidez d'abord dans quel sous-répertoire vous voulez installer votre version d'IM. Un répertoire dédié est le meilleur choix, car il vous suffit alors de supprimer tout ce répertoire pour désinstaller. Dans mon cas, j'installerai dans le sous-répertoire "apps/im" de mon répertoire personnel.

  export MAGICK_HOME=$HOME/apps/im

Maintenant, pour installer une version personnelle, téléchargez, décompressez et placez-vous dans le répertoire des sources d'ImageMagick. Configurez-le ensuite comme une version 'uninstalled' (non installée).

  rm config.status config.log
  nice ./configure --prefix=$MAGICK_HOME --disable-installed \
          --enable-shared --disable-static --without-modules --with-x \
          --without-perl --without-magick-plus-plus --disable-openmp \
          --with-wmf --with-gslib --with-rsvg --with-xml \
          CFLAGS=-Wextra \
          ;
  nice make clean
  nice make
  nice make install

Dans la définition ci-dessus, l'important est le "--disable-installed" associé au "--prefix". Les autres parties désactivent la compilation d'aspects plus facultatifs de votre version personnelle d'ImageMagick. Modifiez-les à votre guise. Maintenant, pour utiliser votre propre version installée au lieu de la version système habituelle, il vous suffit de définir les variables d'environnement suivantes.

  export MAGICK_HOME=$HOME/apps/im
  export PATH="$MAGICK_HOME/bin:$PATH"
  export LD_LIBRARY_PATH="$MAGICK_HOME/lib:$LD_LIBRARY_PATH"

Maintenant, si je tape

  magick -version

je vois par défaut la version plus récente d'IM que vous venez d'installer. Notez que la variable "$MAGICK_HOME" doit être définie pour un ImageMagick créé avec l'option "--disable-installed". Les deux autres variables d'environnement garantissent que nous utilisons la version personnelle plutôt qu'une éventuelle version système également installée.
AVERTISSEMENT : ne mélangez pas les variables ci-dessus. Définissez-les toutes, ou ne les définissez pas de cette manière. L'exécutable IM que vous utilisez DOIT aussi employer les mêmes bibliothèques, codeurs et fichiers de configuration que ceux avec lesquels il a été construit. Mélanger la version système et votre version personnelle provoquera vraisemblablement des erreurs de segmentation et de mémoire.
Vous pouvez déplacer l'emplacement de votre IM personnel sans recompiler, mais vous devrez non seulement modifier les variables d'environnement ci-dessus, mais aussi changer (ou supprimer) les chemins codés en dur vers le programme "magick display" utilisé par le délégué "[show:](files.html#show)" dans votre fichier "delegate.xml" installé personnellement. Voir Délégués pour plus d'informations sur cet aspect d'IM.
Pour pouvoir basculer facilement entre la version installée sur le système et plusieurs versions personnelles d'IM, je ne définis en général pas du tout les variables ci-dessus. J'appelle plutôt un script qui les définit avant d'appeler la version voulue d'IM. Par exemple, j'ai une version personnelle d'IM compilée avec HDRI, que je n'utilise que pour certains exemples d'ImageMagick Examples. Je ne veux normalement pas utiliser cette version, préférant la version système sans HDRI pour l'essentiel du travail d'image. J'ai donc installé une version 'HDRI' d'IM dans mon espace personnel "$HOME/apps/im_hdri", et créé un script que j'ai appelé "hdri" contenant...

#!/bin/sh
#
#   hdri imagemagick_command....
#
# Run the HDRI version of imagemagick (or other personal installed IM)
#
# Where is the HDRI version of IM stored
export MAGICK_HOME=$HOME/apps/im_hdri

# Set the other two environment variables
export PATH="$MAGICK_HOME/bin:$PATH"
export LD_LIBRARY_PATH="$MAGICK_HOME/lib:$LD_LIBRARY_PATH"

# Execute the HDRI version of the command
exec "$@"

Maintenant, si je tape...

  hdri magick -version

[IM Text]

Je vois que j'ai exécuté ma version HDRI d'ImageMagick, mais seulement quand j'en ai besoin. Si je ne préfixe pas ce qui précède par "hdri", j'exécuterais alors la version système normale d'IM. AVERTISSEMENT : si votre version personnelle d'IM n'est pas trouvée par le script, celui-ci revient silencieusement à la version système. La vérification de 'version' ci-dessus est un test important pour m'assurer que j'utilise bien ma version personnelle, et non la version système. Vous pouvez aussi vérifier exactement quelle commande magick le script tente d'exécuter en utilisant 'which'.

  hdri which convert

Autrement dit, le script est assez souple pour que vous n'ayez pas réellement besoin d'exécuter "magick" : vous pouvez lancer n'importe quelle commande, comme des scripts shell ImageMagick, de sorte que ce script utilise le magick HDRI au lieu du convert système normal.

  hdri  some_im_script   image.png   image_result_hdri.png