< 目次

ヒストスプライン手法を使用した画像スケーリング

目的

ヒストスプライン手法を使用してカラーまたはグレースケール画像を再スケーリングします。

ソリューション

ヒストスプライン計算の画像スケーリングおよびスプライン補間に oneMKL のデータ・フィッティング関数を使用します。

[Bosner14] に記述されているように、1 次元ヒストスプラインは 2 次元画像スケーリングの問題に適用できます。2 次元 (サーフェス) 補間および近似に対する 1 次元補間プリミティブのアプリケーションは、[Zavyalov80] に記述されています。

多くの 1 次元ヒストポレーション問題に縮小できる 2 次元問題として、サイズ n1*m1 ピクセル (ここで、n1 は行数で m1 は列数) の入力画像を n2*m2 ピクセルの画像にスケーリングする問題について考えます。

入力画像の各色平面 c で、以下の操作を行います。

  1. 入力画像の各行で、以下の操作を行います。

    1. 現在の行の各ピクセルで、累計のセル平均 (または色の値) を計算して、配列 VX の現在の行に格納します。

    2. 配列を補間された関数値、[0; m1] に一様に分布された (m1+1) ブレークポイント x_breaks[] として考えます。自然 3 次スプラインを使用してヒストスプラインを構築し、その微分を計算して、配列 VXR の現在の行、[0; m1] に一様に分布された (m2+1) サイト x_sites[]i=0..m2 に格納します。

  2. 配列 VXR を配列 VXRT に転置します。

  3. VX と同様に、VY の列ごとに同じ操作を行って配列 VYR の結果を取得します。

    1. ステップ 1.a と同様に、計算した VXRT の値の累計として配列 VY を計算します。

      このステップは、VXR の転置と同時に実行できるため、結果を VXRT に格納する必要はありません。

    2. VYR 配列に微分を格納します。

  4. VYR から VR に転置して整数結果を取得し、出力画像の色平面 c に格納します。

    VR を格納する必要はありません。また、最後の行および列は破棄されます。

