Android OpenCV基础(一、OpenCV入门)-程序员宅基地

技术标签: Android OpenCV基础  android  

一、OpenCV概述

  OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,它提供了很多函数,这些函数非常高效地实现了计算机视觉算法(最基本的滤波到高级的物体检测皆有涵盖)。

  OpenCV 的应用领域非常广泛,包括图像拼接、图像降噪、产品质检、人机交互、人脸识别、动作识别、动作跟踪、无人驾驶等。OpenCV 还提供了机器学习模块,你可以使用正态贝叶斯、K最近邻、支持向量机、决策树、随机森林、人工神经网络等机器学习算法。

二、OpenCV框架

2.1 OpenCV整体结构

  OpenCV 使用 C/C++ 开发,同时也提供了 Python、Java、MATLAB等其他语言的接口,并且提供 Windows、Android(Android库下载)、ios等平台的接口。

1-1Q026141623M5.jpg

  OpenCV是由很多模块组成的,这些模块可以分成很多层:

  • 最底层是基于硬件加速层(HAL)的各种硬件优化。
  • 再上一层是OpenCV核心层,包括core、imgproc、calib3d、objdetect等。
  • 再上一层是opencv_contrib,可以理解为OpenCV的扩展模块,里面包含一些最新的算法,不过API尚未稳定或未充分测试。
  • 接下来是语言绑定和示例应用程序。
  • 处于最上层的是 OpenCV 和操作系统的交互。

2.2 OpenCV核心模块

  我们主要关注OpenCV核心层,下图显示了 OpenCV核心层中包含的模块:

模块 说明
core 该模块包含 OpenCV 库的基础结构以及基本操作。
improc 图像处理模块包含基本的图像转换,包括滤波以及类似的卷积操作。
highgui 在 OpenCV 3.0中,分割为 imcodecs、videoio 以及 highgui 三部分。 这个模块包含可以用来显示图像或者简单的输入的用户交互函数。这可以看作是一个非常轻量级的 Windows UI 工具包。
video 该模块包含读取和写视频流的函数。
calib3d 这个模块包括校准单个、双目以及多个相机的算法实现。
feature2d 这个模块包含用于检测、描述以及匹配特征点的算法。
objdectect 这个模块包含检测特定目标,比如人脸或者行人的算法。也可以训练检测器并用来检测其他物体。
ml 机器学习模块本身是一个非常完备的模块,包含大量的机器学习算法实现并且这些算法都能和 OpenCV 的数据类型自然交互。
flann Flann 的意思是“快速最邻近库”。这个库包含一些你也许不会直接使用的方法,但是其他模块中的函数会调用它在数据集中进行最邻近搜索。
GPU 在 OpenCV 中被分割为多个 cuda* 模块。 GPU 模块主要是函数在 CUDA GPU 上的优化实现,此外,还有一些仅用于 GPU 的功 能。其中一些函数能够返回很好的结果,但是需要足够好的计算资源,如果硬件没有GPU,则不会有什么提升。
photo 这是一个相当新的模块,包含计算摄影学的一些函数工具。
stitching 本模块是一个精巧的图像拼接流程实现。这是库中的新功能,但是,就像 Photo 模块一样,这个领域未来预计有很大的增长。
nonfree 在 OpenCV 3.0 中,被移到 opencv_contrib/xfeatures2d。 OpenCV 包含一些受到专利保护的或者受到使用限制的(比如 SIFT 算法)算法。这些算法被隔离到它们自己的模块中,以表明你需要做一些特殊的工作,才可以在商业产品中使用它们。
contrib 在 OpenCV 3.0 中,融合进了 opencv_contrib。 这个模块包含一些新的、还没有被集成进 OpenCV 库的东西。
legacy 在 OpenCV 3.0 中,被取消。 这个模块包含一些老的尚未被完全取消的东西。
ocl 在OpenCV 3.0 中,被取消,取而代之的是 T-API。 这是一个较新的模块,可以认为它和 GPU 模块相似,它实现了开放并行编程的 Khronos OpenCL 标准。虽然现在模块的特性比 GPU 模块少很多,但 ocl 模块的目标是提供可以运行在任何 GPU 或者是其他可以搭载 Khronos 的并行设备。这与 GPU 模 块形成了鲜明的对比,后者使用 Nividia CUDA 工具包进行开发,因此只能在 Nividia GPU 设备上工作。

  在OpenCV github项目中可以看到Opencv核心的所有模块:
