Android で OpenCL の Image2D を使って画像変換する

[Android で OpenCL を試す](https://qiita.com/ohwada/items/66c5dacb56bab80c3d52)
の続きです

### OpenCL の Image2D

OpenCL では，基本的な1次元の配列の他に，
画像の2次元のデータを取り扱う Image2D　というデータ型が用意されている。

- [OpenCLメモ (その6) - imageの使用](https://rigaya34589.blog.fc2.com/blog-entry-871.html)

- [OpenCL の Image2D メモリを使ってみる](http://codeit.blog.fc2.com/blog-entry-10.html)

- [OpenCL Image2D Variables](http://www.cmsoft.com.br/opencl-tutorial/opencl-image2d-variables/)

- [OpenCL : Image2D Class Reference](https://github.khronos.org/OpenCL-CLHPP/classcl_1_1_image2_d.html)

使い方
clCreateImage2D を使って画像データから Image2D を作成する。
引数は、画像データが格納された１次元配列のアドレスを示すポインター。

カーネルプログラムからは、
read_imagef と write_imagef を使って読み書きする。

- [clCreateImage2D] (https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/xhtml/clCreateImage2D.html)

- [read_imagef (2D)](https://www.khronos.org/registry/OpenCL/sdk/2.0/docs/man/xhtml/read_imagef2d.html)

- [write_image](https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/xhtml/write_image.html)

### Image2D のサンプル

Image2D のサンプルコードとして、
github に公開されているものを試す。
パソコン向けのC++ 版なので、Android向けに変更する。

- [opencl-book-samples](https://github.com/bgaster/opencl-book-samples/tree/master/src/Chapter_8/ImageFilter2D)

このサンプルはガウシアンフィルタを使って画像のぼかしを行う。

- [wikipedia : ガウシアンぼかし](https://ja.wikipedia.org/wiki/%E3%82%AC%E3%82%A6%E3%82%B7%E3%82%A2%E3%83%B3%E3%81%BC%E3%81%8B%E3%81%97)

重みは 3x3 の基本的なもの

|1|2|1|
|2|4|2|
|1|2|1|

ぼかしの効果は画像を拡大しないとわからない。

ガウスぼかしの効果について記事を書いた。
[OpenCL を使って　ガウスぼかしをする](https://qiita.com/ohwada/items/34ca5175ad5aa146d680)

### Android の画像から Image2D を作成する

当初は、Android の Bitmap から Image2D を作成しようとした。
先人の例があるだろと思ったが、全く見つからない。
結局、うまくいかなかった。

OpenCV の Maから Image2D を作成する例が見つかったので、
Mat を仲介役にした。

- [OpenCL leading with OpenCV]
(https://stackoverflow.com/questions/18687178/opencl-leading-with-opencv)

- [How to create cv::Mat from buffer (array of T* data) using a template function](https://stackoverflow.com/questions/48207847/how-to-create-cvmat-from-buffer-array-of-t-data-using-a-template-function/48207940)


Java プログラム

```
画像を Bitmap 形式にする
Bitmap bitmap = BitmapFactory.decodeFile(pathName);

OpenCV の　Mat 形式にする
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    Mat mat = new Mat(height, width, CvType.CV_8UC4);
    Utils.bitmapToMat(bitmap, mat);

native プログラムを呼ぶ
    ImageFilter(
            mat.getNativeObjAddr() );

    public native void ImageFilter(long matAddr);
```

native プログラム

```
JNIEXPORT void JNICALL
Java_jp_ohwada_android_opencl5_MainActivity_ImageFilter(
        jlong addr
        ) 
{

// 引数から Mat を復元する
    Mat& mat  = *(Mat*)addr;

    char *buffer = reinterpret_cast<char *>(mat.data);

 Image2D を作成する
    cl_mem image = clCreateImage2D(context,
                            CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                            &clImageFormat,
                            width,
                            height,
                            0,
                            buffer,
                            &errNum);
```


### アプリを作成する

OpenCL と OpenCV の環境が必要です。

下記を読んでください。

- [Android で OpenCL を試す](https://qiita.com/ohwada/items/66c5dacb56bab80c3d52)

_ [AndroidStudio で OpenCV をインポートする](https://qiita.com/ohwada/items/71eb18296aac46f9f5ee)



(1) プロジェクトを作成する

(2) OpenCV の Android 用 SDK を用意する

(3) OpenCL のライブラリを用意する

(4) app/src/main/Assets フォルダーにOPenCLのカーネルプログラムファイルを配置する


(5) レイアウトファイルに ImageView を配置する

```activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    <ImageView
        android:id="@+id/outputImageView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:src="@drawable/picture" />
```


(6) MainActivity を作成する

```MainActivity.java
public class MainActivity extends Activity

    private native void nativeStepOpenCL (
//　詳細　略
```

(7) C++ のプログラムファイルを作成する

```step.cpp
extern "C" void Java_com_intel_sample_androidbasicocl_MainActivity_nativeStepOpenCL
(
//　詳細　略
```

(8) CMakeLists.txt を作成する


```CMakeLists.txt
add_library( # Sets the name of the library.
             ImageFilter2D

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             ImageFilter2D.cpp )
```


(9)  app/build.gradle を変更する

```build.gradle
    externalNativeBuild {
        cmake {
            path 'src/main/cpp/CMakeLists.txt'
        }
    }
```

### アプリを実行する

analyze apk により下記の4つが同封されていることが確認できる。

- libImageFilter2D.so
- libOpenCL.so
- libopencv_java4.so
- libc++_shared.so

![opencl5_analyze_apk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/423720b9-f556-7ba5-edcc-ad88fd10286c.png)

スクリーンショット
ぱっと見では、変換前後で違いがわからない。
左が変換前、右が変換後

![opencl5_original.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/33eced65-463d-cac8-ffd3-9b66650a7252.png) ![pencl5_filtered.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/269d2363-4920-70a3-b3f0-6c0bd0d21ab9.png)



画像を拡大すると、ぼかしが効いているのがわかる
左が変換前、右が変換後

![ opencl5_comparison.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/30f564e8-99a0-4526-7339-897d5f68df37.png) 


サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/OpenCL5