android studio使用jni調用opencv的人臉檢測

android studio使用jni調用opencv的人臉檢測

個人環境

  • Android Studio 3.1.3
  • opencv-3.4.2-android-sdk

環境配置

Android Studio安裝配置(略)

下載安裝opencv-3.4.2-android-sdk

  1. 官網下載 打開官網 Android - OpenCV library, avatar

打開新手教程,找到sdk的下載地址 avatarjava

當前最新版本是3.4.2 avatarandroid

下載便可 avatarc++

  1. 下載完成後解壓到指定位置

工程建立和配置

  1. 新建一個工程,勾選C++支持 avatar

2.勾選C++14支持和其餘兩個選項 avatarapp

3.等待初始化完成ide

4.配置NDK和CMAKE支持 打開File->Settings..., 在SDK Tools下方勾選CMAKE和NDK, Android Studio會自動下載CMAKE和NDK avatar函數

5.引入opencv測試

  • 打開File->Project Structure... 點擊 Import Module avatargradle

  • 選擇opencv安裝目錄的sdk/java的目錄 avatarui

  • 修改Compile Sdk Version 與app 一致, 打開File->Project Structure... app的版本是 API 28 avatarthis

opencv的版本也須要設置爲 API 28 avatar

app添加opencv module avatar avatar avatar

  • 拷貝opencv的so文件和jni用到的頭文件到工程目錄

拷貝opencv安裝目錄下的sdk\native\jni\include文件夾到工程目錄下面的app/src/main/cpp目錄下

拷貝opencv安裝目錄下的sdk\native\libs文件夾到工程目錄下面的app/src/main/jniLibs目錄下

avatar

  • 修改app/build.gradle

在android節點下面添加

sourceSets{
        main{
            jniLibs.srcDirs = ['src/main/jniLibs/libs']
        }
    }

修改androiddefaultConfigcmake/externalNativeBuild/cmake節點

externalNativeBuild {
        cmake {
            cppFlags "-std=c++14 -frtti -fexceptions "
            arguments '-DANDROID_STL=gnustl_static'
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }
  • 修改app/CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)下方插入

# 添加opencv的頭文件目錄
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)

# 導入opencv的so
add_library(libopencv_java3 SHARED IMPORTED)
set_target_properties(libopencv_java3 PROPERTIES IMPORTED_LOCATION
            ${CMAKE_SOURCE_DIR}/src/main/jniLibs/libs/${ANDROID_ABI}/libopencv_java3.so)

修改target_link_libraries

target_link_libraries( # Specifies the target library.
                       native-lib

                       libopencv_java3 # 連接opencv的so

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
  • 測試是否配置成功

修改class MainActivityonCreate函數末尾添加以下代碼,須要先import org.opencv.core.Mat;

Mat mImg = new Mat();
    mImg.release();

若是app可以在手機上正常運行說明配置成功

實現人臉檢測並畫出人臉所在位置

拷貝默認分類器配置文件

首先得拷貝opencv安裝目錄下的sdk\etc\haarcascades\haarcascade_frontalface_default.xml文件到工程目錄app\src\main\res\raw

native-lib.cpp

#include <jni.h>
#include <string>
#include <opencv2/opencv.hpp>

static cv::CascadeClassifier* face_detecter = nullptr;

// 初始化分類器
extern "C" JNIEXPORT void JNICALL
Java_com_xiong_dalton_cvcamerafacedetection_MainActivity_FaceDetecterInit(
        JNIEnv  *jenv,
        jobject /* this */,
        jstring cascadeFileName
){
    const char* cascade_file_name = jenv->GetStringUTFChars(cascadeFileName, NULL);
    if( face_detecter == nullptr){
        face_detecter = new cv::CascadeClassifier(cascade_file_name);
    }
}

// 找出人臉所在位置並標記出來
extern "C" JNIEXPORT void JNICALL
Java_com_xiong_dalton_cvcamerafacedetection_MainActivity_DetectFaces(
        JNIEnv  /* *env */,
        jobject /* this */,
        jlong addrInputRgbaImage
) {
    cv::Mat& image_input = *(cv::Mat*)addrInputRgbaImage;

    cv::Mat image_gray;
    cv::cvtColor(image_input, image_gray, cv::COLOR_RGBA2GRAY);

    auto width = image_input.size().width;
    auto height = image_input.size().height;
    if(face_detecter != nullptr){
        std::vector<cv::Rect> faces;
        face_detecter->detectMultiScale( image_gray, faces, 1.1, 2, 0|cv::CASCADE_SCALE_IMAGE, cv::Size(width/10, height/5));

        for(auto face_rect: faces){
            cv::rectangle(image_input, face_rect, cv::Scalar(255, 0, 0), 3);
        }
    }
}

MainActivity.java

package com.xiong.dalton.cvcamerafacedetection;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.WindowManager;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Mat;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends Activity implements CameraBridgeViewBase.CvCameraViewListener2 {
    static final String TAG = "MainActivity";

    CameraBridgeViewBase mOpenCvCameraView;
    File mCascadeFile;
    Mat mRgba;

    static {
        System.loadLibrary("native-lib");
    }


    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {

        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    Log.i(TAG, "OpenCV loaded successfully");

                    // 使用opencv自帶的分類器文件初始化
                    try{
                        InputStream is = getResources().openRawResource(R.raw.haarcascade_frontalface_default);
                        File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
                        mCascadeFile = new File(cascadeDir, "haarcascade_frontalface_default.xml");
                        FileOutputStream os = new FileOutputStream(mCascadeFile);

                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = is.read(buffer)) != -1) {
                            os.write(buffer, 0, bytesRead);
                        }
                        is.close();
                        os.close();

                        FaceDetecterInit(mCascadeFile.getAbsolutePath());  //調用jni的初始化接口

                        cascadeDir.delete();
                    } catch (IOException e){
                        e.printStackTrace();
                        Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
                    }

                    mOpenCvCameraView.enableView();
                }

                break;
                default: {
                    super.onManagerConnected(status);
                }
            }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

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

        setContentView(R.layout.activity_main);

        mOpenCvCameraView = findViewById(R.id.cameraView);
        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
        mOpenCvCameraView.setCvCameraViewListener(this);
        mOpenCvCameraView.enableFpsMeter();  //顯示fps信息
    }

    @Override
    public void onPause() {
        super.onPause();
        disableCamera();
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        disableCamera();
    }

    public void disableCamera() {
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    public void onCameraViewStarted(int width, int height) {

    }

    public void onCameraViewStopped() {

    }

    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        mRgba = inputFrame.rgba();

        DetectFaces(mRgba.getNativeObjAddr());
        return mRgba;
    }

    public native long FaceDetecterInit(String cascadeFileName);
    public native long DetectFaces(long addrInputRgbaImage);
}

最終效果

avatar

相關文章
相關標籤/搜索