Wolfram LibraryLink を使うと,ダイナミックライブラリをWolfram言語カーネルに直接ロードして,ライブラリの関数を即座にWolfram言語内部から呼び出せるようにすることができる.整数,実数,パックアレー,文字列等のCのようなデータ型だけでなく,任意のWolfram言語式も交換することができる.また,エラーを送ったり,Wolfram言語にコールバックしたりするような便利な関数もある.

ドキュメントにはWolfram Librariesの例がたくさん含まれている.これらにはWolfram言語からライブラリを呼び出す際のさまざまな側面を例示する多くの短い関数が含まれている.

例を使う

ドキュメントにはサンプルライブラリのソースが含まれている.ライブラリの構築にはCコンパイラへのアクセスが必要となる.CCompilerDriverパッケージが役立つかもしれない.

使用中のプラットフォームでデモサンプルが見付かったことを示している.

In[2]:=
Click for copyable input
Out[2]=

demo例題ライブラリから関数をロードする.

In[2]:=
Click for copyable input
Out[2]=

関数を呼び出す.

In[3]:=
Click for copyable input
Out[3]=

ソース

例題のソースはドキュメントパクレットにある.これは次の入力を評価すると見付かる.

In[3]:=
Click for copyable input
Out[3]=
demo.c基本例題のサンプル
demo_shared.c共有渡しの基本例題のサンプル
demo_error.cエラーキャッチのサンプル
demo_string.c文字列引数と結果を使ったサンプル
demo_LinkObject.c引数と結果指定にLinkObjectを使ったサンプル
demo_managed.cxxCreateManagedLibraryExpressionを使ったサンプル
demo_callback.csample of calling back a CompiledFunction from a library
demo_sparse.csample of using SparseArray
demo_image.cxxsample of using Image

ライブラリ例題ソースファイル

demo

demoの例題には多くの関数が含まれている.以下はそれを使う例である

In[5]:=
Click for copyable input
Out[6]=

demo_shared

demo_sharedの例題にはパックアレーをライブラリ関数と共有する例題が含まれている.以下で多数の関数をロードする.

In[7]:=
Click for copyable input

パックアレーを作成し,ライブラリにロードする.共有メモリ渡しが使われるため,配列は他の関数呼出しで使える.

In[10]:=
Click for copyable input
Out[11]=

10番目の要素である10を取得する.

In[12]:=
Click for copyable input
Out[12]=

配列をアンロードする.その後はこの配列は使えなくなる.

In[13]:=
Click for copyable input
Out[13]=

いくつかの関数のソースである.

DLLEXPORT int loadArray(WolframLibraryData libData,
            mint Argc, MArgument *Args, MArgument Res) {

    tensor = MArgument_getMTensor(Args[0]);
    MArgument_setInteger(Res, 0);
    return LIBRARY_NO_ERROR;
}

DLLEXPORT int getElementVector(WolframLibraryData libData,
            mint Argc, MArgument *Args, MArgument Res) {
    mint pos;
    double value;
    
    pos = MArgument_getInteger(Args[0]);
    value = libData->MTensorVector_getReal( tensor, pos);
    
    MArgument_setReal(Res, value);
    return LIBRARY_NO_ERROR;
}

DLLEXPORT int unloadArray(WolframLibraryData libData,
            mint Argc, MArgument *Args, MArgument Res) {

    libData->MTensor_disown( tensor);
    MArgument_setInteger(Res, 0);
    return LIBRARY_NO_ERROR;
}

demo_error

demo_errorの例題はエラーを引き起こす関数呼出しの例を含む.ライブラリから関数をロードする.

In[14]:=
Click for copyable input

errordemo1関数のソースである.MTensor引数を取り,実数データの取得を試みる.

DLLEXPORT int errordemo1(WolframLibraryData libData,
            mint Argc, MArgument *Args, MArgument Res) {
    MTensor T0, T1;
    mint I0, I1, res;
    mint pos[2];
    double *data;

    T0 = MArgument_getMTensor(Args[0]);
    I0 = MArgument_getInteger(Args[1]);
    
    data = libData->MTensor_getRealData(T0);
    MArgument_setReal(Res, data[I0]);
    return LIBRARY_NO_ERROR;
}

