ヒストスプライン手法を使用してカラーまたはグレースケール画像を再スケーリングします。
ヒストスプライン計算の画像スケーリングおよびスプライン補間に oneMKL のデータ・フィッティング関数を使用します。
[Bosner14] に記述されているように、1 次元ヒストスプラインは 2 次元画像スケーリングの問題に適用できます。2 次元 (サーフェス) 補間および近似に対する 1 次元補間プリミティブのアプリケーションは、[Zavyalov80] に記述されています。
多くの 1 次元ヒストポレーション問題に縮小できる 2 次元問題として、サイズ n1*m1 ピクセル (ここで、n1 は行数で m1 は列数) の入力画像を n2*m2 ピクセルの画像にスケーリングする問題について考えます。
入力画像の各色平面 c で、以下の操作を行います。
入力画像の各行で、以下の操作を行います。
現在の行の各ピクセルで、累計のセル平均 (または色の値) を計算して、配列 VX の現在の行に格納します。
配列を補間された関数値、[0; m1] に一様に分布された (m1+1) ブレークポイント x_breaks[] として考えます。自然 3 次スプラインを使用してヒストスプラインを構築し、その微分を計算して、配列 VXR の現在の行、[0; m1] に一様に分布された (m2+1) サイト x_sites[] の i=0..m2 に格納します。
配列 VXR を配列 VXRT に転置します。
VX と同様に、VY の列ごとに同じ操作を行って配列 VYR の結果を取得します。
ステップ 1.a と同様に、計算した VXRT の値の累計として配列 VY を計算します。
このステップは、VXR の転置と同時に実行できるため、結果を VXRT に格納する必要はありません。
VYR 配列に微分を格納します。
VYR から VR に転置して整数結果を取得し、出力画像の色平面 c に格納します。
VR を格納する必要はありません。また、最後の行および列は破棄されます。
ソースコード: サンプル (https://www.intel.com/content/dam/develop/external/us/en/documents/mkl-cookbook-samples-120115.zip (英語)) の image_scaling フォルダーを参照してください。
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;
}
}
}
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 |