Androidでの画像処理の高速化

画像 Androidを実行している最新のデバイスの中央処理装置とグラフィックコアは、多くの機能を備えています。 たとえば、その処理能力は画像処理に向けられます。

これを行うには、OpenCLおよびRenderScriptテクノロジーに注意する価値があります。

この記事では、プログラミング言語OpenCLおよびRenderScriptを使用した高性能画像処理の方法を示すAndroidアプリケーションの例を説明します。 これらのテクノロジーは、データ(シェーダーユニット)の並列処理用に設計されたグラフィックハードウェアの機能に注目して設計されています。 これにより、大量のデータの処理を高速化し、多数のコマンドの繰り返しに伴う問題を解決できます。 他のテクノロジーを使用してAndroidアプリケーションのグラフィック処理を高速化できますが、この記事では、アプリケーションインフラストラクチャを構築し、OpenCLおよびRenderScriptでグラフィカルアルゴリズムを実装する例を説明します。 また、OpenCL APIのラッパークラスについても説明します。これにより、グラフィックスで動作し、OpenCLを使用するアプリケーションの作成と実行が容易になります。 プロジェクトでこのクラスのソースコードを使用する場合、ライセンスは必要ありません。

この資料を準備するにあたり、読者はOpenCLおよびRenderScriptテクノロジーに精通しており、Androidプラットフォーム向けのプログラミングテクニックを習得していることが前提となりました。 したがって、画像の処理またはプログラムによる作成を加速するメカニズムの検討に主に注意を払います。

動作例を確認するには、OpenCLコードを実行できるようにAndroidデバイスを構成する必要があります。 以下では、Intel INDEとAndroid Studioを使用してOpenCL開発用の作業環境を編成する方法について説明します。

この記事の目的は、OpenCLおよびRenderScriptコードの機能を示すことであり、ここでは他の技術については説明していません。 さらに、OpenCLコードとビデオチップ(GPU)で実行されるRenderScriptを使用したアプリケーションのパフォーマンスの分析に関する資料も計画されています。

1.1アプリケーションインターフェイス


問題のアプリケーションの画面には3つのスイッチがあり、RenderScript、OpenCL、またはAndroidネイティブコードを使用して画像を操作するためのサブシステムを選択できます。 このメニューを使用すると、CPU(中央処理装置)またはGPU(グラフィックコア)でのOpenCLコードの実行を切り替えることができます。 さらに、メニューからグラフィック効果を選択できます。 ターゲットデバイスの選択は、OpenCLコードでのみ使用できます。 Intel x86プラットフォームは、CPUとGPUの両方でOpenCLをサポートしています。

以下に、OpenCLによって生成されるプラズマ効果を表示するアプリケーションのメイン画面を示します。


プログラムのメインウィンドウ、OpenCLコードの実行のためのターゲットデバイスの選択

ウィンドウの右上隅に、パフォーマンスインジケータが表示されます。 これらは、プログラムでサポートされているグラフィックスを操作する3つの方法すべてで表示されます。

パフォーマンスメトリックには、1秒あたりのフレーム数(FPS)、フレームレンダリング時間、効果の計算に必要な時間(効果の計算経過時間)が含まれます。


パフォーマンス指標

これはパフォーマンスの一例にすぎないことに注意してください。 パフォーマンスは、コードが実行されるデバイスによって異なります。

1.2。 使用されているAPIとSDK


ADT(Android開発ツール、Android SDKを含むAndroid開発ツール)に加えて、サンプル開発では、Android環境で動作するように設計されたRenderScript SDKおよびIntel OpenCL SDKを使用しました。

Intel OpenCL SDKはOpenCL仕様に基づいており、その規定を順守しています。 この仕様は、オープンでクロスプラットフォームの開発標準であり、無料で使用できます。 詳細はクロノスのウェブサイトにあります。

RenderScriptはADT 2.2で登場しました。 (APIレベル8)。 Android環境での高性能コンピューティングプラットフォームです。 RenderScriptは、主に計算の並列実行を可能にするタスクを実行するように設計されていますが、連続して実行される計算の恩恵を受けることもできます。 ここでは、RenderScriptの詳細を確認できます。

Google Open Repositoryから入手できる最新バージョンのADTには、RenderScript、JNI(Javaネイティブインターフェイス、ネイティブコード付きJava)、および一連のランタイムAPIを使用するためにインポートする必要があるパッケージが含まれています。

RenderScriptを使用した開発の詳細については、OpenCLの資料を参照してください

1.3アプリケーションサポートインフラストラクチャコード


問題のアプリケーションのインフラストラクチャは、主要なアクティビティと補助機能で構成されています。 ここでは、これらの関数と、ユーザーインターフェイスのカスタマイズ、グラフィック効果の選択、OpenCLの使用、および使用するコンピューティングデバイスの選択に使用されるコードを見ていきます。

ここでは、2つの主要な補助機能について検討します。

最初のbackgroundThread()は、独立した実行スレッドを起動します。このスレッドから、グラフィック効果の段階的なアプリケーションを実行する関数が定期的に呼び出されます。 この関数は、RenderScript入門記事で説明されているアプリケーションから取得されます;この例の詳細については、こちらを参照してください

