OpenCV On Android Best Environment Configuration Guide (Android Studio)

OpenCV On Android Best Environment Configuration Guide (Android Studio)

Introduction

This article is the second in the series of " OpenCV On Android Best Environment Configuration Guide " and the last one in the configuration series. It is suitable for developers who use Android Studio to learn.

This tutorial is summarized after I stepped on the pit many times and combined with many OpenCV On Android configuration tutorials on the Internet. I hope it can help friends who learn OpenCV to avoid detours. If you encounter a problem with the configuration, you can leave a message in the comments and I will try my best to help solve it.

If you are using Eclipse, please refer to the previous chapter OpenCV On Android Best Environment Configuration Guide (Eclipse) .

If reprinted, please indicate the source

(Update time: 2020-10-12)


surroundings

  • Computer: Windows10
  • Java: jdk1.8.0_172
  • Android Studio: Version 4.0.2
  • SDK: The latest SDK that comes with Android Studio 4.0.2
    (Please do not use the same SDK with Eclipse to avoid errors)
    .
  • NDK: The latest NDK that comes with Android Studio 4.0.2
  • OpenCV: V4.4.0

Note: The above configuration is upward compatible, readers can use the newer version, but there may be errors in the lower version


Instructions before configuration:

This configuration does not write multiple Demos like the Eclipse configuration environment introduced in the previous article, but through a Demo, the OpenCV

Java
with
NDK
After all the configuration methods are explained, try to explain it by hand as much as possible. Please don't read it skippingly.


1. Install the necessary components

  1. Open the Android Studio settings interface and enter
    Appearance & Behavior -> System Settings -> Android SDK
    .
  2. Switch the option bar to
    SDK Tools
    , Tick on the bottom left corner
    Show Package Details
    , And then tick as shown below:

Click OK to start downloading. After downloading, you can start to create the project.


2. create an Android Studio project

Create New Project
, Select the last
Native C++
Template, and then enter the configuration interface.

There are two places to pay attention to in this step

1. Package name: Please try to be consistent with me, otherwise novices are prone to errors.
2. Minimum SDK: OpenCV 4.2.0 requires minimum SDK

Not less than 21
.

Finish directly, the project is created successfully!

prompt
: After the project is created, it is best to run it to ensure that the basic environment is okay.


3. OpenCV Java library usage guide

3.1, environment configuration

Step 1 : Import the OpenCV Java library as a Module.

The specific steps are:

File->New->Import Module
, And then import the OpenCV-android-sdk\sdk\java directory. As shown in the figure below, then Next->Finish .

Step 2 : Modify the module name.

The default imported module name is

java
, In order to facilitate the distinction, it is recommended to modify to
opencv
, Just right click on the java module, then
Refactor->Rename

Step 2 : Change the imported opencv module from

application
Change to
library
,Proceed as follows:

1. Switch the file preview mode to Android.
2. Open the build.gradle file of opencv.
3. Put

apply plugin:'com.android.application'
changed to
apply plugin:'com.android.library'
.
4. Delete (or comment) out
defaultConfig
content.
5. Change Run/Debug Configurations from
opencv
Switch to
app
.
6. Click Sync Now to make the modification effective.

Step 3 : Add opencv dependencies to the project

menu

File->Project Structure
,in
Dependencies
Select app, click
+
,select
Module dependency
, Then check
opencv
Module, click OK! As shown below:

3.2, code writing

Add permissions in the AndroidManifest.xml file:

.... < uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE"/> < uses-permission android:name = "android.permission.CAMERA"/> < uses-feature android:name = "android.hardware.camera" android:required = "true"/> < uses-feature android:name = "android.hardware.camera.autofocus" android:required = "false"/> .... Copy code

Modify the content of activity_main.xml to the following:

<?xml version="1.0" encoding="utf-8"?> < FrameLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:app = "http://schemas .android.com/apk/res-auto" android:layout_width = "match_parent" android:layout_height = "match_parent" > < org.opencv.android.JavaCameraView android:id = "@+id/javaCameraView" android:layout_width = "match_parent" android:layout_height = "match_parent" app:camera_id = "back" app:show_fps = "true"/> </FrameLayout > Copy code

Change MainActivity.java to the following:

