美文网首页
搭建NDK开发OpenCV环境

搭建NDK开发OpenCV环境

作者: BrotherTree | 来源:发表于2018-05-07 11:21 被阅读0次

    大家好,今天我们来正式学习Android Studio环境下的OpenCV开发。

    首先看看OpenCV官网,有这么一大段介绍(令人懵逼的开头):

    image

    OpenCV于1999年由Intel建立,如今由Willow Garage提供支持。OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Mac OS、android、iOS等操作系统上。由一系列 C 函数和少量 C++ 类构成,所以它轻量级而且高效。还支持C#、Ch、ruby等语言进行编程,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。最新版本是3.4.1 ,2018年2月27日发布。

    图像处理

    利用计算机对图像进行分析处理,达到所需结果的技术,一般指的是数字图像处理,通过数码设备得到的数字图像是一个很大的二维数组,数组的元素叫像素,像素的值叫灰度值。主要的处理方法有去燥,增强,复原,分割,提取特征得到。

    图像处理侧重于处理图像。

    计算机视觉

    研究如何使计算机可以像人一样“看”的一门科学,属于人工智能范畴,是用计算机来识别、追踪、测量等手机信息的科学。

    计算机视觉侧重于模拟人的视觉。

    应用领域

    人机互动、物体识别、图像分割人脸识别、动作识别、运动跟踪、机器人、运动识别、机器视觉、结构分析、汽车安全驾驶、自动驾驶等等。

    OpenCV模块分析

    1. core 核心功能模块。定义了被所有其他模块和基本数据结构(包括重要的多维数组Mat)使用的基本函数.包含核心功能,尤其是底层数据结构和算法函数。
    基本的C语言数据结构和操作(Basic C Structures and Operations);
    动态数据结构(Dynamic Structures);
    数组操作相关函数(Operations on Arrays);
    绘图功能(Drawing Functions);
    XML和YAML语法的支持(XML/YAML Persistence);
    XML和YAML语法的支持的C语言接口(XML/YAML Persistence (C API));
    聚类(Clustering);
    辅助功能与系统函数和宏(Utility and System Functions and Macros);
    与OpenGL的互操作(OpenGL interoperability);
    
    1. imgproc,是Image Processing的简写。图像处理模块。
    线性和非线性的图像滤波(Image Filtering);
    图像的几何变换(Geometric Image Transformations);
    图像的其他变换(Miscellaneous Image Transformations);
    直方图(Histograms);
    结构分析和形状描述(Structural Analysis and Shape Descriptors);
    运动分析和目标跟踪(Motion Analysis and Object Tracking);
    特征检测(Feature Detection);
    目标检测(Object Detection);
    
    1. features2d,是2D Features Framework的简写。二维特征框架模块。
    人脸识别
    VR和AR
    特征的检测和描述(Feature Detection and Description);
    特征检测器的通用接口(Common Interfaces of Feature Detectors);
    描述符提取器的通用接口(Common Interfaces of Descriptor Extractors);
    描述符匹配器的通用接口(Common Interfaces of Descriptor Matchers);
    通用描述符匹配器通用接口(Common Interfaces of Generic Descriptor Matchers);
    关键点和匹配结果的绘制功能(Drawing Function of Keypoints and Matches);
    目标分类(Object Categorization);
    
    1. flann,Clustering and Search in Multi-Dimensional Spaces,多维空间聚类和搜索模块。
    快速近视最近邻搜索(Fast Approximate Nearest Neighbor Search);
    聚类(Clustering);
    
    1. video,是Video Analysis的简写。视频分析模块。
    运动分析和目标跟踪(Motion Analysis and Object Tracking),视频相关的,上面提到的是图片相关的。
    
    1. calib3d,是Camera Calibration and 3D Reconstruction的简写。这个模块主要是相机校准和三维重建相关的内容,包括基本的多视角几何算法、单个立体摄像头标定、物体姿态估计、立体相似性算法,3D信息的重建等。

    静态库

    image

    OpenCV目录结构(3.3为例)

    apk OpenCV manager针对各个架构的cpu的manager安装包,过去的OpenCV如果不用NDK方式开发,会要求手机上需要安装OpenCV manager的对应指令集的apk

    samples android平台下的官方案例源码和安装包(Java调用方式)

    SDK SDK文件夹,jni开发所需。

    Mat

    Mat是OpenCV的核心数据结构,从来表示任意N维矩阵。图像是二微矩阵的一种特殊场景,所以也可以使用Mat来表示。Mat是OpenCV中用到最多的类。定义在命名空间cv下。

    Android Studio平台上搭建NDK环境(以MacOS环境为例)

    1.Prefrences-->Android SDK-->SDK Tools
    选择安装
    cMake:外部构建工具。如果你准备只使用 ndk-build 的话,可以不使用它。
    LLDB:Android Studio上面调试本地代码的工具
    NDK


    image.png

    Android Studio平台上搭建NDK开发OpenCV环境

    1. file-->New-->New Project (必须选中 Include C++ support)

    image

    2. 一路向下走到下面步骤,C++ Standard 需选中C++11标准。(14标准可能还不太稳定,也不能选择Toolchain Default标准,可能会出现莫名其妙的错误)

    下方的两个复选框须勾选上。

    image

    项目创建成功后,目录结构为:

    image

    为了能够进行OpenCV开发,我们接下来需要对项目主工程下的build.gradle和CMakeLists.txt进行修改。

    build.gradle新增内容

    添加设备支持的二进制指令集
    libs路径使用的是绝对路径,读取速度相对快一些,最好不使用相对路径,因为使用相对路径,需要将libs拷贝到项目中,而libs文件夹大小为四百多M


    image

    CMakeLists.txt修改

    该文件新增:
    文件创建能力;
    库文件的绝对路径;
    头文件的具体路径;
    动态库的引入(库名 libopencv_java3 动态SHARED 引入IMPORTEDS);


    image

    文档尾部的target_link_libraries修改为:

    image

    在环境搭建好之后,如何判断我们已经能够进行OpenCV开发呢?

    其实很简单,在项目的cpp目录下的native-lib.cpp文件中,引入头文件core.hpp,如果能成功引入,则说明环境搭建已经完毕:include <opencv2/core.hpp>

    至此,我们项目的OpenCV环境已经搭建完成。下面我们用一个小小的示例结束本文。

    将图片变为灰度图

    原图:


    image.png

    灰度图:


    image.png

    废话不多说,上代码

    布局activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <Button
            android:id="@+id/button1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="点击处理" />
    
        <ImageView
            android:id="@+id/sample_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
    

    页面MainActivity.java

    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ImageView;
    
    public class MainActivity extends AppCompatActivity {
    
        private ImageView imageview;
        private Button button;
        private Bitmap bitmap;
    
        static {
            System.loadLibrary("native-lib");
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            imageview = findViewById(R.id.sample_text);
            button = findViewById(R.id.button1);
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg);
            imageview.setImageBitmap(bitmap);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // 获取图片的宽
                    int w = bitmap.getWidth();
                    // 获取图片的高
                    int h = bitmap.getHeight();
                    // 建立数组pixels,长度为w * h
                    int[] pixels = new int[w * h];
                    // getPixels方法获取图片像素
                    bitmap.getPixels(pixels, 0, w, 0, 0, w, h);
                    // 调用jni方法获取灰度图片像素数组
                    int[] resultInt = grayP(pixels, w, h);
                    // 创建新的bitmap
                    Bitmap resultImg = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                    // 新的bitmap存入灰度图像素
                    resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
                    imageview.setImageBitmap(resultImg);
                }
            });
        }
    
        /**
         * 将图片灰度化并获取灰度化后的图片像素
         * @param pixels int类型的数组-像素
         * @param w 图片的宽
         * @param h 图片的高
         * @return
         */
        public native int[] grayP(int[] pixels, int w, int h);
    }
    

    上述代码中,我们使用了getPixels方法(setPixels类似),那么此方法需要用到的参数都代表什么意思呢?

        public void getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height) {
            throw new RuntimeException("Stub!");
        }
        public void setPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height) {
            throw new RuntimeException("Stub!");
        }
    

    int[] pixels是存储图片的指定区域的所有像素的一维数组;
    int offset是指定的偏移位置;
    int x, int y, int width, int height是从指定的位置截取指定的宽高;
    int stride 是指定在行之间跳过的像素的数目。图片是二维的,存入一个一维数组中,那么就需要这个参数来指定多少个像素换一行;

    stride的特点:
    在原图上截取的时候,需要读取stride个像素再去读原图的下一行
    如果一行的像素个数足够,就读取stride个像素再下一行读
    如果一行的像素个数不够,用0(透明)来填充到 stride个
    得到的数据,依次存入pixels[]这个一维数组中
    

    JNI native-lib.cpp

    #include <jni.h>
    #include <string>
    #include <opencv2/core.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    
    using namespace std;
    using namespace cv;
    
    extern "C"
    JNIEXPORT jintArray JNICALL
    Java_com_example_ndk_1opencv_MainActivity_grayP(
            JNIEnv *env,
            jclass type,
            jintArray pixels_,
            jint w,
            jint h) {
        jint *pixels = env->GetIntArrayElements(pixels_, NULL);
    
        if (pixels == NULL) {
            return 0;
        }
    
        //RGBA
        Mat imgData(h, w, CV_8UC4, (unsigned char *) pixels);
        uchar *ptr = imgData.ptr(0);
        for (int i = 0; i < w * h; i++) {
            // 灰度值计算公式 R * 0.3 + G * 0.59 + B * 0.11
            uchar gray = (uchar) (ptr[4 * i + 2] * 0.299 + ptr[4 * i + 1] * 0.587 +
                                  ptr[4 * i + 0] * 0.114);
            ptr[4*i+0]=gray; // b
            ptr[4*i+1]=gray;// g
            ptr[4*i+2]=gray; // r
        }
    
        int size = w * h;
        jintArray result = env->NewIntArray(size);
        env->SetIntArrayRegion(result, 0, size, pixels);
    
        env->ReleaseIntArrayElements(pixels_, pixels, 0);
    
        return result;
    }
    

    相关文章

      网友评论

          本文标题:搭建NDK开发OpenCV环境

          本文链接:https://www.haomeiwen.com/subject/pbdmrftx.html