2番目の関数processStep()は、 backgroundThread()から呼び出されます。 彼女 、順番に、画像処理のコマンドを呼び出します。 この関数は、スイッチの状態に基づいて、使用するアルゴリズムの実装を決定します。 関数processStep()では、設定の状態に応じて、OpenCL、RenderScript、またはC / C ++で記述された通常のマシンコードを使用して画像を処理するためのメソッドが呼び出されます。 このコードはバックグラウンドスレッドで実行されるため、ユーザーインターフェイスはブロックされないため、いつでもグラフィカルアルゴリズムの実装を切り替えることができます。 ユーザーがエフェクトを選択すると、アプリケーションはすぐにそのエフェクトに切り替わります。

//  processStep()    () . private void processStep() { try { switch (this.type.getCheckedRadioButtonId()) { case R.id.type_renderN: oclFlag = 0; // OpenCL  stepRenderNative(); break; case R.id.type_renderOCL: oclFlag = 1; // OpenCL  stepRenderOpenCL(); break; case R.id.type_renderRS: oclFlag = 0; // OpenCL  stepRenderScript(); break; default: return; } } catch (RuntimeException ex) { //      Log.wtf("Android Image Processing", "render failed", ex); } } 

1.4 Javaのマシンコードに実装された関数の定義


問題のアプリケーションは、グラフィカル効果を実装するJNIを使​​用してマシンコードレベルのコマンドを呼び出すために使用される関数を定義するNativeLibクラスを実装します。 アプリケーションには、プラズマ効果(プラズマ)、セピア(セピア)の画像の調色、および変色(モノクロ)の3つの効果があります。 したがって、関数はrenderPlasma(...)renderSepia(...)およびrenderMonoChrome(...)を定義しました 。 これらのJava関数は、JNIエントリポイントの役割を果たします。JNIエントリポイントを介して、マシンコードに実装された機能が呼び出されるか、グラフィカルアルゴリズムのOpenCLバージョンが呼び出されます。

対応するJNI関数は、選択したグラフィック効果を開始するときに、C / C ++で記述されたコードを実行するか、OpenCLでプログラムを構成して実行します。 記述されたクラスは、パッケージandroid.graphics.Bitmapおよびandroid.content.res.AssetManagerを使用します。 BitMapオブジェクト 、処理のためにグラフィックデータをサブシステムに送信し、結果を取得するために使用されます。 アプリケーションは、 AssetManagerクラスのオブジェクトを使用して、OpenCLファイル(たとえば、sepia.cl)にアクセスします。 これらのファイルは、グラフィカルアルゴリズムを実装する関数であるOpenCLカーネルを記述しています。

