⚠️ Este es un sitio de traducción no oficial, sin relación con ImageMagick Studio LLC. Para información autorizada, consulte la página original (https://usage.imagemagick.org/windows/index.html).

Ejemplos de ImageMagick -- Uso en Windows

 [ Introducción](#intro)

Introducción

¿De qué sirve un script de IM en mi PC con Windows?

Los ejemplos siguientes asumen básicamente que ejecuta IM en un equipo de escritorio con Windows, probablemente conectado a una red. Bien, existen muchos programas de manipulación de imágenes ya disponibles, como Adobe Photoshop, Paint Shop Pro de Corel, IrfanView (http://www.irfanview.com/) e incluso GIMP (http://www.gimp.org/). Entonces, ¿por qué molestarse en procesar imágenes con los programas de línea de comandos y los scripts de IM? La verdadera ventaja de usar ImageMagick en lugar de una interfaz manejada con el ratón es que puede automatizar por completo las manipulaciones rutinarias, ya sea para archivos individuales o para grandes cantidades de archivos. Tareas como:

Conversión masiva de formatos
La ofrecen bastantes programas de Windows, como IrfanView. Sin embargo, la versatilidad de IM en lo que respecta a los formatos de imagen no tiene rival. Por ejemplo, puede magickear todas las páginas de un PDF en una serie de JPEG (si GhostScript está instalado en su equipo).
Reducir y preprocesar fotografías digitales
Al insertar fotografías digitales en un documento de un procesador de textos, normalmente conviene reducir su resolución para que el documento se imprima más rápido. Lo mismo vale si magickea el documento a un PDF mediante un controlador de impresora PDF como FreePDF. El preprocesamiento también podría incluir rutinas de corrección de color y de lente.

Colocar su logotipo en un gran número de fotografías digitales

Aplicar una serie de operaciones a un gran número de fotografías digitales
Tras haber elaborado una serie de pasos de trabajo con un programa manejado con el ratón, quizá desee automatizar esos pasos para futuros procesamientos masivos. Sin embargo, los lenguajes de script (como el Action Script de Adobe) no son muy habituales en los programas de procesamiento de imágenes de Windows.

Combinar varias imágenes en una imagen de catálogo

Aunque algunas de estas tareas (sobre todo la reducción masiva) también las ofrecen otros programas gratuitos (en especial el procesamiento por lotes de IrfanView), nunca se tiene libertad de elección sobre qué pasos de procesamiento aplicar: hay que conformarse con los que ofrece el programa. Por ejemplo, el procesamiento por lotes de IrfanView permite colocar una cadena de texto en un gran número de fotografías, pero no un logotipo. También permite cambiar el valor de gamma, pero no se puede recortar el histograma de la fotografía en sus extremos (como hace "[-contrast-stretch](https://imagemagick.org/command-line-options/#contrast-stretch)" en IM; consulte Normalize y Contrast Stretch). Los scripts de IM son especialmente adecuados para el uso productivo en la red de una empresa, porque cualquiera puede aplicar scripts ya hechos: los usuarios finales no tienen por qué saber qué ocurre en segundo plano. Así, los pasos estándar del flujo de trabajo sobre las imágenes pueden automatizarse por completo (y estandarizarse de verdad). Varios de los scripts presentados a continuación se derivaron para el uso productivo en nuestra pequeña empresa (que trabaja en el campo de la reconstrucción de accidentes). No soy un programador de scripts de Windows excepcional ni el más familiarizado con las herramientas de línea de comandos de IM. Probablemente existan enfoques más elegantes para algunos de los problemas tratados a continuación. Los puntos que quiero destacar son:

  • mostrar algunas técnicas básicas de programación de scripts de Windows con las herramientas de línea de comandos de IM.
  • demostrar que el uso de scripts basados en IM no es ni arte por el arte ni un pasatiempo académico.
  • mostrar que las herramientas de línea de comandos de IM pueden hacer trabajo real en una red local (y no solo en servidores web).

Como en el resto de las páginas de ejemplos de IM, solo usaremos las herramientas de línea de comandos de IM y dejaremos de lado sus diversas interfaces de programación. Los scripts están pensados para ejecutarse dentro de una red local con letras de unidad asignadas a cada unidad de red. La mayoría de los scripts están pensados para ejecutarse en los equipos cliente de esa red; pocos se dirigen al servidor (de archivos) de la red.

Entornos posibles para las herramientas de línea de comandos de IM

En Windows, los comandos de IM sencillos suelen ejecutarse en el shell de comandos de Windows (un «shell de DOS» que se lanza iniciando cmd.exe). Para operaciones complejas, realizadas en una línea de comandos larga o en una serie de líneas de comandos, conviene escribir un script. Para una serie de comandos sencillos, lo más probable es que sea un archivo por lotes de DOS, ejecutado en el shell de comandos de Windows. Sin embargo, este enfoque tiene sus carencias, ya que el conjunto de comandos de los archivos por lotes es bastante limitado en comparación con el de los shells de comandos UNIX habituales. Al ejecutar IM en Windows tiene básicamente las siguientes alternativas:

El shell de comandos de Windows («ventana de DOS»)
Se ejecuta mediante cmd.exe (modo de 32 bits) en Windows NT 4.0, Windows XP y versiones posteriores, y está presente en cualquier equipo con Windows. Consulte Usar el shell de DOS y archivos por lotes, así como la nota especial sobre El problema de Convert.
Cygwin
Un shell de comandos al estilo de bash (http://www.cygwin.com/). Al usar este shell, los ejemplos de IM presentados en el resto de esta sección de uso pueden ejecutarse exactamente como se indican, ya que se dispone de un shell de línea de comandos al estilo de UNIX. Consulte Usar Cygwin más abajo.
El Windows Script Host
El Windows Script Host se basa en la tecnología .COM. Está presente en cualquier equipo con Windows contemporáneo y los scripts WSH son mucho más potentes que los simples archivos por lotes de DOS. El Windows Script Host ofrece varias interfaces de programación, siendo VBScript (Visual Basic Script) y JScript (Java Script) las más habituales. Las herramientas de línea de comandos de IM pueden invocarse usando los comandos de shell de DOS Run o Exec del objeto Shell. Consulte Visual Basic Script (VBS) más abajo.
La Windows PowerShell
El sucesor mucho más potente del antiguo shell de DOS, basado en la tecnología .NET 2.0. PowerShell se incluyó con Windows 7 y se ejecuta mediante powershell.exe. Para Windows XP y Vista puede descargarse desde el sitio web de Microsoft.

Ejecutar scripts de forma eficaz

Supongamos que tiene un script de Windows perfecto (un archivo por lotes de DOS, un VBScript o lo que sea) que toma el nombre o los nombres de los archivos de entrada como parámetros de la línea de comandos, realiza alguna manipulación y produce el resultado como una sola imagen o una serie de imágenes. Seguramente no le agradará tener que iniciar un shell de comandos de DOS (o una alternativa) cada vez y proporcionar al script los nombres de archivo tecleándolos. Para evitar esas formas engorrosas de proceder, puede usar básicamente arrastrar y soltar o SendTo: al usar arrastrar y soltar, coloca el archivo por lotes de DOS o el VBScript (o lo que sea) en una ubicación de fácil acceso, como el escritorio o el directorio que contiene los archivos de imagen que se van a procesar. Luego selecciona los archivos que va a procesar en el Explorador de Windows y simplemente los suelta sobre el archivo del script. Los nombres de archivo se entregan al script como parámetros de la línea de comandos y pueden referenciarse dentro del script. Como alternativa, puede colocar su script (o un enlace a él) en la carpeta SendTo. Los programas de esta carpeta aparecen en el menú contextual del Explorador de Windows al hacer clic con el botón derecho en el panel de archivos del Explorador. También en este caso, los nombres de los archivos seleccionados se entregan al script como parámetros de la línea de comandos. La carpeta SendTo se llama SendTo. Su ubicación parece moverse con cada nueva versión de Windows. Una forma infalible de encontrarla es escribir shell:sendto en el cuadro Ejecutar. En Windows XP o posterior, una sola línea de comandos puede tener 8192 (= 2¹³) caracteres de largo. Por tanto, si invoca una herramienta de IM directamente desde la línea de comandos, ya sea de forma directa o mediante un archivo por lotes que nombra directamente todos los archivos necesarios, difícilmente topará con esta limitación. Sin embargo, arrastrar y soltar usa la rutina ExecShell, que está limitada a cadenas de «solo» 2048 (= 2¹¹) caracteres. Como todos los archivos se pasan con nombres de archivo totalmente cualificados (es decir, unidad + ruta + nombre), esto puede convertirse en un problema para los archivos por lotes y los VBScripts ejecutados mediante WSH cuando los nombres de ruta se vuelven demasiado largos. El script no puede gestionar adecuadamente este error una vez que ocurre, porque el error se produce antes de que el script se ejecute realmente. La solución en Windows XP suele ser colocar los archivos en una ubicación donde los nombres de ruta sean más cortos.

El problema de Convert

La rutina de instalación de IM en Windows añade el directorio de programas de IM a la ruta de búsqueda, de modo que puede llamar a las herramientas de línea de comandos de IM directamente desde el símbolo del sistema, sin proporcionar un nombre de ruta. De forma predeterminada usará magick como herramienta de línea de comandos. Sin embargo, si selecciona los nombres de programa heredados, los nombres de las herramientas de línea de comandos de IM resultan bastante inespecíficos (p. ej. convert, identify, compare ...), lo que provoca conflictos de nombres con otros programas. En particular, convert es una herramienta del sistema de Windows, ubicada en el directorio del sistema de Windows (c:\Windows\system32\convert.exe), que convierte el sistema de archivos FAT32 al ahora habitual NTFS. Pero también hay otros programas llamados "convert.exe"; por ejemplo, la utilidad de conversión de informes de Delphi. La herramienta magick FAT → NTFS se incluyó por primera vez con Windows XP y generó un conflicto de nombres con la herramienta de línea de comandos "convert" de IM: como el directorio de programas de IM se añadía al final de la ruta de búsqueda de DOS (es decir, de la variable de entorno PATH), la herramienta del sistema se encontraba primero y las simples llamadas a "magick" de los scripts antiguos no se resolvían correctamente. Sin embargo, las versiones actuales del programa de instalación de IM para Windows colocan el directorio de programas de IM al principio de la ruta de búsqueda, asegurando así que, en caso de nombres en conflicto, la herramienta de línea de comandos de IM suele encontrarse primero. No obstante, otras utilidades con el mismo nombre (p. ej. el conversor de informes de Delphi) toparon con el mismo problema y optaron por la misma solución, es decir, colocar la ruta de su programa al principio de la variable path, lo que significa que esta solución no es infalible: si Delphi se instala después de IM, una simple llamada a Convert invocará la herramienta de conversión de informes, no el Convert de IM. La introducción de la herramienta Convert con Windows XP hizo que muchos scripts heredados se bloquearan. La solución habitual era renombrar la herramienta Convert de IM a otra cosa, como "IMconvert" (tenga en cuenta que no puede renombrar el comando del sistema, ya que el siguiente service pack de Windows probablemente se limitaría a restaurarlo, ignorando la versión renombrada). Esta solución, aunque ahora innecesaria, sigue encontrándose por todo Internet. La mejor solución para evitar posibles conflictos de nombres futuros es llamar a las herramientas de línea de comandos de IM por su nombre de ruta completo en cualquier script. Es decir, almacenar su ubicación en una variable o una constante. Por tanto, todo archivo por lotes debería empezar con líneas como

  SETLOCAL EnableDelayedExpansion
  SET IMCONV="%PROGRAMFILES%\ImageMagick\Convert"
  ...
  %IMCONV% -size 128x128 xc:white test.gif

El código supone que IM se instaló en una carpeta llamada "ImageMagick" bajo la carpeta de programas, lo cual no es el nombre estándar de su carpeta de instalación. (Consulte Instalar ImageMagick en Windows para más detalles.) %PROGRAMFILES% es una variable de entorno que se expande al nombre del directorio de programas, es decir, "Program Files" en la versión inglesa de Windows y "Programme" en la versión alemana de Windows. SETLOCAL limita la definición de nuevas variables de entorno (como IMCONV) al ámbito del archivo por lotes. EnableDelayedExpansion aquí no hace realmente falta, pero es buena costumbre usar esta opción cada vez que se usa SETLOCAL; consulte Pautas para programar archivos por lotes para más detalles. Hay formas más sofisticadas e infalibles de averiguar la carpeta de instalación de IM, que se tratarán en Edición, depuración y prueba de errores en tiempo de ejecución. El código VBScript equivalente sería algo así como

  Set wsh = WScript.CreateObject("WScript.Shell")
  IMconv = wsh.ExpandEnvironmentStrings("%PROGRAMFILES%") & "\ImageMagick\Convert"

Por razones de sencillez, no usaremos este código cada vez en lo sucesivo, pero debería tenerlo presente como buena costumbre. Para un buen resumen alternativo y soluciones a este problema, consulte Ron Savage: MS Windows and convert.exe.

Codificación de caracteres

ImageMagick codifica las cadenas en Unicode, más concretamente en UTF-8. En cambio, DOS usa páginas de códigos para codificar los caracteres (en su mayoría en 8 bits). Esto genera problemas al escribir cadenas en las imágenes, como cuando se trabaja con 'label' o '-title': al usar caracteres no ASCII, las cosas saldrán mal en el enfoque sencillo. Por ejemplo, al intentar crear una etiqueta con diéresis alemanas como 'ä', 'ö', 'ü', en Linux puede usar simplemente lo siguiente...

  magick label:äöü test.png

Pero esto no crearía la cadena deseada en Windows. No obstante, puede leer una cadena codificada en UTF-8 desde un archivo de texto:

  magick label:umlauts.txt test.png

e incluso puede crear este archivo de texto usando "echo", si cambia previamente la página de códigos a UTF-8:

  CHCP 65001
  ECHO äöü > umlauts.txt
  magick label:umlauts.txt test.png

Pero si quiere procesar la salida de un comando de DOS, por ejemplo al intentar titular una impresión índice de los JPEG contenidos en un cierto directorio con el nombre de ese directorio, se mete en problemas. Cambiar a la página de códigos 65001 no funcionará con la mayoría de los comandos de DOS, especialmente al recorrer árboles de directorios. Y cambiar la página de códigos de un lado a otro entre, digamos, 1252 (latín de Europa occidental) y 65001 normalmente tampoco funcionará, o al menos se volverá bastante delicado. El enfoque más seguro es magickear las cadenas cuando se necesitan, usando un programa de línea de comandos externo, como "Iconv.exe", una herramienta de UNIX que también está disponible para Windows. Descargue el archivo de instalación desde SourgeForge e instale los archivos en el directorio estándar C:\Program Files\GnuWin32. Luego, en su script, vuelque la salida del comando de DOS a un archivo de texto y magickee ese archivo a UTF-8 de la siguiente manera:

  CHCP 1252
  DIR /B äöü.jpg > temp.txt
  "C:\Program Files\Gnuwin32\bin\iconv.exe" -f ISO-8859-1 -t UTF-8 temp.txt > title.txt
  magick label:@title.txt äöü.jpg -append äöü_labelled.jpg

Los parámetros le indican a Iconv que transcodifique de ISO-8859-1 (página de códigos 1252) a UTF-8. Iconv escribe su salida en stdout, de modo que tiene que redirigirla a un archivo para usarla con 'label'. Tenga en cuenta que volcar la salida a un archivo de texto es recomendable de todos modos, porque le indica a ImageMagick que interprete el contenido del archivo de forma literal, sin tomar la barra invertida ("\") de Windows como un carácter de escape. Por supuesto, el código del ejemplo anterior no tiene mucho sentido tal como se presenta aquí, con fines de demostración. En un archivo por lotes de DOS práctico, el nombre de archivo probablemente se generará en un bucle FOR. Más abajo se da un ejemplo práctico (consulte Procesamiento por lotes de un árbol de (sub)directorios). Para más información sobre el manejo de UTF, consulte los demás ejemplos de IM y la información en Manejo de texto en formato Unicode o UTF8.

Instalar ImageMagick en Windows

ImageMagick está en desarrollo constante; se publican nuevas versiones aproximadamente una vez al mes. Se recomienda encarecidamente usar una versión actualizada de IM, sobre todo cuando IM no parece realizar una tarea exactamente como espera. En la mayoría de los casos, instalar la versión actual resolverá el problema. El programa de instalación de la versión binaria actual puede encontrarse en https://imagemagick.org/script/binary-releases.php#windows. También puede descargar una versión de ImageMagick con capacidad HDRI (calidad de punto flotante) y FFT (transformada rápida de Fourier) desde el Astronomy and Astrophotography Blog. De forma predeterminada, IM se instala en un directorio de programas llamado C:\Program Files\ImageMagick x.x.x-x, donde "x.x.x-x" representa su número de versión. De forma predeterminada, el programa de instalación propone ampliar la variable de entorno PATH cuando se instala IM por primera vez (es decir, "Add application path to your system path" está marcado). Si olvida desinstalar las versiones antiguas, enseguida tendrá una bonita colección de varias versiones de ImageMagick con sus correspondientes ampliaciones de PATH. Por eso, en nuestra oficina adoptamos la costumbre de instalar IM en C:\Program Files\ImageMagick, instalando las versiones más nuevas justo encima de las antiguas y dejando intacta la variable de entorno PATH. No hemos podido encontrar nada malo en esta forma de proceder en años de uso. Si realmente quiere conocer el número de versión de IM, siempre puede llamar a "convert -version", y un script a prueba de fallos puede evaluar el número de versión en caso de que dependa de un cierto número de versión mínimo. Consulte la sección "Edición, depuración y prueba de errores en tiempo de ejecución" para más información sobre esto. ImageMagick escribe unas cuantas claves del Registro en HKEY Local Machine\Software\ImageMagick. Si trabaja con varias versiones instaladas de IM, la clave más importante es HKEY Local Machine\Software\ImageMagick\Current, donde también encontrará la ruta a los binarios de IM, llamada BinPath. Puede consultar esta entrada del registro al principio de cualquier script y determinar así la ruta del programa sin tener que depender de la variable de entorno PATH. Consulte la sección "Edición, depuración y prueba de errores en tiempo de ejecución" para más información sobre esto. El programa de instalación de IM ofrece la opción de instalar un objeto COM+ para ImageMagick mediante la opción Install ImageMagickObject OLE Control for VBscript, Visual Basic, and WSH. La opción no está marcada de forma predeterminada y la instalación del objeto COM+ no es un requisito previo para usar IM en un VBScript, como se demostrará a continuación. De todos modos, no debería dar por sentado que el objeto COM+ esté instalado en una máquina de destino del script que no sea la suya propia. Al trabajar con archivos PostScript, ImageMagick depende de otro programa, "Ghostscript", para la lectura y conversión de archivos PostScript y PDF a un formato de imagen que pueda usar. Es decir, para leer esos documentos, Ghostscript debe estar instalado en su equipo. Su versión más reciente puede descargarse en SourceForge. El orden en que instale GhostScript e ImageMagick no importa. No tiene que instalar GhostScript antes que ImageMagick, e ImageMagick funcionará perfectamente sin él instalado. Solo es necesario si quiere trabajar con archivos PostScript o PDF. IM determinará la ubicación de Ghostscript en tiempo de ejecución, consultándola en el Registro.

Particularidades y trampas

De forma bastante extraordinaria para los programas de Windows, IM permite escribir imágenes en stdout y leerlas desde stdin y, así, usar tuberías para encadenar tareas de procesamiento de imágenes. La operación

  magick -size 128x128 xc:white gif:- | magick - test.gif

es equivalente a

  magick -size 128x128 xc:white test.gif

En el primer comando de la tubería, el operador menos le indica a IM que escriba la imagen en stdout, mientras que el operador menos del segundo comando de la tubería le indica a IM que lea la imagen desde stdin. Esta forma de proceder permite evitar el uso explícito de archivos temporales. Es especialmente útil si algún comando no ofrece ciertas operaciones, por ejemplo el recorte:

  montage -tile 2x2 -geometry 400x300+60+60 1.png 2.png 3.png 4.png miff:- | magick - -trim montage.png

Como consecuencia de esta característica, la salida de texto suele escribirse en stderr en lugar de en stdout. Por ejemplo: si quiere redirigir la salida de texto de Compare a un archivo de texto, tendría que escribir

  magick compare -metric PSNR 1.png 2.png dif_1_2.png 2>result.txt

Así que tiene que redirigir stderr ("2>") al archivo de texto, no stdout ("1>" o simplemente ">").

Obtener ayuda

Las opciones de la línea de comandos están ampliamente documentadas en el sitio web de IM, pero es la sección Usage de su sitio web la que realmente demuestra cómo hacerlas funcionar. Esta sección del sitio web está bastante bien estructurada, lo que permite un enfoque orientado al problema de la tarea que desea realizar. Sin embargo, el enfoque rápido y sucio es una búsqueda en Google sobre esa sección del sitio web respecto a la opción de línea de comandos que tiene en mente. Si quiere realizar un montaje de varias imágenes y quiere informarse sobre la opción -tile, podría hacer una búsqueda en Google con cualquiera de las dos opciones siguientes

y enseguida descubrirá lo esencial. Solo tenga en cuenta que esto revelará código destinado a aplicarse en un entorno UNIX / LINUX, que hay que ajustar ligeramente para aplicarlo en Windows. Otra fuente de información muy útil es el tablón de discusión de IM, también conocido como Discourse Server User Forum, que debería incorporar a los marcadores / favoritos de su navegador. Hacerse miembro le permitirá plantear preguntas, que en su mayoría son respondidas rápidamente por usuarios expertos.

Programas auxiliares y alternativas

Sí, es cierto. Hay ciertas tareas que otros programas pueden hacer mejor que ImageMagick. Normalmente las que están diseñadas con formatos específicos en mente, en lugar de la manipulación general de imágenes que ofrece ImageMagick.

  • IrfanView es probablemente el visor de imágenes más habitual en Windows, que además permite cierta manipulación básica de imágenes.
  • Un programa con interfaz gráfica como Adobe Photoshop o Gimp es más adecuado para la edición directa y la prueba de pasos complejos de manipulación de imágenes.
  • Para manipulaciones de la cabecera EXIF de las fotografías digitales, Jhead y ExifTool son más versátiles que ImageMagick.
  • La extracción de flujos JPEG de los PDF debería hacerse con xPDF.
  • El procesamiento de vídeo es mejor hacerlo con VirtualDub, idealmente en combinación con AviSynth y su editor AvsP.

Esto no quiere decir que deba ignorarse ImageMagick para ese tipo de trabajo con imágenes. Pero puede hacer mucho más combinándolos.


Usar Cygwin

Como se ha dicho arriba, ImageMagick se diseñó pensando en UNIX y Linux, así que el enfoque más sencillo es probablemente instalar un shell Bash en su sistema Windows y ejecutar la variedad de scripts de IM que ya se han escrito para ese sistema, por ejemplo los scripts de Fred Weinhaus. Cygwin, en palabras de sus desarrolladores, proporciona «un entorno al estilo de Linux para Windows». Consta de todas las herramientas que normalmente están disponibles en el shell de Linux. He probado algunos de los scripts bash de Fred Weinhaus en el shell Bash de Cygwin y los he encontrado plenamente funcionales. En la parte inferior de la página raíz del sitio web de Cygwin encontrará un enlace etiquetado Install or update now!, que descargará un instalador inicial llamado Setup.exe. Cuando inicie este programa, le ofrecerá una lista de réplicas (mirrors). Tras elegir una de ellas, la rutina le presentará una vista en árbol de las herramientas que va a instalar. La selección estándar me parece razonable, así que puede continuar sin más. La rutina de instalación descargará entonces los paquetes necesarios e instalará Cygwin en su equipo. Al instalar, asegúrese de incluir el paquete "bc", que no está habilitado de forma predeterminada. Puede instalarse como opción desde el panel de instalación de Cygwin. Es una utilidad de matemáticas de punto flotante y puede ser vital en los scripts de IM, como los que usa Fred Weinhaus.
Cuando inicie Cygwin, su shell Bash se parece bastante a una ventana de DOS, es decir, una ventana de texto con fondo negro. Igual que en la ventana de DOS, la fuente puede elegirse haciendo clic en el menú de sistema de la ventana, situado a la izquierda de la barra de título. Los comandos básicos son:
  • El directorio actual se cambia con el comando CD, más o menos como en DOS. Sin embargo, la barra invertida ("\") tiene que sustituirse por la barra inclinada ("/"). Las unidades también se cambian con CD, igual que en DOS. Así, CD w: cambiará a la unidad w:. A diferencia de un entorno Unix normal, los nombres de ruta no distinguen entre mayúsculas y minúsculas. Pueden usarse caracteres especiales, como las diéresis. Para algunos comandos, los nombres de unidad tienen que pasarse con una sintaxis especial, evitando los dos puntos. Por ejemplo, /cygdrive/w/test.
  • Cygwin lee la variable de entorno PATH de Windows y establece su propio PATH en consecuencia. Puede comprobarlo escribiendo echo $PATH en el shell Bash. Nota: a diferencia de en Windows, los nombres de las variables de entorno distinguen entre mayúsculas y minúsculas, así que tiene que usar mayúsculas al referirse a PATH.
  • A diferencia de en Windows, ¡el directorio actual no está en la ruta de búsqueda de forma predeterminada! Si, por ejemplo, el script de shell "autolevel1" reside en W:\scripts, no basta con cambiar a ese directorio al llamar al script de shell. Debe anteponer al menos una ubicación de directorio mínima al inicio del script, así: ./autolevel1 (para el script ubicado en el directorio actual). O la ruta completa del script, así: /cygdrive/w/scripts.
  • Como alternativa, puede añadir ese directorio de scripts explícitamente a la ruta de búsqueda usando PATH=$PATH:/cygdrive/w/scripts. Como los dos puntos se usan como separador de rutas, tiene que usar esta nomenclatura especial, como /cygdrive/w/scripts en lugar de w:/scripts.

Esta descripción del shell de Cygwin se ampliará en futuras versiones de esta página. Por ahora, la información anterior debería bastar para empezar.


Usar el shell de DOS y archivos por lotes

Convertir scripts: del shell de UNIX al DOS de Windows

Al invocar comandos de IM directamente desde el shell de comandos de DOS (usando cmd.exe) tiene que modificar los scripts presentados en el sitio de ejemplos de IM (si no proceden de esta sección), ya que la mayoría de los ejemplos proporcionados (en otras secciones) están pensados en general para ejecutarse en un shell de comandos de UNIX o LINUX. Para ejecutarlos desde un shell de comandos de DOS, tiene que realizar las siguientes modificaciones:

  • Lo más frecuente es que haya que usar comillas dobles '"' en lugar de comillas simples ''' para que los argumentos del comando sigan siendo correctos. Esté atento a las comillas dentro de comillas, como en el operador [-draw](https://imagemagick.org/command-line-options/#draw). Puede usar comillas simples dentro de un argumento entrecomillado con comillas dobles de DOS, ya que estas se pasan a IM para su tratamiento y no las procesa el script.
  • Las barras invertidas '\' que aparecen al final de las líneas de ejemplo mostradas representan una «continuación de línea» que añade la línea siguiente a la misma secuencia de la línea de comandos. Sustitúyalas por el carácter '^' para denotar la continuación de línea en los archivos por lotes de DOS. En DOS, la línea siguiente también tendrá que empezar con un espacio, aunque eso es una práctica bastante estándar, así que no debería suponer mucho problema. También puede unir todas las líneas en una sola y eliminar esas barras invertidas, aunque eso dificulta mucho más la edición y la lectura posterior del comando. Por eso esta práctica no es recomendable.
  • Todos los caracteres de shell reservados que no estén entre comillas dobles deben escaparse con un '^' (acento circunflejo) si se usan en sentido literal (es decir, sin cumplir su propósito habitual). Estos caracteres de shell reservados son: '&', '|', '(', ')', '<', '>', '^'. Esto significa en particular que:
    • El carácter especial '>' (usado para la geometría de redimensionar) tiene que escaparse usando '^'. Por ejemplo "-resize 100x100^>".
    • De forma similar, la bandera de «redimensionar para encajar dentro» '^' tiene que duplicarse hasta convertirse en '^^'.
  • Las barras invertidas de escape del shell de UNIX '\' no son necesarias para escapar los paréntesis '()' ni los signos de exclamación '!'.
  • En los demás casos, las barras invertidas de escape del shell de UNIX '\' tendrán que sustituirse por un carácter circunflejo '^', cuando ese escape afecte a caracteres como '<' y '>'.
    Por ejemplo: "-resize 100x100\>" se convertirá en "-resize 100x100^>".
  • En los archivos por lotes de DOS, el carácter de porcentaje '%' también tiene un significado especial, ya que hace referencia a los parámetros de la línea de comandos. Por ejemplo %1, %2, ... (en el shell de UNIX se usa un signo '$' con el mismo significado general). En un archivo por lotes de DOS, los signos de porcentaje individuales (tal como aparecen en el "comando FOR") tendrán que duplicarse a '%%'. El propio ImageMagick en general solo comprueba si hay un signo de porcentaje presente, y no le importa que se haya dado más de uno. Así que, salvo que forme parte de una etiqueta de texto o de una cadena de comentario, duplicar todos los signos de porcentaje en general no hace daño.
  • Tenga presente que los nombres de archivo de Windows pueden incluir caracteres de espacio. Los espacios también pueden usarse en UNIX, pero no es tan habitual. Esos nombres de archivo que puedan contener espacios tienen que incluirse entre comillas dobles "file name.jpg" o "file name".jpg. Un nombre de archivo pasado a un script o archivo por lotes mediante arrastrar y soltar o SendTo como parámetro de la línea de comandos requiere especial atención, ya que se pasa al script sin comillas si no contiene caracteres de espacio, y con comillas cuando sí los contiene.
  • Los comentarios en los scripts de shell de UNIX empiezan con un '#' sin comillas en cualquier punto de una línea y continúan hasta el final de la línea. Los ajustes de color (como "#FF0000" para el color rojo) a menudo se entrecomillan para quitarles este significado especial. Este entrecomillado no es necesario en DOS, pero usar comillas dobles '"' a su alrededor no importa y debería mantenerse. En DOS, los comentarios solo pueden aparecer al principio de una línea usando un prefijo 'REM', '@REM' o '::'. Aunque también continúan hasta el final de la línea. Usted elige qué método de comentario usar. Sin embargo, en cualquier archivo por lotes siempre se recomiendan los buenos comentarios, para que sepa qué intenta hacer el comando en cada paso cuando vuelva al script meses o años después. También facilita mucho que otros se hagan una idea. Todos los scripts deberían empezar con un comentario más extenso que explique qué hace el script y cómo debería usarse. Esto es sencillamente una buena práctica de programación.
  • Al ejecutar un archivo por lotes de DOS, los comandos individuales se muestran (echo) de forma predeterminada, es decir, los comandos se muestran en la ventana de DOS de salida. En UNIX, en cambio, tendría que añadir un comando u opción especial para imprimir de este modo los comandos que se ejecutan. Puede desactivar esta salida «mostrada» empezando su script con "@ECHO OFF". El comentario inicial especial "#!/path/to/shell" de los scripts de shell de UNIX no es necesario en los archivos por lotes de DOS. Así que esta línea puede sustituirse por el comando "@ECHO OFF" en los archivos por lotes de DOS.

Por ejemplo, siguiendo las reglas anteriores, este script de shell de UNIX...

  #!/bin/sh
  # Create a negated rose image and overlay a comment
  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

se convertirá en algo así como un archivo por lotes de DOS de Windows...

  @ECHO OFF
  :: Create a negated rose image and overlay a comment
  magick -background none -fill red -gravity center  ^
          -font "C:\path\to the\font\candice.ttf"  ^
          -size 140x92   caption:"A Rose by any Name"  ^
          ( rose: -negate -resize 200%% ) +swap -composite  ^
          C:\where\to\save\output.gif

He escrito un conversor básico de scripts de shell de Linux a archivos por lotes de DOS usando SED (Streaming EDitor). SED es un programa habitual de manipulación de archivos de texto de UNIX / Linux que también está disponible para Windows en http://sed.sourceforge.net/. Igual que IM es un manipulador de imágenes dirigido por comandos, SED es un editor dirigido por comandos. El script de SED cim.txt que realiza las manipulaciones necesarias tiene este aspecto (una vez eliminados los comentarios):

  s/'/\"/g
  s/%/%%/g
  s/\\\([()!]\)/\1/g
  s/\([&|<>]\)/^\1/g
  s/^[ ]*#/::/
  s/\(^.*\)\( #.*$\)/\2\n\1/
  s/\(.:.*\.[a-z,A-Z]*\)[ ]/\"\1\" /g
  s/\\[ ]*$/^/
  s/^[ ][ ]//

Puede descargar la versión del archivo con todos los comentarios sed_script.zip. Si coloca el script de SED cim.txt en la misma carpeta que el script de shell de Linux que se va a convertir, invocará la conversión con:

  %programfiles%\GnuWin32\bin\SED -f  cim.txt linux.scr > windows.bat

También puede invocar la conversión mediante SendTo o arrastrar y soltar usando el siguiente archivo por lotes:

SET SP=%programfiles%\GnuWin32\bin
%SP%\SED -f %SP%\cim.txt "%~1"> "%~dpn1.bat"

Este archivo por lotes supone que ha colocado el script de SED cim.txt dentro de la carpeta de programas de SED. Toma el nombre de archivo del script de shell de Linux como único parámetro de la línea de comandos y genera un archivo por lotes con el mismo nombre, pero con la extensión '.bat', en la misma carpeta. (La críptica manipulación de nombre de archivo "%~dpn1.bat" se explica en la sección siguiente.) Tenga en cuenta: el script de SED anterior solo realizará las sustituciones rudimentarias mencionadas arriba. ¡No convertirá scripts de shell de Linux sofisticados (como los presentados en el sitio web de Fred Weinhaus) en el archivo por lotes equivalente!

Manejo de nombres de archivo en archivos por lotes

Como se ha dicho arriba, IM es particularmente útil al aplicar una secuencia estándar de pasos de procesamiento a un archivo de imagen. En tal caso, el nombre de archivo se pasará al script como parámetro de la línea de comandos, ya sea mediante arrastrar y soltar o mediante SendTo. Con estas técnicas, el nombre de archivo entregado al archivo por lotes de DOS será un nombre de archivo totalmente cualificado, es decir, incluirá el nombre de la unidad y la ruta del directorio. Puede comprobarlo soltando un archivo sobre el siguiente archivo por lotes:

ECHO Filename: %1
PAUSE

Debido a la instrucción PAUSE, la ventana de DOS permanecerá abierta hasta que el usuario pulse una tecla, de modo que pueda inspeccionar el resultado. Pruebe lo anterior con un nombre de archivo que contenga espacios y notará que el nombre de archivo queda entre comillas dobles. Tenga en cuenta que aquí y en todos los ejemplos siguientes asumimos que a cualquier archivo de red se le ha asignado una letra de unidad. En la práctica, nunca he visto otra cosa en una red comercial local. Al trabajar con archivos por lotes, no debería intentar manejar nombres UNC: puede que consiga que su archivo por lotes funcione, pero le causará muchos problemas innecesarios. Al usar este nombre de archivo en una línea de comandos de Convert, este comportamiento puede causar problemas. Realicemos una conversión sencilla desde cualquier otro formato a JPEG. El código más básico sería:

magick %1 %1.jpg
PAUSE

Esto producirá un archivo JPEG (con calidad y resolución estándar) en el mismo directorio, con una extensión ".jpg" adicional al final. El código anterior funciona con cualquier nombre de archivo, contenga espacios o no. Si quiere deshacerse de la extensión original, las cosas se vuelven un poco más delicadas:

magick %1 "%~dpn1.jpg"
PAUSE

El archivo por lotes anterior manipula el nombre de archivo usando el operador ~: %~1 expande %1 quitando las comillas circundantes (")
%~f1 expande %1 a un nombre de ruta totalmente cualificado
%~d1 expande %1 solo a una letra de unidad
%~p1 expande %1 solo a una ruta
%~n1 expande %1 solo a un nombre de archivo
%~x1 expande %1 solo a una extensión de archivo
Estos modificadores pueden combinarse, de modo que "%~dpn1" significa "unidad + ruta + nombre sin extensión ni comillas circundantes". En consecuencia, tenemos que poner el nombre entre comillas dobles, de modo que el código funcione también para nombres de archivo que incluyan espacios. (Si no los incluye, las comillas no hacen daño.) La instrucción PAUSE es solo para fines de prueba y puede omitirse en el archivo por lotes final. Si solo quiere probar su código sin invocar realmente IM, debería escribir:
ECHO magick %1 "%~dpn1.jpg"
PAUSE

lo que simplemente mostrará el resultado de su manipulación de cadenas.

Procesamiento por lotes de varios archivos

Bucles FOR

El comando FOR de DOS puede usarse para procesar una serie de archivos de manera similar a como lo hace en UNIX. Para escalar todos los archivos JPEG del directorio actual al 50%, podría teclear la siguiente línea en una ventana de DOS:

  FOR %a in (*.jpg) DO magick %a -resize 50% small_%a

Tenga en cuenta que el signo de porcentaje no está duplicado. Sin embargo, si coloca este comando en un archivo por lotes, tendrá que sustituirlo por

  FOR %%a in (*.jpg) DO magick %%a -resize 50%% small_%%a

También aquí es cómodo invocar esta operación masiva mediante arrastrar y soltar o SendTo, pasando un nombre de archivo totalmente cualificado (o un nombre de carpeta) a un archivo por lotes de DOS que posiblemente se encuentre en otro directorio (como shell:sendto). En este caso, deberíamos hacer del directorio del archivo el directorio actual en el primer paso:

  %~d1
  CD "%~p1"
  MD small
  FOR %%a in (*.jpg) DO magick %%a -resize 50%% small\%%a
  PAUSE

En este archivo por lotes

  • cambiamos de unidad proporcionando el nombre de la unidad (d: o el que sea)
  • hacemos de la carpeta del archivo el directorio actual
  • creamos un subdirectorio llamado "small"
  • escalamos todos los archivos JPEG al 50% y colocamos estas versiones reducidas en el nuevo subdirectorio.

Tenga en cuenta: como la tilde (~) libera de cualquier posible comilla circundante el nombre de archivo pasado como parámetro de la línea de comandos, normalmente tenemos que volver a poner entre comillas el resultado de cualquier manipulación de ese tipo. Por eso escribimos CD "%~p1" en el ejemplo anterior. En el caso del comando CD, incluso podríamos haber omitido las comillas circundantes, ya que este comando en concreto solo acepta un parámetro y, por tanto, puede manejar los espacios en blanco sin comillas circundantes. Hacer del directorio del archivo el directorio actual en el primer paso facilita un poco los pasos posteriores, ya que las referencias a los nombres de archivo se vuelven un poco más fáciles y cortas. Sin embargo, también podríamos haber escrito:

  MD "%~dp1small"
  FOR %%a in ("%~dp1*.jpg") DO magick "%%a" -resize 50%% "%%~dpasmall\%%~nxa"
  PAUSE

Esto puede acortar un poco las cosas, pero también las vuelve mucho más delicadas. Tenga en cuenta que los modificadores de nombre de archivo también funcionan sobre la variable del bucle For %%a. Nota dos: la barra invertida final forma parte del nombre de ruta. Por tanto, debe ser "%~dp1small" y no "%~dp1\small", lo que no hace el código más legible, sobre todo en el caso de "%%~dpasmall\%%~nxa". La instrucción FOR tiene varias carencias y advertencias. Una de ellas es que básicamente solo se ejecuta un único comando tras DO. Sin embargo, puede agrupar una serie de comandos de DOS entre paréntesis "(", ")" y ejecutar así una secuencia sencilla de comandos:

  @ECHO OFF
  :: Lighten darker areas of all images in a directory
  %~d1
  CD "%~p1"
  FOR %%a in (*.jpg) DO (
    ECHO Processing file: "%%~nxa"
    magick %%a -blur 30 -negate %%a.miff
    magick composite %%a.miff %%a -compose overlay "%%~dpn1_light"%%~xa
    DEL %%a.miff
  )
  PAUSE

Este archivo por lotes procesará todas las imágenes encontradas en el directorio pasado como argumento de la línea de comandos. Primero desenfoca la imagen original y la invierte, almacenando el resultado intermedio en un archivo con una extensión ".miff" adicional. Luego superpone la imagen original sobre esta versión modificada, aclarando así las secciones más oscuras de la imagen original. Por último, se elimina la imagen intermedia. Tenga en cuenta que, en lo anterior, hay que poner el énfasis en la secuencia sencilla de comandos: no puede hacer uso de saltos GOTO dentro del bloque. Si necesita ese comportamiento, tiene que llamar a otro archivo por lotes mediante el bucle FOR:

  %~d1
  CD %~p1
  MD small
  FOR %%a in (*.jpg) DO CALL "%~dp0process" "%%~fa"

Donde "process.bat" es el archivo por lotes que realiza el trabajo real y que se encuentra en el mismo directorio que el archivo por lotes que lo llama. El parámetro 0 de la línea de comandos ("%0") es el nombre del propio archivo por lotes, de modo que "%~dp0process" llama al archivo por lotes process.bat del mismo directorio. La instrucción FOR proporciona solo el nombre de archivo, que se convierte en un nombre de archivo totalmente cualificado mediante "%~fa". En el presente caso, el código del archivo por lotes process.bat sería el mismo que el que se puso entre los paréntesis del ejemplo anterior:

  magick %%1 -blur 30 -negate %%1.miff
  magick composite %%1.miff %%1 -compose overlay "%%~dpn1_light"%%~x1
  DEL %%1.miff

El uso de un archivo por lotes separado, sin embargo, ofrece todas las (limitadas) posibilidades de un archivo por lotes de DOS. En este ejemplo no supone ninguna diferencia, pero mostraremos las ventajas de este enfoque más adelante. Si no quiere lidiar con dos archivos por lotes, puede hacer que el script cree el segundo script process.bat (usando ECHO), lo llame desde el bucle principal y luego lo elimine cuando termine el trabajo:

  ECHO magick %%1 -blur 30 -negate %%1.miff >%~dp0process.bat
  ECHO composite %%1.miff %%1 -compose overlay "%%~dpn1_light"%%~x1 >>%~dp0process.bat
  ECHO DEL %%1.miff >>%~dp0process.bat
  %~d1
  CD %~p1
  MD small
  FOR %%a in (*.jpg) DO CALL "%~dp0process" "%%~fa"
  DEL %~dp0process.bat

Al usar el comando ECHO, tenemos que escapar por segunda vez cualquier carácter especial de DOS, en especial el signo de porcentaje. Y sí, IM podría haber hecho todo lo anterior en un único comando de procesamiento, eliminando la necesidad de la imagen intermedia ".miff", pero esa no es la finalidad de este ejemplo.

Procesamiento por lotes de un árbol de (sub)directorios

Hay varias técnicas para procesar todos los archivos de un árbol de (sub)directorios. El enfoque más sencillo es usar la bandera "/R" en la instrucción FOR para que recorra todos los archivos de todos los subdirectorios bajo el directorio actual. Para magickear todos los archivos TIFF del árbol de subdirectorios a JPEG, basta con teclear:

  FOR /R %%a IN (*.tif) DO imconv "%%~a" "%%~dpna.jpg"

Al usar la bandera "/R", siempre recorre todo el árbol de subdirectorios, sin opciones para ordenar ni filtrar los archivos. En el siguiente ejemplo, generaremos impresiones índice de fotos para todos los subdirectorios y las colocaremos dentro del directorio raíz. Esto ofrece una forma sencilla de realizar una búsqueda visual de una determinada fotografía, similar a la vista previa del Explorador de Windows, pero sin la (lenta) necesidad de volver a escanear todo el árbol de directorios en cada búsqueda. Para empezar, abordamos el problema con la ayuda de dos archivos por lotes, uno que realiza el bucle y otro que hace el trabajo real. Las impresiones índice serán archivos JPEG de baja calidad llamados IDX_0001.jpg, IDX_0002.jpg, IDX_0003.jpg y así sucesivamente. Primero establecemos el bucle:

  DEL IDX_????.JPG
  SET COUNT=0
  FOR /F "delims=" %%a in ('DIR /S /B /AD ^|FIND /I "Porsche" ^|SORT') DO CALL c.bat "%%a"
  DEL title.txt

La primera línea limpia cualquier resultado de búsquedas anteriores. En la segunda línea, definimos la variable de entorno COUNT, que usaremos para generar los nombres de archivo IDX_nnnn.JPG. En la tercera línea, establecemos una lista de todos los subdirectorios mediante DIR /S /B /AD, extraemos los directorios que contienen la palabra "Porsche" (sin distinguir mayúsculas y minúsculas gracias a la opción /I) y ordenamos esta lista filtrada. La ordenación garantizará que el orden numérico de los archivos IDX coincida con el orden alfabético de los nombres de ruta de los directorios. La opción "delims=" impedirá el comportamiento estándar de truncar las líneas tras el primer espacio en blanco. Al llamar al archivo por lotes C.BAT, ponemos el nombre de ruta entre comillas para asegurar que los espacios en blanco se traten correctamente. En la última línea, eliminamos un archivo temporal que crea el archivo por lotes C.BAT. Pasamos ahora al trabajo real:

  CHCP 1252
  DIR %1\*.jpg>nul || GOTO :EOF

  :: Generate IDX filename
  SET /A COUNT+=1
  SET TFILE=000%COUNT%
  SET TFILE=IDX_%TFILE:~-4%.jpg

  :: Generate title without bracketing quotes
  ECHO %~1>temp.txt
  "C:\Program Files\Gnuwin32\bin\iconv.exe" -f ISO-8859-1 -t UTF-8 temp.txt>title.txt

  montage -geometry 210x140+0+5 -tile 6x -title @title.txt %1\*.jpg -quality 30%% %TFILE%
  jhead -cl %1 %TFILE%

En la primera línea cambiamos la página de códigos al latín de Europa occidental (ISO-8859-1). En la segunda línea, comprobamos si el directorio contiene realmente alguna fotografía y omitimos el resto del archivo por lotes si no es así. (Ejecutar en cambio el resto del archivo por lotes en realidad no haría daño, ya que magick montage simplemente no generaría salida, pero el recuento de los archivos IDX dejaría de ser consecutivo.) Desde Windows XP existe una etiqueta de destino de GoTo especial :EOF que permite terminar la ejecución sin definir una etiqueta adecuada. Generamos entonces el nombre de archivo "TFILE" del archivo índice incrementando "COUNT", añadiendo algunos ceros a la izquierda y extrayendo los últimos 4 caracteres mediante %TFILE:~-4%, para luego concatenarlo y crear un nombre de archivo de la forma "IDX_nnnn.jpg". El uso de la instrucción SET /A para realizar cálculos se explica más adelante en Cálculos usando SET. En las líneas siguientes liberamos el nombre de ruta "PNAME" de las comillas y almacenamos el resultado en el archivo intermedio 'temp.txt', que se transcodifica a Unicode (UTF-8) con la ayuda de "Icon.exe" (consulte "Codificación de caracteres"). La cadena Unicode almacenada en 'title.txt' es leída a continuación por el Montage de IM. Esto garantiza que la cadena se trate de forma literal, de modo que no tengamos que escapar las barras invertidas de los nombres de ruta de Windows. Montage combina entonces las fotografías en filas de seis (-tile 6x) y las titula con el nombre de ruta. La impresión índice resultante tendrá 1260 píxeles de ancho y se almacena con una calidad JPG del 30% para reducir las necesidades de almacenamiento. En la última línea, usamos el programa JHEAD para escribir el nombre de ruta en el comentario JPEG. Esto ofrece la posibilidad de filtrar las impresiones índice dentro del Explorador de Windows buscando por texto ciertas subcadenas en el nombre de archivo. Podemos combinar los dos archivos por lotes, colocando el código del «archivo por lotes de trabajo» dentro del bucle FOR:

  SETLOCAL EnableDelayedExpansion
  SET ICONV="%PROGRAMFILES%\Gnuwin32\bin\iconv.exe" -f ISO-8859-1 -t UTF-8
  CHCP 1252
  DEL IDX_????.JPG
  SET COUNT=0
  FOR /F "delims=" %%a in ('DIR /S /B /AD ^|FIND /I "Porsche" ^|SORT') DO (
    DIR "%%a\*.jpg">nul
    IF !ERRORLEVEL!==0 (
      SET /A COUNT+=1
      SET TFILE=000!COUNT!
      SET TFILE=IDX_!TFILE:~-4!.jpg
      ECHO %%a >temp.txt
      %ICONV% temp.txt>title.txt
      montage  -geometry 210x140+0+5 -tile 6x -title @title.txt "%%a\*.jpg" -quality 30%% !TFILE!
      jhead -cl %%a !TFILE!
    )
  )
  DEL temp.txt
  DEL title.txt

Básicamente, hay que aplicar dos modificaciones al código inicial:

  • Tenemos que habilitar la expansión retardada y referirnos a las variables de entorno usadas dentro del bucle FOR poniéndolas entre signos de exclamación en lugar de signos de porcentaje.
  • Tenemos que evitar la instrucción GOTO, que reiniciaría el procesador de comandos.

De forma predeterminada, las variables de entorno dentro de un bucle FOR no se evalúan en tiempo de ejecución. En su lugar, el código se preprocesa usando la lista dada entre paréntesis. Por tanto, una referencia a %COUNT% dentro del bucle FOR siempre devuelve el mismo valor. Para habilitar la evaluación en tiempo de ejecución de las variables de entorno, tiene que activar la expansión retardada. Esto puede hacerse al llamar al procesador de comandos mediante cmd /V:on o activarse de forma general en el registro, usando el siguiente archivo REG:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]
"DelayedExpansion"="1"

[HKEY_CURRENT_USER\Software\Microsoft\Command Processor]
"DelayedExpansion"="1"

Sin embargo, la versión a prueba de fallos consiste en establecer esta opción en el propio archivo por lotes usando SETLOCAL EnableDelayedExpansion, lo que además limitará cualquier cambio en las variables de entorno a la versión actual del procesador de comandos, lo que probablemente es lo deseado. Las referencias a los valores en tiempo de ejecución de las variables de entorno dentro del bucle tienen ahora que usar signos de exclamación en lugar de signos de porcentaje. El uso de instrucciones GOTO dentro de un bucle FOR es una posible fuente de errores muy sutiles y, por tanto, debería evitarse. En nuestro archivo por lotes, sin embargo, podemos sustituir fácilmente el salto por una instrucción IF que encierre el bloque de código con el código de montage.

Procesamiento por lotes de un número arbitrario de archivos

En los archivos por lotes de DOS, solo nueve parámetros de la línea de comandos pueden direccionarse directamente mediante %1 a %9. En versiones anteriores de Windows, solo se podía sortear esta limitación con el comando SHIFT, que provocaba un desplazamiento circular de los parámetros de la línea de comandos. En las versiones más recientes, los parámetros de la línea de comandos pueden tratarse en un bucle For:

  FOR %%i in (%*) DO ...

Esto nos permite montar (Montage) un número arbitrario de imágenes pasadas mediante arrastrar y soltar:

  @ECHO OFF
  SETLOCAL EnableDelayedExpansion
  %~d1
  CD "%~p1"
  DEL Files.txt   2>nul:
  DEL FSorted.txt 2>nul:
  FOR %%I in (%*) DO ECHO %%~nxI>>files.txt
  FOR /F "delims=" %%A in ('TYPE files.txt ^| Sort') Do (
  ECHO %%A>>fsorted.txt
  )
  SET MONTAGE=montage -tile 3x
  FOR /F "delims=" %%A in (FSorted.txt) Do (
  SET MONTAGE=!MONTAGE! %%A
  )
  SET MONTAGE=%MONTAGE% result%~x1
  %MONTAGE%
  DEL Files.txt
  DEL FSorted.txt

El código anterior supone que los archivos entregados como parámetros de la línea de comandos residen en el mismo directorio y tienen que montarse según el orden alfanumérico de sus nombres de archivo. Esto tendría sentido, por ejemplo, para un índice de fotografías digitales que quedarían así ordenadas según su hora de captura. Primero hacemos del directorio de los archivos el directorio actual, lo que simplifica mucho el código posterior. Luego volcamos los nombres de archivo en files.txt. Aunque los archivos se hubieran elegido en orden alfabético, no podemos confiar en que ese orden se conserve cuando los nombres de archivo se entregan al script. Por eso ordenamos los archivos en un segundo paso de trabajo, volcándolos en fsorted.txt. A partir de ese archivo, construimos entonces la línea de comandos de Montage en otro bucle For. El archivo de salida de Montage usa la misma extensión que el primer archivo de entrada (%~x1). (Suponiendo que todos los archivos comparten la misma extensión.) Tenga en cuenta que el comando Montage final se invoca simplemente evaluando la variable de entorno, es decir, %MONTAGE%. Como se ha dicho en , entregar una gran cantidad de archivos con nombres de archivo (completos) largos puede causarle problemas en Windows XP, porque la lista de parámetros de ShellExecute está limitada a 2048 caracteres. Este error no puede gestionarse desde el archivo por lotes, ya que se produce antes de que el control se entregue al archivo por lotes. Una posible solución basada en script es procesar todos los archivos de imagen del directorio cuando solo se entrega un archivo al archivo por lotes:

 %~d1 & IF EXIST %1\* (CD %1) ELSE CD %~p1
 IF "%2"=="" (
 SET PATTERN=*.jpg
 ) ELSE (
 SET PATTERN=%*
 )
 FOR %%v IN (%PATTERN%) DO (...)

En la primera línea comprobamos si está presente el segundo parámetro de la línea de comandos. Si lo está, se procesan todos los parámetros de la línea de comandos (%*). Si solo se entrega un archivo al script, el patrón se establece en todos los archivos JPEG. Esta sencilla coincidencia de patrón requiere que hayamos cambiado a la unidad actual (mediante %~d1) y a la carpeta actual, es decir

  • CD %1 si se entregó un nombre de carpeta al script
  • CD %p1 si se entregó un nombre de archivo al script.

Obtener información de ImageMagick

Reutilizar la salida de un comando de IM

En las versiones recientes de Windows, la instrucción FOR se ha vuelto mucho más potente; consulte DOS "For" Command Help. Usando la opción "/F", puede leer la entrada para la variable de sustitución desde un archivo, una cadena o la salida de otro comando de DOS u otro programa. Esto último es especialmente útil con IM. Para hacerse una idea aproximada de en qué consisten realmente los métodos de superposición de IM, podría usar el siguiente archivo por lotes:

  @ECHO OFF
  :: compose two gradients using all compose methods available
  ::
  magick -size 80x80 -flip gradient: compose_src.png
  magick compose_src.png -rotate 90 compose_dst.png
  FOR /F %%A in ('magick -list compose') DO ^
     magick composite compose_src.png compose_dst.png -compose %%A compose_%%A.png

[IM Output]
Src | [IM Output]
Dst | | [IM Output]
Multiply | [IM Output]
Screen | [IM Output]
Overlay
---|---|---|---|---|---

Este script compone dos imágenes de degradado usando todos los métodos de composición alfa posibles disponibles, de modo que pueda ver cómo afectan los operadores a los colores de la imagen. Arriba solo se muestran algunas de las imágenes que genera. Esto es similar a las imágenes que se generaron para Composition Tables, de modo que pueda ver cómo afectan los operadores a los colores de la imagen. Como se supone que las líneas anteriores forman un archivo por lotes, tenemos que duplicar los signos de porcentaje. El script anterior crea primero dos imágenes en escala de grises ortogonales (alineadas en ángulo recto) con degradados que cubren todo el rango de la escala de grises. El comando de IM Convert -list compose nos proporcionará una lista de las opciones posibles, cada una colocada en una sola línea de salida. Tenga en cuenta que tenemos que usar comillas simples al referirnos a un comando entre paréntesis. Usando la opción "/F", el comando FOR procesará entonces cada una de estas líneas de salida y la entregará al comando ejecutado por DO. En consecuencia, las dos imágenes de degradado se superponen aplicando todos los métodos de superposición que IM conoce. Los archivos de salida se nombran de forma correspondiente al método de superposición. En el siguiente ejemplo, ilustramos los espacios de color que ofrece IM. Usamos la misma técnica de degradado que arriba para generar las caras de un cubo definido por las tres coordenadas del espacio de color:

magick -size 256x256 gradient: gy.miff
magick gy.miff -rotate 90 gx.miff
magick -size 256x256 xc:black black.miff

::R + G top left
magick gy.miff gx.miff -flop black.miff -set colorspace %1 -combine ^
        -resize 260x300! -background none -shear 0x-30 ^
        -virtual-pixel Transparent RG.miff

:: R + B top right
magick gy.miff  black.miff gx.miff -set colorspace %1 -combine ^
        -resize 260x300! -background none -shear 0x30 RB.miff

:: G + B bottom
magick black.miff gx.miff gy.miff -set colorspace %1 -combine ^
        -resize  260x300! -background none -shear 0x30 -rotate 120 ^
        -crop 520x300+0+75 GB.miff

magick -set colorspace %1 RG.miff RB.miff +append top.miff
magick -set colorspace %1 -size 520x150 xc:Transparent w.miff
magick top.miff w.miff -append topx.miff

composite -geometry +0+299 GB.miff  topx.miff colorspace_%1.png
DEL *.miff

[IM Output]
colorspace RGB | [IM Output]
colorspace sRGB
---|---
Un ejemplo similar al anterior pero con scripting de shell de UNIX se da en Cubo isométrico usando cizallas.

Este archivo por lotes toma el espacio de color como parámetro de la línea de comandos "%1". Luego genera las tres caras del cubo y las cizalla y monta de modo que obtengamos una vista isométrica, donde el punto (0,0,0) queda en el centro de un hexágono. La imagen final se nombra según el espacio de color (es decir, "%1") y se almacena como un PNG. Ahora queremos llamar a este archivo por lotes (guardado como "cspace.bat") desde otro archivo por lotes que proporciona los nombres de los espacios de color:

  FOR /F %%A in ('magick -list colorspace') DO CALL cspace %%A

También podemos filtrar la salida de la opción -list canalizándola en DOS:

  magick -list colorspace | FIND "RGB" >>clist.txt
  FOR /F %%A in (clist.txt) DO CALL cspace %%A
  DEL clist.txt

En este ejemplo, filtramos de la salida las líneas que contienen "RGB" y las escribimos en el archivo clist.txt. Este archivo se usa luego como entrada para el comando FOR /F. También podemos hacer esto de una sola pasada, evitando el archivo temporal:

  FOR /F %%A in ('magick -list colorspace ^| FIND "RGB"') DO CALL cspace %%A

En este caso, el símbolo de tubería "|" tiene que escaparse, porque no está entre comillas dobles (solo entre las comillas simples necesarias para la instrucción FOR) y (al menos en la línea de comandos anterior) no se usa en su sentido habitual.

Procesar una salida de una sola línea

Esta técnica también puede aplicarse de forma útil a una salida de una sola línea. Por ejemplo, podemos aplicar una corrección de gamma automática que sitúe aproximadamente el brillo medio de una imagen en la mitad del rango cuántico (es decir, 127 para una profundidad de color de 8 bits) mediante una técnica explicada en el sitio web de Fred Weinhaus:

  FOR /F %%a in ('identify -format "%%[fx:log(mean)/log(0.5)]" %1') DO ^
  magick %1 -gamma %%a "%~dpn1"_c.%~x1

A este archivo por lotes se le entrega un nombre de archivo totalmente cualificado como parámetro de la línea de comandos "%1", muy probablemente mediante arrastrar y soltar o SendTo. La salida del comando Identify de IM proporciona entonces un valor de gamma, que situará el brillo medio de la imagen en la mitad de su rango dinámico. Este valor se calcula usando una expresión de formato FX. La salida de una sola línea del comando Identify se guarda en la variable "%%a" y se pasa al comando Convert como argumento del operador Gamma. El comandoFOR parece ser bastante sensible en lo que respecta a las continuaciones de línea: si las usa, asegúrese de no empezar la línea siguiente con espacios.
Este método de corrección de gamma automática está ahora incorporado en IM mediante "[-auto-gamma](https://imagemagick.org/command-line-options/#auto-gamma)", y se añadió en IM v6.5.5-1. Pero muestra la técnica de reutilizar la salida de un comando para usarla en argumentos de comandos posteriores.
--- ---
Con la misma técnica de FOR, podemos leer de la información EXIF incrustada en una fotografía y escribirla en la esquina superior izquierda de la imagen:
  FOR /F "tokens=1,2" %%i IN ('identify -format "%%[EXIF:DateTime]" %1') DO ^
  magick %1 -pointsize 18 -fill white -gravity northwest ^
    -annotate +0+0 "%%i %%j" "%~dpn1"_dated%~x1

| Las fotos suelen guardarse en formato JPEG. Leer y volver a guardar imágenes JPEG provoca una ligera degradación de la imagen debido a lacompresión con pérdida de JPEG y, por tanto, no se recomienda volver a guardar en JPEG.
---|---
En el archivo por lotes anterior, el nombre de archivo de la fotografía se proporciona como único parámetro de la línea de comandos, al que se hace referencia como "%1". El comando Identify lee la fecha y la hora en que se tomó la fotografía de la información EXIF dentro del archivo JPEG. El comando FOR entrega entonces esta salida a Convert, que anota la fotografía de forma correspondiente en la esquina superior izquierda. La información de fecha y hora EXIF tiene el formato "yyyy:mm:dd hh:mm:ss", p. ej. "2006:12:26 00:22:38". Así, la fecha y la hora están separadas por un carácter de espacio. De forma predeterminada, la instrucción FOR solo encontraría el primer token («palabra») de cada línea, con los caracteres de tabulación y espacio como delimitadores estándar. Por tanto, en el ejemplo anterior, el procesamiento estándar solo devolvería la fecha, pero no la hora. Usando la opción "tokens=1,2" declaramos nuestro interés por ambos tokens, que se nombran de forma consecutiva, es decir, "%x, %y". Sin embargo, podemos cambiar el formato bastante poco convencional de la fecha con el siguiente código:

  FOR /F "tokens=1,2,3,* delims=: " %%i IN ('identify -format "%%[EXIF:DateTime]" %1') DO ^
  magick %1 -pointsize 18 -fill white -gravity northwest ^
    -annotate +0+0 "%%j/%%k/%%i %%l" "%~dpn1"_dated%~x1

Ahora hemos definido los dos puntos (':') como delimitador adicional, lo que hace que la fecha se divida en los tres tokens "%i", "%j", "%k". El siguiente delimitador encontrado es el carácter de espacio que separa la fecha y la hora. Con el asterisco ("*") pedimos que el resto de la línea se almacene en el cuarto token "%l". Ahora podemos dar a la fecha el formato que queramos. Hemos elegido la notación angloamericana "mm/dd/yyyy", como en el ejemplo anterior.

Asignar varios valores desde un solo comando

Una técnica para asignar varios valores desde un único comando de ImageMagick consiste en hacer que el comando dé formato a los datos, de modo que pueda asignar varias variables.

  FOR /F %%L IN ('identify -format "Width=%%w\nHeight=%%h" %1') DO set %%L

Esto da como resultado la asignación de ambas variables de DOS 'Width' y 'Height' desde la única llamada al comando de ImageMagick.

Realizar cálculos

El intérprete de comandos de DOS es pobre en lo que respecta a los cálculos. Puede usarlo para realizar aritmética de enteros sencilla. Pero para hacer matemáticas de punto flotante más complejas, tiene acceso a la expresión de formato FX de IM, o a un programa calculadora de DOS de terceros.

Usar las expresiones FX de IM

La expresión de formato FX de IM puede usarse para matemáticas de punto flotante y puede añadir esas matemáticas a cadenas con formato más extensas, como se ha demostrado arriba en el primer ejemplo de la sección Procesar una salida de una sola línea. Usando el comando SET, el resultado puede almacenarse en una variable de entorno y usarse luego en el archivo por lotes. Como ejemplo sencillo, podríamos querer ajustar el tamaño de fuente de la cadena de fecha y hora del ejemplo anterior en función de las dimensiones de la fotografía:

  FOR /F %%i IN ('identify -format "%%[fx:min(w,h)*0.05]" %1') DO SET psize=%%i

  FOR /F "tokens=1,2,3,* delims=: " %%i IN ('identify -format "%%[EXIF:DateTime]" %1') DO ^
  magick %1 -pointsize %psize% -fill white -gravity northwest ^
    -annotate +0+0 "%%j/%%k/%%i %%l" "%~dpn1"_dated%~x1

En la primera línea evaluamos la dimensión menor de la fotografía mediante "%[fx:min(w,h)]", tomamos el 5% de este valor y lo almacenamos en la variable de entorno PSIZE. Este valor se referencia en la siguiente instrucción (%psize%) para establecer el tamaño de fuente de la información de hora y fecha. Y aquí calculamos un ángulo aleatorio como un entero entre -15° y +15° para crear una imagen en miniatura rotada.

  FOR /F %%x IN ('magick null: -format "%%[fx:int(rand()*31)-15]" info:') DO SET angle=%%x
  magick %1 -thumbnail x90 -alpha set ^
            -background none -rotate %angle%   "%~dpn1"_rotated.png

Las expresiones FX no solo pueden generar números, sino también varios números, incrustados en una cadena más extensa. Por ejemplo, en Borde con esquinas redondeadas se usó para generar directamente una cadena draw compleja basada en la información de anchura y altura de la imagen. Esta característica adicional, junto con el hecho de evitar cualquier otra dependencia de programas externos, convierte este método en el preferible para hacer cálculos en su script por lotes.

Usar el comando SET

El comando SET puede realizar algunas matemáticas de enteros sencillas y algunas manipulaciones básicas de cadenas cuando se invoca la opción "/A". En el siguiente ejemplo, calculamos aproximadamente la anchura de la cadena de hora y fecha usando el comando SET:

  :: Determine the font height
  FOR /F %%i IN ('identify -format "%%[fx:min(w,h)*0.05]" %1') DO SET psize=%%i

  :: The width of the date-time string is roughly 9 times its height
  SET /A pwidth=%psize% * 9

  :: Calculate the average brightness in this section
  :: and choose the text color correspondingly
  FOR /F %%i IN ('identify -format "%%[fx:mean]" -crop %pwidth%x%psize%+0+0 %1') DO SET mean=%%i
  IF %mean% LEQ 0.5 (SET fcolor=white) ELSE SET fcolor=black

  :: Annotate the photograph
  FOR /F "tokens=1,2,3,* delims=: " %%i IN ('identify -format "%%[EXIF:DateTime]" %1') DO ^
  magick %1 -pointsize %psize% -fill %fcolor% -gravity northwest ^
    -annotate +0+0 "%%j/%%k/%%i %%l" "%~dpn1"_dated%~x1

Este archivo por lotes de ejemplo elige el color de la cadena de fecha y hora en función del brillo medio (%mean%) en la zona donde se va a colocar. Si la intensidad media de color es inferior al 50%, la cadena será blanca; de lo contrario, será negra. El ejemplo también hace uso de la instrucción IF. Tenga en cuenta que la parte ELSE tiene que colocarse en la misma línea y que el primer comando tiene que ir entre paréntesis.

Usar otras calculadoras externas

Como alternativa, puede usar un programa de DOS que proporcione matemáticas de punto flotante, como EVAL. Si coloca este archivo en el directorio de programas de IM o en el directorio del sistema de Windows, puede realizar cálculos de punto flotante en cualquier ventana de shell de DOS. Usando el programa EVAL, el comando FOR y variables de entorno, podemos hacer el ejemplo del cubo de color de arriba algo más flexible y sus diversos cálculos más transparentes:

  magick -size 256x256 gradient: gy.gif
  magick gy.gif -rotate 90 gx.gif
  magick -size 256x256 xc:black black.gif

  :: Set the dimension of the color cube / hexagon and calculate the various lengths
  SET l1=512
  FOR /F %%i IN ('EVAL "round(cos(degree*30)*%l1%)"') DO SET l2=%%i
  FOR /F %%i IN ('EVAL "2*%l2%"') DO SET l3=%%i
  FOR /F %%i IN ('EVAL "round((1+sin(degree*30))*%l1%/2)"') DO SET l4=%%i
  FOR /F %%i IN ('EVAL "round(%l1%/4)"') DO SET l5=%%i

  magick gy.gif gx.gif -flop black.gif -set colorspace %1 -combine ^
          -resize %l2%x%l1%! -background none -shear 0x-30 ^
          -virtual-pixel Transparent RG.miff

  magick gy.gif  black.gif gx.gif -set colorspace %1 -combine ^
          -resize %l2%x%l1%! -background none -shear 0x30 RB.miff

  magick black.gif gx.gif gy.gif -set colorspace %1 -combine ^
          -resize  %l2%x%l1%! -background none -shear 0x30 -rotate 120 ^
          -crop %l3%x%l1%+0+%l5% GB.miff

  magick -set colorspace %1 RG.miff RB.miff +append top.miff
  magick -set colorspace %1 -size %l3%x%l4% xc:Transparent w.miff
  magick top.miff w.miff -append topx.miff

  magick composite -geometry +0+%l1% GB.miff  topx.miff colorspace_%1.png
  DEL *.miff
  DEL *.gif

Edición, depuración y prueba de errores en tiempo de ejecución

En principio, los archivos por lotes de DOS pueden escribirse en cualquier editor, incluso con el Bloc de notas de Windows. Sin embargo, debería usar un editor con resaltado de sintaxis para archivos por lotes de DOS. Personalmente creo que Notepad++ es la herramienta de elección, pero hablar de editores tiende a poner a la gente de mal humor. Así que: sí, cualquier otro editor servirá. Que yo sepa, no hay en el mercado ningún IDE (entorno de desarrollo integrado) gratuito para archivos por lotes. Uno pensaría que esto debería ser algo que viene con el sistema operativo, pero nunca ha sido así. Hasta ahora, he escrito todos mis archivos por lotes con Notepad++, pero para quienes escriben archivos por lotes con regularidad, el IDE por lotes Running Steps podría ser de ayuda. Es shareware y cuesta unos 80 $. Pueden encontrarse explicaciones exhaustivas de los comandos de DOS en http://www.computerhope.com/msdos.htm. Como el propio lenguaje por lotes de DOS, depurar archivos por lotes es un asunto bastante extraño. Yo probaría cualquier archivo por lotes en una ventana de DOS para empezar. Al probar arrastrar y soltar o SendTo, es recomendable terminar el archivo por lotes con una instrucción PAUSE de modo que la ventana de DOS permanezca abierta tras finalizar el trabajo por lotes. En cuanto a los mensajes de error en tiempo de ejecución, el enfoque general es comprobar el ERRORLEVEL de DOS y saltar a un mensaje de error correspondiente generado por el comando ECHO. He descubierto que una de las fuentes de error más probables es que el programa Convert no se encuentre correctamente en la máquina que ejecuta el script. Así que, si tiene intención de compartir sus scripts por lotes con otros, lo primero que debería hacer es comprobar si Convert es accesible:

  @ECHO OFF
  magick -version 1>nul: 2>nul:
  IF NOT %errorlevel%==0 GOTO NoMagick:
  magick ...
  GOTO :EOF
  ...
  :NoMagick
  ECHO ImageMagick (convert.exe) not found.
  PAUSE

En la primera línea, consultamos la versión de ImageMagick, suprimiendo la salida estándar mediante 1>nul: (o simplemente >nul:) y cualquier mensaje de error mediante 2>nul:, es decir, redirigimos _stdout_ y _stderr_ a nul:. Si la llamada al Convert de IM falla, se llamará en su lugar al programa del sistema Convert, que no sabe manejar la opción -version y establecerá la variable ERRORLEVEL. Podría intentar determinar por qué no se encuentra Convert e intentar solucionar el problema: puede determinar si la ruta del programa de IM forma parte de la variable de entorno PATH:

  @ECHO OFF
  PATH | FIND /I "ImageMagick"
  IF NOT %errorlevel%==0 GOTO NoPath:
  ...
  :NoPath
  ...

Si Find (llamado con la opción /I que no distingue mayúsculas y minúsculas) no puede encontrar la cadena, establece ERRORLEVEL. En un enfoque más sofisticado, puede comprobar en su lugar la entrada del Registro, sin depender ya de PATH:

  @ECHO OFF
  FOR /F "tokens=1,2,*" %%A in ^
  ('reg  query "HKCM\Software\ImageMagick\Current" ^| FIND "BinPath"') DO ^
  SET MPATH=%%C
  IF [%MPATH%]==[] GOTO NoMagick:
  "MPATH\convert.exe" ...
  ...
  :NoMagick
  ...

Con este código, consultamos la clave del Registro de IM Current y buscamos la entrada BinPath. La línea decisiva de la salida es:LibPath REG_SZ C:\Program Files\ImageMagickLas «palabras» de esta línea de texto están separadas por tabulaciones (en Windows XP) o varios espacios en blanco (Windows Vista). Estos son los delimitadores estándar que usa For /F. La tercera «palabra» (%%C) es la que buscamos y la almacenamos en la variable de entorno MPATH, a la que podemos referirnos al llamar a magick más adelante en el script. Un script podría requerir que esté instalado un cierto número de versión mínimo de ImageMagick. Por ejemplo, el método de distorsión de perspectiva se implementó por primera vez en la versión 6.3.5-9 (en septiembre de 2007). Así que, si su script trata la rectificación de perspectiva, debería comprobar si la versión instalada de IM es más reciente que esa:

  @ECHO OFF
  SETLOCAL EnableDelayedExpansion
  SET MINVERSION=7.5.3-0
  FOR /F "tokens=1,2,3" %%a in ('magick -version ^|FIND "Version"') DO SET VERSION=%%c
  IF %VERSION% LSS %MINVERSION% GOTO GetNewVersion:
  Goto :EOF
  :GetNewVersion
  ECHO This Script requires et least ImageMagick version %MINVERSION%.
  ECHO Yours is %VERSION%.
  PAUSE
  Goto :EOF

SETLOCAL restringe cualquier cambio en las variables de entorno al script actual, de modo que no tengamos que temer efectos secundarios. La opción EnableDelayedExpansion aquí no hace realmente falta, pero es buena costumbre usar esa opción siempre que use SETLOCAL. Luego almacenamos la versión mínima requerida en la variable de entorno MINVERSION. En la tercera línea, llamamos a Convert con la opción '-version', extraemos la primera línea de la salida mediante ^|FIND "Version", obtenemos la tercera palabra de esa línea y la almacenamos en la variable de entorno VERSION. Luego comparamos esta versión con la versión mínima requerida en la cuarta línea.

Optimizar el tiempo de ejecución

Para medir el tiempo de ejecución, puede mostrar el contenido de la variable de entorno %TEMP%. El siguiente script prueba varias formas de calcular el brillo medio de un archivo de imagen (grande), digamos una fotografía digital:

   IF "%1"=="" GOTO :EOF
   ECHO %TIME%
   Identify -verbose %1 | FIND "mean" & ECHO %TIME%
   copy %1 %TEMP%\*.* & ECHO %TIME%
   Identify -verbose %TEMP%\%1 | FIND "mean" & ECHO %TIME%
   Convert %TEMP%\%1  -format %%[fx:mean] info: & ECHO %TIME%
   Convert %TEMP%\%1 -resize 1x1! -format %%[fx:mean] info: & ECHO %TIME%
   DEL %TEMP%\%1

La imagen se entrega al script como único parámetro de la línea de comandos, que se prueba en la primera línea del script. A continuación, se mide el tiempo de ejecución de varios enfoques mostrando con echo la variable de entorno %TIME% antes y después de la instrucción. El símbolo et (&) permite poner varios comandos de DOS en una sola línea y aquí se usa simplemente para acortar el espacio que necesita el código. Si la imagen se coloca en una unidad de red, la transferencia a la memoria del equipo cliente puede consumir una parte considerable del tiempo de ejecución. Por eso el archivo se copia a la carpeta temporal local, cuyo nombre se almacena en la variable de entorno %TEMP%. Resulta que el simple comando Convert tarda exactamente lo mismo que filtrar el resultado de Identify, pero que redimensionar la imagen a un píxel mediante -resize 1x1! acelera notablemente la operación, sin cambiar demasiado su resultado. Tenga en cuenta que usamos ECHO %TIME% en lugar de TIME /T, porque el segundo solo muestra horas y minutos, mientras que el primero proporciona centésimas de segundo. A diferencia de los shells de comandos de UNIX, no hay una forma directa de medir tiempos relativos, es decir, fijar un punto de referencia, ejecutar la instrucción y luego evaluar el tiempo necesario. El cálculo de tiempos relativos dentro de un archivo por lotes se dificulta por el hecho de que los archivos por lotes solo permiten aritmética de enteros. Se pueden extraer los segundos con el comando FOR y luego usar el operador fx de IM para realizar la resta, contemplando un desbordamiento de 60 segundos

   FOR /F "tokens=1,2,* delims=:" %%a in ("%TIME%") DO SET START=%%c
        ... some command(s)...
        FOR /F "tokens=1,2,* delims=:" %%a in ("%TIME%") DO SET STOP=%%c
   Convert null: -format "%%[fx:(%STOP%-%START%<0.0)?%STOP%-%START%+60.0:%STOP%-%START%]" info:

Sin embargo, esto solo funcionará en las versiones de Windows donde el separador decimal esté configurado como el punto decimal, ya que la hora se proporciona según la configuración regional y fx realiza sus cálculos según el esquema estadounidense. A diferencia de VBScript, no hay forma de cambiar la configuración regional dentro de un archivo por lotes (salvo cambiarla para toda la máquina mediante el registro). Como alternativa, se puede magickear la hora entera a centésimas de segundo, lo que puede hacerse con aritmética de enteros, igual que calcular después cualquier diferencia. El código básico es

   for /f "tokens=1-4 delims=:., " %%a in ("%TIME%") do ^
        set /a  Start100S=1%%a * 360000 + 1%%b * 6000 + 1%%c * 100 + 1%%d - 36610100

El "1" insertado antes de cada parte del código de hora dividido evita que los números con un cero a la izquierda se (mal)interpreten como octales. El «error de cálculo» introducido por esta forma de proceder se compensa después restando 36610100 al final. El código de ejemplo completo (y más elegante) puede encontrarse aquí. Otro enfoque es la utilidad TimeIt que ofrece el Windows 2003 Resource Kit, que puede descargarse aquí. Oficialmente, sin embargo, solo es compatible con Windows XP.

Pautas para programar archivos por lotes

Estas son, en pocas palabras, las reglas que hay que seguir al programar archivos por lotes:

  • Empezar con SETLOCAL EnableDelayedExpansion
  • Definir una variable para referenciar la herramienta Convert de IM: SET IMCONV="%PROGRAMFILES%\ImageMagick"
  • Al trabajar con varios archivos, a menudo simplifica el código hacer de la carpeta que contiene los archivos la carpeta actual, es decir
    • cambiar a la unidad mediante %~d1
    • cambiar a la carpeta mediante CD %~p1
  • Usar iconv.exe de GnuTools para magickear texto a UTF-8 y alimentar el texto desde un archivo.
  • Tener presente que Convert y Montage escriben la salida de texto en stderr, no en stdout.
  • Muchos errores surgen de nombres de archivo que contienen espacios en blanco, así que compruebe en cada archivo por lotes si maneja correctamente esos nombres de archivo.
  • No olvidar duplicar cada signo de porcentaje, p. ej. al establecer la calidad JPEG.
  • No olvidar escapar los caracteres especiales en las cadenas, p. ej. "^|".
  • Los cálculos de enteros realizados por SET /A tratan cualquier número que empiece por cero como octal, así que ponga un "1" delante de cada número de ese tipo.

En resumen

Los ejemplos anteriores demuestran que el simple archivo por lotes de DOS es asombrosamente versátil cuando se combina con las posibilidades que ofrece ImageMagick. De hecho, casi todo puede hacerse de alguna manera (tosca) en un archivo por lotes. Una vez que entra en la forma de pensar siguiendo las extrañas vías adoptadas en el desarrollo del lenguaje por lotes de DOS, los scripts pueden resultar incluso bastante cortos. No obstante, estas pocas líneas de código probablemente habrán consumido horas de tediosos experimentos, a menos que esté realmente familiarizado con el lenguaje de los archivos por lotes. Si aspira a algo más que tareas básicas de procesamiento de imágenes, probablemente sea recomendable usar un lenguaje de script más sofisticado, ya que el desarrollo del código resultará más sencillo y estructurado.


Visual Basic Script (VBS)

Las capacidades de scripting del Microsoft Windows Script Host (WSH) son más sofisticadas que las del simple lenguaje de los archivos por lotes. El WSH es independiente del lenguaje en el sentido de que puede hacer uso de distintos motores de lenguaje de Active Scripting. De forma predeterminada, interpreta y ejecuta archivos JScript de texto plano (Java Script) y archivos VBScript (VisualBasic Script). El Windows Script Host se distribuye e instala de forma predeterminada en Windows 98 y posteriores (aunque podría haberse desactivado en una posible máquina de destino por motivos de seguridad). El WSH implementa un modelo de objetos que expone un conjunto de interfaces COM que le permiten dirigirse a los objetos del sistema, en especial el sistema de archivos. No analizaremos aquí el Windows Script Host en detalle, ya que eso se hace en otra parte (y probablemente mejor), sino que daremos algunos ejemplos prácticos de cómo abordar problemas típicos. Los ejemplos se dan en VisualBasic Script, pero el código JScript sería muy parecido, así que debería ser fácil reescribir los ejemplos en JScript si ese es su lenguaje favorito. Como los archivos por lotes, los VBScripts pueden escribirse en cualquier editor y de nuevo sugeriría Notepad++ como el editor de elección. Igual que para los archivos por lotes, Microsoft no ofrece un IDE adaptado para apoyar el desarrollo de VBScripts. Existía un Microsoft Script Editor que se incluyó con Microsoft Office desde 2000 hasta 2003, pero nunca lo he probado. Microsoft también proporciona el (muy rudimentario) Microsoft Script Debugger, pero, de nuevo, no tengo mucha experiencia personal con él. Hay varios IDE de VBS comerciales ofrecidos como shareware a precios razonables, como VbsEdit. Como se ha dicho en la sección Instalar ImageMagick en Windows, en general no usaremos la interfaz COM+ de ImageMagick en lo sucesivo. En su lugar, las herramientas de IM como Convert, Montage e Identify se ejecutarán directamente invocando el comando run del shell, con todas las opciones y nombres de archivo necesarios, de forma muy parecida a como se hace en los archivos por lotes. Sin embargo, al ensamblar la cadena del comando, aprovecharemos las características que ofrece un verdadero lenguaje de programación.

Un ejemplo básico: corrección de lente

Como el uso del WSH genera cierta sobrecarga, nuestro ejemplo de inicio no es demasiado básico, con el fin de demostrar las ventajas de VBScript frente a un simple archivo por lotes. A continuación, corregiremos la distorsión de lente de la cámara digital Nikon 995 usando la distorsión de barril de IM. Los parámetros de corrección dependen de la distancia focal, que se consulta primero mediante magick identify. Para la corrección de la lente de la Nikon 995, solo necesitamos el parámetro b (es decir, a, c = 0), que puede calcularse a partir de la distancia focal f mediante: b = 0.000005142 f ³ - 0.000380839 f ² + 0.009606325 f - 0.075316854 Esta dependencia se halló por medio de la base de datos lensfun, que lista los parámetros de distorsión de barril de esta lente. Así que aquí está nuestro VBScript:

  SetLocale(1033)          ' US, i.e. decimal point
  const strConv = "IMCONV" ' name of the IM Convert program
  const strAdd = "_pt"    ' string attached to the filename
  '
  Dim wsh
  Set wsh = CreateObject("Wscript.Shell")
  '
  ' names of the in- and output files
  strFileIn = WScript.Arguments(0)
  Pos = InStrRev(strFileIn,".")
  strFileOut = Left(strFileIn,Pos - 1) & strAdd & Mid(strFileIn, Pos)
  '
  ' evaluation of the focal length and calculation of parameter b
  command = "identify -format ""%[EXIF:FocalLength]"" """ & strFileIn & """"
  Set objExec = wsh.Exec(command)
  strf = objExec.StdOut.Readline
  f = eval(strf)
  b =  0.000005142 * f * f * f -0.000380839 * f * f + 0.009606325 * f  -0.075316854
 '
  Command = strConv & " """ & strFileIn _
    &  """ -virtual-pixel black -filter point -distort Barrel   ""0.0 " _
    & b & " 0.0 "" """ & strFileOut & """"
  wsh.run command,7,true

En la primera línea establecemos la configuración regional en inglés de EE. UU. (1033), lo que (entre otras cosas) fija el separador decimal en el punto decimal. De lo contrario, los valores decimales se presentarían según la configuración regional, es decir, posiblemente con una coma decimal en lugar de un punto decimal, lo que causaría problemas cuando esos valores se entregan a IM. Las dos líneas que siguen a la definición de las constantes de cadena son una sobrecarga estándar, ya que siempre necesitamos un objeto Shell para iniciar los programas de IM mediante su método Exec o Run. El único argumento del script es un nombre de archivo, que normalmente proporcionamos mediante arrastrar y soltar o SendTo. Al nombrar el archivo de salida, añadimos "pt" al nombre de archivo original, igual que haría PTlens, p. ej. _Photo.JPG → Photo_pt.JPG. El nombre de archivo se almacena en strFileIn, del que derivamos el nombre del archivo de salida strFileOut. Luego ejecutamos el programa Identify de IM. El resultado (es decir, el racional EXIF que representa la distancia focal) se almacena en strf. Los racionales EXIF se proporcionan como numerador / denominador, p. ej. 82 / 10 = 8.2 mm. Por tanto, el racional tiene que evaluarse antes de usarlo en la fórmula que calcula el parámetro b. En las dos últimas líneas, construimos la línea de comandos de Convert y ejecutamos la instrucción mediante el método Run del objeto Shell. El parámetro 7 minimiza la ventana y TRUE le indica al script que espere el resultado. El script anterior esboza la estrategia general al usar VBScript con las herramientas de línea de comandos de IM (y no el objeto COM+). Estas se invocan de una de dos formas

  • mediante el comando Run del objeto Shell, si no se espera salida de texto
  • mediante el comando Exec del objeto Shell si hay que evaluar salida de texto, como suele ser el caso con Identify.

Este primer VBScript no hace nada que no se pudiera haber hecho con un archivo por lotes como

   SETLOCAL EnableDelayedExpansion
   FOR /F %%i in ('identify -format "%%[EXIF:FocalLength]" %1') DO SET FL=%%i
   FOR /F %%i IN ('Convert null: -format "%%[fx:0.000005142*(%FL%)^3 - 0.000380839 * (%FL%)^2 + 0.009606325 * %FL% - 0.075316854]" info:') DO SET B=%%i
   Convert %1 -distort barrel "0.0 !B! 0.0" "%~dpn1_pt%~x1"

pero el código VBS es más directo, más fácil de modificar y puede ampliarse con facilidad en cuanto a comprobación de errores y similares. Tenga en cuenta que en la segunda instrucción FOR los saltos de línea son imposibles, de modo que tenemos que dejarla tan larga como es.

Trabajar con varios archivos

Una verdadera ventaja de VBScript en comparación con los archivos por lotes de DOS es que puede recorrer con facilidad un número arbitrario de argumentos de la línea de comandos. Por ejemplo, podría elegir varios archivos en el Explorador de Windows y combinar las imágenes seleccionadas en una impresión índice mediante el Montage de IM. El código básico sería:

  Dim wsh
  Set wsh = CreateObject("Wscript.Shell")
  '
  strInputFiles  = ""
  For EACH Arg IN WScript.Arguments
      strInputFiles = strInputFiles & " """ & Arg & """"
  next
 '
  IndexFile= Left(WScript.Arguments(1),InStrRev(WScript.Arguments(1),"\")) & "index.jpg"
  Command = "montage -geometry 210x140+0+5 -tile 6x " & strInputfiles & " -quality 80% """ & IndexFile & """"
  wsh.run command, 7, true

El script concatena primero los nombres de archivo entregados al script mediante arrastrar y soltar. Luego deriva el nombre de un archivo JPEG de salida en la misma carpeta mediante alguna manipulación de cadenas sencilla. Por último, se llama a Montage con los parámetros apropiados. Para scripts más grandes, es cómodo almacenar los nombres de archivo en un array:

  Dim FName()
  Dim wsh
  Set wsh = CreateObject("Wscript.Shell")
  '
  NArgs = WScript.Arguments.Count
  Redim FName(NArgs-1)
  FOR i = 0 TO NArgs - 1
    FName(i) = """" & WScript.Arguments(i) & """"
  NEXT

Primero definimos un array dinámico mediante Dim FName() y luego lo redimensionamos mediante Redim FName(NArgs-1). La línea de comandos de Montage posiblemente se vuelva muy larga, porque en la lista de archivos de entrada cada archivo se nombra por su nombre de archivo totalmente cualificado. La longitud máxima no es la habitual de 8192 caracteres, sino que la determina la longitud máxima permitida para una llamada a la función ShellExecute, que es de solo 2048 caracteres para Windows XP. Esto puede causar problemas cuando el nombre del directorio es muy largo. El error causado no puede gestionarse desde el script, ya que el error se produce antes de que el script se ejecute. Una posible solución es colocar los archivos en una carpeta local con un nombre más corto. La solución (parcial) basada en script es la misma que para los archivos por lotes: si solo se da un nombre de archivo, se procesan todas las imágenes del directorio padre. Para procesar todos los GIF dentro de una carpeta, podríamos hacer algo en esta línea:

  Dim fs, folder
  Set fs = CreateObject("Scripting.FileSystemObject")
  If WScript.Arguments.Count <> 1 Then WScript.Quit(1)
  set Folder = fs.getFolder(fs.GetParentFoldername(WScript.Arguments(0)))
  FN=""
  FOR EACH file in folder.files
     If instr(file,"gif") <> 0 THEN FN = FN & File & vbLF
  NEXT
  MsgBox FN

Aquí usamos el objeto FileSystem para determinar el nombre de la carpeta padre. Sin embargo, la coincidencia de la extensión de archivo se comprueba de la forma habitual, ya que el objeto Files solo ofrece en su lugar la propiedad Type. Muy a menudo, los nombres de archivo tendrán que ordenarse alfabéticamente, ya que arrastrar y soltar o SendTo los pasarán al script en orden aleatorio. Esto puede lograrse con una ordenación por burbuja:

  for i = 0 to NArgs - 1
    for j = i + 1 to NArgs - 1
      if FName(i) > FName(j) then
        Temp = FName(i)
        FName(i) = FName(j)
        FName(j) = Temp
      end if
    next
  next

[clip] A la derecha se presenta una aplicación más sofisticada del concepto esbozado arriba: un conjunto de fotogramas de vídeo se ha montado en dos «tiras de película» paralelas por medio de un VBScript e ImageMagick. La perforación da una pista visual de que la progresión de los fotogramas es de arriba abajo, es decir, columna por columna, en contraste con el patrón de lectura occidental habitual de izquierda a derecha y luego de arriba abajo. El script completo que realiza el trabajo puede descargarse del archivo strip.zip. Nota al margen: el código de tiempo en rojo de la parte superior derecha de cada fotograma se generó con un script de AVIsynth. Los fotogramas se volcaron exportándolos desde VirtualDub. Con el código de tiempo incrustado, los fotogramas no tienen por qué ser necesariamente equidistantes en el tiempo, es decir, pueden elegirse según convenga en el Explorador de Windows y enviarse al script, que se coloca en la carpeta SendTo.

Trabajar con archivos de texto

Al trabajar con scripts en un equipo cliente, la información de entrada se proporciona en general mediante arrastrar y soltar o SendTo, es decir, consta básicamente de nombres de archivo que se procesarán de una manera predefinida por el script. Cualquier información adicional tiene que proporcionarse mediante interacción del usuario en tiempo de ejecución o en forma de un archivo de texto. Básicamente, tenemos las siguientes opciones:

  • El script acepta archivos de imagen como entrada, acompañados de un archivo de texto (posiblemente opcional) que proporciona información adicional.
  • El script acepta un único archivo de texto como entrada, que enumera las imágenes a procesar así como cualquier información adicional necesaria.

En el primer caso, conviene colocar el archivo de texto opcional en el mismo directorio que las imágenes, asignándole un nombre estándar. El script puede entonces derivar el nombre del directorio padre a partir de las imágenes de entrada y abrir el archivo de texto del mismo directorio si está presente. Un ejemplo de este enfoque es la «tira de película» mencionada arriba: al principio del script probablemente definiremos algún orden estándar de los fotogramas, en función del número de imágenes pasadas al script. Pero podría haber escenarios en los que queramos desviarnos del orden estándar de los fotogramas. Así, podríamos colocar un archivo de texto llamado ordering.txt en el directorio de los fotogramas que, si está presente, controlará el orden de los fotogramas:

  strTxtFile="ordering.txt"
  PDir = fs.getParentFolderName(FName(0)) & "\"
  Wsh.CurrentDirectory = PDir

  If fs.FileExists(strTxtFile) then
    Set objFile = fs.OpenTextFile(strTxtFile, 1)
    bCtrlFile = True
    NCols = objFile.ReadLine
    objFile.close
  else
    bCtrlFile = False
  end if

[clip] Una aplicación útil del segundo concepto puede encontrarse en el mapeo en perspectiva de una imagen sobre un plano de destino, como se demuestra en el sitio web de Fred Weinhaus. Podríamos usar este concepto para «revestir» una imagen distorsionada en perspectiva sobre una versión (en su mayor parte) correcta en perspectiva, como se demuestra a la derecha: en la parte superior, la imagen de la izquierda muestra una fotografía tomada en el lugar de un accidente leve. La fotografía de la derecha se tomó en una visita posterior al lugar, desde una posición algo elevada. En la parte inferior, la fotografía del accidente (es decir, la superficie plana de la carretera) se mapea sobre la fotografía de destino por medio de una transformación de perspectiva. (El objetivo de esto es visualizar el ángulo de orientación de la débil marca de derrape que dejó el neumático delantero derecho del coche negro.) Para realizar esta tarea, el usuario tiene que elegir cuatro puntos en la imagen de origen y sus puntos de destino en la imagen correcta en perspectiva. Podríamos hacerlo a mano, determinando las coordenadas en la imagen de origen y en la de destino seleccionando los puntos en un visor de imágenes (como IrfanView), anotando sus coordenadas y proporcionándolas a una línea de comandos de Convert. Sin embargo, este tedioso trabajo puede simplificarse con el programa gratuito WinMorph, que ofrece una cómoda interfaz para hacer precisamente esto: elegir algunos puntos de origen y sus correspondientes puntos de destino de dos imágenes. Las polilíneas amarillas de las dos fotografías conectan los cuatro puntos elegidos en cada imagen. Sin embargo, el propio algoritmo de morphing no es adecuado para realizar una corrección de perspectiva. (El funcionamiento básico de este algoritmo se explica en la parte Distorts de la sección Usage del sitio web de IM. Una demostración de su utilidad para el morphing se encuentra en el script ShapeMorph de Fred Weinhaus.) WinMorph almacena su información en un archivo de texto estructurado que contiene (entre otra información) los nombres de archivo tanto del origen como del destino, así como las coordenadas de los puntos de origen y de destino. Así, podemos derivar del archivo WinMorph toda la información necesaria para la distorsión de perspectiva de IM. Puede descargar una copia de todos los archivos e imágenes implicados en el archivo wmpr.zip.

Tuberías

Hasta ahora hemos invocado las herramientas de línea de comandos de IM (como Convert, Identify, Montage) directamente mediante la función Run o Exec del objeto Shell. Sin embargo, si queremos usar las capacidades de tubería de IM, es decir, alimentar un comando con la salida del anterior, tenemos que llamar a las herramientas de línea de comandos mediante un entorno de comandos. Por ejemplo, si queremos recortar los bordes blancos de la impresión índice generada por el script anterior, el código tendría que ser:

  Dim wsh
  Set wsh = CreateObject("Wscript.Shell")
  '
  strInputFiles  = ""
  For EACH Arg IN WScript.Arguments
      strInputFiles = strInputFiles & " """ & Arg & """"
  next
 '
  IndexFile= Left(WScript.Arguments(1),InStrRev(WScript.Arguments(1),"\")) & "index.jpg"
  Command = "cmd /c montage -geometry 210x140+0+5 -tile 6x " & strInputfiles & " miff:- | magick - -trim """ & IndexFile & """"
  wsh.run command, 7, true

Aquí llamamos al procesador de comandos cmd con la opción /c para cerrar automáticamente la ventana de comandos al terminar. Con fines de depuración, también podríamos invocarlo con la opción /k, que deja la ventana de comandos abierta y nos permite leer posibles mensajes de error.

Probar y depurar VBScripts

Básicamente, usamos VBScript para construir la lista de argumentos de las herramientas de línea de comandos de IM, que luego se ejecutan por sí mismas o dentro de una ventana de DOS. Esto significa que, ante todo, debería asegurarse de que

  • la propia línea de comandos hace lo que esperamos que haga
  • la línea de comandos la construye correctamente el script.

Así que, para empezar, debería probar la propia línea de comandos dentro de una ventana de DOS. Al probar el script por primera vez, no debería ejecutar el comando de IM, sino mostrar la cadena de texto en un cuadro de mensaje mediante MsgBox(strCommand), porque si la propia línea de comandos es incorrecta, poco puede hacer cualquier herramienta de depuración. El simple cuadro de mensaje también es útil al depurar el script y nunca sentí realmente la necesidad de un depurador sofisticado. En cuanto a las pruebas en tiempo de ejecución, debería asegurarse de que

  • se puede acceder correctamente a las herramientas de línea de comandos de IM
  • el usuario ha seleccionado lo que esperaba que seleccionara, es decir, varios archivos (posiblemente de cierto tipo), un directorio, un archivo de texto, etc.

Los mensajes de error pueden mostrarse fácilmente usando MsgBox(...).


Más información

Por desgracia, no se conoce ningún tutorial (aparte de este) que cubra específicamente el uso de los comandos de ImageMagick en archivos por lotes de DOS. La página del sitio web DOS "For" Command Help tiene una mejor explicación del uso del comando "FOR". También podría interesarle echar un vistazo a la página de Batch Script de Bonzo.