public class MainActivity extends CameraActivity implements CameraBridgeViewBase . CvCameraViewListener2 { private JavaCameraView javaCameraView; private BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback( this ) { @Override public void onManagerConnected ( int status) { switch (status) { case LoaderCallbackInterface.SUCCESS: { javaCameraView.enableView(); } break ; default : super .onManagerConnected(status); break ; } } }; @Override protected List<? extends CameraBridgeViewBase> getCameraViewList() { List<CameraBridgeViewBase> list = new ArrayList<>(); list.add(javaCameraView); return list; } @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); javaCameraView = findViewById(R.id.javaCameraView); javaCameraView.setVisibility(SurfaceView.VISIBLE); javaCameraView.setCvCameraViewListener( this ); } @Override public void onPause () { super .onPause(); if (javaCameraView != null ) { javaCameraView.disableView(); } } @Override public void onResume () { super .onResume(); if (!OpenCVLoader.initDebug()) { OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this , baseLoaderCallback); } else { baseLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS); } } @Override public void onCameraViewStarted ( int width, int height) { } @Override public void onCameraViewStopped () { } @Override public Mat onCameraFrame (CameraBridgeViewBase.CvCameraViewFrame inputFrame) { return inputFrame.gray(); } } Copy code

It s important to note here that our

MainActivity
Inherited to
CameraActivity
, This is to solve the black screen problem. If you don t do this, you need to manually handle permissions and call
JavaCameraView
of
setCameraPermissionGranted()
method.

3.3, run the program

At this point, we have been able to compile an app, but it does not run normally. This is because we only imported the opencv api module, but the specific code is not included in the apk.

We can now solve this problem in the following two ways:

1. Install in the phone

OpenCV Manager.apk

This method has the following disadvantages:

1. The user needs to install this apk separately, and the apk is different for mobile phones with different ARM architectures.
2. In the high version of Android, some mobile phone manufacturers have made certain restrictions on mobile phones in order to prevent applications from waking up each other (such as the startup management in Huawei mobile phone manager), which may cause our App to not be able to call OpenCV Manager
3 , OpenCV high version no longer provides OpenCV Manager.apk


2. Will

libopencv_java4.so
Import into apk

The disadvantages of this approach are:

trouble!!!
, But if we have determined the target model, this method is undoubtedly better.

Generally speaking, if it is a real machine, import

armeabi
or
armeabi-v7a
The so file of the architecture; if it is a virtual machine, it is generally selected
x86
The so file of the architecture.

Method 2 The specific implementation steps are as follows:

1. The OpenCV library

OpenCV-android-sdk\sdk\native\libs
There are 4 subdirectories under the directory, copy them to the libs directory of our project.

2. Modify the build.gradle file and add the following content:

sourceSets { main{ jniLibs.srcDirs = [ "libs" ]; } } Copy code

as the picture shows:

After doing this step,

libopencv_java4.so
Will be automatically packaged into the apk, but an error may still be reported.

Generally speaking, it will prompt that

c++_shared
, Which requires us to modify the build.gradle file again and add arguments:

