在《Android Studio增加NDK代码编译支持--Mac环境》和《Mac平台下Opencv开发环境搭建》两篇文章中,介绍了如何使用NDK环境和Opencv环境搭建与测试使用,现在,在PC端对图像处理算法测试没问题后,该在移动端进行功能移植了,ios平台的很简单,直接把类库拷进工程就行了,android的稍微麻烦点,这里就以android平台为例说明移植步骤。

  为了更好的模块移植,这里使用Android源码的make文件写法:*.mk,Android源码是一个很大的工程,它的编译采用一个大的mk文件,通过脚本文件的配置来自定义编译的,在build/core/下面的Android.mk文件就是总的编译文件入口:

  

   这里写的opencv安卓模块也使用mk文件写法来编译so库。这里新建了一个测试工程,可以在GitHub上download或fork来查看源码: https://github.com/linjk/TestOpenCV

   下面开始移植步骤:

   1. 新建测试工程OpenCVTest:

    

    2. 拷贝下载的opencv的android平台的开发包,这里下载3.1.0版本的:

     

      这里把sdk目录下的native目录拷贝到工程根目录,这个目录下是c/c++语法的,java目录是已封装好的一些java接口,按需选择吧,为了更好的算法移植而不用每次改写,这里选择native库,复制后工程结构如下:

      

    3. 新建jni目录,用于编写本地c++代码:

      在src目录单击右键,按下图操作:

      

      结果如下:

      

    4. 编写java类的本地接口声明,用于给java层调用:

      这里声明一个很简单的opencv本地方法,用于把一副图像编程灰度图像,当然,这个效果用安卓的图像矩阵来处理就行了,但是,复杂一点的功能,如边缘检测、身份证识别就要借助opencv来弄了,这里仅做功能测试:

      

    5. 生成本地方法桥接头文件:

      命令行进入src/main/java路径,然后执行命令: javah -jni cn.linjk.jniBridge.OpenCVUtils, -jni参数后面参数格式是:包名+类名,结果如下:

        

        我们把这个文件移动到jni目录下,并新建一个同名的cpp类cn_linjk_jniBridge_OpenCVUtils.cpp:

    6. 由于之前使用了android studio生成的jni目录,因此,编译上可能会和使用mk文件编译生成so库不一样,这里取消它的路径属性:

    在app/build.gradle文件的android块下增加这个配置:

sourceSets{
        main{
            jni.srcDirs = []
        }
    }

    可以发现,jni目录由蓝色变成了黄色:

    

    7. 编写编译规则文件,指定ndk路径:

      7.1 指定ndk路径:

      

      7.2 在jni目录下新建两个mk编译文件,内容分别如下:

      

      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 := testopencv

LOCAL_SRC_FILES := cn_linjk_jniBridge_OpenCVUtils.cpp

LOCAL_LDLIBS +=  -lm -llog -landroid

include $(BUILD_SHARED_LIBRARY)

    LOCAL_MODULE声明的是模块名称,必须与在OpenCVUtils声明的加载库名一样。

    Application.mk文件内容如下:

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := arm64-v8a armeabi armeabi-v7a mips mips64

    APP_ABI声明了声明针对那些CPU架构的so库。

    7.3 在app/build.gradle声明一个task,用于编译生成so库。

      7.3.1 编辑jni的cpp文件,内容如下,为测试能否调用库,这里先在函数打印cv版本:

//
// Created by LinJK on 21/11/2016.
//

#include "cn_linjk_jniBridge_OpenCVUtils.h"

#include <opencv2/opencv.hpp>

#include <Android/log.h>
#include <Android/asset_manager.h>
#include <Android/asset_manager_jni.h>

#define TAG "cn.linjk.opencvtest.jni"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)

extern "C" {

JNIEXPORT void JNICALL Java_cn_linjk_jniBridge_OpenCVUtils_img2gray
    (JNIEnv *env , jclass objClass, jstring imgFilePath) {

        LOGI("OpenCV version: %s", CV_VERSION);

    }

}

    7.3.2 增加生成so库的task:

      

    7.3.3 执行任务,生成so库:

      执行命令"gradle cv_ndkBuild",结果如下:

      

      把对应得so库复制到app/libs目录下对应cpu架构目录下:

      

    8. 在MainActivity调用看能否输出opencv库版本:

      MainActivity.java内容:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        OpenCVUtils.img2gray("");
    }
}

    运行后,发现出现如下错误:

    

    这是我们指定生成动态链接库,因此,还需要libopencv_java3.so这个库,复制到我们的libs下,再次运行,结果如下:

    

    输出正确,可以继续下一步了。

    附:

    也可以使用命令“arm-none-eabi-readelf -d libtestopencv.so”查看其需要的链接库,命令执行结果如下:

    

    .so文件是ELF(Excutable and Linking Formar)格式的缩写,最初由UNIX系统实验室发布,它是应用程序二进制接口的一部分,ELF文件以节(section)的方式组织在一起,“节”描述了文件的各项信息,例如代码、数据、符号表、重定位表、全局偏移表等。

  9. 编写图像处理类:

  9.1 这里使用照相机获取输入图像,代码看github的源码就行,这里主要看看c++最终代码:

    cn_linjk_jniBridge_OpenCVUtils.cpp

