android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!从此在andrid中自由使用 图像匹配、识别、检测

目录:

1,过程感慨;

2,运行环境;

3,准备工作;

4,编译 .so

5,遇到的关键问题及其解决方法

6,实现效果截图。

------------------------------------------------------------------------

(原创:转载声明出处:http://www.cnblogs.com/linguanh/)

1,过程感慨(想直接看教程,请跳过此部分)

在写具体内容之前,我先说下我搞这个东西的过程,由于导师之前说过要搞个图像匹配的androi APP,具体就是匹配前后两张图片的相似度,类似 安卓5.0 引入的刷脸解锁。

当时觉得,要实现这样一个东西,肯定没现成的API 可供使用,第一时间想到的 无疑就是opencv,这个拥有一套强大的图像处理函数的库,它的开发语言主要是C++,但是,也有 jar 包可供android开发使用,如果单单是使用里面已经写好了的效果的话,肯定是不能完成图像匹配的。

也就是说,我必须要调用它里面的函数再结合自己算法重新去实现这样一个功能,再使用 ndk 环境去实现 jni 编程,把我自己写好的 c++ 代码,在生成 .so 动态库的基础上,引入并使用。

刚开始,思路很清晰,然后便着手百度 android studio(下面简称 as) 的 opencv jni编程使用教程,十分遗憾,所能搜到的,关于 as 和 opencv、jni 搭边的例子 几乎为0,很多的例子是 eclipse。没办法,只有自己亲手搞了。

刚动手的时候,很快地把所有装备工作都搞定了,.so 动态库文件(下面会介绍)也编译出来了,但是,就在此时,我遇到了一个 令我第一阶段切底放弃的 bug!!

这个 bug 是:(下面我会说明白,它的真实起因和解决方法)

fatal error: opencv2/opencv.hpp: No such file or directory, 意思是 我所要编译的 cpp文件中的 头文件 opencv2/opencv.hpp 找不到。当时,无论是自己请教别人、百度、google 还是查书,都无法解决,足足耗时 一星期!!

逐保留项目信息,放弃不搞。

直到 2 天前,开始决定重新尝试,并于今天正式解决后,现发表此文。

2,运行环境

win 7, 系统;

android studio 版本 0.8.0 beta,使用  build:gradle:0.12.+,tools版本:21.1.2,api 21;

opencv for android 包,我使用的版本是 OpenCV-3.0.0-android-sdk,2.4.9的也可以,可以到 opencv 官网下载,我这里提供个链接

http://downloads.sourceforge.net/project/opencvlibrary/opencv-android/3.0.0/OpenCV-3.0.0-android-sdk-1.zip?r=http%3A%2F%2Fopencv.org%2F&ts=1436167636&use_mirror=nchc

编译.so 动态库 使用 cygwin,安装了所有包,这里提示,不一定要用它,可以直接使用 cmd 进行编译;

ndk 为 android-ndk-r10d(强烈建议使用 r9 或 r10 系列,因为这两个能在 cmd 中编译出 .so),r10d 能够支持的 android api 最高到 21,如果你的是 22 的请修改,否则会有会编译不出 jni.h 头文件。

3,准备工作

1,---ndk 的下载、安装即配置,此部分不说,网上教程很多,很多可行。

2,---cygwin 的下载和安装, 参照 http://blog.csdn.net/asmcvc/article/details/9311573,我上面说了,不一定要用它,win 自带的 cmd 也可以编译。如果使用 cygwin,要做好心理准备,下载和安装它 它,非常非常的久,文件总体积 20 多G!!!!我是用了9个多小时。

3,---opencv for android 的sdk 下载完成后。打开 该文件夹,sdk/native/libs,里面有很多平台的文件夹,能在里面出现的,证明你能够在下面的 Application.mk 中设置生成对应的架构的 .so文件,我举个例子,我的是:

在下面介绍的 Application.mk 文件中有一句话 ,它是用来设置生成 对应架构的 .so 文件,我这里是armeabi-7a,如果要生所有的,写出 :=all,注意,这样很可能会报错,错误信息是,某种架构找不到,所以,我要你看清楚,上面文件夹里面有哪些架构,这些 坑是网上找不到,如果你要生成两种,可以轮着来编译,第二次的编译,不同的架构是不会覆盖的。现在打开 sdk/native/jni,如无意外,里面肯定有个 文件叫做 OpenCV.mk,它就是我们在 android.mk 脚本文件中要引入 opencv C++库所要参照的文件。请用记事本 或者Notepad++ 打开。

4,---了解 Android.mk 和 Application.mk 文件的基本内容信息:下面我使用默认的 Android.mk 来说明,和我的例子的 Application.mk 来说明。

它们都是脚本文件。

Android.mk 

Application.mk

4,编译 .so

使用你的 as 创建一个新项目,然后在你的 项目的 main 目录下创建一个一个 jni 文件夹,这样创建:

创建好了之后,是这样的:

首先编译 项目的头文件 .h,一般编译出来后,它的名字结构是:包名_类名.h

编译命令如下,请在你的 as 下面的 Terminal 里面输入:

SourcePath:    D:\work\androidstudio\VisualRecognition\app\src\main\java (绝对路径)

TargetPath:    D:\work\androidstudio\VisualRecognition\visual\src\main\jni (绝对路径)

TargetClassName:    com.yf.visualrecognition.UnityPlayerActivity  (你的包名+类名)

格式:  javah -d ${SourceFile} -classpath ${TargetPath} ${TargetClassName}

控制台指令:  javah -d D:\work\androidstudio\VisualRecognition\visual\src\main\jni

-classpath D:\work\androidstudio\VisualRecognition\app\src\main\java io.github.froger.jni.MyActivity

然后在你的jni 文件夹下面 分别创建 Android.mk 、Application.mk 和你要编译的 .cpp 或者.c 文件,前两个的 内容可以模仿我上面介绍的, .cpp 我这里提供一个。

Android.mk 、Application.mk 、ImgFuncpp 分别如下,util.c 是空文件,之所以创建它是为了避免另外一个 bug,这不说:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OPENCV_LIB_TYPE:=STATIC
ifeq ("$(wildcard $(OPENCV_MK_PATH))","")

include E:\OpenCV-3.0.0-android-sdk-1\OpenCV-android-sdk\sdk\native\jni\OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif

LOCAL_MODULE := ImgFun
LOCAL_SRC_FILES := ImgFun.cpp
LOCAL_LDLIBS += -lm -llog
include $(BUILD_SHARED_LIBRARY)

---------------------------------------

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a
APP_PLATFORM := android-8 #这句是设置最低安卓平台,可以不弄

-------------------------------------

 1 #include <io_github_froger_jni_MyActivity.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <opencv2/opencv.hpp>
 5 using namespace cv;
 6 IplImage * change4channelTo3InIplImage(IplImage * src);
 7
 8 extern "C" {
 9 JNIEXPORT jintArray JNICALL Java_io_github_froger_jni_MyActivity_ImgFun(
10     JNIEnv* env, jobject obj, jintArray buf, int w, int h);
11 JNIEXPORT jintArray JNICALL Java_io_github_froger_jni_MyActivity_ImgFun(
12     JNIEnv* env, jobject obj, jintArray buf, int w, int h) {
13
14   jint *cbuf;
15   cbuf = env->GetIntArrayElements(buf, false);
16   if (cbuf == NULL) {
17     return 0;
18   }
19
20   Mat myimg(h, w, CV_8UC4, (unsigned char*) cbuf);
21   IplImage image=IplImage(myimg);
22   IplImage* image3channel = change4channelTo3InIplImage(&image);
23
24   IplImage* pCannyImage=cvCreateImage(cvGetSize(image3channel),IPL_DEPTH_8U,1);
25
26   cvCanny(image3channel,pCannyImage,50,150,3);
27
28   int* outImage=new int[w*h];
29   for(int i=0;i<w*h;i++)
30   {
31     outImage[i]=(int)pCannyImage->imageData[i];
32   }
33
34   int size = w * h;
35   jintArray result = env->NewIntArray(size);
36   env->SetIntArrayRegion(result, 0, size, outImage);
37   env->ReleaseIntArrayElements(buf, cbuf, 0);
38   return result;
39 }
40 }
41
42 IplImage * change4channelTo3InIplImage(IplImage * src) {
43   if (src->nChannels != 4) {
44     return NULL;
45   }
46
47   IplImage * destImg = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);
48   for (int row = 0; row < src->height; row++) {
49     for (int col = 0; col < src->width; col++) {
50       CvScalar s = cvGet2D(src, row, col);
51       cvSet2D(destImg, row, col, s);
52     }
53   }
54
55   return destImg;
56 }

