MagickWand (API de C)
La API de MagickWand es la interfaz recomendada entre el lenguaje de programación C y las bibliotecas de procesamiento de imágenes de ImageMagick. A diferencia de la API de C MagickCore, MagickWand usa solo unos pocos tipos opacos. Hay accesores disponibles para establecer u obtener propiedades importantes del wand. La descripción de los métodos públicos de MagickWand se encuentra en GitHub:
- Métodos de Magick Wand
- Establecer u obtener propiedades de Magick Wand
- Métodos de imagen de Magick Wand
- Métodos de Pixel Iterator
- Métodos de Pixel Wand
- Dibujo vectorial de imágenes
- Interfaz de línea de comandos
- Métodos de Wand View
- Métodos obsoletos
- Códigos de error y advertencia
Una vez que hayas escrito tu programa MagickWand, compílalo así:
cc `MagickWand-config --cflags --cppflags` -O2 -o wand wand.c `MagickWand-config --ldflags --libs`
Establece la variable de entorno PKG_CONFIG_PATH si ImageMagick no está en la ruta predeterminada de tu sistema:
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
Para empezar, aquí tienes un programa de ejemplo que utiliza la API de MagickWand, wand.c. Lee una imagen, crea una miniatura y escribe el resultado en disco.
#include <stdio.h>
#include <stdlib.h>
#include <MagickWand/MagickWand.h>
int main(int argc,char **argv)
{
#define ThrowWandException(wand) \
{ \
char \
*description; \
\
ExceptionType \
severity; \
\
description=MagickGetException(wand,&severity); \
(void) fprintf(stderr,"%s %s %lu %s\n",GetMagickModule(),description); \
description=(char *) MagickRelinquishMemory(description); \
exit(-1); \
}
MagickBooleanType
status;
MagickWand
*magick_wand;
if (argc != 3)
{
(void) fprintf(stdout,"Usage: %s image thumbnail\n",argv[0]);
exit(0);
}
/*
Read an image.
*/
MagickWandGenesis();
magick_wand=NewMagickWand();
status=MagickReadImage(magick_wand,argv[1]);
if (status == MagickFalse)
ThrowWandException(magick_wand);
/*
Turn the images into a thumbnail sequence.
*/
MagickResetIterator(magick_wand);
while (MagickNextImage(magick_wand) != MagickFalse)
MagickResizeImage(magick_wand,106,80,LanczosFilter,1.0);
/*
Write the image then destroy it.
*/
status=MagickWriteImages(magick_wand,argv[2],MagickTrue);
if (status == MagickFalse)
ThrowWandException(magick_wand);
magick_wand=DestroyMagickWand(magick_wand);
MagickWandTerminus();
return(0);
}
Aquí tienes otro programa que muestra una forma de obtener y establecer los píxeles de una imagen con la API de MagickWand, contrast.c. Lee una imagen, aplica un control de contraste de no linealidad sigmoidal y escribe el resultado en disco.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <MagickWand/MagickWand.h>
int main(int argc,char **argv)
{
#define SigmoidalContrast(x) \
(QuantumRange*(1.0/(1+exp(10.0*(0.5-QuantumScale*x)))-0.0066928509)*1.0092503)
#define ThrowWandException(wand) \
{ \
char \
*description; \
\
ExceptionType \
severity; \
\
description=MagickGetException(wand,&severity); \
(void) fprintf(stderr,"%s %s %lu %s\n",GetMagickModule(),description); \
description=(char *) MagickRelinquishMemory(description); \
exit(-1); \
}
long
y;
MagickBooleanType
status;
MagickWand
*contrast_wand,
*image_wand;
PixelInfo
pixel;
PixelIterator
*contrast_iterator,
*iterator;
PixelWand
**contrast_pixels,
**pixels;
register long
x;
unsigned long
width;
if (argc != 3)
{
(void) fprintf(stdout,"Usage: %s image sigmoidal-image\n",argv[0]);
exit(0);
}
/*
Read an image.
*/
MagickWandGenesis();
image_wand=NewMagickWand();
status=MagickReadImage(image_wand,argv[1]);
if (status == MagickFalse)
ThrowWandException(image_wand);
contrast_wand=CloneMagickWand(image_wand);
/*
Sigmoidal non-linearity contrast control.
*/
iterator=NewPixelIterator(image_wand);
contrast_iterator=NewPixelIterator(contrast_wand);
if ((iterator == (PixelIterator *) NULL) ||
(contrast_iterator == (PixelIterator *) NULL))
ThrowWandException(image_wand);
for (y=0; y < (long) MagickGetImageHeight(image_wand); y++)
{
pixels=PixelGetNextIteratorRow(iterator,&width);
contrast_pixels=PixelGetNextIteratorRow(contrast_iterator,&width);
if ((pixels == (PixelWand **) NULL) ||
(contrast_pixels == (PixelWand **) NULL))
break;
for (x=0; x < (long) width; x++)
{
PixelGetMagickColor(pixels[x],&pixel);
pixel.red=SigmoidalContrast(pixel.red);
pixel.green=SigmoidalContrast(pixel.green);
pixel.blue=SigmoidalContrast(pixel.blue);
pixel.index=SigmoidalContrast(pixel.index);
PixelSetPixelColor(contrast_pixels[x],&pixel);
}
(void) PixelSyncIterator(contrast_iterator);
}
if (y < (long) MagickGetImageHeight(image_wand))
ThrowWandException(image_wand);
contrast_iterator=DestroyPixelIterator(contrast_iterator);
iterator=DestroyPixelIterator(iterator);
image_wand=DestroyMagickWand(image_wand);
/*
Write the image then destroy it.
*/
status=MagickWriteImages(contrast_wand,argv[2],MagickTrue);
if (status == MagickFalse)
ThrowWandException(image_wand);
contrast_wand=DestroyMagickWand(contrast_wand);
MagickWandTerminus();
return(0);
}
Ahora vamos a realizar la misma mejora de contraste aprovechando nuestro sistema de procesamiento de doble o cuádruple núcleo ejecutando el algoritmo en paralelo mediante las wand views. El módulo sigmoidal-contrast.c lee una imagen, aplica un control de contraste de no linealidad sigmoidal y escribe el resultado en disco igual que el programa de mejora de contraste anterior, pero ahora hace su trabajo en paralelo (asume que ImageMagick está compilado con soporte de OpenMP).
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <MagickWand/MagickWand.h>
static MagickBooleanType SigmoidalContrast(WandView *contrast_view,
const ssize_t y,const int thread_id,void *context)
{
#define SigmoidalContrast(x) \
(QuantumRange*(1.0/(1+exp(10.0*(0.5-QuantumScale*x)))-0.0066928509)*1.0092503)
PixelInfo
pixel;
PixelWand
**pixels;
RectangleInfo
extent;
register ssize_t
x;
extent=GetWandViewExtent(contrast_view);
pixels=GetWandViewPixels(contrast_view);
for (x=0; x < (ssize_t) extent.width; x++)
{
PixelGetMagickColor(pixels[x],&pixel);
pixel.red=SigmoidalContrast(pixel.red);
pixel.green=SigmoidalContrast(pixel.green);
pixel.blue=SigmoidalContrast(pixel.blue);
pixel.index=SigmoidalContrast(pixel.index);
PixelSetPixelColor(pixels[x],&pixel);
}
return(MagickTrue);
}
int main(int argc,char **argv)
{
#define ThrowWandException(wand) \
{ \
char \
*description; \
\
ExceptionType \
severity; \
\
description=MagickGetException(wand,&severity); \
(void) fprintf(stderr,"%s %s %lu %s\n",GetMagickModule(),description); \
description=(char *) MagickRelinquishMemory(description); \
exit(-1); \
}
MagickBooleanType
status;
MagickWand
*contrast_wand;
PixelInfo
pixel;
WandView
*contrast_view;
if (argc != 3)
{
(void) fprintf(stdout,"Usage: %s image sigmoidal-image\n",argv[0]);
exit(0);
}
/*
Read an image.
*/
MagickWandGenesis();
contrast_wand=NewMagickWand();
status=MagickReadImage(contrast_wand,argv[1]);
if (status == MagickFalse)
ThrowWandException(contrast_wand);
/*
Sigmoidal non-linearity contrast control.
*/
contrast_view=NewWandView(contrast_wand);
if (contrast_view == (WandView *) NULL)
ThrowWandException(contrast_wand);
status=UpdateWandViewIterator(contrast_view,SigmoidalContrast,(void *) NULL);
if (status == MagickFalse)
ThrowWandException(contrast_wand);
contrast_view=DestroyWandView(contrast_view);
/*
Write the image then destroy it.
*/
status=MagickWriteImages(contrast_wand,argv[2],MagickTrue);
if (status == MagickFalse)
ThrowWandException(contrast_wand);
contrast_wand=DestroyMagickWand(contrast_wand);
MagickWandTerminus();
return(0);
}
MagickWandTerminus() es una función de la biblioteca ImageMagick que se usa para limpiar y liberar recursos al cerrar una aplicación que utiliza ImageMagick. Esta función debe llamarse en el hilo principal del proceso de la aplicación durante el proceso de cierre. Es crucial que esta función se invoque solo después de que hayan terminado todos los hilos que estén usando funciones de ImageMagick.
ImageMagick podría usar internamente hilos mediante OpenMP (un método de programación en paralelo). Por ello, es importante asegurarse de que todas las llamadas a funciones de ImageMagick hayan finalizado antes de llamar a MagickWandTerminus(). Esto evita problemas con los hilos de trabajo de OpenMP que accedan a recursos destruidos por esta función de terminación.
Si se está usando OpenMP (a partir de la versión 5.0), la propia implementación de OpenMP se encarga de iniciar y detener los hilos de trabajo y de asignar y liberar recursos con sus propios métodos. Esto significa que, después de llamar a MagickWandTerminus(), algunos recursos e hilos de trabajo de OpenMP podrían seguir asignados. Para solucionarlo, se puede invocar la función omp_pause_resource_all(omp_pause_hard). Esta función, introducida en la versión 5.0 de OpenMP, garantiza que se liberen todos los recursos asignados por OpenMP (como hilos y memoria específica de hilo). Se recomienda llamar a esta función después de que MagickWandTerminus() haya completado su ejecución.
Ejemplos de MagickWand en C ilustra cómo usar la API de MagickWand de ImageMagick. Cada ejemplo se presenta como una función de C, completa con sus cabeceras, de modo que pueda copiarse a un archivo e incluirse en tu propio proyecto en C.