owned this note
owned this note
Published
Linked with GitHub
# How to Import OpenCV in Android NDK
###### tags: `android` `opencv` `ndk`
## Pre-Requirement
* Please install Android SDK and NDK packages. (see [Android Native Development Kit (NDK)](https://hackmd.io/WO-nZYsfR1icmYbWy3jFrQ))
* Download and unzip OpenCV for Android [(link)](https://opencv.org/releases.html)
## Demo APP - OpenCVDemo
### Create a New Project
* Enable C++ support
![](https://i.imgur.com/OKGg9Qw.png)
* Enable Exception and RTTI Support
![](https://i.imgur.com/sG5mfsX.png)
* Modify build.gradle(Module: app) to configure the ABI (armeabi-v7a as example)
```
defaultConfig {
applicationId "com.jd.opencvdemo"
minSdkVersion 23
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
}
}
// Add below
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
abiFilters 'armeabi-v7a'
}
}
```
### User Interface
![](https://i.imgur.com/UcnOTHu.png)
* There are several UI components
* (Button) btn_load : open the Android Image Pick and load image from sd card.
* (Button) btn_proess : trigger our native image process function
* (Button) btn_save : save the current image into sd card
* (ImageView) img_main : display the image
### Request Permission (AndroidManifest.xml)
* Since this demo app requires the permission of accessing sd card to get the image, we need to add **<user-permission>** into the manifest file.
```xml=
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jd.opencvdemo">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
...
```
* for more information about permission, see [Permission](https://developer.android.com/guide/topics/permissions/overview).
### Import Library
* Copy OpenCV-android-sdk/sdk/native/jni/include folder to YourProject/app/src/main/cpp/
![](https://i.imgur.com/9FDyAma.png)
* Copy the libopencv_java3.so of corresponsed ABI (or all) in OpenCV-android-sdk/sdk/native/libs to your Android project app/src/main/jniLibs
* arm64-v8a
* armeabi
* armeabi-v7a
* mips
* mips64
* x86
* x86_64
![](https://i.imgur.com/qAQvGU2.png)
* Modify CmakeList.txt
* set the library path and its properties.
```
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
add_library(
libopencv_java3
SHARED
IMPORTED
)
set_target_properties(
libopencv_java3
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so
)
```
* link *libopencv_java3*
```
target_link_libraries(
native-lib
libopencv_java3
${log-lib}
${android-lib})
```
* Rebuild the project and you should find *libopencv_java3* is added.
![](https://i.imgur.com/DZ565A2.png)
### Build the Native Function (native-lib.cpp)
* include the .h file
```cpp=
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
using namespace cv;
```
* define the native function : imgProcess
```cpp=
extern "C"
JNIEXPORT void JNICALL
Java_com_jd_opencvdemo_MainActivity_imgProcess(
JNIEnv *env,
jobject, /*this*/
jint h, jint w, jintArray RGBFrameData, jintArray ResFrameData) {
// get data
jint * pRGBFrameData = env->GetIntArrayElements(RGBFrameData, 0);
jint * pResFrameData = env->GetIntArrayElements(ResFrameData, 0);
// create the matrix of original image
Mat mRGB(h, w, CV_8UC4, (unsigned char*)pRGBFrameData);
// convert to gray image
Mat mGray;
cvtColor(mRGB, mGray, CV_RGBA2GRAY);
// find edges by canny
Mat mCanny;
Canny(mGray,mCanny,10,100);
// convert to RGBA image as result
Mat mRes(h, w, CV_8UC4, (unsigned char*)pResFrameData);
cvtColor(mCanny, mRes, CV_GRAY2RGBA);
}
```
### Call the Native Function (OpenCV.java)
* load library
* declare the native function
```java=
public class OpenCV {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native static void canny(int h, int w, int[] RGBFrameData, int[] ResFrameData);
}
```
### Control UI Component and Trigger the Process (MainActivity.java)
* load image from Android gallery (btn_load)
```java=
btn_load.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// open gallery
Intent i = new Intent(Intent.ACTION_PICK);
i.setType("image/*");
startActivityForResult(i, SELECT_IMAGE);
}
});
```
* call the function by clicking button (btn_proc)
```java=
btn_proc.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// get image
Bitmap bitmap = ((BitmapDrawable)img_main.getDrawable()).getBitmap();
int w = bitmap.getWidth();
int h = bitmap.getHeight();
// get rgb format data
int[] rgb = new int[w * h];
bitmap.getPixels(rgb, 0, w, 0, 0, w, h);
// call native function to process the image
int[] img_res = new int[w * h];
OpenCV.canny(h, w, rgb, img_res);
// show the result
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
bmp.setPixels(img_res,0,w,0,0,w,h);
img_main.setImageBitmap(bmp);
}
});
```
* save image in Pictures folder (btn_save)
```java=
btn_save.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// permission check
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_SDCARD_PERMISSION);
return;
}
boolean sdAvailable = checkSDCard();
if (!sdAvailable){
Log.e(LOG_TAG,"SD card is not available!");
return;
}
// find path
File appDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
if (!appDir.exists() && !appDir.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
String fileName = "test_"+System.currentTimeMillis() + ".jpg";
File file = new File(appDir, fileName);
// save file into gallery
Bitmap bitmap = ((BitmapDrawable)img_main.getDrawable()).getBitmap();
try {
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
MediaStore.Images.Media.insertImage(getApplication().getContentResolver(), file.getAbsolutePath(), fileName, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// make a notification to system to update the gallery
getApplication().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse(file.getAbsolutePath())));
// check whether it is saved successfully
if(file.exists()){
Toast.makeText(getApplicationContext(),"Success!", Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(getApplicationContext(), "Failed!", Toast.LENGTH_LONG).show();
}
}
});
```
### Result
* Original Image
![](https://i.imgur.com/2c1CMZo.jpg)
* Canny Image
![](https://i.imgur.com/ZNmsKjW.jpg)
## Reference
* [Android NDK学习笔记:Android Studio3.1+CMAKE+OpenCV3.4配置](https://blog.csdn.net/CV_Jason/article/details/79758823)
* [OpenCV使用Canny边缘检测器实现图像边缘检测](http://kongqw.com/2016/08/19/2016-08-19-OpenCV%E4%BD%BF%E7%94%A8Canny%E8%BE%B9%E7%BC%98%E6%A3%80%E6%B5%8B%E5%99%A8%E5%AE%9E%E7%8E%B0%E5%9B%BE%E5%83%8F%E8%BE%B9%E7%BC%98%E6%A3%80%E6%B5%8B/)