Ejemplos de ImageMagick -- API y scripting
- Windows y DOS
- PHP
- PHP mediante comandos de shell
- PHP Imagick (la API de ImageMagick para PHP)
-
PerlMagick (scripts de Perl Magick)
-
Metacaracteres en los nombres de archivo
Consejos para mejores scripts de shell/PHP con ImageMagick Por qué usar varios comandos "magick" Hacer IM más rápido (en general) Compilar ImageMagick desde las fuentes
- ImageMagick desde las fuentes en Ubuntu
- ImageMagick en MacOSX
- Compilar versiones HDRI de IM
- Crear una instalación personal de ImageMagick
La interfaz de línea de comandos (CLI) de ImageMagick, de la que tratan estos ejemplos, es solo uno de los métodos con los que puede usar, modificar y controlar imágenes mediante la biblioteca de funciones del núcleo de ImageMagick (MagickCore). Es, en esencia, la interfaz de API de tipo 'shell'. Existen muchas otras interfaces de programación de aplicaciones (API) que puede usar de forma más directa desde numerosos lenguajes de programación; consulte las API de ImageMagick. Aquí examino formas de mejorar su scripting y programación con IM, las diferencias entre el scripting en Windows y en Unix, y los fundamentos del uso de IM desde otras API y lenguajes de programación.
API y otros métodos de uso de IM
Las API (interfaz de programación de aplicaciones) para el procesamiento real de imágenes no son, de hecho, más rápidas que el uso de los comandos de la CLI (como "magick", que en sí mismo representa un tipo de API de shell). La misma biblioteca del 'núcleo' se utiliza para todo el procesamiento de imágenes en IM. Por eso, si realiza una tarea compleja como la distorsión de imágenes, usar una API en lugar del "magick" de shell supone muy poca diferencia en términos de 'velocidad' global de procesamiento. Entonces, ¿por qué usar una API en lugar de la línea de comandos? Un shell constantemente 'bifurca' (fork) muchos comandos distintos (no solo comandos "magick" de IM, aunque los shells tienen algunas funciones 'integradas'), cada uno de los cuales debe cargarse e inicializarse cada vez que se ejecuta. Cada comando de IM también tiene que reinicializar sus archivos de configuración, analizar los argumentos de la línea de comandos, releer las imágenes con las que trabaja y, a menudo, guardar los resultados de nuevo en disco. Todo ello lleva tiempo y los hace más lentos. Es decir, todos esos pasos adicionales consumen tiempo y capacidad de procesamiento, así que, si los repite mucho, una API empieza a tener sentido. Una API también puede permitirle hacer otras cosas que la línea de comandos no puede.
- Si la API ya está en ejecución, tiene poco o ningún tiempo de arranque.
- Almacenar en memoria un arreglo con muchas listas de imágenes que pueden procesarse en el orden que prefiera. Por ejemplo, tengo un programa que lee cientos de imágenes, creando miniaturas de 32x32 píxeles a medida que las lee, y luego las compara todas con magick por pares, una pareja cada vez (9.900 comparaciones para 100 imágenes). Otro ejemplo sería ordenar las imágenes por su color general en memoria.
- Puede tener en marcha muchos hilos distintos de procesamiento de imágenes, en cualquier orden. No tiene por qué 'terminar' una secuencia de procesamiento antes de trabajar en la siguiente imagen o paso. Por ejemplo, ¡imagine un programa que resuelve un rompecabezas!
- Puede recuperar información de las imágenes y usar esa información de formas complejas para modificar el procesamiento sin tener que reinicializarlo (releyendo configuraciones e imágenes) una y otra vez. Por ejemplo, obtener el tamaño de la imagen antes de calcular los requisitos del marco para esa imagen.
- Recorrer las imágenes en un bucle, ¡haciendo algo muy distinto con cada una de ellas! Por ejemplo, generar una secuencia de animación en la que cada fotograma se distorsiona de una forma ligeramente diferente.
- Acceso directo y completamente aleatorio a los datos de la imagen. Por ejemplo, buscar una 'cara' en una imagen.
Pero para la mayoría de tareas que no impliquen grandes cantidades de imágenes, o el procesamiento general de imágenes de formas bien definidas, la línea de comandos es prácticamente igual de rápida. No obstante, puede ahorrar mucho tiempo y reprocesamiento si... * Aprovecha el guardado de imágenes MPC (para relecturas más rápidas) de las imágenes intermedias. Vea mi script divide_vert. * Usa el procesamiento paralelo mediante canalización (pipelining), ¡incluso empleando distintas máquinas para distintos pasos!, para aprovechar mejor el procesador y evitar guardar imágenes intermedias en disco. Vea enlarge_image como ejemplo de script canalizado en el que un paso envía la imagen o imágenes mediante una tubería al siguiente paso (a veces opcional) de la secuencia. * Usa un bucle para procesar cada imagen individualmente antes de enviar un flujo de resultados, mediante una tubería, a un paso final que 'los fusiona todos'. Tengo muchos scripts así... Vea Composición programada de imágenes en capas como ejemplo de uno de ellos.
Por supuesto, en una API también se podrían haber empleado técnicas distintas y más rápidas para una tarea de procesamiento especial que requiera acceso a la imagen. Y, a medida que las encontramos y disponemos de tiempo, a menudo programamos esas técnicas en la biblioteca del núcleo. Las distorsiones de imagen y las diversas expresiones FX son un ejemplo de ello.
Windows y DOS
Los ejemplos sobre el uso del scripting en Windows y DOS con la API de la CLI se han trasladado a Uso en Windows.
PHP (comandos de IM desde la función "system()")
Los usuarios de PHP tienen tres formas de usar ImageMagick:
- La interfaz PECL "
imagick" - La interfaz "
MagicWand" - Usar las funciones "system()" y "exec()" para ejecutar el comando "
magick" de la CLI.
Debido a la existencia de los ejemplos de IM, este último método (y el primero que veremos a continuación) se ha convertido en los últimos años en la forma más común de usar IM desde PHP. Por supuesto, para algunas cosas puede no ser el mejor método (vea más arriba), en cuyo caso están disponibles las interfaces de API, aunque puede que necesite que un administrador de sistemas las habilite en su entorno PHP.
PHP mediante comandos de shell
La mejor fuente de información específica sobre el uso de esta técnica es el usuario del foro de IM Bonzo y su sitio web RubbleWebs. Tenga en cuenta que PHP ejecuta "magick" en un entorno distinto, y probablemente incluso como un usuario distinto, del que obtendría desde la línea de comandos. Por eso, lo que funciona en la línea de comandos puede requerir algunos ajustes para que funcione desde un script PHP ejecutado en la web. Lo siguiente es el procedimiento recomendado para las pruebas iniciales de la interfaz de línea de comandos de IM de un ISP, suponiendo que no tenga acceso directo a un 'shell' de línea de comandos en el sistema remoto. Es decir, solo puede subir archivos web para su ejecución. Así que lo primero que hay que hacer es intentar localizar el comando "magick" en el sistema, saber qué versión está instalada y cuál es el entorno en el que se ejecuta PHP. En un servicio web Linux, suba y acceda a este script PHP desde el servidor web del ISP...
<?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");
?>
Esto ejecutará un buen número de comandos para ver cómo es su entorno. El primero, "pwd", le indica el directorio actual en el que se ejecutó el script PHP. Puede coincidir o no con el directorio donde está ubicado el script PHP, y quizá no pueda escribir en ese directorio desde el script PHP. Los dos comandos siguientes le indican si "magick" está disponible en el PATH de comandos proporcionado por defecto, usando "type", y, en caso afirmativo, dónde está. El comando "locate" debería encontrar todos los comandos "magick" que existan en el servidor (suponiendo que sea un servidor linux), pero puede encontrar otros comandos, archivos y directorios "magick" ajenos a ImageMagick. Tendrá que interpretar los resultados. Los tres comandos siguientes suponen que "magick" está en el PATH de comandos y le piden que informe de su número de versión y de qué fuentes cree IM que tiene acceso. Qué comando informa de qué fuentes depende de lo antigua que sea la versión de IM instalada. Si solo ve errores, entonces "magick" no está en la ruta de la línea de comandos, y su proveedor de ISP NO inicializó correctamente las variables "PATH" y "LD_LIBRARY_PATH" del servidor web para incluirlo. Vea la salida del comando "env" para saber qué definieron. Si es el caso, tendrá que averiguar dónde está ubicado exactamente y usar algo como el siguiente script PHP. Esto hará que su script sea menos portable, ya que queda codificado de forma fija para ese ISP concreto. Por ejemplo, suponga que el comando "magick" está en "/opt/php5extras/ImageMagick/bin"; entonces puede definir una variable para especificar su ubicación. Esto suele hacerse como parte del proceso de configuración e instalación de la aplicación, para scripts PHP que se usan en distintos hosts de ISP.
<?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 obtiene errores de bibliotecas de "ldd", la variable "LD_LIBRARY_PATH" es incorrecta y el ISP definitivamente ha fallado en su trabajo durante la instalación; debe informar del error y conseguir que corrijan la variable de entorno "LD_LIBRARY_PATH" del servidor web, o que reinstalen ImageMagick. En lugar de fijar la ubicación del comando magick, también puede ajustar la variable de entorno PATH con una línea como esta al principio. No obstante, la configuración típica de un sistema PHP suele 'denegar' este método por defecto...
putenv("PATH=" . $_ENV["PATH"] . ":/opt/php5extras/ImageMagick/bin");
putenv("LD_LIBRARY_PATH=" . $_ENV["LD_LIBRARY_PATH"] .
":/opt/php5extras/ImageMagick/lib");
Después de eso, pruebe algunos de los ejemplos más sencillos de los ejemplos de IM y trate de hacerlos funcionar. Por ejemplo, para enviar la imagen 'rose' de IM como un archivo de imagen JPEG de vuelta al usuario web...
<?php
header( 'Content-Type: image/jpeg' );
passthru("magick rose: jpg:-");
?>
Si necesita fijar la ubicación del comando magick, puede usar...
<?php
header( 'Content-Type: image/jpeg' );
$magick="/opt/php5extras/ImageMagick/bin/magick"
passthru("$magick rose: jpg:-");
?>
o, si tiene problemas con las bibliotecas, puede probar algo como...
<?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 aún así no ve nada, consulte mi archivo en bruto de consejos y trucos de PHP para obtener información sobre técnicas de redirección de mensajes de error.
Cuando tenga funcionando ese script básico, puede probar una de las fuentes listadas por sus scripts de prueba de PHP (modifique lo siguiente para adaptarlo a su servidor PHP). Por ejemplo, en un servidor Solaris del que disponía en aquel momento, observé que el conjunto de fuentes 'Utopia' estaba disponible, así que pude intentar crear una etiqueta con esa fuente...
<?php
header('Content-Type: image/gif');
passthru("magick -pointsize 72 -font Utopia-Italic label:'Font Test' gif:-");
?>
Ejemplo de conversión de shell a PHP
Aquí tenemos un comando de ImageMagick bastante típico...
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
Al convertirlo a PHP, quedará algo así...
<?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:-" );
?>
Observe cómo sigo dividiendo la larga línea de comandos del "magick" para que la secuencia de procesamiento de imágenes sea más fácil de seguir y de editar después. Lo hice mediante concatenación de cadenas en PHP, en lugar de la continuación de línea del shell que se usa en los scripts de shell. Fíjese también en el espacio adicional al comienzo de las líneas posteriores. Y en el doblado de las demás barras invertidas que había en el comando original. Como alternativa, puede proteger esas opciones usando comillas simples en lugar de barras invertidas. También usé algunas variables de PHP para facilitar el ajuste de la imagen generada por el script PHP y controlar mejor los resultados. No obstante, al insertar esas opciones en el "magick" usé comillas simples para protegerlas de modificaciones posteriores por parte del shell. ¡Pero cuidado con las comillas simples dentro de esas cadenas insertadas! Podría convertir esas opciones en argumentos de entrada de PHP, de modo que pueda generar una imagen para cualquier texto de entrada que se le pase desde una petición web. También puede ejecutar varios comandos de shell desde la misma cadena de la llamada a system. De hecho, ¡una sola llamada a system puede contener un script de shell completo si lo desea! Así que puede ejecutar bucles de shell y varios comandos (con sus limpiezas) todo en una única llamada a system. Algo que mucha gente no se da cuenta de que es posible. En esencia, si tiene cuidado, puede aprovechar bien las matemáticas que ofrece PHP y la capacidad de scripting del shell. Todo al mismo tiempo. Solo vigile las comillas. Para ver varios ejemplos de llamadas a comandos de ImageMagick desde PHP, consulte Rubble Web, escribir código de IM en PHP, que describe unas cuatro técnicas distintas.
Cuidado con las comillas adicionales
Tenga en cuenta que, por lo general, los comandos de IM en PHP van envueltos en un nivel adicional de comillas (normalmente comillas dobles), por lo que hay que tener cuidado para tener en cuenta el uso de ese nivel adicional de comillas. Recuerde que, cuando PHP ejecuta una cadena... PHP procesa sus comillas, barras invertidas y sustituciones de variables. El shell divide después los argumentos y realiza sus propias sustituciones de variables y comillas. También realizará cualquier redirección de descriptores de archivo del tipo "2>&1" si está presente. ImageMagick recibe un arreglo de argumentos, pero también realiza su propio tratamiento de metacaracteres en los nombres de archivo, específicamente para DOS (el entorno dos no maneja los metacaracteres) y para argumentos como coder:*.gif[50x50] que el shell no logra expandir debido al prefijo coder: o al modificador de lectura [...]. Es decir, ¡MUCHO análisis de argumentos! Lo que puede significar mucho trabajo de comillas y barras invertidas. Se requiere precaución y previsión. Le recomiendo que al menos lea los manuales de PHP sobre funciones de ejecución de programas, que incluyen: PHP exec(), system() y passthru(). Vea también el operador de comillas invertidas. Es especialmente importante que comprenda qué se devuelve exactamente (en general, solo la última línea) y qué se pasa al cliente que realiza la llamada (todo lo demás).
Seguridad en PHP
Recuerde...
_On the net, the only users you can trust not to be potentially hostile
are those who are *activally* hostile.
-- Programming Perl - Camel Book, r3_
Debe comprobar a fondo todos los argumentos de entrada que se pasan desde el usuario a un comando de IM. Asegúrese de que el argumento sea exactamente lo que espera. Es mucho mejor pecar de restrictivo que de permisivo cuando se trata de la World Wide Web. Algunas cosas habituales que conviene vigilar son:
- Caracteres binarios en un argumento: una técnica habitual de los crackers.
- Espacios, tabulaciones, saltos de línea y retornos de carro inesperados en los argumentos.
- Barras invertidas (separadores de directorio) y rutas con '
..'. También, en windows, '\' y ';' en los nombres de archivo. - Metacaracteres de expansión de archivos, incluidos "
~*?[]{}<>", además del metacarácter especial "@" propio de ImageMagick. - Otros metacaracteres del shell, incluidos "
$#;", y los tres caracteres de comilla ''', '"', '```' - El argumento no coincide con lo que ImageMagick espera. Use "
[-list](https://imagemagick.org/command-line-options/#list)" para ver qué tipos de argumento entiende IM para opciones concretas. Por ejemplo, una opción "[-gravity](https://imagemagick.org/command-line-options/#gravity)" introducida por el usuario solo tiene 10 ajustes distintos. - Y así sucesivamente...
Para cualquier tipo de trabajo de programación web, es vital comprender la seguridad y cómo los hackers pueden usar argumentos especialmente diseñados para subvertir los comandos invocados. No solo para PHP, sino también para el shell y para ImageMagick. IM requiere un cuidado especial, ya que, por ejemplo, puede leer y convertir en imagen un archivo de contraseñas para devolverlo. De nuevo... Es mucho mejor ser demasiado restrictivo que abrir un agujero de seguridad imprevisto, cuando hay una web de por medio.
Escribir en el sistema de archivos
Como se ha mencionado antes, y si siguió el procedimiento inicial descrito arriba, sabrá con certeza que PHP suele ejecutarse como un usuario distinto y más restringido en el servidor. Por ello, normalmente NO podrá escribir en el directorio que contiene el script (o dondequiera que se esté ejecutando). Por motivos de seguridad, en general no querrá escribir en ese directorio. Si realmente quiere que PHP escriba archivos, haga que guarde la imagen (o los datos) con un nombre de archivo único en "/tmp" y, sobre todo, haga limpieza después, tanto en la salida normal como ante CUALQUIER error. Es asombroso lo rápido que se puede llenar un disco con archivos temporales sobrantes de una aplicación que no hace limpieza correctamente. Si los archivos guardados (imágenes) deben ser visibles para el servidor web, cree un subdirectorio especial de 'escritura por programa' para esos archivos. Cómo debería hacerse. La mayoría de las aplicaciones PHP evitan en realidad escribir nada en el sistema de archivos usando una base de datos como backend. Es decir, las cookies, los tokens, los datos de usuario, las imágenes, etc., se escriben todos en una base de datos como (en orden de complejidad y escala) SQLite, PostgresSQL, MySQL y Oracle. No se guarda nada en el sistema de archivos. Los programadores de sistemas suelen configurar la aplicación PHP con esa información cuando se instala la aplicación. Las imágenes se reproducen normalmente mediante el mismo script PHP, u otro distinto, que busca el 'blob' de la imagen y lo envía al cliente. Las imágenes pueden enviarse como imágenes 'en línea' dentro del propio HTML (vea el formato "[inline:](files.html#inline)", que incluye una demostración de imágenes HTML en línea), o como una única imagen múltiple 'todo en uno', de modo que el HTML/JAVA del cliente solo haga una petición de imagen en lugar de 20 peticiones separadas. Un último punto. Siempre debería existir algún método para limpiar los datos antiguos. A un usuario que no ha iniciado sesión en 2 años probablemente convenga eliminarle los datos.
Obtener la salida de error
Pruebe uno de estos métodos...
<?php
exec("/usr/local/bin/magick -version",$out,$returnval);
print_r($out[0]);
?>
o
<?php
exec("/usr/local/bin/magick -list",$out,$returnval);
print_r($out);
?>
o use shell_exec de la siguiente manera
<?php
$IM_version=shell_exec("/usr/local/bin/magick -version");
echo $IM_version
?>
Para incluir la salida de 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>";
?>
Lo anterior procede de la discusión en el foro de usuarios de IM... ¿Cómo mostrar la información de error de IM en php?. Vea mis propias notas sobre el registro de errores de depuración en PHP, que remiten al manual de PHP sobre el registro de errores, Manejo y registro de errores en PHP; fíjese especialmente en la sección de ejemplos.
Comandos de ImageMagick más seguros...
Idealmente, por motivos de seguridad, conviene evitar usar el shell para analizar una única cadena larga y separarla en el comando y sus argumentos. ¡Es mejor hacerlo usted mismo! Esto significa proporcionar los argumentos al comando como un arreglo de cadenas separadas, en lugar de como una única cadena analizada por el shell. Al hacerlo, evita la posibilidad de errores de sintaxis del shell, la carga adicional de comillas que exige el shell y la posibilidad de que algún hacker rompa el comando del shell y ejecute su propio comando (algo muy malo). Por otro lado, pierde el scripting, las tuberías y la redirección de archivos del shell, pero eso no suele ser una gran pérdida cuando ya está usando PHP u otro lenguaje envolvente. En PHP, la única función que pude encontrar que permite llamar a un comando directamente sin un shell es la función pcntl_exec(). En esencia, evita el shell y llama al comando directamente. No obstante, es una verdadera llamada al sistema 'execl()', que reemplaza el proceso actual por el comando indicado. Es decir, no realiza el 'fork()' ni los enlaces de descriptores de archivo necesarios para ejecutarlo como un subproceso. Por ello, pcntl_exec() es en realidad demasiado de bajo nivel para un uso general, e implementar un comando 'sin shell' puede volverse bastante complejo. Me sorprende mucho que la interfaz de PHP no proporcione ya una llamada a comando más sencilla y segura que 'evite el shell'. Pero, claro, no soy programador de PHP. Perl, en cambio, ofrece varios métodos para llamar de forma segura a subcomandos y procesos, lo que a menudo lo hace preferible a PHP como interfaz web. ¿Alguien con conocimientos de seguridad en PHP podría arrojar luz o indicar dónde encontrar más información?
La API 'IMagick' de PHP
Para probar si el módulo PHP PECL Imagick funciona realmente, suba una sencilla imagen de prueba "image.jpg" y este script PHP al mismo directorio accesible desde la 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 );
?>
Existe en línea un libro de PHP IMagick, y se pueden encontrar más ejemplos de uso de IMagick en el blog de Mikko. El único problema de IMagick es que no se ha mantenido ni actualizado, por lo que puede haber varias funciones que no funcionen o que falten. Asegúrese de usar la versión 3.x de IMagick y una versión actual de IM. Funciona y, para la mayoría de las cosas, lo hace bien, pero si necesita hacer otras cosas, otros métodos de PHP pueden ser una mejor opción.
'MagickWand' de PHP
Puede comprobar si el módulo PHP MagickWand forma parte de la instalación de PHP usando...
<?php
if (extension_loaded('magickwand')) {
echo "PHP MagickWand is available!\n";
} else {
echo "PHP MagickWand is NOT available!\n";
}
?>
Pero, para comprobar que funciona realmente de forma correcta, suba alguna imagen de prueba "image.png" y este 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);
}
?>
No hay garantías con lo anterior, aunque agradezco cualquier comentario adicional. Por lo general no programo en PHP, pero usé lo anterior para probar una instalación de prueba SunONE-PHP5 (con los tres métodos: línea de comandos, magick y MagickWand).
Scripts PHP complejos...
Si necesita generar y enviar tanto HTML como imágenes, considere diseñar su script PHP de modo que peticiones HTML separadas u opciones de entrada distintas generen las diversas partes que necesita en su documento web, ya sea desde el mismo script PHP o desde scripts distintos. Es decir, un script PHP de nivel superior puede enviar HTML con las etiquetas apropiadas, que llaman a sí mismo (o a otro script PHP) con las opciones adecuadas para crear o modificar las imágenes que se muestran en ese primer script PHP de nivel superior. Esto es lo que hacen muchos scripts PHP de álbumes de fotos y de generación de gráficos. Todo controlado mediante las extensiones GET y PATH_INFO de las llamadas a la URL. Tenga en cuenta que no puede usar POST dentro de una etiqueta IMG. Haciéndolo así, debería poder evitar por completo la necesidad de generar, guardar y limpiar imágenes temporales para las páginas web generadas por PHP. Una solución llena de problemas, como las limitaciones de recursos y la recolección de basura, que la convierten en una técnica de programación muy mala. Esta técnica se examina en los dos libros de ImageMagick, aunque el ImageMagick que realmente usa ya va quedando algo anticuado.
Scripts de Perl Magick
La API de PerlMagick es una buena forma de convertir comandos "magick" en un script que también pueda manejar bases de datos, grandes cantidades de imágenes o un procesamiento de imágenes más complejo que el que de otro modo sería posible. La mejor ayuda es examinar los scripts 'demo' de PerlMagick, que están tanto en el código fuente como, normalmente, instalados en el área de documentación de PerlMagick. En mi sistema estaban en "/usr/share/doc/ImageMagick-perl-*/demo/". En este directorio hay un número creciente de ejemplos sencillos de lectura, escritura y procesamiento de diversas imágenes. También está presente el script "demo.pl", que enumera prácticamente todas las opciones comunes de procesamiento de imágenes y cómo puede usarlas. Al convertir un comando "magick" de línea de comandos a perl, hay que recordar algunas cosas.
- Lo primero que hay que recordar es que PerlMagick no elimina automáticamente las imágenes nuevas que se han procesado. Muchos operadores crean una nueva imagen modificada a partir de la antigua, mientras que otros modifican directamente una imagen existente.
- Además, muchos operadores no aplican una operación concreta a una lista completa de imágenes, sino solo a la primera imagen de la lista que les pase. Esto significa que tendrá que recorrer la secuencia de imágenes (el arreglo de perl) usted mismo.
- Puede tener muchas secuencias de imágenes. De hecho, lo habitual es leer cada imagen en su propia secuencia de imágenes separada, en lugar de tener que apañarse con una única secuencia de imágenes, como ocurre en la línea de comandos.
- Además, una vez que tiene la imagen en memoria, puede extraer fácilmente el tamaño de la imagen existente. Eso significa que puede crear nuevos lienzos sin necesidad de clonar y vaciar una imagen existente, como hace en la línea de comandos. No obstante, clonar una imagen también copia sus metadatos, por lo que quizá quiera llevar un registro de esos metadatos para imágenes como las fotos digitales.
- Compruebe si hay errores de imagen (como se muestra en la página de PerlMagick) después de cada proceso importante, especialmente al leer imágenes.
Para convertir una línea de comandos a perl, en esencia realizaría exactamente las mismas operaciones, en exactamente el mismo orden. No obstante, como las imágenes en general no se eliminan y las secuencias de imágenes múltiples son habituales, el uso de paréntesis y de operaciones de clonado adicionales en los comandos "magick" no suele ser un problema. La parte más difícil al convertir scripts suele ser asociar una opción de la línea de comandos con una llamada a función de PerlMagick. La forma más rápida que he encontrado es obtener el código fuente de IM y mirar el archivo "MagickWand/mogrify.c" y buscar la opción de línea de comandos concreta con la que tiene problemas. Por ejemplo, para la opción -threshold, busque "threshold" incluyendo las comillas. Habrá dos coincidencias: una para un análisis sintáctico rápido que asegura que se encuentran todas las opciones, y la segunda con las llamadas internas reales para esa opción. Ahí encontrará el nombre de la función de la biblioteca que se usa, y esa suele asociarse directamente con la función de Perl. En este caso... BilevelImageChannel()
Advertencias de seguridad
Al escribir un script de uso público, especialmente un script PHP basado en web que CUALQUIERA en el mundo podría ejecutar, es de vital importancia comprobar todo lo que pueda provenir de un usuario desconocido (o incluso conocido). Y me refiero a TODO: argumentos, nombres de archivo, URL y también imágenes. Hasta que no verifique algún argumento de entrada, ese argumento podría contener letras, números, espacios, signos de puntuación, o incluso caracteres 'nulos' y de control. Hasta que no lo haya comprobado a fondo, debe tratarse como sospechoso y no debe usarse. No importa que esté usando algún formulario de entrada controlado por la web. Una persona con algo de conocimiento puede llamar fácilmente a su PHP con sus propios argumentos sin usar en absoluto ese formulario de entrada. Y no crea que no lo harán: hay robots por ahí, leyendo formularios de entrada y creando sus propios argumentos 'manipulados' para intentar penetrar en scripts al azar.
Metacaracteres en los nombres de archivo
Como cuestión de seguridad, debe vigilar especialmente los nombres de archivo que contengan espacios, comillas, signos de puntuación, caracteres de control u otros metacaracteres, ya que tanto IM como los shells pueden intentar expandirlos. El problema es que un archivo llamado '*?@${&) .jpg' es en realidad un nombre de archivo perfectamente legal bajo UNIX, pero MUCHOS programas tendrán problemas para manejarlo si ese programa (como el shell e IM) también realiza la expansión de nombres de archivo. Recuerde que, aunque impida que el shell realice la expansión de metacaracteres 'glob', IM también realiza esta expansión por sí mismo (para el uso en DOS). Por ello, impedir todos esos caracteres (y producir un error) es probablemente lo más prudente. Como medida de seguridad, a menudo es buena idea dar error y abortar si un nombre de archivo contiene CUALQUIER carácter desconocido o inusual, cualquier cosa que no sean, por ejemplo, letras, números o el sufijo esperado, antes de pasar ese nombre de archivo a un comando del shell o a IM. Es mejor ser MUCHO más restrictivo e impedir cosas que ser permisivo y dejar pasar algo malo.
Consejos para mejores scripts de shell/PHP con ImageMagick
Estos eran algunos puntos básicos de programación de scripts que comenté sobre un script de shell que se envió a la lista de correo de IM para que otros lo usaran. Originalmente se los envié al autor en privado (que permanecerá anónimo), y me lo agradeció. No todos son específicos de IM, pero deberían aplicarse de todos modos, como práctica de programación estándar. Especialmente si tiene previsto que otra persona use, examine o corrija errores de su programa o script. A su vez, hará que su script sea más útil.
- Coloque la 'ayuda' o la 'documentación' al principio de los scripts y programas. Esto facilita mucho que otras personas entiendan qué hace un programa concreto sin necesidad de instalar o ejecutar el script. Yo mismo a menudo desecho un programa que carece de un comentario claro sobre para qué sirve, en lugar de intentar compilar o ejecutar ese script desconocido. De hecho, he visto enormes proyectos en los que el primer archivo README ni siquiera dice qué hace el proyecto, enorme y complejo. ¡El programador da por sentado que, si lo descargó, ya debe saber qué hace! Asegúrese también de que una 'opción incorrecta' como '-?' imprima una sinopsis no solo de las opciones, sino también un resumen rápido de qué hace el programa, o de dónde encontrar esa ayuda. No se limite a apuntar a un sitio web remoto, que puede desaparecer dentro de 10 años. Como ejemplo de un script que imprime sus propios 'comentarios iniciales', vea el script "jigsaw" en el área de scripts de IM. Perl puede usar POD para la autodocumentación (vea el módulo "
Pod::Usage" de perl). Por ejemplo, vea el script contribuido dpx_timecode.pl. También es aceptable tener la ayuda como un 'here-document' en la primera subrutina, y funciona en la mayoría de los lenguajes. Pero coloque esa subrutina AL PRINCIPIO, no al final ni en el medio del script o programa. - Asegúrese de limpiar su código, de eliminar el código y los comentarios obsoletos, dejando las cosas tan ordenadas y pulcras como pueda. Sea conciso, con etapas comentadas de forma sencilla (si es posible). De nuevo, vea el script "jigsaw".
- Asegúrese de que los archivos temporales se limpian al final. Use un comando de shell "trap" para eliminarlos al salir o al recibir una interrupción. Por supuesto, puede reutilizar un único archivo temporal varias veces, así que no necesita muchos, especialmente con los comandos magick de IM v6. De nuevo, vea el script "jigsaw" y busque "trap".
- Además, no todo el mundo usa el sistema X, aunque a usted se lo pueda parecer. No haga referencia a requisitos de sistema específicos ni a métodos para solucionar el problema. Lo que a usted le funciona puede ser completamente inapropiado para el sistema y la configuración de otra persona. ¡Puede que ni siquiera tengan acceso a Internet! Limítese a decir que no se encontró el comando "
magick" de ImageMagick. Si quiere añadir requisitos de instalación o sugerencias, agréguelos como parte de una documentación aparte más extensa. - Compruebe que el IM que se usa tiene una versión suficientemente alta, o añada cambios retrocompatibles. Yo, precisamente con este fin, incluyo notas de 'aviso de versión' a lo largo de todos los ejemplos de IM sobre cuándo se añadieron diversas funciones especiales. Esto facilita crear scripts con una única comprobación mínima de versión. Aquí tiene una forma sencilla de obtener un único número de versión con fines de prueba en un script de shell. Extrae los 4 números de versión e inserta el número adecuado de ceros para que cada número tenga 2 dígitos, produciendo un sencillo número de 8 dígitos.
IM_VERSION=`magick -list configure | \ sed '/^LIB_VERSION_NUMBER /!d; s//,/; s/,/,0/g; s/,0*\([0-9][0-9]\)/\1/g'`
Por ejemplo, IM v6.3.5-10 generará "06030510", mientras que la siguiente versión, IM v6.3.6.0, produce "06030600". Una versión en PHP de lo anterior está disponible en la página de ejemplos RubbleWeb, lista de fuentes. La cadena resultante puede comprobarse, mediante una sencilla prueba numérica o de cadena, para averiguar si la versión de ImageMagick disponible es lo bastante moderna para lo que su script intenta hacer. Por ejemplo...
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
Fíjese también en cómo indico al usuario el motivo exacto del aborto y, en concreto, la función o funciones especiales para las que se hizo la comprobación de versión. De lo contrario, podría olvidar más tarde por qué se necesitaba esa versión concreta (o superior). También puede modificar el comportamiento de IM para versiones concretas. Por ejemplo, supongamos que quiero obtener una lista de las fuentes disponibles. Antes de la versión v6.3.5-7 de IM, el ajuste "type" de "[-list](https://imagemagick.org/command-line-options/#list)" devolvía la lista de 'fuentes conocidas'. Con versiones posteriores, hay que usar en su lugar el ajuste "font". Así que aquí puedo hacer una comprobación de versión para usar el ajuste correcto y obtener la información necesaria.
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$)'`
ADVERTENCIA: ¡una cadena que empiece por '0' en PERL podría interpretarse como un número octal! No obstante, comparar dos números octales seguirá dando un resultado correcto, siempre que el primer dígito siga siendo '0'. Se aconseja precaución y comprobar la prueba de versión. Otra alternativa para probar la versión es usar "expr" en lugar de la prueba "['...
if expr + "$im_version" \>= "06030507" >/dev/null; then
...
ADVERTENCIA: el '+' adicional de lo anterior no suele ser necesario, al menos para esta prueba, pero sí lo es si la variable pudiera contener la palabra clave especial 'match", que daría problemas a "expr", especialmente si se usa para trabajar con cadenas o subcadenas.
* También puede aprovechar la información que produce "[-list](https://imagemagick.org/command-line-options/#list)" para comprobar si se ha añadido alguna función especial al ImageMagick instalado actualmente.
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
No obstante, tenga en cuenta que a menudo un método nuevo, como "[-distort](https://imagemagick.org/command-line-options/#distort) Arc", puede aparecer durante el desarrollo de IM antes de estar realmente listo para un uso general. Por ello, una comprobación de versión podría seguir siendo la mejor idea. Por eso, en los ejemplos de IM intento anotar (busque los símbolos) la versión de IM en la que una nueva función se ha vuelto lo bastante estable para un uso general.
* No use líneas únicas muy muy largas. Especialmente en los comandos 'convert' complejos. Divídalas usando métodos de continuación de línea (mostrados arriba), como '\' en UNIX, '^' en DOS y la concatenación de cadenas '.' en PHP. Sin embargo, no me refiero a colocar TODOS y cada uno de los ajustes y operaciones en una línea aparte. Realice una operación o etapa principal por línea: crear una nueva imagen, modificar una imagen, fusionar con otras imágenes, etc. Coloque todos los ajustes operativos que necesite un operador concreto justo antes de ese operador. Piense en cada línea como un único paso de procesamiento. Esto le permite separar las líneas para facilitar la lectura y la comprensión de los pasos de procesamiento individuales. Cuanto más clara sea la separación de los operadores, más fácil será seguir un proceso de imagen complejo. Usar paréntesis adicionales, indentar las líneas a lo largo de las distintas etapas o incluso añadir líneas vacías entre los grandes bloques de procesamiento puede hacer aún más fácil ver las etapas principales de una operación grande y larga. Uso estas técnicas por todos los ejemplos de IM, para que sean más fáciles de seguir y entender, ¡así que eche un vistazo! Por último, los comentarios adicionales sobre qué hace un comando concreto pueden marcar una gran diferencia para que otra persona (o usted mismo 2 meses después) lea y entienda su script. ¡Una lástima que de momento no se puedan insertar comentarios en una línea de comandos larga!
* Procure no depender de demasiados programas externos, o úselos solo si están disponibles (con posibles alternativas). Otras personas probablemente no tengan ese programa, o prefieran usar otra cosa. Si el uso de un programa puede ser opcional, hágalo opcional, ya sea bajo el control del usuario o de uso automático si se encuentra. Procure no convertirlo en un requisito obligatorio, si es posible. Por ejemplo, podría usar "pngcrush", "optipng" o "pngnq" para comprimir PNG mejor de lo que IM lo haría normalmente (IM está diseñado para ser general, no específico). Asimismo, "gifsicle", "intergif" u otros optimizadores de compresión LZW para animaciones GIF tienen sus pros y sus contras. Pero no lo convierta en un requisito imprescindible para que un script funcione. Como ejemplo práctico, una versión antigua de "gif2anim" no usaba el "magick identify" de ImageMagick para consultar metadatos específicos de GIF, sino que dependía de una versión parcheada de "[giftrans](http://www.ict.griffith.edu.au/anthony/software/#giftrans)". Este requisito dejó de ser necesario más tarde gracias a las mejoras del "magick identify" de ImageMagick, así que eliminé ese requisito para que el script fuera más usable en general. El propio ImageMagick tiene MUCHOS requisitos opcionales, como "ghostscript" para la lectura de documentos postscript y PDF, o "librsvg" para el manejo correcto de imágenes vectoriales SVG. IM funcionará sin problemas cuando estén disponibles. IM trata estas bibliotecas como opcionales, y solo las necesita si quiere procesar imágenes de esos formatos. Aquí tiene un fragmento de código que puede usar para comprobar las dependencias de un script (especialmente útil en un entorno cygwin muy mínimo)...
# 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
- Llevando el punto anterior más allá. Si necesita matemáticas de punto flotante en un script de shell que usa IM, puede usar el propio IM para hacer esos cálculos, en lugar de depender de algún otro programa como '
awk' o 'bc', que puede no estar disponible (especialmente bajo cygwin en windows). Por ejemplo, aquí calculamos el sin() de un ángulo concreto dado en grados usando 'magick' para hacer el trabajo...angle=-20 sine=`magick xc: -format "%[fx:sin( $angle *pi/180)]" info:` echo $sine
Lo anterior produciría el valor '-0.34202'. Puede ajustar el número de decimales usando el control operativo de precisión. Por defecto está fijado en 6 dígitos.
* Deje que el usuario decida qué formatos de imagen de entrada/salida usar. ImageMagick es ante todo un conversor de imágenes y puede usar muchos formatos distintos. Puede enviar la salida a la pantalla, a postscript, a impresoras, o canalizar la imagen hacia otro comando para su posterior procesamiento. No limite al usuario a un formato concreto. Por ejemplo, los scripts "jigsaw" y "gif_anim_montage" permiten al usuario especificar cualquier imagen de entrada o salida. De ese modo, los usuarios pueden canalizar imágenes hacia dentro o hacia fuera de ese script, lo que permite un procesamiento adicional con otros programas y scripts. Por ejemplo, a menudo uso un comando como...
gif_anim_montage animation.miff show:
para mostrar el resultado del script en mi pantalla, en lugar de guardarlo en un archivo. De hecho, en muchos de mis scripts, si falta la salida, esta usa "show:" por defecto. No limité la entrada ÚNICAMENTE a un archivo de animación GIF, ni limité la salida solo a los formatos de imagen GIF, PNG o JPEG, sino que permití que el script leyera y procesara cualquier formato que ImageMagick pueda manejar. De hecho, IM puede leer desde archivos, tuberías, la pantalla actual, o incluso desde la World Wide Web usando un formato de entrada "URL:" o "HTTP:". No limite estas posibilidades, a menos que se convierta en un problema de seguridad (como en el uso web).
* Lea las imágenes de entrada y produzca las imágenes múltiples de salida ¡una SOLA vez! Si el usuario proporciona el nombre de un archivo de tubería o una URL, no debería intentar leer esas imágenes más de una vez, o las cosas pueden salir mal. Use un archivo temporal, imágenes clonadas o guarde una copia de la imagen de entrada con "[MPC:](files.html#mpc)" cuando necesite referirse a esa imagen varias veces. Si puede manejar varias imágenes desde la tubería, mejor aún. De nuevo, vea el script "jigsaw" como ejemplo de cómo guardar las imágenes de entrada en un archivo temporal cuando se ve obligado a procesar la imagen de entrada varias veces, o en un orden distinto del que implican los argumentos del programa.
Estas cosas, en esencia, dan al usuario de su programa más libertad para hacer lo que ÉL quiere, en lugar de lo que USTED cree que quiere. No los limite a ellos ni a usted mismo haciendo suposiciones sobre para qué se usará el script. PD: Mi principal experiencia es la escritura de scripts en UNIX, a través de muchas arquitecturas y 'variantes' distintas de UNIX, LINUX y otros sistemas tipo UNIX, con más de 25 años de experiencia a mis espaldas. Debería saber de qué hablo respecto a lo anterior.
Por qué usar varios comandos "magick"
- Willem, el miércoles 25 de octubre de 2006, escribió...
- Me preguntaba: a veces veo en sus ejemplos que invoca Convert más de una vez para obtener el resultado deseado. En general, yo esperaría que invocar magick más de una vez no fuera necesario; todo debería poder hacerse en 1 invocación (aunque el comando sería más complejo). ¿Está de acuerdo con esta afirmación?
Estoy totalmente de acuerdo. Aunque antes de la versión 6 de IM eso era en realidad imposible, ya que IM en aquella época no estaba diseñado para hacer más de una o, como mucho, dos operaciones por comando. Sin embargo, IMv7 debería permitirle hacer todo el procesamiento en un único comando. Pero, lamentablemente, ni siquiera eso es siempre posible. Uso varios comandos por una serie de motivos posibles. Normalmente, en las páginas de ejemplos lo hago para poder mostrar el resultado de la imagen intermedia, y así demostrar mejor las etapas intermedias de procesamiento que intervienen. Más adelante, en la misma área del ejemplo, puede que repita el proceso pero usando un único comando, quizá con algo más de complejidad. Por eso, en principio, sí, un único comando puede hacer todo el procesamiento de imágenes. Es muy libre de combinar las técnicas de procesamiento de imágenes en un único comando. Yo mismo lo hago constantemente. La excepción a esto se da en los casos en que necesito extraer información y luego insertarla en el siguiente comando. Un ejemplo de ello es la técnica de recorte difuso, que requiere extraer los resultados de un recorte sobre una copia desenfocada de la imagen. Ese resultado se usa después para recortar la imagen original. También hice esto en la actualización del ejemplo de esquinas redondeadas en miniaturas, donde usé el propio IM para generar un comando draw a partir del tamaño de una imagen para el siguiente comando. No obstante, existen propuestas que permitirán generar opciones directamente a partir de imágenes leídas previamente en memoria. En scripts como 'jigsaw' (vea Técnicas avanzadas, piezas de rompecabezas), a menudo acabo usando varios comandos por un motivo distinto: el procesamiento opcional. Esto permite que las diversas opciones de entrada proporcionadas por el usuario seleccionen pasos adicionales en la secuencia de procesamiento de imágenes. Así que, para el procesamiento opcional, también suelo usar comandos separados para cada etapa del procesamiento. En tal caso, un archivo temporal es prácticamente inevitable. No obstante, normalmente solo necesito a lo sumo una o dos imágenes temporales, y cada paso vuelve a procesar la imagen sobre el mismo nombre de archivo temporal, o el anterior, para que continúe el siguiente paso de procesamiento opcional. Por ejemplo, procesar la imagen reemplazando la imagen de origen.
magick /tmp/image1.png ..operations.. /tmp/image1.png
En este caso, un archivo MPC puede acelerar la lectura de los archivos intermedios hasta hacerla casi instantánea en el siguiente paso de procesamiento, ya que la imagen simplemente se vuelca de la memoria al disco y luego el siguiente comando la 'pagina' de vuelta. Esto evita que IM tenga que formatear y analizar un formato de archivo de imagen, aunque hace que el archivo temporal sea más grande, ya que es simplemente memoria sin comprimir. Otra alternativa que uso para evitar los archivos temporales es canalizar la imagen o imágenes de trabajo de un comando de shell (un if-then-else-fi o un bucle while) a otro. Esto se conoce como canalizaciones de imágenes y se demuestra en varios ejemplos. Un ejemplo de ello se da en transmisión de imágenes MIFF, donde se generan varias imágenes una tras otra en la misma tubería de salida, para que el siguiente comando las recoja y las fusione todas en la imagen final. Y, por último, puede que necesite cambiar su estilo de procesamiento en función de los resultados de los pasos de procesamiento anteriores. Por ejemplo, en las comparaciones de imágenes a menudo necesito descubrir cierta información para usarla en pasos de procesamiento posteriores, o para cambiar cómo debe procesarse la imagen en etapas posteriores. Comparar un diagrama o una caricatura puede requerir una técnica de comparación muy distinta de la de una imagen fotográfica de la vida real. Si el uso de varios comandos empieza a ser un problema, quizá sea hora de pasar a una interfaz de API como PerlMagick, donde se pueden mantener en memoria varias secuencias de imágenes para evitar operaciones de E/S de disco innecesarias.
Hacer IM más rápido (en general)
Hay muchas formas de hacer que IM funcione más rápido. Aquí están los aspectos más importantes que conviene tener en cuenta. A medida que se baja por la lista, la aceleración se vuelve menor, o requiere cambios más complejos en la instalación de IM.
- IM Q8 (es decir, 8 bits por valor de color, de 3 a 4 bytes por píxel) es mucho más rápido (de 3 a 4 veces más rápido) que el IM Q16 por defecto, con su mayor resolución de color. Si no necesita Q16 para sus imágenes, quizá debería reemplazar su IM por una versión Q8. No obstante, tenga en cuenta que usar solo una calidad interna de 8 bits puede afectar al procesamiento global de la imagen, ya que las imágenes intermedias pierden información. Vea HDRI para lo contrario de esto.
- Use un único comando "
magick" si es posible, de modo que realice todo el procesamiento necesario para una sola imagen. Esto ahorra en la inicialización, en tener que crear archivos temporales o tuberías entre comandos, e incluso en la codificación/decodificación a formatos de archivo de imagen para esas tuberías y la E/S de disco. Por supuesto, a veces aún necesita usar varios comandos para permitir pasos de procesamiento de imagen opcionales, como cálculos que implican el tamaño de la imagen, los colores o, incluso, pasos de procesamiento opcionales. El scripting de IMv7 ayudará en este aspecto. - Los scripts de shell son intrínsecamente lentos. Son interpretados, requieren varios pasos y un manejo de archivos adicional en disco. Esto, por supuesto, ha mejorado gracias al nuevo manejo de opciones de IM v6, que permite realizar un gran número de operaciones de procesamiento de imágenes en un único comando. Aun así, rara vez puede hacerlo todo en un único comando "
magick", por lo que a menudo tiene que usar varios comandos para lograr lo que desea. Por ello, una API como perl, ruby o un módulo magick de PHP es más rápida, ya que elimina todos los aspectos de interpretación tanto del shell como de la API de línea de comandos de IM. También reduce los pasos de inicialización por los que pasa IM al leer las definiciones de fuentes y colores. - Una API también puede mantener todas las imágenes, o incluso varias listas de imágenes, durante toda la vida del programa (siempre que tenga memoria suficiente). Esto significa que puede cambiar libremente qué imágenes se están procesando, sin necesidad de barajar y hacer malabares con las imágenes, como hace en la línea de comandos. Esto es especialmente útil cuando también necesita hacer cálculos adicionales basados en pasos de procesamiento de imágenes anteriores.
- Escribir el formato de archivo de imagen GIF es lento. IM tiene que trabajar mucho para reducir el color (cuantizar) de una imagen y ajustarlo a los colores limitados del formato de archivo. Aun así, a menudo necesita trabajo adicional para hacerlo bien, especialmente en las animaciones GIF. PNG y JPEG son más rápidos, pero a costa del tamaño en PNG y de la pérdida de calidad en JPEG. Aunque, en realidad, ¡las imágenes GIF son peores en cuanto a calidad!
- Preparar y almacenar en caché por adelantado imágenes como fondos, superposiciones, marcos o máscaras, o pregenerar tablas de búsqueda de color, mapas de distorsión, plantillas, máscaras, etc. Todas estas cosas pueden marcar grandes diferencias en su tiempo de procesamiento. Piense en qué puede hacerse de antemano. Una gran biblioteca de imágenes pregeneradas puede ser mucho más rápida que intentar crear las imágenes a medida que se necesitan. Vea también el uso de "MPC:" para imágenes intermedias y en caché. Son imágenes mapeadas en memoria sobre disco y, en esencia, tienen un tiempo de lectura nulo, pero son inútiles para cualquier otra cosa o en otras máquinas. Solo deberían crearse al inicio de un proceso importante de manejo de imágenes, y no almacenarse durante mucho tiempo, ya que la más mínima actualización de software o del sistema las invalidará y probablemente provocará fallos de segmentación.
- Evite usar FX, el operador de imagen de efectos especiales si puede usar en su lugar la composición alfa, o el más sencillo Evaluate, operaciones matemáticas simples, u otras técnicas. Si necesita usarlos, intente restringir su uso a la imagen más pequeña posible, o a un único canal de la imagen (cuando maneje escala de grises).
- Evite usar desenfoques de imagen de gran tamaño cuando unos cuantos más pequeños puedan ser más rápidos. Haga algunas pruebas de tiempo para ver cuál es más rápido. Lo mismo ocurre con otros operadores de morfología y convolución, como los operadores gaussiano y de sombra.
- Use subimágenes o regiones más pequeñas para el procesamiento complejo de áreas reducidas. Por ejemplo, encontrar y extraer los ojos de una persona (usando regiones) antes de enmascarar y recolorear puede suponer un enorme aumento de velocidad frente a procesar la versión grande y completa de una imagen. Cuanto mayor sea la diferencia, mayor será el ahorro.
- Al leer imágenes grandes, o incluso un gran número de imágenes, es mejor usar un modificador de lectura para redimensionarlas o recortarlas inmediatamente después de leer cada imagen, reduciendo así su requisito global de memoria. Para JPEG, puede usar el modificador especial de la biblioteca '
[jpeg:size](formats.html#jpg_read)' para evitar incluso la asignación de la memoria. Esto, a su vez, previene la 'hiperpaginación de disco' (que vuelve los ordenadores MUY lentos). Especialmente cuando intervienen muchas imágenes grandes, como al generar índices de directorio en montaje u otros collages de imágenes múltiples. - Para imágenes realmente enormes que tengan que procesarse desde disco, puede ser mejor procesarlas en fragmentos más pequeños.
- También para imágenes grandes... Si tiene un SO Windows de 64 bits, use la distribución de ImageMagick de 64 bits. Usa un espacio de direcciones mayor y puede alojar en memoria imágenes más grandes que Windows de 32 bits.
- IM, por defecto, usa varios hilos para las operaciones de procesamiento de imágenes individuales. Eso significa que un ordenador con dos o más 'núcleos' procesará en general las imágenes más rápido que una máquina con una sola CPU. Para imágenes grandes, las capacidades multihilo de OpenMP pueden producir una clara ventaja de velocidad, ya que usan más CPU para completar las operaciones de procesamiento de imágenes individuales. Tenga en cuenta que dentro de IM solo se paralelizan las operaciones de procesamiento de imágenes individuales. Así que el ahorro es mayor con el procesamiento de imágenes grandes, y no al procesar grandes cantidades de imágenes (vea lo siguiente).
- Para imágenes pequeñas, usar las capacidades multihilo de IM no le dará mucha ventaja. En este caso, ejecutar varios convert simultáneamente sobre distintas imágenes puede producir más rendimiento. Esto también puede ocurrir en situaciones en las que varias peticiones web de PHP podrían lanzar varios comandos "magick" de imagen. En cualquiera de estas situaciones, tener el multihilo habilitado podría ser muy perjudicial debido a la contención de la CPU, y es mejor desactivar OpenMP fijando la variable de entorno '
MAGICK_THREAD_LIMIT' a '1'. Vea la discusión del foro de IM El multihilo ralentiza 'convert'. Quizá también le interese examinarMAGICK_THROTTLEpara conseguir que ImageMagick ceda el control de las CPU con más frecuencia en puntos más adecuados. - Si realiza muchas operaciones pequeñas sobre las imágenes (como dibujar), procure no usar ningún nombre de color. Especifique los colores usando colores con almohadilla, como "
#00AA99", o números rgb, como "rgb(0,160,100)", para evitar que IM tenga que cargar las tablas de nombres de color (¡que son bastante grandes!). También puede intentar eliminar o recortar los archivos de definición de la lista de 'fuentes' del sistema (de "type.xml"). O eliminar esos archivos por completo y especificar las fuentes directamente por su nombre de archivo. En esencia, reduzca la carga de la información de configuración adicional que IM lee e inicializa cuando la necesita para un procesamiento de imagen concreto. Así que, o bien no los use, o bien reduzca el tamaño y el impacto de los archivos de configuración. - Compilar ImageMagick como una biblioteca compartida (lo predeterminado) puede reducir mucho el tiempo de carga. Las bibliotecas y los módulos de codificación solo se cargan a medida que se necesitan, así que una versión dinámica de IM no cargará nada que no tenga que usar durante el procesamiento de imágenes. Además, las bibliotecas compartidas tienden a permanecer disponibles, por lo que quizá no sea necesario recargarlas para una segunda ejecución.
- Si llama a ImageMagick como parte de un módulo de Apache, también reducirá el tiempo de arranque, ya que algunas partes se cargarán una vez y se mantendrán disponibles para usos múltiples, en lugar de tener que recargarse una y otra vez. Esto puede volverse más práctico en el futuro con un proceso 'demonio' de IM en ejecución permanente.
Compilar ImageMagick desde las fuentes
Construir RPM de ImageMagick para linux a partir de SRPM
NO necesita ser root para construir realmente los RPM, aunque sí necesita ser root para instalarlos. Yo uso esto para generar e instalar IM bajo sistemas Fedora Linux, pero también se ha informado de que funciona en sistemas CentOS 5.4 (Enterprise Redhat) Linux (vea unas notas más específicas sobre IM en CentOS). Primero obtenga la última versión del RPM de código fuente de RPM de código fuente para Linux. Primero asegúrese de que su máquina tiene todos los compiladores y herramientas que necesita.
sudo yum groupinstall "Development Tools"
sudo yum install rpmdevtool libtool-ltdl-devel
| "sudo" es un programa para ejecutar comandos como root, si está autorizado; de lo contrario, use un shell de root y elimine la parte "sudo" de lo anterior.
---|---
En sistemas más antiguos como CentOS 5.5, parece que también necesita estos paquetes
sudo yum compat-libstdc++ gcc-c++ gcc-objc++ libstdc++ libstdc++-devel
A continuación, también debería instalar los paquetes de desarrollo de las bibliotecas que IM necesita para compilar. La forma sencilla de obtener las más comunes es instalar primero la versión de desarrollo de IM, aunque luego construyamos una nueva para reemplazarla.
sudo yum install ImageMagick-devel
También debería asegurarse de que estos paquetes y sus dependencias (como las bibliotecas de desarrollo de jpeg y png) también estén instalados:
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
Algunos ejemplos de "ImageMagick examples" también pueden usar programas que proporcionan estos paquetes y bibliotecas opcionales, pero no son necesarios para el proceso de construcción.
gnuplot autotrace
En general, todos estos paquetes son opcionales, pero si no están instalados, los 'codificadores' (coders) y operadores que usan esas bibliotecas quizá no se construyan automáticamente. Por ejemplo, el módulo "liblqr" es necesario para habilitar el operador Liquid Rescale. Ahora descargue un paquete SRPM (RPM de código fuente) a partir del cual construir sus RPM binarios. O construya un SRPM a partir de una descarga TAR o SVN existente usando...
rm config.status config.log
nice ./configure
rm *.src.rpm
make srpm
Tenga en cuenta que, una vez que tenga el SRPM, puede construir los RPM reales para instalar.
nice rpmbuild --nodeps --rebuild ImageMagick*.src.rpm
Esto creará un subdirectorio "rpmbuild" en su directorio personal, en el que extraerá las fuentes del SRPM y construirá la versión RPM del paquete compilado de IM. |
En versiones antiguas de Fedora y Redhat esto se hacía en "/usr/src", que en general está restringido solo a root. No obstante, puede hacer que este directorio sea de su propiedad o escribible por usted, de modo que aún pueda hacerlo sin necesitar acceso completo de root para la construcción. |
|---|---|
| Ahora obtenga los RPM recién construidos desde el directorio de construcción. Esto solo toma los paquetes del núcleo de ImageMagick y de PerlMagick; quizá quiera tomar más que estos dos, pero eso depende de usted... |
cp -p ~/rpmbuild/RPMS/*/ImageMagick-[6p]*.i[36]86.rpm .
Limpie y elimine las áreas de construcción (incluidas las que hayan podido crearse para usted)...
rm -rf /var/tmp/rpm-tmp.* ~/rpmbuild
Ahora puede instalar los paquetes RPM que construyó. Necesitará ser root para esto (vea la nota sobre el comando "sudo" más arriba)...
sudo rpm -ihv --force --nodeps ImageMagick-*.i[36]86.rpm
El "--nodeps" suele ser necesario debido a algunas dependencias inusuales que a veces existen en los sistemas Linux. Para actualizar una instalación existente, en general hago esto (como root).
sudo rpm -Uhv --force --nodeps ImageMagick-*.i[36]86.rpm
Si quiere ir más allá, le recomiendo que consulte la guía avanzada de instalación desde fuentes en Unix del sitio web de IM.
Para eliminar IM más tarde, basta con eliminar el paquete, así (de nuevo, como root)...
sudo rpm -e --nodeps ImageMagick\*
A veces simplemente quiero limpiar y borrar por completo todo rastro de IM del sistema. Para ello, primero uso el comando anterior para eliminar el paquete real del sistema (más abajo se muestra una variante). Luego ejecuto los siguientes comandos de eliminación. NOTA: no doy ninguna garantía sobre esto, y comprobaría los comandos a fondo de antemano para asegurarme de que no eliminen algo que no deben. Si me he dejado algo, o si elimina algo que no debería, hágamelo saber para que pueda actualizarlo.
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
Advertencia: otros paquetes pueden necesitar un IM instalado, así que si lo elimina, le sugiero que actualice de inmediato los paquetes de su sistema, para que vuelva a instalar la versión original por defecto (y normalmente bastante antigua) de ImageMagick que se suministró con su sistema Linux. Esto en general implica usar un paquete de 'actualización de software con interfaz gráfica' o el comando "yum upgrade". Que lo disfrute.
ImageMagick desde las fuentes en Ubuntu
Para obtener todas las bibliotecas de desarrollo necesarias para construir ImageMagick, use lo siguiente
sudo apt-get install imagemagick libmagick++-dev
Una página web de "Shane" describe cómo instalar ImageMagick desde las fuentes en Ubuntu 8.04. No lo he probado, pero esto instaló IM directamente en "/usr/local" usando "make". No genera un paquete de instalación 'DEB', lo cual no es una solución ideal. Si alguien sabe cómo crear un paquete 'DEB' para Ubuntu, hágamelo saber. Quizá usando la introducción al empaquetado en Debian
Compilar en MacOSX
La forma más fácil de instalar ImageMagick en MacOSX es usar MacPorts. Pero lo siguiente son referencias a información sobre la compilación para MacOSX. No sé si funciona ni si va a ser útil, ya que nunca lo he usado. Pero vea instalar ImageMagick sin Fink ni MacPorts e instalar ImageMagick en Snow Leopard. Lo anterior es una paráfrasis de una discusión en el foro de usuarios de IM.
Compilar versiones HDRI de IM
Para obtener información sobre la compilación de una versión HDRI de IM, vea habilitar HDRI en ImageMagick en el sitio web principal de IM; también, para información específica de Windows y Ubuntu Linux, vea la discusión del anuncio de las transformadas de Fourier en los foros de usuarios.
Crear un ImageMagick personal
No siempre se tiene el lujo de disponer de acceso de superusuario en la máquina en la que se hace el trabajo con imágenes, y a menudo quienes sí lo tienen no quieren actualizar su instalación de ImageMagick. Quizá por cuestiones de gestión de paquetes, o por problemas de compatibilidad. Si tiene acceso a la línea de comandos (por ejemplo, vía SSH), no todo está perdido. Puede instalar y usar una versión personal de ImageMagick. La mala noticia es que aún necesitará que los administradores de sistemas instalen los compiladores y los paquetes de desarrollo (vea más arriba), pero a menudo ya estarán presentes, así que no siempre es un problema. Primero decida en qué subdirectorio quiere instalar su versión de IM. Un directorio dedicado es la mejor opción, ya que significa que solo tiene que eliminar ese directorio completo para eliminar su instalación. En mi caso, lo instalaré en el subdirectorio "apps/im" de mi directorio personal.
export MAGICK_HOME=$HOME/apps/im
Ahora, para instalar una versión personal, descargue, descomprima y entre en el directorio de las fuentes de ImageMagick. Luego configúrelo como una versión 'uninstalled' (no instalada).
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
Lo importante en la definición anterior es el "--disable-installed" junto con el "--prefix". Las demás partes desactivan la compilación de los aspectos más opcionales de su versión personal de ImageMagick. Modifíquelas a su gusto. Ahora, para usar su propia versión instalada, en lugar de la versión normal del sistema, solo necesita fijar las siguientes variables de entorno.
export MAGICK_HOME=$HOME/apps/im
export PATH="$MAGICK_HOME/bin:$PATH"
export LD_LIBRARY_PATH="$MAGICK_HOME/lib:$LD_LIBRARY_PATH"
Ahora, si escribo
magick -version
puedo ver la versión más actualizada de IM que acaba de instalar, por defecto. Tenga en cuenta que la variable "$MAGICK_HOME" debe estar fijada para un ImageMagick creado con la opción "--disable-installed". Las otras dos variables de entorno garantizan que usemos la versión personal en lugar de cualquier versión del sistema que pueda estar también instalada.
ADVERTENCIA: no mezcle las variables anteriores. O las define todas, o no las tiene definidas de esta forma. El ejecutable de IM que use TAMBIÉN debe usar las mismas bibliotecas, codificadores y archivos de configuración con los que se construyó ese ejecutable. Mezclar la versión del sistema y su versión personal probablemente provoque fallos de segmentación y de memoria.
Puede mover la ubicación de su IM personal sin recompilar, pero tendrá que modificar no solo las variables de entorno anteriores, sino también cambiar (o eliminar) las rutas codificadas de forma fija al programa "magick display" que usa el delegado "[show:](files.html#show)" en el archivo "delegate.xml" de su versión personal instalada. Vea delegados para más información sobre este aspecto de IM.
Para que me resulte fácil cambiar entre la versión instalada del sistema y varias versiones personales de IM, normalmente no fijo en absoluto las variables anteriores. En su lugar, llamo a un script que fija esas variables antes de invocar la versión concreta de IM. Por ejemplo, tengo una versión personal de IM compilada con HDRI, que solo uso para ejemplos concretos de los ejemplos de ImageMagick. Normalmente no quiero usar esta versión, ya que prefiero la versión no HDRI instalada en el sistema para la mayoría del trabajo con imágenes. Por eso instalé una versión 'HDRI' de IM en mi área personal "$HOME/apps/im_hdri" y creé un script al que llamé "hdri" que contiene...
#!/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 "$@"
Ahora, si escribo...
hdri magick -version
Veo que ejecuté mi versión de calidad HDRI de ImageMagick, pero solo cuando la necesito. Si no antepongo "hdri" a lo anterior, entonces ejecutaría la versión normal del sistema de IM. ADVERTENCIA: si el script no encuentra su versión personal de IM, revertirá silenciosamente al uso de la versión del sistema. La comprobación de 'version' anterior es una prueba importante para asegurarme de que realmente estoy usando mi versión personal, y no la del sistema. También puede comprobar exactamente qué comando magick intenta ejecutar el script usando 'which'.
hdri which convert
Es decir, el script es lo bastante flexible como para que en realidad no necesite ejecutar "magick", sino que puede ejecutar cualquier comando, como los scripts de shell de ImageMagick, de modo que ese script use el magick HDRI en lugar del convert normal del sistema.
hdri some_im_script image.png image_result_hdri.png
![[IM Text]](../static/img/api/hdri_version.txt.gif)