image.png
  如果项目中只需要用到OpenCV中的部分模块,并且项目体积要求比较严格,可根据项目需要裁剪OpenCV,把需要的模块单独打包成so。

三、集成OpenCV

  在集成OpenCV之前,需要了解一些NDK开发的基础知识(可以参考:Android NDK开发基础)。

  要集成OpenCV,可以从OpenCV release页面直接下载官方编译产物,或者自己去OpenCV github下载并按需编译产物。本文主要介绍官方提供的Android平台OpenCV-4.5.5版本集成方式。

image.png

3.1 产物介绍

  在下载了官方Android平台OpenCV-4.5.5产物后,里面内容如下:

  • sample:官方示例;
  • sdk:opencv核心sdk;
    • java:opencv java层;java层包结构与上述opencv模块划分一致,里面提供了对应java类,实际实现采用jni的形式实现;
    • libcxx_helper:存放CMakeList;
    • native:opencv native层;
      • jni:opencv jni层;
      • libs:opencv包含所有核心库及jni的动态库;
      • staticlibs:openvc核心各个模块的静态库;
      • 3rdparty:第三方库;
        拼接图-2.jpeg
          官方产物大小共900M,我们从中选择我们需要的部分进行集成即可,或者从静态库中选择业务中所需要的.a自己编译产物进行集成。

3.2 Java + Native集成

  如果项目中需要在java层编写OpenCV代码,则需要集成java层及native层。

3.2.1 OpenCV Java类简介

  在上述OpenCV java目录中,除了提供了OpenCV核心模块的java类之外,org.opencv.android包内还提供了OpenCVNativeLoader加载类、JavaCameraView、CameraGLSurfaceView相机封装等。

  OpenCV java目录中提供的核心模块java类,底层实现都是通过native层实现,java层只引用了native层对象的地址,例如Opencv基础图像类Mat:

public class Mat {
    
    /**
    * Native object address
    * native层对象地址
    **/
    public final long nativeObj;

    public Mat(long addr) {
    
        if (addr == 0)
            throw new UnsupportedOperationException("Native object address is NULL");
        nativeObj = addr;
    }

    // javadoc: Mat::Mat()
    public Mat() {
    
        nativeObj = n_Mat();
    }
    // C++: Mat::Mat()
    private static native long n_Mat();
    
    // javadoc: Mat::dims()
    public int dims() {
    
        return n_dims(nativeObj);
    }
    // C++: int Mat::dims()
    private static native int n_dims(long nativeObj);
}

  可以看到其对应的jni中,都是调用了opencv核心模块的c/c++方法:

#include "opencv2/core.hpp"

#define LOG_TAG "org.opencv.core.Mat"
#include "common.h"

using namespace cv;
/*
 * Class:     org_opencv_core_Mat
 * Method:    n_Mat
 * Signature: (IIILjava/nio/ByteBuffer;J)J
 *
 * Mat::Mat(int rows, int cols, int type, void* data, size_t step)
 */
JNIEXPORT jlong JNICALL Java_org_opencv_core_Mat_n_1Mat__IIILjava_nio_ByteBuffer_2J
  (JNIEnv* env, jclass, jint rows, jint cols, jint type, jobject data, jlong step);

JNIEXPORT jlong JNICALL Java_org_opencv_core_Mat_n_1Mat__IIILjava_nio_ByteBuffer_2J
  (JNIEnv* env, jclass, jint rows, jint cols, jint type, jobject data, jlong step)
{
    
    static const char method_name[] = "Mat::n_1Mat__IIILjava_nio_ByteBuffer_2J()";
    try {
    
        LOGD("%s", method_name);
        return (jlong) new Mat(rows, cols, type, (void*)env->GetDirectBufferAddress(data), (size_t)step);
    } catch(const std::exception &e) {
    
        throwJavaException(env, &e, method_name);
    } catch (...) {
    
        throwJavaException(env, 0, method_name);
    }

    return 0;
}

3.2.2 集成内容

  要在项目中集成OpenCV,首先我们新建一个opencv_sdk模块,将官网产物中的java目录、libcxx_helper目录、native下的lib目录(架构只选择了主流的arm64-v8a、armeabi-v7a),如下所示:

拼接图-3.jpeg

  至此,opencv_sdk模块集成完毕,可以在其他模块中通过java对象调用opencv的方法。