例題は整数MTensorを取る.関数はMTensor_getRealDataを呼び出すがこれがエラーとなり,LibraryFunctionError式を返す.

In[15]:=
Click for copyable input
Out[15]=

demo_string

demo_stringの例題は要求されたメモリ管理で文字列の引数と結果を使う方法を示す.ASCII文字列に対して簡単なシフト暗号化を行う関数をロードする.

In[17]:=
Click for copyable input

以下がエンコーディング関数のソースである.引数はエンコードする文字列で,整数シフトが適用される.

DLLEXPORT int reverseString( WolframLibraryData libData, MLINK mlp)
{
    int res = LIBRARY_FUNCTION_ERROR;
    long len;
    const char *inStr = NULL;
    char* outStr = NULL;
    
    if ( !MLTestHead( mlp, "List", &len))
        goto retPt;
    if ( len != 1)
        goto retPt;

    if(! MLGetString(mlp, &inStr))
        goto retPt;

    if ( ! MLNewPacket(mlp) )
        goto retPt;

    outStr = reverseStringImpl(inStr);
    
    if (!MLPutString( mlp,outStr))
        goto retPt;
    res = 0;
retPt:
    if ( inStr != NULL)
        MLReleaseString(mlp, inStr);
    if ( outStr != NULL)
        free( (void*) outStr);
    return res;
}

これが例題である.

In[18]:=
Click for copyable input
Out[18]=

文字列参照は関数外のスコープの変数に保管される.この参照は関数が再び呼ばれ(そして新しい文字列が参照され)るか,(関数WolframLibrary_uninitializeの中で)ライブラリがアンロードされるかすると解放される.文字列引数のメモリはライブラリ関数がすべて所有するので,メモリ内でインプレースエンコーディングが行われ,別の文字列への割当てを必要としない.

demo_LinkObject

demo_LinkObjectの例題は,引数と結果の指定としてLinkObjectを使う例を示す.ライブラリから関数をロードする.

In[19]:=
Click for copyable input

以下はreverseString関数のソースである.WSLINK引数を取り,Wolfram Symbolic Transfer Protocol (WSTP) APIを使ってリスト中の引数を読み取る.結果は生成されるとリンク上に書き出される.

DLLEXPORT int reverseString( WolframLibraryData libData, WLINK mlp)
{
    int res = LIBRARY_FUNCTION_ERROR;
    long len;
    const char *inStr = NULL;
    char* outStr = NULL;
    
    if ( !WSTestHead( mlp, "List", &len))
        goto retPt;
    if ( len != 1)
        goto retPt;

    if(! WSGetString(mlp, &inStr))
        goto retPt;

    if ( ! WSNewPacket(mlp) )
        goto retPt;

    outStr = reverseStringImpl(inStr);
    
    if (!WSPutString( mlp,outStr))
        goto retPt;
    res = 0;
retPt:
    if ( inStr != NULL)
        WSReleaseString(mlp, inStr);
    if ( outStr != NULL)
        free( (void*) outStr);
    return res;
}

文字列を渡す,結果がリンクから読み取られ,表示される.

In[20]:=
Click for copyable input
Out[20]=

demo_managed

demo_managedの例題は,Wolfram言語式に関連付けられたクラスやオブジェクトのインスタンスを別々に保存するために,管理されたライブラリ式をどのように使えばよいかを示すものである.

この例題の関数は,異なる状態(またはパラメータ)を同時に許可する,非常に簡単であまり不規則ではない(あるいは,特に効率的に実装された)線形合同法を実装する.

コードの重要な部分は,registerLibraryExpressionManagerunregisterLibraryExpressionManagerが使われている初期化関数と初期化解除関数にある.

/* ライブラリの初期化 */
EXTERN_C DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData)
{
    return (*libData->registerLibraryExpressionManager)("LCG", manage_instance);
}

/* ライブラリの初期化解除 */
EXTERN_C DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData)
{
    int err = (*libData->unregisterLibraryExpressionManager)("LCG");
}

ファイルdemo_managed.cxxのコードは,hashmapクラスについてのみC++を使っている.これはIDをインスタンスにマップするのに便利である.関数manage_instanceは,モードが0の場合(CreateManagedLibraryExpressionが使われている場合)はIDをhashmapに加え,モードが1の場合はMTensorを解放してhashmapからIDを消去するよう定義されている.