//
// Created by LinJK on 21/11/2016.
//

#include "cn_linjk_jniBridge_OpenCVUtils.h"

#include <opencv2/opencv.hpp>
#include <string>
#include <iostream>

#include <Android/log.h>
#include <Android/asset_manager.h>
#include <Android/asset_manager_jni.h>

#define TAG "cn.linjk.opencvtest.jni"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)

using namespace std;
using namespace cv;

class ImageUtils{
public:
    void imageToGray(Mat inputImg, string outFilePath);
};

void ImageUtils::imageToGray(Mat inputImg, string outFilePath) {
    Mat gray;

    Mat input = inputImg.clone();

    cvtColor(input, gray, COLOR_BGR2GRAY);

    imwrite(outFilePath,  gray);
}

extern "C" {

JNIEXPORT void JNICALL Java_cn_linjk_jniBridge_OpenCVUtils_img2gray
    (JNIEnv *env , jclass objClass, jstring imgFilePath) {

        LOGI("OpenCV version: %s", CV_VERSION);

        char buf[128];
        const char *str = env->GetStringUTFChars(imgFilePath, 0);
        LOGD("图像路径: %s", str);

        Mat img = imread(str);
        if (!img.data) {
            LOGE("-----CV: 读取相片数据出错");
        }
        else {
            LOGD("-----CV: 读取相片数据成功");
            ImageUtils().imageToGray(img, str);
        }
    }

}

      MainActivity.java方法内容如下:

package cn.linjk.opencvtest;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Display;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

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

import cn.linjk.jniBridge.OpenCVUtils;

public class MainActivity extends AppCompatActivity {

    private Button btnOpenCamera;
    private ImageView ivImgOutput;
    private Button btnImgGray;

    private String imageFilePath;

