查看原文
其他

Android平台上OpenCV 深度网络实现对象检测

gloomyfish OpenCV学堂 2020-02-04

Android平台上OpenCV 深度网络实现对象检测

自OpenCV3.3发布包含深度神经网络(DNN)模块的SDK以后,OpenCV4Android SDK就开始支持Android客户端使用深度学习实现对象检测,特别是基于SSD的mobilenet网络模型,可以在移动端达到较高的帧率,实时视频对象检测,SSD mobilenet支持20种对象检测。模型的下载可以到本人GIHUB:

https://github.com/gloomyfish1998/opencv_tutorial

在data/model目录里面下载即可。

下面就说说如何在Android Studio中开发一个基于深度学习-SSD网络的对象检测演示程序。

一:下载与导入网络模型

从上面的GITHUB目录下载模型文件与描述文件之后,在Android Studio中新建一个空项目,导入OpenCV4Android 3.4的SDK支持,如果不知道怎么配置Android Studio与OpenCV4Android SDK,就请看下面这篇文章即可:

OpenCV3.2集成Android Studio2.2开发配置

配置好之后,新建res/raw目录,然后copy下载好的模型文件与描述文件到raw目录里面,修改文件名称,最终整个目录结构如下:

然后通过如下代码加载SSD网络的权重数据与描述

  • 加载数据文件

  1. private String initBinaryData()throws IOException {

  2.    InputStream input = getResources().openRawResource(R.raw.mobilenetssd_binary);

  3.    File cascadeDir = this.getDir("ssd", Context.MODE_PRIVATE);

  4.    File file = new File(cascadeDir.getAbsolutePath() + "mobilenetssd_binary.caffemodel");

  5.    FileOutputStream out = new FileOutputStream(file);

  6.    byte[] buff = new byte[1024];

  7.    int len = 0;

  8.    while((len = input.read(buff)) != -1) {

  9.        out.write(buff, 0, len);

  10.    }

  11.    input.close();

  12.    out.close();

  13.    return file.getAbsolutePath();

  14. }

  • 加载描述文件

  1. private String initDescData()throws IOException {

  2.    InputStream input = getResources().openRawResource(R.raw.mobilenetssd_desc);

  3.    File cascadeDir = this.getDir("ssd", Context.MODE_PRIVATE);

  4.    File file = new File(cascadeDir.getAbsolutePath() + "mobilenetssd_desc.prototxt");

  5.    FileOutputStream out = new FileOutputStream(file);

  6.    byte[] buff = new byte[1024];

  7.    int len = 0;

  8.    while((len = input.read(buff)) != -1) {

  9.        out.write(buff, 0, len);

  10.    }

  11.    input.close();

  12.    out.close();

  13.    return file.getAbsolutePath();

  14. }

二:使用JavaCameraView打开Android手机摄像头

在Android平台上使用摄像头,需要调用OpenCV4Android SDK的JavaCameraView来打开摄像头实现预览。通过CvCameraViewListener2接口类加载监听摄像对预览帧数据的接收、处理、与返回显示,该类有三个方法需要重载,分别是:

  • 相机启动时候调用

  1. public void onCameraViewStarted(int width, int height);

  • 相机停止时候调用

  1. public void onCameraViewStopped();

  • 相机工作时候,预览帧接收与处理

  1. public Mat onCameraFrame(CvCameraViewFrame inputFrame);

首先看一下JavaCamerview的布局定义:

  1. <?xml version="1.0" encoding="utf-8"?>

  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

  3.    xmlns:tools="http://schemas.android.com/tools"

  4.    android:layout_width="match_parent"

  5.    android:layout_height="match_parent"

  6.    android:paddingTop="3dp"

  7.    android:paddingBottom="3dp"

  8.    android:paddingLeft="3dp"

  9.    android:paddingRight="3dp"

  10.    tools:context="com.example.zhigang.ssddemo.MainActivity">

  11.    <org.opencv.android.JavaCameraView

  12.        android:layout_width="match_parent"

  13.        android:layout_height="match_parent"

  14.        android:visibility="gone"

  15.        android:id="@+id/camera_view_id"/>

  16. </RelativeLayout>

然后通过下面的一段代码打开相机,设置监听

  1. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

  2. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

  3. mcameraView = findViewById(R.id.camera_view_id);

  4. mcameraView.setVisibility(SurfaceView.VISIBLE);

  5. mcameraView.setCvCameraViewListener(MainActivity.this); // setup frame listener

  6. mcameraView.setCameraIndex(0);

  7. mcameraView.enableFpsMeter();

  8. mcameraView.enableView();