DLLEXPORT void manage_instance(WolframLibraryData libData, mbool mode, mint id)
{
    if (mode == 0) {
        MTensor *T = new(MTensor);
        map[id] = T;
        *T = 0;
    } else {
        MTensor *T = map[id];
        if (T != 0) {
            if (*T != 0) (*libData->MTensor_free)(*T);
            map.erase(id);
        }
    }
}

以下のようにしてライブラリをロードし(名前「LCG」を持つマネージャのレジストレーションはライブラリが最初にロードされたときに実行される),インスタンスを操作するLibraryFunctionsをいくつか定義する.

In[1]:=
Click for copyable input

次で,インスタンスのハンドルとして使われる頭部LCGを持つ式の型を設定するための定義をいくつか作成する.

In[5]:=
Click for copyable input

次で乱数生成器を定義する.

In[9]:=
Click for copyable input

以下はインスタンスを設定(パラメータは「Numerical Recipes (1992)」のものを使用),テストし,そのインスタンスから数を生成する.

In[12]:=
Click for copyable input
Out[12]=
In[13]:=
Click for copyable input
Out[13]=
In[14]:=
Click for copyable input
Out[14]=

別のインスタンスを設定し,そのインスタンスから2つの数を生成する.IDが一意的であることに注目のこと.

In[15]:=
Click for copyable input
Out[15]=
In[16]:=
Click for copyable input
Out[16]=

すべてのインスタンスを状態とともに表示する.

In[17]:=
Click for copyable input
Out[17]=

もう1つ数を生成した後,最初と2つ目のインスタンスは同じ状態になる.

In[18]:=
Click for copyable input
Out[18]=
In[19]:=
Click for copyable input
Out[19]=

次で3つ目のインスタンスを設定するが,割当てが確実にインスタンス式への唯一の参照になり,行列を生成するためにそれを使うように注意する.

In[20]:=
Click for copyable input
Out[20]=

以下で2つ目のインスタンスを解放する.

In[21]:=
Click for copyable input
Out[21]=

g2 の値の設定を解除すると,3つ目のインスタンスへの参照すべてがなくなり,manage_instance関数が自動的に呼び出され,hashmapからインスタンスを削除する.

In[22]:=
Click for copyable input
Out[22]=

ライブラリがアンロードされると,残りのインスタンスは削除されgは管理されたライブラリ式ではなくなる.

In[23]:=
Click for copyable input
Out[23]=
In[24]:=
Click for copyable input
Out[24]=

demo_callback

demo_callback例題は,Wolfram言語でライブラリからCompiledFunctionへコールバックする方法を示す.

コードで重要な部分は,registerLibraryCallbackManagerunregisterLibraryCallbackManagerが使われている初期化関数と初期化解除関数にある.

/* ライブラリの初期化 */
DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData)
{
    call_id = 0;
    call_nargs = 0;
    return (*libData->registerLibraryCallbackManager)("demo", manage_callback);
}

/* ライブラリの初期化解除 */
DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData)
{
    (*libData->unregisterLibraryCallbackManager)("demo");
}

コールバックマネージャ関数manage_callbackは,この例題では非常に単純であり,一度に一つだけの接続関数を許可する.関数が呼び出されたとき,正のID(システムによって生成されるIDはすべて正である)がすでに存在するならば,そのIDに関連付けられた関数が解放され,新しいIDが保存される.複数のIDを保存するためにもっと高度なコードを使うと,一度に複数の関数を接続することができる.

