⚠️ これは 非公式の翻訳サイトです。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);
}

では、ウォンドビューを使ってアルゴリズムを並列実行し、デュアル・クアッドコア処理システムを活用しながら同じコントラスト強調を行います。sigmoidal-contrast.c モジュールは、画像を読み込み、シグモイド非線形コントラスト制御を適用し、前のコントラスト強調プログラムと同様に結果をディスクに書き出しますが、今度は並列に動作します(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() の実行完了後にこの関数を呼び出すことを推奨します。