Android 使用OpenCV的三种方式(Android Studio)

2023-11-02

其实最早接触OpenCV是很久很久之前的事了,大概在2013年的5,6月份,当时还是个菜逼(虽然现在也是个菜逼),在那一段时间,学了一段时间的android(并不算学,一个月都不到),之后再也没接触android,而是一直在接触java web。那次接触OpenCV是因为一个学长的毕业设计,这次接触OpenCV是因为自己的毕业设计。2013年那年技术太菜,ndk环境都搭不好,当初还是eclipse环境,一直按照网上的教程去搭,下什么cygwin,简直就是个坑,网上的文章转来转去,都是过时的。后来一个机会看到了google官方的一个文档,就像发现了新大陆一样,发现ndk环境根本不需要装cygwin,装了你就坑了,装这个东西有好多G呢,时间浪费不说,简直误人子弟啊。后来在那年7月写下一篇博客

NDK开发环境

这段时间在填自己毕业设计的坑,要用到OpenCV,首先得下载到sdk吧,这个从官网上下载就好了
OpenCV for Android

注意下载的是OpenCV for android。当前版本是3.0

这里写图片描述

解压后,里面的内容如下

这里写图片描述

samples目录下是样例代码,sdk目录下是我们需要用到的java层和jni层的代码。apk目录是manager的apk安装包

其实OpenCV最简单的使用方式是使用manager,也就是使用apk目录下的安装包,安装对应的apk,将java层代码导入,使用OpenCVLoader.initAsync()加载库,之后你就可以直接用java代码调用Opencv相关的功能了。

这里写图片描述

但是这种方式除了安装我们自己的apk还需要安装上面提到的manager的apk,用户体验十分不好,不推荐使用,本文的三种方式将完全脱离这个manager的apk。

本文下面的三种方式的内容参考自文章 OpenCV4Android释疑: 透析Android以JNI调OpenCV的三种方式(让OpenCVManager永不困扰)

本篇文章使用android studio作为开发环境,由于实验性的构建工具对ndk支持还不好,所以使用旧的构建方式,在原来写的一篇博客基础上修改即可android studio下ndk开发

这正式介绍三种方式之前,我们需要做一些前期准备。

首先新建一个项目,将OpenCV中sdk目录下的native目录拷到项目根目录

这里写图片描述

然后新建Jni目录

这里写图片描述

在里面新建两个文件

这里写图片描述

编辑gradle.properties文件,增加下面的属性使用旧版的ndk功能(不添加会使用实验性的ndk构建工具)

android.useDeprecatedNdk=true

在local.properties文件中配置ndk目录

ndk.dir=D\:\\AndroidSDK\\sdk\\ndk-bundle

编辑build.gradle,在android节点中增加下面的代码

sourceSets.main.jni.srcDirs = []
    //禁止自带的ndk功能
    sourceSets.main.jniLibs.srcDirs = ['src/main/libs','src/main/jniLibs']
    //重定向so目录为src/main/libs和src/main/jniLibs,原来为src/main/jniLibs

    task ndkBuild(type: Exec, description: 'Compile JNI source with NDK') {
        Properties properties = new Properties()
        properties.load(project.rootProject.file('local.properties').newDataInputStream())
        def ndkDir = properties.getProperty('ndk.dir')

        if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {
            commandLine "$ndkDir/ndk-build.cmd", '-C', file('src/main/jni').absolutePath
        } else {
            commandLine "$ndkDir/ndk-build", '-C', file('src/main/jni').absolutePath
        }
    }

    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn ndkBuild
    }

    task ndkClean(type: Exec, description: 'Clean NDK Binaries') {
        Properties properties = new Properties()
        properties.load(project.rootProject.file('local.properties').newDataInputStream())
        def ndkDir = properties.getProperty('ndk.dir')

        if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {
            commandLine "$ndkDir/ndk-build.cmd",'clean', '-C', file('src/main/jni').absolutePath
        } else {
            commandLine "$ndkDir/ndk-build",'clean', '-C', file('src/main/jni').absolutePath
        }
    }

    clean.dependsOn 'ndkClean'

在之前新建的Application.mk中增加下面的内容

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi armeabi-v7a
APP_PLATFORM := android-8