DLLEXPORT mbool manage_callback(WolframLibraryData libData, mint id, MTensor argtypes)
{
    mint i;
    mint *dims;
    mint *typerank;
    if (call_id) {
        (*libData->releaseLibraryCallbackFunction)(call_id);
        call_id = 0;
        free(cbArgs);
        free(tdata);
    }
    call_id = id;
    dims = (*libData->MTensor_getDimensions)(argtypes);
    call_nargs = dims[0] - 1;
    if (call_nargs == 0) {
        call_id = 0;
        return False;
    }
    typerank = (*libData->MTensor_getIntegerData)(argtypes);
    /* 引数と結果(i <= call_nargs ループ制御)がスカラーのmrealかどうかをチェックする */
    for (i = 0; i <= call_nargs; i++) {
        /* 各行は{type, rank} */
        if ((typerank[0] != MType_Real) || (typerank[1] != 0)) {
            call_id = 0;
            call_nargs = 0;
            return False;
        }
        typerank += 2;
    }
    cbArgs = (MArgument *) malloc((call_nargs + 1)*sizeof(MArgument));
    tdata = (mreal **) malloc(call_nargs*sizeof(mreal *));
    return True;
}

次でライブラリをロードし(名前"demo_callback_manager"を含むマネージャのレジストレーションは,ライブラリが最初にロードされたときに実行される),型mrealの配列の各要素に対するコールバック関数を呼び出すLibraryFunctionを定義する.

In[2]:=
Click for copyable input

以下は正弦関数をコンパイルし,CompiledFunctionをライブラリに接続する.

In[3]:=
Click for copyable input
Out[3]=
In[4]:=
Click for copyable input
Out[4]=

以下は乱数実数の配列にLibraryFunctionを呼び出す.

In[5]:=
Click for copyable input
Out[6]=

結果は直接Sinを評価するのと同じである.

In[7]:=
Click for copyable input
Out[7]=

コールバックを使う必要時間を比較するために,標準的なC sin()関数を直接使う関数が定義される.

In[8]:=
Click for copyable input
In[9]:=
Click for copyable input
In[10]:=
Click for copyable input
Out[10]=
In[11]:=
Click for copyable input
Out[11]=

値は等しい.

In[12]:=
Click for copyable input
Out[12]=

もちろん,Wolfram言語でSin関数を使うと,並列評価になる可能性があるので速くなる.

In[13]:=
Click for copyable input
Out[13]=

この場合,並列評価で標準のCライブラリとは異なる関数が使用されるため,わずかな差がある.

In[14]:=
Click for copyable input
Out[14]=

CompilationTarget->"C"を使って関数をCにコンパイルすると,オーバーヘッドはかなり小さくなる.

In[15]:=
Click for copyable input
In[16]:=
Click for copyable input
Out[16]=
In[17]:=
Click for copyable input
Out[17]=

このメカニズムはCompiledFunctionがWolfram言語評価子を使う必要があるときでも動作する.

In[18]:=
Click for copyable input
Out[18]=
In[19]:=
Click for copyable input
Out[19]=
In[20]:=
Click for copyable input
Out[21]=

しかし,この場合の関数は,最コンパイルなしで修正される可能性がある.

In[22]:=
Click for copyable input
Out[23]=

管理関数は,CompiledFunctionの結果と引数がすべてスカラーの実数でない場合接続を拒否する.そのためConnectLibraryCallbackFunctionFalseを返す.

In[24]:=
Click for copyable input
Out[24]=
In[25]:=
Click for copyable input
Out[25]=

コールバックを適用しようとするとエラーが出る.

In[26]:=
Click for copyable input
Out[26]=

以下のコードは複数の引数を許可する.次はロジスティック写像 を1000回繰り返す例である.

In[35]:=
Click for copyable input
Out[35]=
In[36]:=
Click for copyable input
Out[36]=

これを使うためには,2つの引数を使うLibraryFunctionオーバーロードが必要になる.

In[43]:=
Click for copyable input
In[45]:=
Click for copyable input
Out[45]=

demo_sparse

demo_sparse例題は,Wolfram言語でMSparseArrayの引数と結果をSparseArrayオブジェクトと使う方法を示す.

デモの関数sparse_propertiesにより圧縮行格納(CSR)データ構造がどのように作用するかを調べることが可能になる.次でライブラリからこの関数をロードする.

(P10.0) In[1]:=
Click for copyable input

これはsparse_properties関数のソースである.引数は希望のプロパティを見付けるための文字列とMSparseArrayであり,どのような型,階数でもよい.この関数はMSparseArray引数の読込みアクセスのみを使うので,"Constant"渡しの使用は妥当である.