ソースコード: サンプル (https://www.intel.com/content/dam/develop/external/us/en/documents/mkl-cookbook-samples-120115.zip (英語)) の image_scaling フォルダーを参照してください。

oneMKL のデータ・フィッティング関数を使用した画像スケーリング

for( c=0; c<nc; c++ ) { 
    /* 1) PIXELS 行列 n1*m1 から --> VX 行列 n1*(m1+1) へ */ 
    for( y1=0; y1<n1; y1++ ) { 
        /* 1.a) ピクセル強度の累積合計として VX を取得 */ 
        for( x1=0, s=0; x1<=m1; x1++ ) { 
            VX[y1*(m1+1)+x1]=(fptype)s; 
            s+=row_pointers1[y1][x1*nc+c]; 
        } 
        Vx[y1*(m1+1)+x1]=(fptype)s; 
    } 
    for( y1=0; y1<n1; y1++ ) { 
        /* 1.b) 派生商品のみ取得 */ 
        interpolate_der(m1+1,x_breaks, 1,&VX[y1*(m1+1)+0], m2+1,x_sites, &VXR[y1*(m2+1)+0]); 
    } 
    /* 2) VXR を VXRT に転置する必要はありません (スキップできます) */ 
    /* 3) */ 
    for( x2=0; x2<=m2; x2++ ) { 
        /* 3.a) VXR を転置し、計算された値の累積合計として VY を取得 */ 
        for( y1=0,fs=0.0; y1<=n1; y1++ ) { 
            VY[x2*(n1+1)+y1]=fs; fs+=VXR[y1*(m2+1)+x2]; 
        } 
        Vy[x2*(n1+1)+y1]=fs; 
    } 
    /* 3.b) 派生商品のみ取得 */ 
    for( x2=0; x2<=m2; x2++ ) { 
        interpolate_der(n1+1,y_breaks, 1,&VY[x2*(n1+1)+0], n2+1,y_sites, &VYR[x2*(n2+1)+0]); 
    } 
    /* 4) VYR を VR に転置して整数の結果を取得します (保存に VR は必要ありません) */ 
    for( y2=0; y2<n2; y2++ ) { /* 最後の行は使用しません */ 
        for( x2=0; x2<m2; x2++ ) { /* 最後の列は使用しません */ 
            fptype v; 
            int i; 
            v=VYR[x2*(n2+1)+y2]; 
            /* 次回の整数への変換時に最も近い値に丸めるため 0.5 を加算 */ 
            v = v + 0.5f; i = (int)v; 
            /* 飽和 */ 
            if(i<0) i=0; 
            if(i>255) i=255; 
            /* 整数に変換し、出力画像ピクセルのカラープレーン c に保存 */ 
            row_pointers2[y2][x2*nc+c]=(png_byte) i; 
        } 
    } 
}

oneMKL のデータ・フィッティング関数を使用したスプライン計算

interpolate_der 関数はスプライン補間を使用してヒストスプラインを計算します。

oneMKL のデータ・フィッティング・ルーチンを使用して nx ブレークポイント x[] で関数値 f[] の指定された ny = 1 行について自由端境界条件で自然 3 次スプラインを計算した後、nsite サイト xx[] で微分を計算し、関数微分の nsite 値の行を r[] に出力します。

void interpolate_der(int nx, fptype* x, int ny, fptype* f, int nsite, fptype* xx, fptype* r) { 
    /* ... */ 
    scoeff=(fptype*)mkl_malloc(sizeof(fptype)*SPLORDER*ny*(nx-1),64); 
    if(scoeff==NULL) { 
        printf("Error: not enough memory for scoeff.\n"); 
        exit(-1); 
    } 

    errorCode = NewTask1D(&interpTask, nx,x,xhint, ny,f, yhint); 
    if(errorCode)printf("NewTask1D errorCode=%d\n",errorCode); 

    errorCode = EditPPSpline1D(interpTask, 
        SPLORDER, 
        SPLTYPE, 
        DF_BC_FREE_END, NULL, /* 自由端境界条件 */ 
        DF_NO_IC, NULL, /* 内部条件はありません */ 
        scoeff, DF_NO_HINT); 
    if(errorCode)printf("EditPPSpline1D errorCode=%d\n",errorCode); 

    errorCode = Construct1D(interpTask, 0, 0); 
    if(errorCode)printf("Construct1D errorCode=%d\n",errorCode); 

    errorCode = Interpolate1D(interpTask, DF_INTERP, ny, nsite,xx, siteHint, der_orders_sz,der_orders, datahint,r,rhint, nxx_cell_indexes); 
    if(errorCode)printf("Interpolate1D errorCode=%d\n",errorCode); 

    errorCode = dfDeleteTask(&interpTask); 
    if(errorCode)printf("dfDeleteTask errorCode=%d\n",errorCode); 

    mkl_free(scoeff); 
}

説明

サンプルは入力画像の各行 (および中間画像の各行) についてループの interpolate_der 関数を呼び出すため、interpolate_der の ny パラメーターは 1 で、単一の補間関数 (または画像行) を表します。しかし、データ・フィッティング・ルーチンは ny > 1 をサポートしています。これは、interpolate_der の呼び出しのループを、処理する画像の行の総数を設定する ny パラメーターを含む 1 つの呼び出しに置換して、サンプルを書き直すことができることを意味します。

画像が十分大きくなると、データ・フィッティング構築および補間ルーチンの内部で自動並列化が行われます。

ny = 1 の場合、#pragma omp parallel for を使用して interpolate_der の呼び出しのループを並列化してもかまいませんが、データ・フィッティング・ルーチン内部の自動並列化を利用するほうが良いでしょう。理論上は、各色平面のアルゴリズムはほかの色平面から独立しているため、このプラグマを c ループに追加することもできます。しかし、異なるスレッドがメモリーの同じライン内の 1 バイトのフラグメント (同じピクセルの RGB コンポーネント) にアクセスできるため、パフォーマンスが低下する可能性があります。また、小さな画像では相対的な並列化オーバーヘッドが大きくなります。パフォーマンスの低下を回避する 1 つの方法は、画像データをブロックに分割することです。どんな場合でも、並列化は大量のデータを扱う場合にのみ効果があります。

また、ベクトル化に #pragma simd を使用する場合は注意が必要です。

外部ライブラリーは画像ファイルのロードと保存用のプリミティブを供給できます。サンプルコードは、Linux* および OS X* では libpng を、Windows® では Microsoft® Foundation Class (MFC) ライブラリーの CImage クラスを使用します。

サンプル入力画像は image_scaling/png_input フォルダーに、出力画像は image_scaling/png_output フォルダーにあります。

結果

サンプル入力

ヒストスプライン手法を使用したスケーリング後の出力

画像スケーリングの結果を評価するには、image_scaling/png_output にある実際の出力イメージを参照してください。

使用するルーチン

タスク

ルーチン

1 次元データ・フィッティング・タスクの新しいタスク・ディスクリプターを作成して初期化する

dfsNewTask1D

スプラインの順序、種類、データ・フィッティング・タスク・ディスクリプターのスプラインを表す境界条件パラメーターを変更する

dfsEditPPSpline1D

自然 3 次スプラインを構築する

dfsConstruct1D

指定された場所でデータ・フィッティング補間およびスプライン微分の計算を実行する

dfsInterpolate1D

データ・フィッティング・タスク・オブジェクトを破棄してメモリーを解放する

dfDeleteTask

64 ビット境界でアライメントされたメモリーバッファーを割り当てる

mkl_malloc

mkl_malloc で割り当てられたメモリーを解放する

mkl_free