3.2.3 简单用法

  下面列举一个简单的例子:在java层通过bitmap创建一个OpenCV的Mat,并打印log。首先在build.gradle中加入依赖:

dependencies {
    implementation(project(':opencv_sdk'))
}

  然后就可以在app模块使用OpenCV的java类了:

Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.adventure_time);
// OpenCV提供的加载libopencv_java4.so的封装类
OpenCVNativeLoader openCVNativeLoader = new OpenCVNativeLoader();
openCVNativeLoader.init();
ByteBuffer buffer = ByteBuffer.allocateDirect(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(buffer);
Mat mat = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC4, buffer);
Log.d("OpenCV", "mat channels:"+mat.channels()+", cols:"+mat.cols()+", rows:"+mat.rows());

  运行结果如下,说明Mat已经创建成功:

D/OpenCV: mat channels:4, cols:3000, rows:4500

3.3 Native集成

  如果项目中只需要在native层编写OpenCV代码,则只需要集成native层即可,可以删除上述的java目录以减小包体积。

  项目中的opencv_sdk模块中只需要引入OpenCV头文件(在官网产物的OpenCV-android-sdk/sdk/native/jni/include目录下)和OpenCV动态库,如下所示:

Unknown.jpeg

3.3.1 新建native模块

  首先新建一个sample_native模块来存放jni及native代码,模块结构如下:

Unknown-1.jpeg

3.3.2 配置cmake

  在sample_native模块的build.gradle中配置cmake及opencv:

android {
    defaultConfig {
        ndk {
            // 指定编译的abi架构
            abiFilters "armeabi-v7a", "arm64-v8a"
        }
        externalNativeBuild {
            // cmake配置
            cmake {
                cppFlags "-std=c++11 -frtti -fexceptions"
                arguments "-DANDROID_STL=c++_shared"
                arguments "-DANDROID_TOOLCHAIN=clang"
                // 配置opencv头文件目录和动态库目录
                arguments "-DOPENCV_HEADER_DIR=${project.rootProject.project("opencv_sdk").file("include").path}"
                arguments "-DOPENCV_LIBS_DIR=${project.rootProject.project("opencv_sdk").file("native/libs").path}"
            }
        }
    }
    externalNativeBuild {
        cmake {
            // 构建脚本路径
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

  到此就完成了cmake的基本配置,里面通过arguments配置了opencv头文件和动态库的位置,方面后面代码中引入。

3.3.3 编写jni代码

  在sample_native模块中,创建jni接口如下:

public class OpenCVSample {
    

    private static boolean isLoadSuccess = false;

    static {
    
        try {
    
            System.loadLibrary("native-lib");
        } catch (Throwable throwable) {
    
            throwable.printStackTrace();
        }
    }

    public static native void createMat(Bitmap bitmap);

}

  接着创建cpp目录,并创建native-lib.h头文件和native-lib.cpp文件,以及CMakeLists.txt,在CMakeLists.txt中配置opencv依赖如下:

# cmake最低版本号
cmake_minimum_required(VERSION 3.4.1)

# 导入opencv头文件
include_directories(${OPENCV_HEADER_DIR})

# add_library:把一个library添加到工程
add_library(
        native-lib
        SHARED
        native-lib.cpp)

# 添加一个已构建的库,使用IMPORTED
add_library(opencv_java4 SHARED IMPORTED)
set_target_properties(opencv_java4
        PROPERTIES IMPORTED_LOCATION
        "${OPENCV_LIBS_DIR}/${ANDROID_ABI}/libopencv_java4.so")

# 首个参数是target,后面的参数是item;target必须先用add_library()创建过;
target_link_libraries(
        native-lib
        opencv_java4
        jnigraphics
        log )

  然后把JNI对应的头文件native-lib.h代码完善如下:

#include <jni.h>
#include <android/log.h>
#include <android/bitmap.h>

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>

#define TAG    "NativeLibSample"
#define AndroidLOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)

extern "C"
JNIEXPORT void JNICALL
Java_com_bc_sample_OpenCVSample_createMat(
        JNIEnv* env,
        jclass thiz, jobject bitmap);

void bitmapToMat(JNIEnv *env, jobject bitmap, cv::Mat &dst);

  然后,把JNI对应的native-lib.cpp文件代码完善如下,这里在c++代码中创建了Mat,然后打印出Mat的信息:

#include "native-lib.h"

extern "C"
JNIEXPORT void JNICALL
Java_com_bc_sample_OpenCVSample_createMat(
        JNIEnv* env,
        jclass thiz, jobject bitmap) {
    
    cv::Mat rgbMat;
    // 把java层的bitmap转换为Mat
    bitmapToMat(env, bitmap, rgbMat);
    // log输出Mat的信息
    AndroidLOGD("native mat channels:%d, cols:%d, rows:%d", rgbMat.channels(), rgbMat.cols, rgbMat.rows);
}

void bitmapToMat(JNIEnv *env, jobject bitmap, cv::Mat &dst) {
    
    AndroidBitmapInfo info;
    void *pixels = 0;
    try {
    
        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);
        dst.create(info.height, info.width, CV_8UC4);
        if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
    
            cv::Mat tmp(info.height, info.width, CV_8UC4, pixels);
            tmp.copyTo(dst);
        } else {
    
            cv::Mat tmp(info.height, info.width, CV_8UC2, pixels);
            cvtColor(tmp, dst, CV_BGR5652RGBA);
        }
        AndroidBitmap_unlockPixels(env, bitmap);
        return;
    }catch (...) {
    
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nBitmapToMat}");
        return;
    }
}