在重载的相机开始方法中实现SSD网络初始化:

  1. @Override

  2. public void onCameraViewStarted(int width, int height) {

  3.    try {

  4.        net = Dnn.readNetFromCaffe(initDescData(), initBinaryData());

  5.    } catch (IOException ioe) {

  6.        ioe.printStackTrace();

  7.    }

  8. }

在MainActiviy的onCreate方法最后调用下面代码,加载OpenCV库支持

  1. //OpenCV库静态加载并初始化

  2. private void staticLoadCVLibraries(){

  3.    boolean load = OpenCVLoader.initDebug();

  4.    if(load) {

  5.        Log.i("CV", "Open CV Libraries loaded...");

  6.    }

  7. }

最后千万别忘记在manifest.xml中添加相机权限支持:

  1. <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

  2. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  3. <uses-permission android:name="android.permission.CAMERA" />

  4. <uses-feature android:name="android.hardware.camera" />

  5. <uses-feature android:name="android.hardware.camera.autofocus" />

  6. <uses-feature android:name="android.hardware.camera.flash" />

三:在预览帧中检测对象

在重载的预览帧处理方法onCameraFrame中实现SSD网络对每帧图像的实时对象检测,代码实现如下:

  1. // Get a new frame

  2. Imgproc.cvtColor(frame, frame, Imgproc.COLOR_RGBA2RGB);

  3. // Forward image through network.

  4. Mat blob = Dnn.blobFromImage(frame, IN_SCALE_FACTOR,

  5.        new Size(IN_WIDTH, IN_HEIGHT),

  6.        new Scalar(MEAN_VAL, MEAN_VAL, MEAN_VAL), false, false);

  7. net.setInput(blob);

  8. Mat detections = net.forward();

  9. int cols = frame.cols();

  10. int rows = frame.rows();

  11. Size cropSize;

  12. if ((float)cols / rows > WH_RATIO) {

  13.    cropSize = new Size(rows * WH_RATIO, rows);

  14. } else {

  15.    cropSize = new Size(cols, cols / WH_RATIO);

  16. }

  17. int y1 = (int)(rows - cropSize.height) / 2;

  18. int y2 = (int)(y1 + cropSize.height);

  19. int x1 = (int)(cols - cropSize.width) / 2;

  20. int x2 = (int)(x1 + cropSize.width);

  21. Mat subFrame = frame.submat(y1, y2, x1, x2);

  22. cols = subFrame.cols();

  23. rows = subFrame.rows();

  24. detections = detections.reshape(1, (int)detections.total() / 7);

  25. for (int i = 0; i < detections.rows(); ++i) {

  26.    double confidence = detections.get(i, 2)[0];

  27.    if (confidence > THRESHOLD) {

  28.        int classId = (int)detections.get(i, 1)[0];

  29.        int xLeftBottom = (int)(detections.get(i, 3)[0] * cols);

  30.        int yLeftBottom = (int)(detections.get(i, 4)[0] * rows);

  31.        int xRightTop   = (int)(detections.get(i, 5)[0] * cols);

  32.        int yRightTop   = (int)(detections.get(i, 6)[0] * rows);

  33.        // Draw rectangle around detected object.

  34.        Imgproc.rectangle(subFrame, new Point(xLeftBottom, yLeftBottom),

  35.                new Point(xRightTop, yRightTop),

  36.                new Scalar(0, 255, 0));

  37.        String label = classNames[classId] + ": " + confidence;

  38.        int[] baseLine = new int[1];

  39.        Size labelSize = Imgproc.getTextSize(label, Core.FONT_HERSHEY_SIMPLEX, 0.5, 1, baseLine);

  40.        // Draw background for label.

  41.        Imgproc.rectangle(subFrame, new Point(xLeftBottom, yLeftBottom - labelSize.height),

  42.                new Point(xLeftBottom + labelSize.width, yLeftBottom + baseLine[0]),

  43.                new Scalar(255, 255, 255), Core.FILLED);

  44.        // Write class name and confidence.

  45.        Imgproc.putText(subFrame, label, new Point(xLeftBottom, yLeftBottom),

  46.                Core.FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(0, 0, 0));

  47.    }

  48. }

运行显示截图如下:


伏久者,飞必高;

开先者,谢独早!


关注【OpenCV学堂】

长按或者扫码下面二维码即可关注

+OpenCV识别交流群 657875553

进群暗号:OpenCV


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存