在Android.mk中增加下面的内容

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)


OpenCV_INSTALL_MODULES := on
OpenCV_CAMERA_MODULES := off

OPENCV_LIB_TYPE :=STATIC

ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
include ..\..\..\..\native\jni\OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif

LOCAL_MODULE := OpenCV

LOCAL_SRC_FILES :=

LOCAL_LDLIBS +=  -lm -llog

include $(BUILD_SHARED_LIBRARY)

这时候,使用gradle构建一下,如果能成功构建出so,说明配置没问题,如下图,点击as右侧的gradle展开,双击ndkBuild进行构建

这里写图片描述

这里写图片描述

下面开始讲第一种方法,纯jni层的代码,该方法基于上面的所有步骤,为静态链接库

声明java层的native方法

public class OpenCVHelper {
    static {
        System.loadLibrary("OpenCV");
    }
    public static native int[] gray(int[] buf, int w, int h);
}

使用javah命令生成头文件,内容如下

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class cn_edu_zafu_opencv_OpenCVHelper */

#ifndef _Included_cn_edu_zafu_opencv_OpenCVHelper
#define _Included_cn_edu_zafu_opencv_OpenCVHelper
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     cn_edu_zafu_opencv_OpenCVHelper
 * Method:    gray
 * Signature: ([III)[I
 */
JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray
        (JNIEnv *, jclass, jintArray, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

新建cpp文件,实现对应的方法,就是灰度处理

#include "cn_edu_zafu_opencv_OpenCVHelper.h"
#include <stdio.h>
#include <stdlib.h>
#include <opencv2/opencv.hpp>

using namespace cv;

extern "C" {

JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(
        JNIEnv *env, jclass obj, jintArray buf, int w, int h);



JNIEXPORT jintArray JNICALL Java_cn_edu_zafu_opencv_OpenCVHelper_gray(
        JNIEnv *env, jclass obj, jintArray buf, int w, int h) {

    jint *cbuf;
    cbuf = env->GetIntArrayElements(buf, JNI_FALSE );
    if (cbuf == NULL) {
        return 0;
    }

    Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);

    uchar* ptr = imgData.ptr(0);
    for(int i = 0; i < w*h; i ++){
        //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
        //对于一个int四字节,其彩色值存储方式为:BGRA
        int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
        ptr[4*i+1] = grayScale;
        ptr[4*i+2] = grayScale;
        ptr[4*i+0] = grayScale;
    }

    int size = w * h;
    jintArray result = env->NewIntArray(size);
    env->SetIntArrayRegion(result, 0, size, cbuf);
    env->ReleaseIntArrayElements(buf, cbuf, 0);
    return result;
}
}

之后,需要将cpp文件编译进去,在Andorid.mk文件中加入

LOCAL_SRC_FILES := cn_edu_zafu_opencv_OpenCVHelper.cpp

然后在java层写个测试方法测试一下是否进行灰度化了

Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(
        R.drawable.ic)).getBitmap();
int w = bitmap.getWidth(), h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int [] resultPixes=OpenCVHelper.gray(pix,w,h);
Bitmap result = Bitmap.createBitmap(w,h, Bitmap.Config.RGB_565);
result.setPixels(resultPixes, 0, w, 0, 0,w, h);
img.setImageBitmap(result);

运行效果如下,灰度化后的结果

这里写图片描述

上面的这种方法生成的so库的大小见下图,大约有1.4M左右

这里写图片描述

第二种方法也是纯jni的,但是是动态链接库,在第一种基础上,修改Android.mk文件为

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)


OpenCV_INSTALL_MODULES := on
OpenCV_CAMERA_MODULES := off

OPENCV_LIB_TYPE := SHARED

ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
include ..\..\..\..\native\jni\OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif

LOCAL_MODULE := OpenCV

LOCAL_SRC_FILES := cn_edu_zafu_opencv_OpenCVHelper.cpp

LOCAL_LDLIBS +=  -lm -llog

include $(BUILD_SHARED_LIBRARY)

注意上面的OPENCV_LIB_TYPE属性的改动,从STATIC改为了SHARED,这时候再用ndkBuild一下,你会发现会输出一些警告以及一部分红色的内容