DLLEXPORT int sparse_properties( WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) 
{
    int err = LIBRARY_NO_ERROR;
    char *what;
    mint *data;
    MSparseArray S;
    MTensor *T, Tres = 0;
    WolframSparseLibrary_Functions sparseFuns = libData->sparseLibraryFunctions;

    if (Argc != 2) return LIBRARY_FUNCTION_ERROR;
        
    what = MArgument_getUTF8String(Args[0]);
    S = MArgument_getMSparseArray(Args[1]);

    if (!strcmp(what, "ImplicitValue")) {
        T = (*(sparseFuns->MSparseArray_getImplicitValue))(S);
    } else if (!strcmp(what, "ExplicitValues")) {
        T = (*(sparseFuns->MSparseArray_getExplicitValues))(S);
    } else if (!strcmp(what, "RowPointers")) {
        T = (*(sparseFuns->MSparseArray_getRowPointers))(S);
    } else if (!strcmp(what, "ColumnIndices")) {
        T = (*(sparseFuns->MSparseArray_getColumnIndices))(S);
    } else if (!strcmp(what, "ExplicitPositions")) {
        err = (*(sparseFuns->MSparseArray_getExplicitPositions))(S, &Tres);
    } else if (!strcmp(what, "Normal")) {
        err = (*(sparseFuns->MSparseArray_toMTensor))(S, &Tres);
    } else {
        err = LIBRARY_FUNCTION_ERROR;
    }
    if (err) return err;
    if (!Tres) (*(libData->MTensor_clone))(*T, &Tres);

    MArgument_setMTensor(Res, Tres);
    return err;
}

ソースで特筆すべき点は,MSparseArray_getImplicitValueMSparseArray_getImplicitValueMSparseArray_getColumnIndicesMSparseArray_getRowPointersによりCSRデータに対して返されたMTensor参照はMSparseArrayに属しているため,それらを返すためには,MSparseArrayデータののMTensorsが故意でなく解放されることはないということである.

次は,さまざまな疎配列に対する特性のいくつかを示す例である.

In[32]:=
Click for copyable input
In[33]:=
Click for copyable input
Out[33]=

以下はCSRデータ構造の表を作成し,明示的な位置と通常の配列も示す.

In[34]:=
Click for copyable input
In[35]:=
Click for copyable input
Out[35]//TableForm=

暗示的な値,あるいはいずれかの値が機械精度を持つ場合,値はすべてSparseArrayからMSparseArrayへの変換の強制段階に強制される.

In[36]:=
Click for copyable input
Out[37]//TableForm=

混合型のSparseArrayにWolfram言語のNormalを使うと,厳密値が維持されるが,パックアレーではない.

In[38]:=
Click for copyable input
Out[38]=

疎ベクトルはCSRでは1行の行列として表される.

In[39]:=
Click for copyable input
Out[39]//TableForm=

階数4の配列は,要素につき3つの列指標を持つ.

In[40]:=
Click for copyable input
Out[40]//TableForm=

以下でその場でSparseArrayの値を変更することができる関数をロードする.

In[41]:=
Click for copyable input
Out[41]=

以下はsparse_modify_values関数のソースである.

DLLEXPORT int sparse_modify_values( WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res)
{
    char *what;
    int err = 0;
    mbool resparsify;
    mint i, nz;
    mreal *t, *v;
    MSparseArray S = 0, Sout = 0;
    MTensor T = 0, *Vp = 0;
    WolframSparseLibrary_Functions sparseFuns = libData->sparseLibraryFunctions;

    if (Argc != 2) return LIBRARY_FUNCTION_ERROR;

    S = MArgument_getMSparseArray(Args[0]);
    Vp = (*sparseFuns->MSparseArray_getExplicitValues)(S);
    if ((*libData->MTensor_getType)(*Vp) != MType_Real) return LIBRARY_TYPE_ERROR;
    nz = (*libData->MTensor_getFlattenedLength)(*Vp);
    v = (*libData->MTensor_getRealData)(*Vp);

    T = MArgument_getMTensor(Args[1]);
    if ((*libData->MTensor_getType)(T) != MType_Real) return LIBRARY_TYPE_ERROR;
    if ((*libData->MTensor_getFlattenedLength)(T) != nz) return LIBRARY_DIMENSION_ERROR;
    t = (*libData->MTensor_getRealData)(T);

    for (i = 0; i < nz; i++) v[i] = t[i];

    /* 明示的な位置を再計算する */
    err = (*sparseFuns->MSparseArray_resetImplicitValue)(S, NULL, &Sout);

    (*sparseFuns->MSparseArray_disown)(S);

    if (!err)
        MArgument_setMSparseArray(Res, Sout);

    return err;
}