上面 .cpp 文件的有几句话要说明下,注意 .c 文件和 .cpp 文件是不一样的:
      请用  extern "C" { } 包住 你要你的 c++ 函数体的定义和里面的变量,函数声明可以在外面。下面这句:

JNIEXPORT jintArray JNICALL Java_io_github_froger_jni_MyActivity_ImgFun(JNIEnv* env, jobject obj, jintArray buf, int w, int h);

jintArray 是你定义的函数的返回值,我这里的是int数组,它在类型的前面有一个 j ,如果是字符串,那么就是 jstring,数组加上Array;

JNICALL Java 这句不变,所有都一样,注意java的 j 是大写;

io_github_froger_jni 这里是你的包名;

MyActivity 你的类名,要引用这个这里C++函数的类名;

ImgFun  是你要在java中调用的函数名字,哪些不用直接被调用的,不用写;

JNIEnv* env, jobject obj, 这个固定不变,第一个的意思是虚拟机引用,第二个是项目;

jintArray buf, int w, int h 函数的参数。

第一次写是声明,第二次写是定义。

-------------------------------

好了,上面该介绍的已经介绍完了,接下来是编译 .so 的正式操作(我这里使用cmd做例子,因为它更简单操作,cygwin也可以)。

你可以在 as 的 cmd 中或者 系统的 cmd框中实现编译,首先使用命令进入到当前的 jni 文件夹的 目录,例如,我的是