这里写图片描述

生成的so库的大小为310k,小了好几倍

这里写图片描述

这时候如果你直接取运行程序,会报错误

这里写图片描述

原因是我们使用的是动态库加载方式,还需要将依赖的so加进去,这个so就是图中的libopencv_java3.so,他在我们的最开始加到项目里的native目录中

这里写图片描述

将它拷到我们的jniLibs目录中去,这里只拷贝armeabi和armeabi-v7a中的,至于其他的按需拷贝

这里写图片描述

这时候运行就不会报错了。

既然我们使用了动态链接库,那么我们同样也可以使用java层的接口,优点是java开发速度相对快一点。第三种方法在第二种方法基础上,使用纯java层代码进行处理。

在此之前,我们需要将sdk目录中的java代码拷到项目中去

这里写图片描述

但是org.opencv.engine包中是一个aidl,我们需要将它剪贴到aidl目录中去,就像这样子

这里写图片描述

最后还有一个资源文件attrs.xml,拷过来

这里写图片描述

build一下项目,不出意外应该会报错,这时候找到该类,引入自己的R文件包就可以了

这里写图片描述

再次build应该就不会有什么问题了。

java层的测试方法

OpenCVLoader.initDebug();
Mat rgbMat = new Mat();
Mat grayMat = new Mat();
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic);
Bitmap grayBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.RGB_565);
Utils.bitmapToMat(srcBitmap, rgbMat);//convert original bitmap to Mat, R G B.
Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//rgbMat to gray grayMat
Utils.matToBitmap(grayMat, grayBitmap); //convert mat to bitmap
img.setImageBitmap(grayBitmap);

注意使用OpenCVLoader.initDebug();进行初始化而不是使用OpenCVLoader.initAsync()

这种方法的特点是处理都在java层,不怎么会涉及jni层的代码,除非java层完成不了的工作会转移到jni层去。

三种方法各有各的优点,根据自己的情况进行选择。

  • 如果c++特别好的,建议使用第一种方法
  • 如果更习惯java代码的,并且java层的函数都能进行处理的,建议选择第三种方法
  • 第二种方法建议在第三种方法不满足条件的情况下进行辅助使用,因为使用了第三种方法的前提是使用第二种方法的动态链接库。

最后附上源码

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 使用OpenCV的三种方式(Android Studio) 的相关文章