    private static final int CAMERA_RESULT = 1112;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnOpenCamera = (Button)findViewById(R.id.btn_open_camera);
        btnOpenCamera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View pView) {
                openCamera();
            }
        });

        ivImgOutput = (ImageView)findViewById(R.id.img_output);

        btnImgGray   = (Button)findViewById(R.id.img_gray);
        btnImgGray.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View pView) {
                OpenCVUtils.img2gray(imageFilePath);
                //
                BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
                bmpFactoryOptions.inJustDecodeBounds = true;
                Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);

                bmpFactoryOptions.inSampleSize = calculateInSampleSize(bmpFactoryOptions, 1280, 800);

                bmpFactoryOptions.inJustDecodeBounds = false;

                bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);

                ivImgOutput.setImageBitmap(bmp);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK) {
            BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
            bmpFactoryOptions.inJustDecodeBounds = true;
            Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);

            bmpFactoryOptions.inSampleSize = calculateInSampleSize(bmpFactoryOptions, 1280, 800);

            bmpFactoryOptions.inJustDecodeBounds = false;

            bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);

            ivImgOutput.setImageBitmap(bmp);

            saveBitmap(bmp);
        }
    }

    private void openCamera() {
        imageFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/testImage.jpg";
        File imageFile = new File(imageFilePath);
        Uri imageFileUri = Uri.fromFile(imageFile);

        Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        i.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
        startActivityForResult(i, CAMERA_RESULT);
    }

    private int calculateInSampleSize(BitmapFactory.Options options,
                                      int reqWidth, int reqHeight) {

        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int heightRatio = Math.round((float) height
                    / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);

            inSampleSize = heightRatio < widthRatio ? widthRatio : heightRatio;
        }

        return inSampleSize;
    }

    private void saveBitmap(Bitmap bm) {
        File f = new File(imageFilePath);
        if (f.exists()) {
            f.delete();
        }
        try {
            FileOutputStream out = new FileOutputStream(f);
            bm.compress(Bitmap.CompressFormat.PNG, 90, out);
            out.flush();
            out.close();
        }
        catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

      运行结果如下:

      

     这里,opencv在android平台的移植和简单功能测试已经完成了,后面更多精彩opencv算法就可以继续实现啦~~

      详细代码移步我的GitHub查阅即可:

      https://github.com/linjk/TestOpenCV

Android平台下OpenCV移植与使用---基于C/C++的更多相关文章

  1. Android平台下OpenGL初步

    Android OpenGL ES 开发教程 从入门到精通 http://blog.csdn.net/zhoudailiang/article/details/50176143 http://blog ...

  2. Mac平台下Opencv开发环境搭建

    OpenCV(Open Source Computer Vision Library),是一个开源的跨平台的计算机视觉库,它实现了图像处理和计算机视觉领域的很多通用算法,可以在多种计算机平台上运行,支 ...

  3. 【原创】--linux平台下opencv安装

    1.到opencv官网下载源码 也可以下载此链接http://pan.baidu.com/s/1mgId5ZM 2.解压到任意目录 可以使用右键-提取到此处,也可以在命令行中使用指令解压(linux中 ...

  4. Android平台下的JNI开发

    JNI是Java Native Interface的缩写,通过JNI可以方便我们在Android平台上进行C/C++编程.要用JNI首先必须安装Android的NDK,配置好NDK环境之后就可以在Ec ...

  5. win7平台下React-Native Android:Unable to upload some APKs

    一.问题描述 根据网络上的Win7平台下React-native配置教程配置好开发环境的过程中,在艰难进行到react-native run-android这一步时,发现一直出现错误,截图如下: 错误 ...

  6. Android NDK 和 OpenCV 整合开发总结(3)

    Android NDK 和 OpenCV 整合开发总结(3) http://hujiaweibujidao.github.io/blog/2013/11/18/android-ndk-and-open ...

  7. [转]Windows平台下Makefile学习笔记

    Windows平台下Makefile学习笔记(一) 作者:朱金灿 来源:http://blog.csdn.net/clever101 决心学习Makefile,一方面是为了解决编译开源代码时需要跨编译 ...

  8. Mac平台与Windows平台下AndroidStudio增量升级

    Android Studio增量升级什么情况下使用最合适呢? 比如现在的as版本是2.2版本,而你的as版本2.0版本,这个时候点Check For Updates就没有反应了,因为你已经2个有版本没 ...

  9. .NET平台下,关于数据持久层框架

    在.NET平台下,关于数据持久层框架非常多,本文主要对如下几种做简要的介绍并推荐一些学习的资源: 1.NHibernate 2.NBear 3.Castle ActiveRecord 4.iBATIS ...

随机推荐

  1. UVA 11235Frequent values(RMQ)

    训练指南P198 题意:给出一个非降序排列的整数数组a1, a2…… an,你的任务是对于一系列询问(i,j),回答ai, ai+1 ……aj 中出现的次数最多的次数 这题不仅学到了rmq的应用还学到 ...

  2. thinkphp 3.2 join

    $res2 = M('stat_info a') ->join(C('DB_PREFIX').'stock b ON a.goods_id = b.goods_id') ->field(' ...

  3. 在不动用sp_configure的情况下,如何 =》去掉列的自增长,并保留原数据

    异常处理汇总-数据库系列  http://www.cnblogs.com/dunitian/p/4522990.html 后期博客首发:http://dnt.dkill.net/Article/Det ...

  4. 给Source Insight做个外挂系列之五--Insight “TabSiPlus”

    “TabSiPlus 外挂插件”主要有两部分组成,分别是“外挂插件加载器”和“插件动态库”.“插件动态库”完成Source Insight窗口的Hook,显示Tab标签栏,截获Source Insig ...

  5. [zz] Principal Components Analysis (PCA) 主成分分析

    我理解PCA应该分为2个过程:1.求出降维矩阵:2.利用得到的降维矩阵,对数据/特征做降维. 这里分成了两篇博客,来做总结. http://matlabdatamining.blogspot.com/ ...

  6. 从零开始系列--R语言基础学习笔记之一 环境搭建

    R是免费开源的软件,具有强大的数据处理和绘图等功能.下面是R开发环境的搭建过程. 一.点击网址 https://www.r-project.org/ ,进入"The R Project fo ...

  7. Python 2.7_发送简书关注的专题作者最新一篇文章及连接到邮件_20161218

    最近看简书文章关注了几个专题作者,写的文章都不错,对爬虫和数据分析都写的挺好,因此想到能不能获取最新的文章推送到Ipad网易邮箱大师.邮件发送代码封装成一个函数,从廖雪峰大神那里学的  http:// ...

  8. SVN版本控制系统

    SVN 版本控制系统 1.SVN作用 防止代码丢失 : 因为没有哪个项目能够一次性开发完成 代码版本回退 : 你可以在开发过程中找到以前上传到服务器上面的所有版本 多人代码整合 : 公司中多个人开发同 ...

  9. java问题小总结

    1.在使用equals的时候,把  "".equals(name);放在左边 如果右边的没有初始化,可以避免出错. 2.对于 ObjectId id; 在mongodb里面对其进行 ...

  10. The Adapter of ListView: Just adapt data to view, don’t do anything else

    The design of SimpleAdapter is not good in my opinion. An adapter should just adapter the data to vi ...