⚠️ 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://imagemagick.org/magick-core/).

MagickCore (API C de bajo nivel)

La API MagickCore es una interfaz de bajo nivel entre el lenguaje de programación C y las bibliotecas de procesamiento de imágenes de ImageMagick, y se recomienda únicamente para programadores de nivel experto. A diferencia de la API C de MagickWand, que usa solo unos pocos tipos opacos y accesores, con MagickCore se accede casi exclusivamente a los miembros de las estructuras de forma directa. La descripción de los métodos públicos de MagickCore se encuentra en GitHub:

Después de escribir tu programa MagickCore, compílalo así:

cc `MagickCore-config --cflags --cppflags` -O2 -o core core.c `MagickCore-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 MagickCore, core.c. Lee una imagen GIF, crea una miniatura y la escribe en disco en el formato de imagen PNG.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <MagickCore/MagickCore.h>

int main(int argc,char **argv)
{
  ExceptionInfo
    *exception;

  Image
    *image,
    *images,
    *resize_image,
    *thumbnails;

  ImageInfo
    *image_info;

  if (argc != 3)
    {
      (void) fprintf(stdout,"Usage: %s image thumbnail\n",argv[0]);
      exit(0);
    }
  /*
    Initialize the image info structure and read an image.
  */
  MagickCoreGenesis(*argv,MagickTrue);
  exception=AcquireExceptionInfo();
  image_info=CloneImageInfo((ImageInfo *) NULL);
  (void) strcpy(image_info->filename,argv[1]);
  images=ReadImage(image_info,exception);
  if (exception->severity != UndefinedException)
    CatchException(exception);
  if (images == (Image *) NULL)
    exit(1);
  /*
    Convert the image to a thumbnail.
  */
  thumbnails=NewImageList();
  while ((image=RemoveFirstImageFromList(&images)) != (Image *) NULL)
  {
    resize_image=ResizeImage(image,106,80,LanczosFilter,exception);
    if (resize_image == (Image *) NULL)
      MagickError(exception->severity,exception->reason,exception->description);
    (void) AppendImageToList(&thumbnails,resize_image);
    DestroyImage(image);
  }
  /*
    Write the image thumbnail.
  */
  (void) strcpy(thumbnails->filename,argv[2]);
  WriteImage(image_info,thumbnails,exception);
  /*
    Destroy the image thumbnail and exit.
  */
  thumbnails=DestroyImageList(thumbnails);
  image_info=DestroyImageInfo(image_info);
  exception=DestroyExceptionInfo(exception);
  MagickCoreTerminus();
  return(0);
}

Ahora realicemos el mismo realce de contraste aprovechando nuestro sistema de procesamiento de doble o cuádruple núcleo, ejecutando el algoritmo en paralelo mediante vistas de wand. El módulo sigmoidal-contrast.c lee una imagen, aplica un control de contraste no lineal sigmoidal y escribe el resultado en disco igual que el programa anterior de realce de contraste, pero ahora lo hace en paralelo (asume que ImageMagick está compilado con soporte de OpenMP).

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <MagickCore/MagickCore.h>

static MagickBooleanType SigmoidalContrast(ImageView *contrast_view,
  const ssize_t y,const int id,void *context)
{
#define QuantumScale  ((MagickRealType) 1.0/(MagickRealType) QuantumRange)
#define SigmoidalContrast(x) \
  (QuantumRange*(1.0/(1+exp(10.0*(0.5-QuantumScale*x)))-0.0066928509)*1.0092503)

  RectangleInfo
    extent;

  register IndexPacket
    *indexes;

  register PixelPacket
    *pixels;

  register ssize_t
    x;

  extent=GetImageViewExtent(contrast_view);
  pixels=GetImageViewAuthenticPixels(contrast_view);
  for (x=0; x < (ssize_t) (extent.width-extent.x); x++)
  {
    SetPixelRed(pixels,RoundToQuantum(SigmoidalContrast(GetPixelRed(pixels)));
    SetPixelGreen(pixels,RoundToQuantum(SigmoidalContrast(GetPixelGreen(pixels)));
    SetPixelBlue(pixels,RoundToQuantum(SigmoidalContrast(GetPixelBlue(pixels)));
    SetPixelOpacity(pixels,RoundToQuantum(SigmoidalContrast(GetPixelOpacity(pixels)));
    pixels++;
  }
  indexes=GetImageViewAuthenticIndexes(contrast_view);
  if (indexes != (IndexPacket *) NULL)
    for (x=0; x < (ssize_t) (extent.width-extent.x); x++)
      SetPixelIndex(indexes+x,RoundToQuantum(SigmoidalContrast(GetPixelIndex(indexes+x))));
  return(MagickTrue);
}

int main(int argc,char **argv)
{
#define ThrowImageException(image) \
{ \
 \
  CatchException(exception); \
  if (contrast_image != (Image *) NULL) \
    contrast_image=DestroyImage(contrast_image); \
  exit(-1); \
}
#define ThrowViewException(view) \
{ \
  char \
    *description; \
 \
  ExceptionType \
    severity; \
 \
  description=GetImageViewException(view,&severity); \
  (void) fprintf(stderr,"%s %s %lu %s\n",GetMagickModule(),description); \
  description=DestroyString(description); \
  exit(-1); \
}

  ExceptionInfo
    *exception;

  Image
    *contrast_image;

  ImageInfo
    *image_info;

  ImageView
    *contrast_view;

  MagickBooleanType
    status;

  if (argc != 3)
    {
      (void) fprintf(stdout,"Usage: %s image sigmoidal-image\n",argv[0]);
      exit(0);
    }
  /*
    Read an image.
  */
  MagickCoreGenesis(*argv,MagickTrue);
  image_info=AcquireImageInfo();
  (void) CopyMagickString(image_info->filename,argv[1],MaxTextExtent);
  exception=AcquireExceptionInfo();
  contrast_image=ReadImage(image_info,exception);
  if (contrast_image == (Image *) NULL)
    ThrowImageException(contrast_image);
  /*
    Sigmoidal non-linearity contrast control.
  */
  contrast_view=NewImageView(contrast_image);
  if (contrast_view == (ImageView *) NULL)
    ThrowImageException(contrast_image);
  status=UpdateImageViewIterator(contrast_view,SigmoidalContrast,(void *) NULL);
  if (status == MagickFalse)
    ThrowImageException(contrast_image);
  contrast_view=DestroyImageView(contrast_view);
  /*
    Write the image then destroy it.
  */
  status=WriteImages(image_info,contrast_image,argv[2],exception);
  if (status == MagickFalse)
    ThrowImageException(contrast_image);
  contrast_image=DestroyImage(contrast_image);
  exception=DestroyExceptionInfo(exception);
  image_info=DestroyImageInfo(image_info);
  MagickCoreTerminus();
  return(0);
}

MagickCoreTerminus() 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 únicamente después de que hayan terminado todos los hilos que estén usando funciones de ImageMagick.

ImageMagick podría usar hilos internamente mediante OpenMP (un método de programación paralela). Por ello, es importante asegurarse de que todas las llamadas a funciones de ImageMagick hayan finalizado antes de llamar a MagickCoreTerminus(). 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 mediante sus propios métodos. Esto significa que, tras llamar a MagickCoreTerminus(), algunos recursos de OpenMP y algunos hilos de trabajo podrían seguir asignados. Para abordar esto, 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 MagickCoreTerminus() haya completado su ejecución.