3.3.4 运行程序

  最后,在代码中就可以按如下方式调用刚才创建的jni方法:

Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.adventure_time);
OpenCVSample.createMat(bitmap);

  运行结果如下,说明Mat已经创建成功:

D/NativeLibSample: native mat channels:4, cols:3000, rows:4500

3.4.5 其他

  如果在打包过程中碰到了动态库重复的问题,可以在app模块的build.gradle中添加如下配置解决:

android {
    packagingOptions {
        pickFirst 'lib/arm64-v8a/libc++_shared.so'
        pickFirst 'lib/armeabi-v7a/libc++_shared.so'
        pickFirst 'lib/arm64-v8a/libopencv_java4.so'
        pickFirst 'lib/armeabi-v7a/libopencv_java4.so'
    }
}

The End

欢迎关注我,一起解锁更多技能:BC的掘金主页~ BC的CSDN主页
请添加图片描述

OpenCV官网:https://opencv.org/releases/
OpenCV官方文档:https://docs.opencv.org/4.5.5/
OpenCV github:https://github.com/opencv/opencv/tree/master
OpenCV入门文档:http://c.biancheng.net/opencv/
OpenCV 入门官方文档
LearnOpenCV学习资料
裁剪OpenCV

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_55626853/article/details/123941565

智能推荐

编译错误记录_the shapes of the array-程序员宅基地

文章浏览阅读2.9k次。segmentation errorcall MPI_Allreduce的时候少了最后一个参数ierr,fortran的MPI实现都有这个额外的ierr参数,竟然没提示错误,执行的时候就出现了segmentation fault,结果找了很久forrtl: severe (174): SIGSEGV, segmentation fault occurred_the shapes of the array

bash脚本编写-程序员宅基地

文章浏览阅读4.5k次。一,概括:read命令是用于从终端或文件中读取输入的内部命令读取整行输入每行末尾的换行符不被读入二,read命令使用从标准输入读取输入并赋值给变量:read var从标准输入读取多个内容:read var1 var2 var3不指定变量(默认赋值给REPLY)三,脚本参数传递$0 脚本名称$1~$n 获取参数$# 传递到脚本的参数个数$$ 脚本运行的当前id号$* 以一个单字符串显示所有向脚本传递的参数$? 显示最后命令的退出状态,0表示没有错误,其他任何值表明有错_bash脚本编写

tomcat和weblogic服务器在数据库意外断开后重连方法-程序员宅基地

文章浏览阅读454次。本文地址:http://qzone.qq.com/blog/454389842-1217557897 &lt;!-- 文章内容开始--&gt; Tomcat设置方法 在配置Context连接池参数时候加上如下参数:复制内容到剪贴板代码: &lt;parameter&gt;..._tomcat断开数据库连接后不会自动重连

torch.nn.functioanl及torch.nn(未完待续。。。)_torch functi0on-程序员宅基地

文章浏览阅读285次。文章目录torch.nn.functional包的内置函数Convolution 函数Pooling函数非线性激活函数Normalization函数线性函数Dropout函数距离函数损失函数vision functionstorch.nn包的内置函数loss functiontorch.nn.functional包的内置函数Convolution 函数Pooling函数非线性激活函数Py..._torch functi0on

