OpenCV 在 Android Studio 的使用教程

本文内容是本人经过多次踩坑,并参考网上众多OpenCV On Android的配置教程总结而来,尽希望能帮助学习移动图像处理的朋友们少走弯路,如有转载,请标明出处

开发环境

Android Studio:Version 3.6.1

SDK:Android Studio 3.6.1自带的最新SDK。

NDK:Android Studio 3.6.1自带的最新NDK

OpenCV:V4.5.1

注:以上配置向上兼容,读者可使用更新的版本,但低版本可能出现错误

(本人开发时间:2021/3/30)

第一步:安装必要的组件

1、打开Android Studio。如果是欢迎界面,选择Configure->SDK Manager。如果是项目界面,选择Tools->Android->SDK Manager

2、将选项条切换到SDK Tools,勾上左下角的Show Package Details,然后勾选以下四项,然后OK,开始下载。

          下载完后,就可以开始创建项目了。

第二步:创建Android Studio工程 

 Create New Projec,开始选择模板,这时选择最后一项Native C++,然后进入配置界面。

 

这一步需要注意两个地方

1、包名,请尽量与我保持一致,否则新手容易出错。
2、最小SDK:OpenCV 4.5.1要求最小SDK必须大于21。

 

这一步选择C++14

下一步直接Finish,项目创建成功!

 第三步:OpenCV Java库使用指南

3.1、环境配置:

第一步、将OpenCV Java库作为Module导入。具体步骤为:File->New->Import Module,然后将OpenCV-android-sdk\sdk\java目录导入,最好顺便将模块名从java改成opencv。如下图,然后Next->Finish

点击 next -> finish

第二步、将导入的opencv模块从application改成library,步骤如下:

第三步、给项目添加opencv依赖

菜单File->Project Structure,在Dependencies中选择app,点击+,选择Module dependency,如下图:

勾选上opencv模块,点击OK即可!

3.2、将opencv注入apk:

1、在手机中安装OpenCV Manager.apk

这种方式极其不好,原因有以下几点:

1、用户需要单独安装这个apk,且不同CPU架构的手机,apk也不一样。
2、Android高版本中,即使安装了这个apk,我们的程序也可能不会正常运行。这是因为一些手机厂商,为了防止应用通过相互唤醒来实现应用保活,所以对不同应用进程间调用进行了限制。比如华为手机管家中的启动管理。
3、OpenCV高版本现已不再提供OpenCV Manager.apk

2、将libopencv_java4.so导入到apk中

这种方式的缺点就是:麻烦!!!

如果我们已经确定了目标机型,这种方式无疑是比较好的。通常来说,如果是真机,导入armeabiarmeabi-v7a架构的so文件;如果是虚拟机,则一般选择x86架构的so文件。

 

方法2实现步骤如下:

1、将OpenCV库中的OpenCV-android-sdk\sdk\native\libs目录下4个子目录,copy到我们项目的libs目录下。

2、修改build.gradle文件,添加以下内容:

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

做完这一步,libopencv_java4.so将被自动打包进apk中,但是依旧不能正常运行,提示缺少c++_shared,这需要我们再次修改build.gradle文件,添加arguments:

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

做完以上内容,基本上就OK了。

第四步:OpenCV NDK库使用指南

4.1、环境配置:

配置方式:打开CMakeLists.txt,内容修改如下,(将OpenCV_DIR设置为你的路径,注意分隔符,使用'/'或'\\')

cmake_minimum_required(VERSION 3.4.1)

# ##################### OpenCV 环境 ############################
#设置OpenCV-android-sdk路径
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)

# ###################### 项目原生模块 ###########################

add_library( native-lib
             SHARED
             native-lib.cpp)

target_link_libraries( native-lib
                       ${OpenCV_LIBS}
                       log
                       jnigraphics)

OK,环境配置好了,嘿嘿嘿,接下来开始代码编写。

4.2、代码编写

下面开始编写布局文件activity_main.xml,内容如下:

<?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>

MainActivity.java内容如下:

package com.wust.opencv;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private ImageView imageView;

    static {//加载so库
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        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);
        }
    }

    //获得Canny边缘
    native void getEdge(Object bitmap);
}

 将一张名为test.jpg的图片放置在drawable目录下.

 应用层写好了,现在开始原生层操作:

进入 src/main/java 目录下 

仔细检查一下,路径不要搞错了 

输入如下命令: 

javah -encoding UTF-8 包名.类名
javah -encoding UTF-8 com.wust.opencv.MainActivity

你会发现在Java文件夹下多一个头文件,将该头文件移动到app\src\main\cpp目录下。

第二步:编写NDK代码

native-lib.cpp内容修改为

#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);
}

注意,naive-lib.cpp中只有一个函数,这个函数名必须与生成的头文件中定义的一致

运行程序,点击SHOW按钮,效果如下: 

点击PROCESS,效果如下:

总结

如果大家觉得这篇文章帮助你了,可以支持一下。

猜你喜欢

转载自blog.csdn.net/qq_41885673/article/details/115324283
今日推荐