D:asproject/JniDemo/app/main/jni,然后使用命令 ndk-build,(使用ndk-build命令这一步,需要你已经配置好了 ndk 环境,请参照百度上面的教程)然后回车,如无意外,将会生成如下文件:

其中的 .so 文件就是我们所需要的,现在打开你项目app下的  build.gradle 文件,在 android{} 里面加入:

sourceSets {
  main() {
    jniLibs.srcDirs = [‘src/main/libs‘]
  }
}

这样是为了使用 .so文件,上面我们仅仅是生产!

OK,到这里基本大功告成了,不过,笔者我就是在这一步之后,运行程序的时候,出现的简单的致命的 bug,导致我找了近2星期,现在想起来真是蠢..............

5,遇到的关键问题及其解决方法

运行程序,出现,如下错误,这里声明下,不仅仅是 opencv2/opencv.hpp,还可能是其他的 hpp。

出现的原因:

原来是这样的,android studio 在我们编译完 .so 文件后,我们再 Android.mk 文件中设置引入的 opencv 函数库,是被弄进去了 .so 动态库里面的,而我们编译所需要的 cpp 文件,它在 jni 文件夹呢,自然就没有 opencv 库可依赖,所以。

解决方法:

在你编译完.so 文件后,就可以把 cpp 或者 c 文件里面的内容 注释或者删除了,不然 在你运行程序的时候 就会抛出 头文件找不到的 错误,哎,真是辛酸泪,这样一个 bug 搞了我 那么多时间,不过还好,还是解决了。

6,实现效果截图。

 

时间: 2024-07-29 12:33:44

android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!从此在andrid中自由使用 图像匹配、识别、检测的相关文章

Android之JNI:Android Studio使用Gradle编译C/C++源码

