Android sample for DNN module
BIN
doc/tutorials/dnn/dnn_android/10_opencv_dependency.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
doc/tutorials/dnn/dnn_android/11_demo.jpg
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
doc/tutorials/dnn/dnn_android/1_start_new_project.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
doc/tutorials/dnn/dnn_android/2_start_new_project.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
doc/tutorials/dnn/dnn_android/3_start_new_project.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
doc/tutorials/dnn/dnn_android/4_start_new_project.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
doc/tutorials/dnn/dnn_android/5_setup.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
doc/tutorials/dnn/dnn_android/6_run_empty_project.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
doc/tutorials/dnn/dnn_android/7_import_module.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
doc/tutorials/dnn/dnn_android/8_import_module.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
doc/tutorials/dnn/dnn_android/9_opencv_dependency.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
97
doc/tutorials/dnn/dnn_android/dnn_android.markdown
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# How to run deep networks on Android device {#tutorial_dnn_android}
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
In this tutorial you'll know how to run deep learning networks on Android device
|
||||||
|
using OpenCV deep learning module.
|
||||||
|
|
||||||
|
Tutorial was written for the following versions of corresponding software:
|
||||||
|
- Android Studio 2.3.3
|
||||||
|
- OpenCV 3.3.0
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Download and install Android Studio from https://developer.android.com/studio.
|
||||||
|
|
||||||
|
- Get the latest pre-built OpenCV for Android release from https://github.com/opencv/opencv/releases and unpack it. At this moment it's an `opencv-3.3.0-android-sdk.zip`.
|
||||||
|
|
||||||
|
- Download MobileNet object detection model from https://github.com/chuanqi305/MobileNet-SSD. We need a configuration file `MobileNetSSD_deploy.prototxt` and weights `MobileNetSSD_deploy.caffemodel`.
|
||||||
|
|
||||||
|
## Create an empty Android Studio project
|
||||||
|
- Open Android Studio. Start a new project. Let's call it `opencv_mobilenet`.
|
||||||
|

|
||||||
|
|
||||||
|
- Keep default target settings.
|
||||||
|

|
||||||
|
|
||||||
|
- Use "Empty Activity" template. Name activity as `MainActivity` with a
|
||||||
|
corresponding layout `activity_main`.
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Wait until a project was created. Go to `Run->Edit Configurations`.
|
||||||
|
Choose `USB Device` as target device for runs.
|
||||||
|

|
||||||
|
Plug in your device and run the project. It should be installed and launched
|
||||||
|
successfully before we'll go next.
|
||||||
|
@note Read @ref tutorial_android_dev_intro in case of problems.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Add OpenCV dependency
|
||||||
|
|
||||||
|
- Go to `File->New->Import module` and provide a path to `unpacked_OpenCV_package/sdk/java`. The name of module detects automatically.
|
||||||
|
Disable all features that Android Studio will suggest you on the next window.
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Open two files:
|
||||||
|
|
||||||
|
1. `AndroidStudioProjects/opencv_mobilenet/app/build.gradle`
|
||||||
|
|
||||||
|
2. `AndroidStudioProjects/opencv_mobilenet/openCVLibrary330/build.gradle`
|
||||||
|
|
||||||
|
Copy both `compileSdkVersion` and `buildToolsVersion` from the first file to
|
||||||
|
the second one.
|
||||||
|
|
||||||
|
`compileSdkVersion 14` -> `compileSdkVersion 26`
|
||||||
|
|
||||||
|
`buildToolsVersion "25.0.0"` -> `buildToolsVersion "26.0.1"`
|
||||||
|
|
||||||
|
- Make the project. There is no errors should be at this point.
|
||||||
|
|
||||||
|
- Go to `File->Project Structure`. Add OpenCV module dependency.
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Install once an appropriate OpenCV manager from `unpacked_OpenCV_package/apk`
|
||||||
|
to target device.
|
||||||
|
@code
|
||||||
|
adb install OpenCV_3.3.0_Manager_3.30_armeabi-v7a.apk
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
- Congratulations! We're ready now to make a sample using OpenCV.
|
||||||
|
|
||||||
|
## Make a sample
|
||||||
|
Our sample will takes pictures from a camera, forwards it into a deep network and
|
||||||
|
receives a set of rectangles, class identifiers and confidence values in `[0, 1]`
|
||||||
|
range.
|
||||||
|
|
||||||
|
- First of all, we need to add a necessary widget which displays processed
|
||||||
|
frames. Modify `app/src/main/res/layout/activity_main.xml`:
|
||||||
|
@include android/mobilenet-objdetect/res/layout/activity_main.xml
|
||||||
|
|
||||||
|
- Put downloaded `MobileNetSSD_deploy.prototxt` and `MobileNetSSD_deploy.caffemodel`
|
||||||
|
into `app/build/intermediates/assets/debug` folder.
|
||||||
|
|
||||||
|
- Modify `/app/src/main/AndroidManifest.xml` to enable full-screen mode, set up
|
||||||
|
a correct screen orientation and allow to use a camera.
|
||||||
|
@include android/mobilenet-objdetect/AndroidManifest.xml
|
||||||
|
|
||||||
|
- Replace content of `app/src/main/java/org/opencv/samples/opencv_mobilenet/MainActivity.java`:
|
||||||
|
@include android/mobilenet-objdetect/src/org/opencv/samples/opencv_mobilenet/MainActivity.java
|
||||||
|
|
||||||
|
- Launch an application and make a fun!
|
||||||
|