以下は、 NativeLibクラスのコードです。 // TODOでコメントされているように、簡単に拡張してグラフィック効果を追加できます。

 package com.example.imageprocessingoffload; import android.content.res.AssetManager; import android.graphics.Bitmap; public class NativeLib { //   libimageeffects.so public static native void renderPlasma(Bitmap bitmapIn, int renderocl, long time_ms, String eName, int devtype, AssetManager mgr); public static native void renderMonoChrome(Bitmap bitmapIn, Bitmap bitmapOut, int renderocl, long time_ms, String eName, int simXtouch, int simYtouch, int radHi, int radLo, int devtype, AssetManager mgr); public static native void renderSepia(Bitmap bitmapIn, Bitmap bitmapOut, int renderocl, long time_ms, String eName, int simXtouch, int simYtouch, int radHi, int radLo, int devtype, AssetManager mgr); //TODO public static native <return type> render<Effectname>(…); //  static { System.loadLibrary("imageeffectsoffloading"); } } 

Android AssetManagerおよびBitMapオブジェクトは、入力および出力パラメーターとしてマシンコードに渡されることに注意してください。 AssetManagerオブジェクト 、OpenCLカーネルを記述するCLファイルにアクセスするためにマシンコードによって使用されます。 BitMapオブジェクトには、マシンコードで処理されるピクセルデータが含まれています。 処理の結果を返すために同じデータ型が使用されます。

deviceTypeユーザーインターフェイスパラメーターは、OpenCLコードが実行されるターゲットデバイス(CPUまたはGPU)を示すために使用されます。 この柔軟性を実現するには、それに応じてAndroid OSを構成する必要があります。 最新のIntel AtomおよびIntel Coreプロセッサは、OpenCL命令を独立して実行でき、システムの統合グラフィックチップを使用します。

eNameパラメーターは、どのOpenCLカーネルをコンパイルして実行するかを指定します。 アプリケーションでは、各グラフィック効果に独自のJNI関数があります。その結果、カーネル名の転送は不要に思えるかもしれません。 ただし、1つのCLファイル(同じことがJNI機能にも当てはまります)では、いくつかの同様のグラフィカルアルゴリズムをエンコードできます。 そのような状況では、 eNameパラメーターを使用して、どの特定のCLプログラム(またはカーネル)をコンパイルおよびロードする必要があるかを示すことができます。

renderoclパラメーターは、OpenCLコードまたはC / C ++で記述されたマシンコードを実行するかどうかを示すフラグとして機能します。 ユーザーがOpenCLスイッチをアクティブにした場合、その値はtrueと解釈されます。それ以外の場合、フラグは未設定のままです。

time_msパラメーター 、パフォーマンスインジケーターの計算に使用されるタイムスタンプ(ミリ秒単位)を渡すために使用されます。 さらに、プラズマ効果は、画像の段階的な計算中にこの値に焦点を合わせます。

他の引数は、グラフィック効果の実装の機能、特に、治療領域の放射状の拡大を反映しています。 たとえば、パラメーターsimXtouchsimYTouchradLo 、およびradHiは 、幅と高さの値とともに、画像をセピアと変色に調整する効果で使用されます。 これらを使用して、特定のポイントで画像処理が開始される方法の計算と表示が行われ、その後、領域全体が画像全体に広がるまで放射状に拡大します。

1.5マシンコードの実行に必要な定義とリソース(CまたはOpenCL)


ここでは、例に示されている効果を実装するマシンJNI関数の定義を見ていきます。 既に述べたように、各関数には1つの関数があります。 これは、話を複雑にせず、OpenCLを使用して画像処理を加速するために使用される機能要素をより明確に強調するために行われます。

Cで記述されたコードへのリンクがあり、このコードのフラグメントもここに含まれています。 これは、例の将来のバージョンで検討中のテクノロジーのパフォーマンスの比較に基づいて行われます。

1つのJNI関数は、1つのマシンJava関数に対応します。 したがって、JNI関数を正しく宣言および定義することが非常に重要です。 Java SDKには、正確で正確なJNI関数宣言の生成に役立つjavahツールがあります 。 このツールは、コードが正しくコンパイルされても実行時エラーが発生する困難な状況に陥らないようにするために使用することをお勧めします。

以下は、画像処理を高速化するための低レベル関数に対応するJNI関数です。 関数シグネチャは、javahツールを使用して生成されます。

 //    JNI-,     //      #ifndef _Included_com_example_imageprocessingoffload_NativeLib #define _Included_com_example_imageprocessingoffload_NativeLib #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_imageprocessingoffload_NativeLib * Method: renderPlasma * Signature: (Landroid/graphics/Bitmap;IJLjava/lang/String;)Ljava/lang/String; */ JNIEXPORT void JNICALL Java_com_example_imageprocessingoffload_NativeLib_renderPlasma (JNIEnv *, jclass, jobject, jint, jlong, jstring, jint, jobject); /* * Class: com_example_imageprocessingoffload_NativeLib * Method: renderMonoChrome * Signature: (Landroid/graphics/Bitmap;Landroid/graphics/Bitmap;IJLjava/lang/String;)Ljava/lang/String; */ JNIEXPORT void JNICALL Java_com_example_imageprocessingoffload_NativeLib_renderMonoChrome (JNIEnv *, jclass, jobject, jobject, jint, jlong, jstring, jint, jint, jint, jint, jint, jobject); /* * Class: com_example_imageprocessingoffload_NativeLib * Method: renderSepia * Signature: (Landroid/graphics/Bitmap;Landroid/graphics/Bitmap;IJLjava/lang/String;)Ljava/lang/String; */ JNIEXPORT void JNICALL Java_com_example_imageprocessingoffload_NativeLib_renderSepia (JNIEnv *, jclass, jobject, jobject, jint, jlong, jstring, jint, jint, jint, jint, jint, jobject); } #endif 

javahツールは、JNI関数の正しい署名を生成できます。 ただし、Javaマシン関数を定義する1つまたは複数のクラスは、Androidプロジェクトに既にコンパイルされている必要があります。 ヘッダーファイルを生成する必要がある場合は、次のようにjavahコマンドを使用できます。

{javahLocation} -o {outputFile} -classpath {classpath} {importName}

この例では、関数シグネチャは次のコマンドで作成されました。

javah -o junk.h -classpath bin \ classes com.example.imageprocessingoffloading.NativeLib

次に、 junk.hファイルのJNI関数の署名がimageeffects.cppファイル追加され、OpenCLまたはCコードの準備と実行が実装されます。 次に、OpenCLの実行に必要なリソース、またはプラズマ効果、変色、セピア色合いのマシンコードを割り当てます。

1.5.1プラズマ効果


Java_com_example_imageprocessingoffload_NativeLib_renderPlasma(...)関数は、プラズマ効果を実装するOpenCLまたはマシンコードを実行するためのエントリポイントです。 関数startPlasmaOpenCL(...)runPlasmaOpenCL(...) 、およびrunPlasmaNative(...)は、 imageeffects.cppファイルのコードの外部にあり、別のplasmaEffect.cppファイルで宣言されています。 plasmaEffect.cppのソースコードは、 ここにあります

エントリポイントであるrenderPlasma(...)関数は、OpenCLラッパーを使用して、OpenCLのAndroidサポートを要求します。 ラッパークラス関数:: initOpenCL(...)を呼び出して、OpenCL環境を初期化します。 デバイスタイプとして、OpenCLコンテキストを作成するときに、CPUまたはGPUが転送されます。 AndroidリソースマネージャーはceNameパラメーターを使用して、必要なカーネルを含むCLファイルを識別、ダウンロード、コンパイルします。

OpenCL環境を正常に構成できる場合、 renderPlasma(...)関数の次のステップは、 startPlasmaOpenCL()関数を呼び出すことです 。この関数は、OpenCLリソースを割り当て、プラズマ効果を実装するカーネルの実行を開始します。 gOCLは、OpenCLラッパークラスのインスタンスを格納するグローバル変数であることに注意してください。 この変数は、エントリポイントであるすべてのJNI関数に表示されます。 このアプローチのおかげで、サポートされているグラフィック効果にアクセスするときにOpenCL環境を初期化できます。

プラズマ効果を実証する場合、既製の画像は使用されません。 画面に表示されるものはすべて、プログラムによって生成されます。 bitmapInパラメーターは、アルゴリズムの操作中に生成されたグラフィックデータを格納するBitMapクラスのオブジェクトです。 startPlasma(...)関数に渡されるピクセルパラメーターは、ラスターテクスチャに表示され、マシンコードまたはOpenCLカーネルコードによって使用され、画面に表示されるピクセルデータを読み書きします。 もう一度、 assetManagerオブジェクト使用して、プラズマ効果を実装するOpenCLカーネルを含むCLファイルにアクセスします。

 JNIEXPORT void Java_com_example_imageprocessingoffload_NativeLib_renderPlasma(JNIEnv * env, jclass, jobject bitmapIn, jint renderocl, jlong time_ms, jstring ename, jint devtype, jobject assetManager) { … //     //    BitMapIn    “pixels”,   OpenCL-     . ret = AndroidBitmap_lockPixels(env, bitmapIn, &pixels); … //     If OCL not initialized AAssetManager *amgr = AAssetManager_fromJava(env, assetManager); gOCL.initOpenCL(clDeviceType, ceName, amgr); startPlasmaOpenCL((cl_ushort *) pixels, infoIn.height, infoIn.width, (float) time_ms, ceName, cpinit); else runPlasmaOpenCL(infoIn.width, infoIn.height, (float) time_ms, (cl_ushort *) pixels); … //     } 

外部のstartPlasmaOpenCL(...)関数は、 PaletteバッファーとAnglesバッファーを作成して塗りつぶします。これらのバッファーには、プラズマ効果の作成に必要なデータが含まれています。 この効果の原因となるOpenCLカーネルを実行するために、関数は、ラッパークラスのメンバーデータとして定義されているOpenCLコマンドキュー、コンテキスト、およびカーネルに依存しています。

runPlasmaOpenCL(...)関数は、プラズマ画像を生成するOpenCLカーネルを継続的に呼び出します。 OpenCLカーネルの起動時に別の関数が1回使用されます。その後のカーネル呼び出しでは、入力に新しいタイムスタンプ値のみが必要です。 後続のカーネル呼び出しでは、タイムスタンプの形式で引数を渡すだけでよいため、追加の関数が必要です。

 extern int startPlasmaOpenCL(cl_ushort* pixels, cl_int height, cl_int width, cl_float ts, const char* eName, int inittbl); extern int runPlasmaOpenCL(int width, int height, cl_float ts, cl_ushort *pixels); extern void runPlasmaNative( AndroidBitmapInfo* info, void* pixels, double t, int inittbl ); 

runPlasmaNative(...)関数には、Cにプラズマ効果を作成するためのアルゴリズムの実装が含まれています。inittbl引数論理引数として使用されます。その値は、アルゴリズムが機能するために必要なPaletteおよびAnglesデータセットが必要かどうかを示します。 プラズマ効果を実装するOpenCLカーネルコードは、 plasmaEffect.cppファイルにあります。

 #define FBITS 16 #define FONE (1 << FBITS) #define FFRAC(x) ((x) & ((1 << FBITS)-1)) #define FIXED_FROM_FLOAT(x) ((int)((x)*FONE)) /*  ,     */ #define PBITS 8 #define ABITS 9 #define PSIZE (1 << PBITS) #define ANGLE_2PI (1 << ABITS) #define ANGLE_MSK (ANGLE_2PI - 1) #define YT1_INCR FIXED_FROM_FLOAT(1/100.0f) #define YT2_INCR FIXED_FROM_FLOAT(1/163.0f) #define XT1_INCR FIXED_FROM_FLOAT(1/173.0f) #define XT2_INCR FIXED_FROM_FLOAT(1/242.0f) #define ANGLE_FROM_FIXED(x) ((x) >> (FBITS - ABITS)) & ANGLE_MSK ushort pfrom_fixed(int x, __global ushort *palette) { if (x < 0) x = -x; if (x >= FONE) x = FONE-1; int idx = FFRAC(x) >> (FBITS - PBITS); return palette[idx & (PSIZE-1)]; } __kernel void plasma(__global ushort *pixels, int height, int width, float t, __global ushort *palette, __global int *angleLut) { int yt1 = FIXED_FROM_FLOAT(t/1230.0f); int yt2 = yt1; int xt10 = FIXED_FROM_FLOAT(t/3000.0f); int xt20 = xt10; int x = get_global_id(0); int y = get_global_id(1); int tid = x+y*width; yt1 += y*YT1_INCR; yt2 += y*YT2_INCR; int base = angleLut[ANGLE_FROM_FIXED(yt1)] + angleLut[ANGLE_FROM_FIXED(yt2)]; int xt1 = xt10; int xt2 = xt20; xt1 += x*XT1_INCR; xt2 += x*XT2_INCR; int ii = base + angleLut[ANGLE_FROM_FIXED(xt1)] + angleLut[ANGLE_FROM_FIXED(xt2)]; pixels[tid] = pfrom_fixed(ii/4, palette); } 

1.5.2画像の変色


Java_com_example_imageprocessingoffload_NativeLib_renderMonochrome(...)関数は、マシンコードまたはOpenCLツールを使用して実装された画像漂白機能を呼び出すためのエントリポイントです。 関数executeMonochromeOpenCL(...)およびexecuteMonochromeNative(...)は、 imageeffects.cppのコードの外部にあり、別のファイルで宣言されています。 プラズマ効果の場合と同様に、エントリポイントとして機能する関数は、OpenCLラッパーを使用して、Androidデバイス管理サブシステムへのOpenCLサポートに関連する要求を実行します。 また、OpenCL環境を初期化する:: initOpenCL(...)関数も呼び出します。

次のコード行は、 executeMonochromeOpenCL(...)およびexecuteMonochromeNative(...)関数がexternキーワードで宣言されていることを示しています。 これにより、それらはNDKコンパイラから見えるようになります。 これらの関数は別のファイルで宣言されているため、これが必要です。

 extern int executeMonochromeOpenCL(cl_uchar4 *srcImage, cl_uchar4 *dstImage, int radiHi, int radiLo, int xt, int yt, int nWidth, int nHeight); extern int executeMonochromeNative(cl_uchar4 *srcImage, cl_uchar4 *dstImage, int radiHi, int radiLo, int xt, int yt, int nWidth, int nHeight); 

プラズマ効果とは異なり、ここでは入力画像と出力画像が使用されます。 bitmapInbitmapOutは両方ともARGB_888形式のビットマップ画像です。 どちらもcl_uchar4などのベクターのCLバッファーにマップされます。 ここ pixelsInpixelsOutの変換 が実行されることに注意してください。これはOpenCLがBitMapオブジェクトをcl_uchar4ベクトルバッファにマップできるようにするために必要です。

 JNIEXPORT void JNICALL Java_com_example_imageprocessingoffload_NativeLib_renderMonochrome(JNIEnv * env, jclass obj, jobject bitmapIn, jobject bitmapOut, jint renderocl, jlong time_ms, jstring ename, jint xto, jint yto, jint radHi, jint radLo, jint devtype, jobject assetManager) { … //     //    BitMapIn    “pixelsIn”,   OpenCL-     . ret = AndroidBitmap_lockPixels(env, bitmapIn, &pixelsIn); //    BitMapOut    “pixelsOut”,   OpenCL-      ret = AndroidBitmap_lockPixels(env, bitmapOut, &pixelsOut); … //     If OpenCL If OCL not initialized AAssetManager *amgr = AAssetManager_fromJava(env, assetManager); gOCL.initOpenCL(clDeviceType, ceName, amgr); else executeMonochromeOpenCL((cl_uchar4*) pixelsIn,(cl_uchar4*) pixelsOut, radiHi, radiLo, xt, yt, infoIn.width, infoIn.height); //  , ,  OCL  else executeMonochromeNative((cl_uchar4*) pixelsIn,(cl_uchar4*) pixelsOut, radiHi, radiLo, xt, yt, infoIn.width, infoIn.height); //   OpenCL … //     } 

executeMonochromeOpenCL(...)関数が呼び出されると、 pixelsInpixelsOutは cl_uchar4バッファーのタイプに変換されて転送されます。 この関数は、OpenCL APIを使用して、作業に必要なバッファーおよびその他のリソースを作成します。 カーネル引数を設定し、OpenCLカーネルの実行に必要なコマンドをキューに入れます。 入力イメージバッファーは読み取り専用バッファー(read_only)として形成され、 pixelsInポインターを使用してアクセスします。 カーネルコードはこのポインターを使用して、画像の入力ピクセルデータを取得します。 このデータは、カーネルによって処理され、入力画像は変色します。 出力バッファーは、読み取りと書き込み(read_write)の両方のために設計されたバッファーで、画像処理の結果を保存し、 pixelsOutそれを指します。 OpenCLの詳細については、 Intelのプログラミングおよび最適化ガイドを参照してください。

executeMonochromeNative(...)関数では、画像漂白アルゴリズムはCで実装されます。これは、ネスト(ループ)を使用する非常に単純なアルゴリズムです。外部(y)および内部(x)。 pixelIn指すsrcImage変数は、アルゴリズム式で入力ピクセルデータを取得するために使用され、カラーイメージがモノクロに変換されます。

漂白効果を実装するOpenCLカーネルコードは次のとおりです。

 constant uchar4 cWhite = {1.0f, 1.0f, 1.0f, 1.0f}; constant float3 channelWeights = {0.299f, 0.587f, 0.114f}; constant float saturationValue = 0.0f; __kernel void mono (__global uchar4 *in, __global uchar4 *out, int4 intArgs, int width) { int x = get_global_id(0); int y = get_global_id(1); int xToApply = intArgs.x; int yToApply = intArgs.y; int radiusHi = intArgs.z; int radiusLo = intArgs.w; int tid = x + y * width; uchar4 c4 = in[tid]; float4 f4 = convert_float4 (c4); int xRel = x - xToApply; int yRel = y - yToApply; int polar = xRel*xRel + yRel*yRel; if (polar > radiusHi || polar < radiusLo) { if (polar < radiusLo) { float4 outPixel = dot (f4.xyz, channelWeights); outPixel = mix ( outPixel, f4, saturationValue); outPixel.w = f4.w; out[tid] = convert_uchar4_sat_rte (outPixel); } else { out[tid] = convert_uchar4_sat_rte (f4); } } else { out[tid] = convert_uchar4_sat_rte (cWhite); } } 

1.5.3セピア調


セピア色の画像に色を付けるコードは、ブリーチアルゴリズムの実装に非常に似ています。 主な違いは、ピクセルの色情報の処理方法です。 ここでは、他の式と定数が使用されます。 以下は、OpenCLおよびCによって実装されたアルゴリズムの実装を呼び出すための関数の宣言です。ご覧のとおり、名前を除く関数は、漂白アルゴリズムの実装を呼び出すための関数と同じに見えます。

 extern int executeSepiaOpenCL(cl_uchar4 *srcImage, cl_uchar4 *dstImage, it int radiHi, int radiLo, int xt, int yt, int nWidth, int nHeight); extern int executeSepiaNative(cl_uchar4 *srcImage, cl_uchar4 *dstImage, int radiHi, int radiLo, int xt, int yt, int nWidth, int nHeight); JNIEXPORT jstring JNICALL Java_com_example_imageprocessingoffload_NativeLib_renderSepia(JNIEnv * env, jclass obj, jobject bitmapIn, jobject bitmapOut, jint renderocl, jlong time_ms, jstring ename, jint xto, jint yto, jint radHi, jint radLo, jint devtype, jobject assetManager) { … } 

Java_com_example_imageprocessingoffload_NativeLib_renderSepia(...)のコードも、ブリーチアルゴリズムで見たものと非常に似ているため、ここでは示しません。

executeSepiaOpenCL(...)関数が呼び出されると 、渡された値を目的のタイプに変換し、 cl_uchar4バッファーの形式で、 pixelsInpixelsOut渡します。 OpenCL APIを使用して、バッファーやその他の必要なリソースを作成します。 また、OpenCLカーネルの引数を設定し、コマンドを実行待ちにします。 入力イメージバッファーは読み取り専用バッファー(read_only)として形成され、 pixelsInポインターを使用してアクセスします。 カーネルコードは、ポインターを使用してピクセル画像データを取得します。 次に、このデータはカーネルによって処理され、入力画像はセピア色になります。 出力バッファーは、読み取りと書き込み(read_write)の両方のために設計されたバッファーで、画像処理の結果を保存し、 pixelsOutそれを指します。

executeSepiaNative(...)関数には、 Cのセピア調色アルゴリズムの実装が含まれています。これは、外部(y)および内部(x)のネストされたループのペアで構成される単純なアルゴリズムです。 データ処理はサイクルで実行され、結果は、 pixelsOutで示されるdstImage変数に保存されます。 pixelIn指すsrcImage変数は、アルゴリズム式で入力ピクセルデータを取得するために使用されます。ここで、カラーイメージはセピアトーンでペイントされます。

以下は、画像をセピア色に着色するためのOpenCLカーネルコードです。

 constant uchar4 cWhite = {1, 1, 1, 1}; constant float3 sepiaRed = {0.393f, 0.769f, 0.189f}; constant float3 sepiaGreen = {0.349f, 0.686f, 0.168f}; constant float3 sepiaBlue = {0.272f, 0.534f, 0.131f}; __kernel void sepia(__global uchar4 *in, __global uchar4 *out, int4 intArgs, int2 wh) { int x = get_global_id(0); int y = get_global_id(1); int width = wh.x; int height = wh.y; if(width <= x || height <= y) return; int xTouchApply = intArgs.x; int yTouchApply = intArgs.y; int radiusHi = intArgs.z; int radiusLo = intArgs.w; int tid = x + y * width; uchar4 c4 = in[tid]; float4 f4 = convert_float4(c4); int xRel = x - xTouchApply; int yRel = y - yTouchApply; int polar = xRel*xRel + yRel*yRel; uchar4 pixOut; if(polar > radiusHi || polar < radiusLo) { if(polar < radiusLo) { float4 outPixel; float tmpR = dot(f4.xyz, sepiaRed); float tmpG = dot(f4.xyz, sepiaGreen); float tmpB = dot(f4.xyz, sepiaBlue); outPixel = (float4)(tmpR, tmpG, tmpB, f4.w); pixOut = convert_uchar4_sat_rte(outPixel); } else { pixOut= c4; } } else { pixOut = cWhite; } out[tid] = pixOut; } 

1.6 , RenderScript


, RenderScript- ? , – , , , . Android- , .

MainActivity.java .

 private RenderScript rsContext; 

rsContext RenderScript, RS-. RenderScript. RenderScript.

 private ScriptC_plasma plasmaScript; private ScriptC_mono monoScript; private ScriptC_sepia sepiaScript; 

plasmaScript , monoScript , sepiaScript – -, RS-. Eclipse IDE Android Studio Java- rs-. , plasma.rs ScriptC_plasma , mono.rsScriptC_mono . sepia.rs ScriptC_sepia . RenderScript- , gen . , sepia.rs ScriptC_sepia.java . Java-, rs- , RenderScript-, . - - MainActivity.java.

 private Allocation allocationIn; private Allocation allocationOut; private Allocation allocationPalette; private Allocation allocationAngles; 

Allocation RenderScript-. , allocationIn allocationOut . , allocationIn , allocationOut , RS- , , .

RenderScript-, , Activity . , , allocationPalette allocationAngle .

, RS-, RS-. initRS(…) .

 protected void initRS() { … }; 

RenderScript, create RenderScript . , RenderScript, . RenderScript RS-. , RenderScript MainActivity . RenderScript.create(…) « this ».

 rsContext = RenderScript.create(this); 

, RS-, RenderScript-, . , , initRS() , RenderScript- , .

 if (effectName.equals("plasma")) { plasmaScript = new ScriptC_plasma(rsContext); } else if (effectName.equals("mono")) { monoScript = new ScriptC_mono(rsContext); } else if (effectName.equals("sepia")) { sepiaScript = new ScriptC_sepia(rsContext); } //         

stepRenderScript(…) , RenderScript- . RenderScript- RS-. stepRenderScript(…) , .

 private void stepRenderScript(…) { … //     if(effectName.equals("plasma")) { plasmaScript.bind_gPalette(allocationPalette); plasmaScript.bind_gAngles(allocationAngles); plasmaScript.set_gx(inX - stepCount); plasmaScript.set_gy(inY - stepCount); plasmaScript.set_ts(System.currentTimeMillis() - mStartTime); plasmaScript.set_gScript(plasmaScript); plasmaScript.invoke_filter(plasmaScript, allocationIn, allocationOut); } else if(effectName.equals("mono")) { //      ,        int radius = (stepApply == -1 ? -1 : 10*(stepCount - stepApply)); int radiusHi = (radius + 2)*(radius + 2); int radiusLo = (radius - 2)*(radius - 2); //   . monoScript.set_radiusHi(radiusHi); monoScript.set_radiusLo(radiusLo); monoScript.set_xInput(xToApply); monoScript.set_yInput(yToApply); //  . monoScript.forEach_root(allocationIn, allocationOut); if(stepCount > FX_COUNT) { stepCount = 0; stepApply = -1; } } else if(effectName.equals("sepia")) { … // ,      } … //     }; 

RenderScript-, , gPalette , gAngles , gx , gy gScript . RS , . plasma.rs . , rs_allocation , bind_<var> . , bind_<gvars> , allocationPalette allocationAngles RenderScript-. , , gx , gy , ts gScript, set_<var> , . , , RenderScript- , x, y , . invoke_filter(…) RenderScript. , , filter() , , , RenderScript.

, radius radiusHi radiusLo . , xInput yInput , . , , , , forEach_root() . forEach_root(…) –, , RenderScript. , radiusHi , radiusLo , xInput yInput . set_<var> .

RenderScript , .

RenderScript :

 #pragma version(1) #pragma rs java_package_name(com.example.imageprocessingoffload) rs_allocation *gPalette; rs_allocation *gAngles; rs_script gScript; float ts; int gx; int gy; static int32_t intFromFloat(float xfl) { return (int32_t)((xfl)*(1 << 16)); } const float YT1_INCR = (1/100.0f); const float YT2_INCR = (1/163.0f); const float XT1_INCR = (1/173.0f); const float XT2_INCR = (1/242.0f); static uint16_t pfrom_fixed(int32_t dx) { unsigned short *palette = (unsigned short *)gPalette; uint16_t ret; if (dx < 0) dx = -dx; if (dx >= (1 << 16)) dx = (1 << 16)-1; int idx = ((dx & ((1 << 16)-1)) >> 8); ret = palette[idx & ((1<<8)-1)]; return ret; } uint16_t __attribute__((kernel)) root(uint16_t in, uint32_t x, uint32_t y) { unsigned int *angles = (unsigned int *)gAngles; uint32_t out = in; int yt1 = intFromFloat(ts/1230.0f); int yt2 = yt1; int xt10 = intFromFloat(ts/3000.0f); int xt20 = xt10; int y1 = y*intFromFloat(YT1_INCR); int y2 = y*intFromFloat(YT2_INCR); yt1 = yt1 + y1; yt2 = yt2 + y2; int a1 = (yt1 >> 7) & ((1<<9)-1); int a2 = (yt2 >> 7) & ((1<<9)-1); int base = angles[a1] + angles[a2]; int xt1 = xt10; int xt2 = xt20; xt1 += x*intFromFloat(XT1_INCR); xt2 += x*intFromFloat(XT2_INCR); a1 = (xt1 >> (16-9)) & ((1<<9)-1); a2 = (xt2 >> (16-9)) & ((1<<9)-1); int ii = base + angles[a1] + angles[a2]; out = pfrom_fixed(ii/4); return out; } void filter(rs_script gScript, rs_allocation alloc_in, rs_allocation alloc_out) { //rsDebug("Inputs TS, X, Y:", ts, gx, gy); rsForEach(gScript, alloc_in, alloc_out); } 

RenderScript :

 #pragma version(1) #pragma rs java_package_name(com.example.imageprocessingoffload) int radiusHi; int radiusLo; int xToApply; int yToApply; const float4 gWhite = {1.f, 1.f, 1.f, 1.f}; const float3 channelWeights = {0.299f, 0.587f, 0.114f}; float saturationValue = 0.0f; uchar4 __attribute__((kernel)) root(const uchar4 in, uint32_t x, uint32_t y) { float4 f4 = rsUnpackColor8888(in); int xRel = x - xToApply; int yRel = y - yToApply; int polar = xRel*xRel + yRel*yRel; uchar4 out; if(polar > radiusHi || polar < radiusLo) { if(polar < radiusLo) { float3 outPixel = dot(f4.rgb, channelWeights); outPixel = mix( outPixel, f4.rgb, saturationValue); out = rsPackColorTo8888(outPixel); } else { out = rsPackColorTo8888(f4); } } else { out = rsPackColorTo8888(gWhite); } return out; } 

RenderScript- .

 #pragma version(1) #pragma rs java_package_name(com.example.imageprocessingoffload) #pragma rs_fp_relaxed int radiusHi; int radiusLo; int xTouchApply; int yTouchApply; rs_script gScript; const float4 gWhite = {1.f, 1.f, 1.f, 1.f}; const static float3 sepiaRed = {0.393f, 0.769f, 0.189f}; const static float3 sepiaGreen = {0.349f, 0.686, 0.168f}; const static float3 sepiaBlue = {0.272f, 0.534f, 0.131f}; uchar4 __attribute__((kernel)) sepia(uchar4 in, uint32_t x, uint32_t y) { uchar4 result; float4 f4 = rsUnpackColor8888(in); int xRel = x - xTouchApply; int yRel = y - yTouchApply; int polar = xRel*xRel + yRel*yRel; if(polar > radiusHi || polar < radiusLo) { if(polar < radiusLo) { float3 out; float tmpR = dot(f4.rgb, sepiaRed); float tmpG = dot(f4.rgb, sepiaGreen); float tmpB = dot(f4.rgb, sepiaBlue); out.r = tmpR; out.g = tmpG; out.b = tmpB; result = rsPackColorTo8888(out); } else { result = rsPackColorTo8888(f4); } } else { result = rsPackColorTo8888(gWhite); } return result; } 

1.7


OpenCL, .

Intel INDE . IDE, . – Android Studio. , , -, IDE, ( – Android SDK NDK, ), - – OpenCL- OpenCL-. Android-, . , Root-.

, , OpenCL Android . Eclipse IDE, , Android Studio.

Android Studio . , Android Studio, . , , Android SDK, NDK , Intel OpenCL.

, Android.mk , OpenCL-. :

 INTELOCLSDKROOT="C:\Intel\INDE\code_builder_5.1.0.25" 

local.properties Android SDK NDK.

 sdk.dir=C\:\\Intel\\INDE\\IDEintegration\\android-sdk-windows ndk.dir=C\:\\Intel\\INDE\\IDEintegration\\android-ndk-r10d 

Android-. Intel Nexus 7 x86. Android Virtual Device Manager.

, , , OpenCL. , Run Android Studio . , Serial Number. emulator-5554 .
Windows :

 C:\Intel\INDE\code_builder_5.1.0.25\android-preinstall>opencl_android_install –d emulator-5554 

OpenCL- , . , , Android Studio , , OK. .


OpenCL-

, OpenCL- .
, OpenCL, RenderScript-. Android Studio Eclipse- RenderScript Android . , , RenderScript. .

OpenCL-. OpenCL .

2. - OpenCL


- OpenCL API OpenCL-. , - API, OpenCL. . . .

 class openclWrapper { private: cl_device_id* mDeviceIds; //   OpenCL- (CPU, GPU,   ) cl_kernel mKernel; //   cl_command_queue mCmdQue; //    CL- cl_context mContext; //  OpenCL cl_program mProgram; //  OpenCL- public: openclWrapper() { mDeviceIds = NULL; mKernel = NULL; mCmdQue = NULL; mContext = NULL; mProgram = NULL; }; ~openclWrapper() { }; cl_context getContext() { return mContext; }; cl_kernel getKernel() { return mKernel; }; cl_command_queue getCmdQue() { return mCmdQue; }; int createContext(cl_device_type deviceType); bool LoadInlineSource(char* &sourceCode, const char* eName); bool LoadFileSource(char* &sourceCode, const char* eName, AAssetManager *mgr); int buildProgram(const char* eName, AAssetManager *mgr); int createCmdQueue(); int createKernel(const char *kname); //   int initOpenCL(cl_device_type clDeviceType, const char* eName, AAssetManager *mgr=NULL); }; 

::createContext(cl device) . , (CPU GPU), , OpenCL . , OpenCL. OpenCL. , , SUCCESS ( mContext ). , , OpenCL, FAIL.

::createCmdQue() , OpenCL-. mContext . SUCCESS ( mCmdQue ). , , createContext(…) , FAIL.

::buildProgram(effectName, AssetManager) . ( effectName ) Android JNI. OpenCL-, . - (inline) OpenCL-. NULL . , effectName , , . , OpenCL-, , . , , – OpenCL-. , OpenCL- , – , API OpenCL- .


::createKernel(…) . SUCCESS. mKernel , , , .

::getContext() , ::getCmdQue() ::getKernel() , , , . JNI , OpenCL-.

まとめ


OpenCL-, Android-. OpenCL RenderScript. , , . OpenCL , , , . , .

Source: https://habr.com/ru/post/J263843/


All Articles