使用Gradle编译C/C++源码步骤 申明NDK工具类,内部定义native方法 package com.coca.firstdemo; /** * Created by Administrator on 2016/6/6. */public class JniShareUtils { public native String getLogCount(String params);} 定位至项目的app文件夹,调用javah命令生成.h文件: javah com.coca.firstdemo.

Android Studio 开发JNI应用

Android的JNI开发主要有两中情况:一种是使用已经编译好的.so动态库:一种是使用c/c++源代码开发.下面分别介绍: 一.使用已经编译好的so 这种情况比较件简单,只要把的.so文件放到相应的目录即可.如下: .[module_name]         . . [src]         . . .[main]         . . . .[jniLibs]         . . . . .[armeabi]         . . . . .[armeabi-v7a]     

Android Studio使用jni、so库

Android Studio使用jni.so库 在Android Studio1.1之后,AS就已经支持jni和so库了,马上发布的1.3正式版,更是可以在clion环境下编译c.c++,更加方便的使用NDK进行开发,网上有很多讲在Android Studio中使用jni的方法,但大多都是在1.1之前的,那时候还没有直接支持jni,所以需要通过给gradle增加task的方式来添加支持.而现在,这一切都不是事!!! 添加lib库 切换到project标签,直接将jar包复制到libs目录下,在添

Android Studio下jni应用

最近在将一个小应用从eclipse开发迁移到android studio,程序中有native代码实现,在eclipse是靠Android.mk这么个mk文件来组织编译的,但到android studio上就不行了,因其是靠gradle组织,所以makefile里的配置要转换为build.gradle里的语句(尽管实际上gradle也是组织一个mk文件),同时在迁移过程中遇到了一些问题,这里记录一下,以备后查. Android的JNI开发主要有两中情况:一种是使用已经编译好的.so动态库:一种是

Android Studio设置自动编译工程

在Eclipse使用习惯了自动编译的兄弟们,刚迁移到Android Studio,肯定也很怀念这功能,自动编译意味着每次运行都能缩短很多时间. Android Studio里面其实也是有自动编译功能的,不过个人觉得还是不如Eclipse好使.具体设置看下图: 把图中的checkbox的勾点上即可. Android Studio设置自动编译工程,布布扣,bubuko.com

【转】实践最有效的提高Android Studio运行、编译速度方案

原文:https://blog.csdn.net/xwh_1230/article/details/60961723 实践最有效的提高Android Studio运行.编译速度方案 最有效提升Android studio编译速度的方式:提升电脑配置!-- 鲁迅 鲁迅所说确实是实情,提升配置是最有效的方式,但对于我等屌丝来说,有时候很难办到,因此我们接着看下面的优化. 一.安装时的问题 1.安装完成后启动卡死 刚刚打开studio就卡在gradle building的界面再也不动了(去连接墙外的网

Android Studio建立jni开发环境

声明: 文章参考自 csdn sodino 的博客 Step: 1. 添加native接口注意写好native接口和System.loadLibrary()即可了,并无特别之处. Step: 2.执行Build->Make Project生成了.class文件..class文件的生成路径是在 app_path/build/intermediates/classes/debug下的. 这个在android studio里面看不到, 到文件管理器里面翻到的, 里面还有好多东西. Step: 3.ja

Android Studio 运行、编译卡死的解决办法

Android stuido作为google主推的IDE,配合gradle编译,有很多的优点和便捷性.唯一使用过程中不舒服的地方就是莫名其妙的卡顿,经常在Gradle Build的时候卡死强制重启电脑,非常无语,我的机器是8G内存新机器也没开其他程序.总之非常编译速度很慢有时还会经常卡死.尝试了很多优化方案,卡死次数少了编译也快了不少.将我的发现和大家分享一下.  加大运行内存和优化编译 以PC 端为例,MAC大同小异 1.申请大内存 installation path\studio64.exe

android studio笔记之编译运行错误

错误类型: Error:java.lang.UnsupportedClassVersionError: com/android/dx/command/Main : Unsupported major错误解决 Error:java.lang.UnsupportedClassVersionError: com/android/dx/command/Main : Unsupported major.minor version 52.0Error: at java.lang.ClassLoader.de