⚠️ 这是一个非官方翻译网站,与 ImageMagick Studio LLC 无关。准确信息请参阅原文(https://imagemagick.org/magick-core/)

MagickCore(底层 C API)

MagickCore API 是 C 编程语言与 ImageMagick 图像处理库之间的底层接口,仅推荐给巫师级别的程序员使用。与仅使用少数不透明类型和访问器的 MagickWand C API 不同,使用 MagickCore 时你几乎完全是直接访问结构体成员。MagickCore 公开方法的说明可在 GitHub 上找到:

写好 MagickCore 程序后,按如下方式编译:

cc `MagickCore-config --cflags --cppflags` -O2 -o core core.c `MagickCore-config --ldflags --libs`

如果 ImageMagick 不在默认的系统路径中,请设置 PKG_CONFIG_PATH 环境变量:

export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig

下面是一个使用 MagickCore API 的示例程序 core.c,可帮助你入门。它读取一张 GIF 图像,创建缩略图,并以 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);
}

现在,我们利用 wand 视图并行运行算法,从而在执行相同的对比度增强时充分发挥双核或四核处理系统的优势。sigmoidal-contrast.c 模块读取一张图像,应用 S 形(sigmoidal)非线性对比度控制,并像之前的对比度增强程序一样将结果写入磁盘,但这次是并行完成(前提是 ImageMagick 构建时启用了 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() 是 ImageMagick 库中的一个函数,用于在关闭使用 ImageMagick 的应用程序时清理并释放资源。该函数应在关闭过程中由应用程序进程的主线程调用。至关重要的是,只有在所有使用 ImageMagick 函数的线程都已终止之后,才能调用该函数。

ImageMagick 可能会在内部通过 OpenMP(一种并行编程方法)使用线程。因此,在调用 MagickCoreTerminus() 之前,确保对 ImageMagick 的所有函数调用都已完成非常重要。这可以防止 OpenMP 工作线程访问被该终止函数销毁的资源所引发的问题。

如果使用了 OpenMP(自 5.0 版本起),OpenMP 实现本身会使用自己的方法来处理工作线程的启动与停止,以及资源的分配与释放。这意味着在调用 MagickCoreTerminus() 之后,某些 OpenMP 资源和工作线程可能仍处于已分配状态。为解决这一问题,可以调用函数 omp_pause_resource_all(omp_pause_hard)。这个在 OpenMP 5.0 版本中引入的函数可确保 OpenMP 分配的资源(如线程和线程专属内存)得到释放。建议在 MagickCoreTerminus() 执行完成后调用此函数。