随机推荐

  • 【LSTM分类】基于白鲸算法优化双向长短期记忆神经网络的数据分类预测附matlab代码 BWO-BiLSTM...

    作者简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 matlab项目合作可私信 个人主页 Matlab科研工作室 个人信条 格物致知 内容介绍 白鲸优化算法 Beluga whale optimization BWO 由Chan
  • 图像处理Matlab(二)——生成随机点

    摘要 生成40个数据对 并作图 程序如下 figure set gcf Position 400 300 200 200 a1 40 set axes Color 128 255 128 255 128 255 hold on C rand
  • CentOS-7,网络ping不通详解

    官网下载地址 https www centos org download 问题1 就是题目说的ifconfig命令不起效果了 而且在sbin目录中没有ifconfig文件 原因 这是因为centos7已经不适用ifconfig命令了 已经用
  • mmsegmentaion安装记录

    mmsegmentaion安装记录 安装anaconda环境 conda create name Hopenmmlab python 3 8 y conda activate Hopenmmlab 安装GPU torch pip3 inst
  • 静态单赋值(二)—gcc中的SSA化算法

    版权声明 本文为CSDN博主 ashimida 的原创文章 遵循CC 4 0 BY SA版权协议 转载请附上原文出处链接及本声明 原文链接 https blog csdn net lidan113lidan article details
  • 分布式高频量化交易系统架构讲解(企业版,期货ctp,股票xtp,数字货币,附全部源码)(值得收藏)

    目录 1 量化交易系统简介 1 1行情数据 1 2交易策略 1 3交易 2 分布式高频量化交易系统 2 1 架构图 2 1 1量化交易系统教程地址 2 1 2量化交易系统教程中讲解的期货ctp知识点 2 2 交易系统功能介绍 2 3 账户系
  • UE4--材质地形篇——地形材质的运用(混合材质)

    引言 我们所做的的地形材质都是多种多样的 有的地形是山地 其中可能有植被与岩石 有的地形是荒漠 它就包括了泥土与沙子 总之 其中材质远不止一种 那么我们如何创建多个材质组成的混合体呢 我们创建材质的时候通常只能创建一种材质 比如透明材质 水
  • easy-monitor3.0 nodejs性能监控和分析工具

    easy monitor性能监控和分析工具 Easy Monitor 3 0 https blog csdn net qq 36791889 article details 115420116 git地址 https github com
  • qbxt国庆刷题班 游记$总结

    今天是 2019 10 4 距离 csp 也就是 AFO 不远了 鬼知道我为什么拖到今天才写这次清北学堂的游记 准确的说鬼知道我为啥要写游记 而且到现在才写 也许是给未来留点回忆吧 Day4 早上懒床拖到 7 26 赶紧吃了昨天买的牛肉粉方
  • Java学习笔记7——类和对象

    类和对象 类的特点 类的定义 定义样例 对象的使用 对象在内存中的位置 类 类是对现实生活种一类具有 类的特点 类是对象得数据类型 类是具有相同属性和行为得一组对象得集合 属性指的是对象具有的各种特征 每个对象的每个属性都有特定的值 行为指
  • 什么是透明、匿名、高匿代理?详解!

    随着大数据的应用越来越广泛 应用的行业也越来越多 我们每日都可以看到大数据的一些新颖的应用 从而帮助人们从中获取到真正有用的价值 随着很多工作的开展 我们需要大量的IP操作 这时为了避免IP被封 使用代理IP是个很好的选择 而IP代理按匿名
  • IDEA+Gradle创建Springboot项目整合mybatis

    1 之前用的maven管理项目 现在公司用gradle 所以自己学习创建一个 特此记录一下 前提是你得配置了gradle 1 首先是build gradle文件 buildscript ext springBootVersion 2 1 2
  • Axios使用AbortController取消请求

    从 v0 22 0 开始 Axios 支持以 fetch API 方式 AbortController 取消请求 const controller new AbortController axios get foo bar1 signal
  • VBA怎么获取单元格的内容/值(数字,文本,公式)(如需获取选中单元格内容,使用select命令即可)

    通过本地窗口可以清晰看出三者的区别 记得按 F8 走调试 直接运行本地窗口在这里出不来结果 Value是单元格的数字内容 Text是文本内容 Formula是最原本的 输入内容 Sub 宏3 测试 就不去定义变量类型了 a Range b1
  • 大语言模型生态系统:助你自由调教 AI 模型

    这些开源项目都是在语言模型领域具有重要影响力的优秀项目 它们共同的特点是强调了对大规模语言模型进行训练和推理的高效性 灵活性和可扩展性 无论是通过提供定制化的语言模型 支持并行计算和分布式训练 还是通过优化内存管理和硬件资源利用效率来提高运
  • Java import 详解

    Java import 详解 1 package 机制 Java 的 package 机制类似于 C 的 namespace 机制 在编写 Java 程序时 随着程序架构越来越大 类的个数也越来越多 这时就会发现管理程序中维护类名称也是一件
  • el-form-item rules validator validate函数传参

    validator只能传3个参数 rule value cb 如果想传入额外的参数来做校验 那么需要通过在rules上嵌套一层 传入参数 如row 之后在函数中定义validator 就可以直接用到自己需要的参数了 我这边需要的是row 校
  • Android Studio apk 文件路径

    out production
  • java实现图片与base64转换

    如果你是一个软件开发 不论前端后端工程师 图片的处理你是肯定要会的 关于图片的Base64编码 你可能有点陌生 但是这是一个软件工程师应该要掌握的知识点 现在很多网友把图片与base64转换都做成了小工具如 http www yzcopen
  • Android 使用OpenCV的三种方式(Android Studio)

    其实最早接触OpenCV是很久很久之前的事了 大概在2013年的5 6月份 当时还是个菜逼 虽然现在也是个菜逼 在那一段时间 学了一段时间的android 并不算学 一个月都不到 之后再也没接触android 而是一直在接触java web