android { //... defaultConfig { //... externalNativeBuild { cmake { cppFlags "" arguments "-DANDROID_STL=c++_shared" } } } } Copy code

After finishing the above content, it is basically OK.

In fact, there is still a pit here. Since we created a Native C++ project from the beginning, we can package c++_shared.so into the apk by adding arguments in the build.gradle file, but if it is created It is a normal project, this method will be invalid, you need to manually add c++_shared.so to the directory corresponding to libs.


4. OpenCV NDK library usage guide

4.1, environment configuration

Android Studio is very simple to configure the OpenCV environment (yes, that's right), you only need to modify a file to successfully configure the environment, what Android.mk , Application.mk , all go away.

Configuration method: open

CMakeLists.txt
, The content is revised as follows:
(Set OpenCV_DIR to your path, pay attention to the separator, use'/' or'\\')
:

cmake_minimum_required (VERSION 3.4 . 1 ) # ##################### OpenCV Environment######################### ## #Set OpenCV-android-sdk path set (OpenCV_DIR D:\\OpenCV\\OpenCV-android-sdk\\sdk\\native\\jni) find_package (OpenCV REQUIRED) if (OpenCV_FOUND) include_directories ( ${OpenCV_INCLUDE_DIRS} ) message (STATUS "OpenCV library status:" ) message (STATUS "version: ${OpenCV_VERSION}" ) message (STATUS " libraries: ${OpenCV_LIBS}" ) message (STATUS "include path: ${OpenCV_INCLUDE_DIRS}" ) else (OpenCV_FOUND) message (FATAL_ERROR "OpenCV library not found" ) endif (OpenCV_FOUND) # ###################### Project Native Module###################### ### add_library (native-lib SHARED src/main/cpp/native-lib.cpp) target_link_libraries (native-lib ${OpenCV_LIBS} log jnigraphics) Copy code

OK, the environment is configured, hehehe, then start writing OpenCV code.

4.2, write application layer code

Menu File->New->Activity->Empty Activity , create a new Activity, name it as shown in the figure below, and set it as the startup page, Finish .

In order to distinguish the two program entries on the desktop, please specify the label for the two activities in the AndroidManifest.xml file , as shown below:

Let's start to write the layout file activity_native.xml , the content is as follows:

<?xml version="1.0" encoding="utf-8"?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "match_parent" android:layout_height = "match_parent" > < ImageView android:id = "@+id/imageView" android:layout_width = "match_parent" android:layout_height = "match_parent"/> < LinearLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_alignParentBottom = "true" android:orientation = "horizontal" > < Button android:id = "@+id/show" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_weight = "1" android:text = "show"/> < Button android:id = "@+id/process" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_weight = "1" android:text = "process"/> </LinearLayout > </RelativeLayout > copy code

The content of NativeActivity.java is as follows:

public class the NativeActivity the extends AppCompatActivity the implements View . OnClickListener to { Private the ImageView the imageView; static { //Load so library System.loadLibrary( "native-lib" ); } @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_native); imageView = findViewById(R.id.imageView); findViewById(R.id.show).setOnClickListener( this ); findViewById(R.id.process).setOnClickListener( this ); } @Override public void onClick (View v) { if (v.getId() == R.id.show) { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); imageView.setImageBitmap(bitmap); } else { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); getEdge(bitmap); imageView.setImageBitmap(bitmap); } } //Get Canny edge native void getEdge (Object bitmap) ; } Copy code

Place a picture named test.jpg in the drawable directory, hehehe!

4.3, write native layer code

The native code, to put it bluntly, is to write programs in C/C++.

1. Generate method name

Due to the in NativeActivity

getEdge
Is an
native
Method, there is no specific implementation, all in Android Studio, will report a red warning.

At this point, just point the mouse to the method name, and then

Alt
+
Enter
, Will be at
native-lib.cpp
The declaration of the method is generated in the file.

Note: If you cannot generate it, please use

javah
Order, please Baidu.

1. Write NDK code

The content of native-lib.cpp is modified to:

# include "com_demo_opencv_NativeActivity.h" # include <android/bitmap.h> # include <opencv2/opencv.hpp> using namespace cv; extern "C" JNIEXPORT void JNICALL Java_com_demo_opencv_NativeActivity_getEdge (JNIEnv *env, jobject obj, jobject bitmap) { AndroidBitmapInfo info; void *pixels; CV_Assert ( AndroidBitmap_getInfo (env, bitmap, &info) >= 0 ); CV_Assert (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 || info.format == ANDROID_BITMAP_FORMAT_RGB_565); CV_Assert ( AndroidBitmap_lockPixels (env, bitmap, &pixels) >= 0 ); CV_Assert (pixels); if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) { Mat temp (info.height, info.width, CV_8UC4, pixels) ; Mat gray; cvtColor (temp, gray, COLOR_RGBA2GRAY); Canny (gray, gray, 45 , 75 ); cvtColor (gray, temp, COLOR_GRAY2RGBA); } else { Mat temp (info.height, info.width, CV_8UC2, pixels); Mat gray; cvtColor (temp, gray, COLOR_RGB2GRAY); Canny (gray, gray, 45 , 75 ); cvtColor (gray, temp, COLOR_GRAY2RGB); } AndroidBitmap_unlockPixels (env, bitmap); } Copy code

Note that only replace the content, do not replace the method name.

Run the program, the effect is as follows:

Perfect, close work, go home for dinner!

5. summary

This is the end of the OpenCV On Android series configuration tutorials. It is indeed not easy to write these two articles. They have been modified many times, especially this Android Studio, which was completed in a busy schedule, and it has been delayed for a long time. I stepped on countless pits in the configuration process. I hope my experience can help everyone avoid detours, and at the same time accept everyone's criticism and corrections with an open mind.

The road to mobile image processing is still long, and I will continue to learn to enrich myself. These two articles are a wish of mine. Below is the learning group I created. I will also help you solve problems from time to time.

Please join the group to maintain a kind heart, share experience, and make progress together, remember: it is not necessary for others to help you
.