返される疎配列は明示的な位置を再計算することで生成される.これはSparseArray[s]sSparseArray)を使った場合に起こることとほぼ同じである.値をその場で変更するために,データは,MSparseArrayにより所有されている値に対する既存のMTensorにコピーされる. カーネルのSparseArrayに影響が及ぶためには,渡すことが"Shared"である必要がある.

In[42]:=
Click for copyable input
Out[42]=
In[43]:=
Click for copyable input
Out[43]=

新しい値の一つが0だったので,その位置は明示的に表現される必要はなく, の構造はもっとコンパクトになる.

In[44]:=
Click for copyable input
Out[44]//TableForm=
In[45]:=
Click for copyable input
Out[45]//TableForm=

demo_image

demo_imageの例題はWolfram言語のImageImage3Dで,MImageの引数と結果を使う方法を示す.

デモの関数color_negateは,すべての色が反転した負のImageまたはImage3Dオブジェクトを取得する方法を示す.ライブラリからこの関数をロードする.

In[6]:=
Click for copyable input

以下がcolor_negate関数のソースである.引数は反転されるMImageである.

このコードは画像に存在するすべての画素を調べ,それらを反転する.これが最速の実装である.しかし,アルファチャンネルも反転されるため,アルファチャンネルのある画像を正しく反転させることはできない.


template <typename T> static T maxValue() {
return -1; // ERROR
}

template <> char maxValue<char>() { return 1; }

template <> raw_t_ubit8 maxValue<raw_t_ubit8>() { return 255; }

template <> raw_t_ubit16 maxValue<raw_t_ubit16>() { return 65535; }

template <> raw_t_real32 maxValue<raw_t_real32>() { return 1.0f; }

template <> raw_t_real64 maxValue<raw_t_real64>() { return 1.0; }

template <typename T>
static void icolor_negate(void *out0, const void *in0, mint length) {
mint ii;
T *out = reinterpret_cast<T *>(out0);
const T *in = reinterpret_cast<const T *>(in0);
for (ii = 0; ii < length; ii++) {
out[ii] = maxValue<T>() - in[ii];
}
}

/* 画像の色の反転 */
EXTERN_C DLLEXPORT int color_negate(WolframLibraryData libData, mint Argc,
MArgument *Args, MArgument res) {
mint length;
MImage image_in, image_out = 0;
void *data_in, *data_out;
int err = LIBRARY_FUNCTION_ERROR;
imagedata_t type;
WolframImageLibrary_Functions imgFuns = libData->imageLibraryFunctions;

if (Argc < 1) {
return err;
}

image_in = MArgument_getMImage(Args[0]);

err = imgFuns->MImage_clone(image_in, &image_out);
if (err)
return err;

type = imgFuns->MImage_getDataType(image_in);
length = imgFuns->MImage_getFlattenedLength(image_in);

data_in = imgFuns->MImage_getRawData(image_in);
data_out = imgFuns->MImage_getRawData(image_out);
if (data_in == NULL || data_out == NULL)
goto cleanup;

switch (type) {
case MImage_Type_Bit:
icolor_negate<char>(data_out, data_in, length);
break;
case MImage_Type_Bit8:
icolor_negate<raw_t_ubit8>(data_out, data_in, length);
break;
case MImage_Type_Bit16:
icolor_negate<raw_t_ubit16>(data_out, data_in, length);
break;
case MImage_Type_Real32:
icolor_negate<raw_t_real32>(data_out, data_in, length);
break;
case MImage_Type_Real:
icolor_negate<raw_t_real64>(data_out, data_in, length);
break;
default:
goto cleanup;
}

MArgument_setMImage(res, image_out);
return err;

cleanup:
imgFuns->MImage_free(image_out);
return err;
}

以下は,画像の色を反転させる方法を示した例題である.