|
||||||
28
samples/android/mobilenet-objdetect/AndroidManifest.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.opencv.samples.opencv_mobilenet">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.AppCompat.NoActionBar"> <!--Full screen mode-->
|
||||||
|
<activity android:name=".MainActivity"
|
||||||
|
android:screenOrientation="landscape"> <!--Screen orientation-->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
<!--Allow to use a camera-->
|
||||||
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
|
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||||
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
||||||
|
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
|
||||||
|
<uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
6
samples/android/mobilenet-objdetect/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
set(sample example-mobilenet-objdetect)
|
||||||
|
|
||||||
|
add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET})
|
||||||
|
if(TARGET ${sample})
|
||||||
|
add_dependencies(opencv_android_examples ${sample})
|
||||||
|
endif()
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context="org.opencv.samples.opencv_mobilenet.MainActivity">
|
||||||
|
|
||||||
|
<org.opencv.android.JavaCameraView
|
||||||
|
android:id="@+id/CameraView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="visible" />
|
||||||
|
</FrameLayout>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">opencv_mobilenet</string>
|
||||||
|
</resources>
|
||||||
@ -0,0 +1,182 @@
|
|||||||
|
package org.opencv.samples.opencv_mobilenet;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.opencv.android.BaseLoaderCallback;
|
||||||
|
import org.opencv.android.CameraBridgeViewBase;
|
||||||
|
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
|
||||||
|
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
|
||||||
|
import org.opencv.android.LoaderCallbackInterface;
|
||||||
|
import org.opencv.android.OpenCVLoader;
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.Point;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.core.Size;
|
||||||
|
import org.opencv.dnn.Net;
|
||||||
|
import org.opencv.dnn.Dnn;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity implements CvCameraViewListener2 {
|
||||||
|
|
||||||
|
// Initialize OpenCV manager.
|
||||||
|
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
|
||||||
|
@Override
|
||||||
|
public void onManagerConnected(int status) {
|
||||||
|
switch (status) {
|
||||||
|
case LoaderCallbackInterface.SUCCESS: {
|
||||||
|
Log.i(TAG, "OpenCV loaded successfully");
|
||||||
|
mOpenCvCameraView.enableView();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
super.onManagerConnected(status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, mLoaderCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
// Set up camera listener.
|
||||||
|
mOpenCvCameraView = (CameraBridgeViewBase)findViewById(R.id.CameraView);
|
||||||
|
mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
|
||||||
|
mOpenCvCameraView.setCvCameraViewListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load a network.
|
||||||
|
public void onCameraViewStarted(int width, int height) {
|
||||||
|
String proto = getPath("MobileNetSSD_deploy.prototxt", this);
|
||||||
|
String weights = getPath("MobileNetSSD_deploy.caffemodel", this);
|
||||||
|
net = Dnn.readNetFromCaffe(proto, weights);
|
||||||
|
Log.i(TAG, "Network loaded successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
|
||||||
|
final int IN_WIDTH = 300;
|
||||||
|
final int IN_HEIGHT = 300;
|
||||||
|
final float WH_RATIO = (float)IN_WIDTH / IN_HEIGHT;
|
||||||
|
final double IN_SCALE_FACTOR = 0.007843;
|
||||||
|
final double MEAN_VAL = 127.5;
|
||||||
|
final double THRESHOLD = 0.2;
|
||||||
|
|
||||||
|
// Get a new frame
|
||||||
|
Mat frame = inputFrame.rgba();
|
||||||
|
Imgproc.cvtColor(frame, frame, Imgproc.COLOR_RGBA2RGB);
|
||||||
|
|
||||||
|
// Forward image through network.
|
||||||
|
Mat blob = Dnn.blobFromImage(frame, IN_SCALE_FACTOR,
|
||||||
|
new Size(IN_WIDTH, IN_HEIGHT),
|
||||||
|
new Scalar(MEAN_VAL, MEAN_VAL, MEAN_VAL), true);
|
||||||
|
net.setInput(blob);
|
||||||
|
Mat detections = net.forward();
|
||||||
|
|
||||||
|
int cols = frame.cols();
|
||||||
|
int rows = frame.rows();
|
||||||
|
|
||||||
|
Size cropSize;
|
||||||
|
if ((float)cols / rows > WH_RATIO) {
|
||||||
|
cropSize = new Size(rows * WH_RATIO, rows);
|
||||||
|
} else {
|
||||||
|
cropSize = new Size(cols, cols / WH_RATIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
int y1 = (int)(rows - cropSize.height) / 2;
|
||||||
|
int y2 = (int)(y1 + cropSize.height);
|
||||||
|
int x1 = (int)(cols - cropSize.width) / 2;
|
||||||
|
int x2 = (int)(x1 + cropSize.width);
|
||||||
|
Mat subFrame = frame.submat(y1, y2, x1, x2);
|
||||||
|
|
||||||
|
cols = subFrame.cols();
|
||||||
|
rows = subFrame.rows();
|
||||||
|
|
||||||
|
detections = detections.reshape(1, (int)detections.total() / 7);
|
||||||
|
|
||||||
|
for (int i = 0; i < detections.rows(); ++i) {
|
||||||
|
double confidence = detections.get(i, 2)[0];
|
||||||
|
if (confidence > THRESHOLD) {
|
||||||
|
int classId = (int)detections.get(i, 1)[0];
|
||||||
|
|
||||||
|
int xLeftBottom = (int)(detections.get(i, 3)[0] * cols);
|
||||||
|
int yLeftBottom = (int)(detections.get(i, 4)[0] * rows);
|
||||||
|
int xRightTop = (int)(detections.get(i, 5)[0] * cols);
|
||||||
|
int yRightTop = (int)(detections.get(i, 6)[0] * rows);
|
||||||
|
|
||||||
|
// Draw rectangle around detected object.
|
||||||
|
Imgproc.rectangle(subFrame, new Point(xLeftBottom, yLeftBottom),
|
||||||
|
new Point(xRightTop, yRightTop),
|
||||||
|
new Scalar(0, 255, 0));
|
||||||
|
String label = classNames[classId] + ": " + confidence;
|
||||||
|
int[] baseLine = new int[1];
|
||||||
|
Size labelSize = Imgproc.getTextSize(label, Core.FONT_HERSHEY_SIMPLEX, 0.5, 1, baseLine);
|
||||||
|
|
||||||
|
// Draw background for label.
|
||||||
|
Imgproc.rectangle(subFrame, new Point(xLeftBottom, yLeftBottom - labelSize.height),
|
||||||
|
new Point(xLeftBottom + labelSize.width, yLeftBottom + baseLine[0]),
|
||||||
|
new Scalar(255, 255, 255), Core.FILLED);
|
||||||
|
|
||||||
|
// Write class name and confidence.
|
||||||
|
Imgproc.putText(subFrame, label, new Point(xLeftBottom, yLeftBottom),
|
||||||
|
Core.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCameraViewStopped() {}
|
||||||
|
|
||||||
|
// Upload file to storage and return a path.
|
||||||
|
private static String getPath(String file, Context context) {
|
||||||
|
AssetManager assetManager = context.getAssets();
|
||||||
|
|
||||||
|
BufferedInputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
// Read data from assets.
|
||||||
|
inputStream = new BufferedInputStream(assetManager.open(file));
|
||||||
|
byte[] data = new byte[inputStream.available()];
|
||||||
|
inputStream.read(data);
|
||||||
|
inputStream.close();
|
||||||
|
|
||||||
|
// Create copy file in storage.
|
||||||
|
File outFile = new File(context.getFilesDir(), file);
|
||||||
|
FileOutputStream os = new FileOutputStream(outFile);
|
||||||
|
os.write(data);
|
||||||
|
os.close();
|
||||||
|
// Return a path to file which may be read in common way.
|
||||||
|
return outFile.getAbsolutePath();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Log.i(TAG, "Failed to upload a file");
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TAG = "OpenCV/Sample/MobileNet";
|
||||||
|
private static final String[] classNames = {"background",
|
||||||
|
"aeroplane", "bicycle", "bird", "boat",
|
||||||
|
"bottle", "bus", "car", "cat", "chair",
|
||||||
|
"cow", "diningtable", "dog", "horse",
|
||||||
|
"motorbike", "person", "pottedplant",
|
||||||
|
"sheep", "sofa", "train", "tvmonitor"};
|
||||||
|
|
||||||
|
private Net net;
|
||||||
|
private CameraBridgeViewBase mOpenCvCameraView;
|
||||||
|
}
|
||||||