CEF使用入门_cef 打开选项卡-程序员宅基地

文章浏览阅读1.3k次。CEF使用文档:CEF General Usage(CEF3预览)https://github.com/fanfeilong/cefutil/blob/master/doc/CEF%20General%20Usage-zh-cn.mdCEF3的二进制包其中包含了在特定平台(Windows,Mac OS X 以及 Linux)编译特定版本CEF3所需的全部文件http://opensource.sp..._cef 打开选项卡

桥接模式-基于接口开发-程序员宅基地

文章浏览阅读159次。结构性设计模式,可将一个大类或一系列紧密相关的类拆分抽象和实现两个独立的层次结构,从而能能在开发时分别使用。1 桥接模式结构1.抽象部分(Abstraction) 提供高层控制逻辑, 依赖于完成底层实际工作的实现对象。2.实现部分(Implementation) 为所有具体实现声明通用接口。 抽象部分仅能通过在这里声明的方法与实现对象交互。抽象部分可以列出和实现部分一样的方法, 但是抽象部分通常声明一些复杂行为, 这些行为依赖于多种由实现部分声明的原语操作。3.具体实现(Concre._基于接口开发

随便推点

禅道项目管理安装-程序员宅基地

文章浏览阅读409次。(推荐)linux用一键安装包Cenos7 服务器安装禅道项目管理原作者 王春生 482372 最后编辑:张玉洁 于 2019-11-26 10:44:01简介:本文介绍如何在linux下面使用禅道一键安装包搭建禅道的运行环境。一、安装二、如何访问数据库三、9.2.stable版本起Linux一键安装包安全级别升级,禁用了php解析。linux一键安装包内置了XXD、apache, ...

ccpc河北大学生程序设计竞赛dp小总结-程序员宅基地

文章浏览阅读1w次,点赞221次,收藏216次。近期题目来自校赛,赛前训练,省赛热身,河北ccpc正式比赛。题目一:题目描述:由于第m个台阶上有好吃的薯条,所以薯片现在要爬一段m阶的楼梯.薯片每步最多能爬k个阶梯,但是每到了第i个台阶,薯片身上的糖果都会掉落ai个,现在问你薯片至少得掉多少糖果才能得到薯条?输入:多组数据输入,每组数据第一行输入两个数字m(1<m<=1000),k(1<=k<m),接...

OpenGLED画一个简单的三角形-程序员宅基地

文章浏览阅读78次。清单文件添加权限<uses-feature android:glEsVersion="0x00020000" android:required="true" />MyViewpackage com.example.myopengles;import android.content.Context;import android.opengl.GLES20;import a..._opengled

基于51单片机的水流量传感器测试系统_51单片机的水位传感器说明-程序员宅基地

文章浏览阅读7.5k次,点赞7次,收藏44次。热水器上水报警器_51单片机的水位传感器说明

一分钟获取吉比特UNG220Z广电光猫设备超级管理员密码_ung430n管理员账号密码-程序员宅基地

文章浏览阅读892次,点赞9次,收藏8次。Step 1.看到这篇文章的朋友,相信大家的光猫都是已经配置好,并可以使用无线或者有线的方式进行上网了吧,此时使用接入到此网络的电脑设备,访问光猫后的网关地址,并可以使用光猫设备背面其标识的普通用户user及其密码进行登录,例如:作者UNG220Z光猫设备背面图片。Step 7.使用获取到的光猫超级管理员账号密码,登录设备管理界面,登录前请退出原本已经登录的用户,否则有可能显示已有用户正在管理,若伟正常退出用户的请重启光猫。,输入上述的账户密码,等待输入符变成 /# 之后就表示登录好了,此处如果显示的是。_ung430n管理员账号密码

android mtk camera startpreview,stopCameraPreview再startCameraPreview后有概率黑屏-程序员宅基地

文章浏览阅读543次。机型为魅蓝note,系统为5.1.11.0A。问题为TextureView下stopCameraPreview后再startCameraPreview有概率黑屏。nexus 5上也有同样问题代码:@Overridepublic void onWindowFocusChanged(boolean hasWindowFocus) {super.onWindowFocusChanged(hasWindo..._startpreview

推荐文章

热门文章

相关标签