In[7]:=
Click for copyable input
Out[7]=
In[8]:=
Click for copyable input
Out[8]=

次の例題は,RGB画像をグレースケールに変換する方法を示す.関数rgb_to_grayをロードする.

In[13]:=
Click for copyable input
Out[13]=

以下がrgb_to_gray関数のソースである.

template <typename T>
static void irgb_to_gray(void *out0, const void *in0, mint rows, mint cols,
mbool alphaQ) {
mint row;
mint col;
T r, g, b;

T *out = reinterpret_cast<T *>(out0);
const T *in = reinterpret_cast<const T *>(in0);

if (alphaQ) {
for (row = 0; row < rows; row++) {
for (col = 0; col < cols; col++) {
mint idx = row * cols + col;
r = in[4 * idx];
g = in[4 * idx + 1];
b = in[4 * idx + 2];
out[2 * idx] = (T)(.299 * r + .587 * g + .114 * b);
out[2 * idx + 1] = in[4 * idx + 3];
}
}
} else {
for (row = 0; row < rows; row++) {
for (col = 0; col < cols; col++) {
mint idx = row * cols + col;
r = in[3 * idx];
g = in[3 * idx + 1];
b = in[3 * idx + 2];
out[idx] = (T)(.299 * r + .587 * g + .114 * b);
}
}
}
}

/* RGB画像からグレースケールへの変換 */
EXTERN_C DLLEXPORT int rgb_to_gray(WolframLibraryData libData, mint Argc,
MArgument *Args, MArgument res) {
mint rows, columns;
mbool alphaQ;
int err = 0;
imagedata_t type;
MImage image_in, image_out;
void *data_in, *data_out;
WolframImageLibrary_Functions imgFuns = libData->imageLibraryFunctions;

if (Argc < 1) {
return LIBRARY_FUNCTION_ERROR;
}

image_in = MArgument_getMImage(Args[0]);
if (imgFuns->MImage_getColorSpace(image_in) != MImage_CS_RGB)
return LIBRARY_FUNCTION_ERROR;

/*This function accepts only 2D images, but can be easily extended to work
* with Image3D.*/
if (imgFuns->MImage_getRank(image_in) == 3)
return LIBRARY_FUNCTION_ERROR;

type = imgFuns->MImage_getDataType(image_in);
rows = imgFuns->MImage_getRowCount(image_in);
columns = imgFuns->MImage_getColumnCount(image_in);
alphaQ = imgFuns->MImage_alphaChannelQ(image_in);

err = imgFuns->MImage_new2D(columns, rows, alphaQ ? 2 : 1, type,
MImage_CS_Gray, True, &image_out);
if (err)
return LIBRARY_FUNCTION_ERROR;

data_in = imgFuns->MImage_getRawData(image_in);
data_out = imgFuns->MImage_getRawData(image_out);
if (data_in == NULL || data_out == NULL)
return LIBRARY_FUNCTION_ERROR;

switch (type) {
case MImage_Type_Bit:
// RGB binary images are not allowed
imgFuns->MImage_free(image_out);
return LIBRARY_FUNCTION_ERROR;
case MImage_Type_Bit8:
irgb_to_gray<raw_t_ubit8>(data_out, data_in, rows, columns, alphaQ);
break;
case MImage_Type_Bit16:
irgb_to_gray<raw_t_ubit16>(data_out, data_in, rows, columns, alphaQ);
break;
case MImage_Type_Real32:
irgb_to_gray<raw_t_real32>(data_out, data_in, rows, columns, alphaQ);
break;
case MImage_Type_Real:
irgb_to_gray<raw_t_real64>(data_out, data_in, rows, columns, alphaQ);
break;
default:
imgFuns->MImage_free(image_out);
return LIBRARY_FUNCTION_ERROR;
}
MArgument_setMImage(res, image_out);
return LIBRARY_NO_ERROR;
}

この関数は2D画像のみを受け付けるが,Image3Dが使えるように簡単に拡張することができる.

In[28]:=
Click for copyable input
Out[28]=

MImage_getByteMImage_setByte等のヘルパー関数もCの指標計算を避けるために使用することができる.これらの関数はWolfram言語の部分と同じ番号付け方法を使っている.つまり,最初の要素は1となるのである.