简单的调用OpenCV库的Android NDK开发 工具Android Studio
前言
本博客写于2017/08/11, 博主非专业搞安卓开发, 只是工作的需要倒腾了下Android NDK相关的开发, 博文中有什么不正确、不严格的地方欢迎指正哈 本文后续也许还会有删改, 就这样。
一、工具、开发环境
博主的操作系统是Windows 10 x64位,虽然感觉Windows 7更适合用来搞开发, 但是用着Win 10也是挺好使的(没必要在操作系统上有很大纠结,Win 10和Win 7都可以)。
1.搭建并测试JAVA开发环境
首先要搭建Java开发环境,Java开发环境的搭建教程网上可以找到,这里不予赘述; 测试是否搭建成功的时候,需要打开命令提示符依次输入java、javac、javah命令进行测试,显示如下界面结果就成功了:
2.搭建Android开发环境
很长一段时间内,Eclipse + ADT插件是Android开发的主流,不过现在流行的是Android Studio。 还记得Android Studio刚出来的时候很是不被看好, 不过现在它也是很好用的安卓开发工具了,此文介绍的所需要的工具只有Android Studio。
网上有介绍说设置ndk-build环境变量的, 但是要弄明白一件事, 他们设置这个变量是因为他们的"NDK"这个依赖包是单独下载的, 并没有在Android Studio里面下载, 本文的"NDK"依赖包是在Android Studio里面下载的, 因此并不需要设置ndk-build环境变量。
开始正式搭建环境,我用的是Android Studio 2.2.3版本。 安装完Android Studio之后, 选择"File" -> "Settings" -> "Appearence & Behavior" -> "System Settings" -> "Android SDK", 配置 "SDK Platforms"
和 "SDK Tools" :
(1)"SDK
Platforms":这个是选择安卓SDK版本,根据自己想法跟需要下载,我下载了安卓4.4、5.0、7.1.1
Studio的时候默认有什么包我忘了,"Android SDK Build-Tools"这个包应该不用自己勾选,默认就有。NDK开发现在提供了CMake的方式来进行编译调试,通过下载"CMake"跟"LLDB"这俩(可选)就可以用CMake来开发了,本文未基于CMake的方式进行编译调试,而是采用的传统的gradle方式(相对而言,我其实更推荐使用CMake的方式)。
这一步就是为了更方便的进行gradle方式的开发而写的。
$JDKPath$/bin/javah
Parameters:
-d
../jni -jni $FileClass$
Working directory:
$SourcepathEntry$\..\java
最后单击"OK"按钮进行保存。
2) 添加ndk-build,它用于构建so包
你的NDK目录\build\ndk-build.cmd
Parameters: 什么都不用填
Working directory:
$ModuleFileDir$\src\main
最后单击"OK"按钮进行保存。
3) 添加ndk-build
clean,它用于清理so包
你的NDK目录\build\ndk-build.cmd
Parameters:
clean
Working directory:
$ModuleFileDir$\src\main
最后单击"OK"按钮进行保存。
3.来一个栗子
一个NDK开发项目总体就分为生成so库、调用so库两部分,本博客栗子就是先将基于OpenCV的C++代码编译生成so库,再通过jni接口来实现对安卓摄像头的灰度化处理。
(1)首先新建一个工程
1)选择新建项目:
切换到"Project"视图界面,
NDK开发将C/C++代码生成so库, 留一个对单幅图片进行灰度化的接口, 然后我们在Java层获取摄像头数据, 通过JNI调用所生成so库的接口, 对每一帧摄像头数据进行灰度化。
2)新建一个类OpencvClass,负责与C/C++代码对接。
public native static int convertGray(long matAddrRgba, long matAddrgray);
接下来,右键单击类OpencvClass,来生成C/C++头文件
上一步生成了头文件, 此头文件内容是:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_hoos_grayprocessing_OpencvClass */
#ifndef _Included_com_hoos_grayprocessing_OpencvClass
#define _Included_com_hoos_grayprocessing_OpencvClass
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_hoos_grayprocessing_OpencvClass
* Method: convertGray
* Signature: (JJ)I
*/
JNIEXPORT jint JNICALL Java_com_hoos_grayprocessing_OpencvClass_convertGray
(JNIEnv *, jclass, jlong, jlong);
#ifdef __cplusplus
}
#endif
#endif
其中, JNIEXPORT是接口声明, 我们需要在cpp源文件里将它实现, 这里当然需要我们有C/C++基础, 可以写出cpp文件的框架了:
//
// Created by HooS on 2017/8/14.
//
#include <com_hoos_grayprocessing_OpencvClass.h>
JNIEXPORT jint JNICALL Java_com_hoos_grayprocessing_OpencvClass_convertGray
(JNIEnv *, jclass, jlong, jlong){
}
在实现时, 接口的两个jlong类型需要用来传输OpenCV里面的Mat类型数据, 所以要略微修改一下那里:
//
// Created by HooS on 2017/8/14.
//
#include <com_hoos_grayprocessing_OpencvClass.h>
JNIEXPORT jint JNICALL Java_com_hoos_grayprocessing_OpencvClass_convertGray
(JNIEnv *, jclass, jlong addrRgba, jlong addrGray){
}
然后完善代码,完善JNI接口数据转换,实现单幅图像的灰度化功能:
//
// Created by HooS on 2017/7/25.
//
#include <com_hoos_ndkopencvtest_OpencvNativeClass.h>
JNIEXPORT jint JNICALL Java_com_hoos_ndkopencvtest_OpencvNativeClass_convertGray
(JNIEnv *, jclass, jlong addrRgba, jlong addrGray){
// 实现jlong类型到Mat类型的转换
Mat& mRgb = *(Mat*)addrRgba;
Mat& mGray = *(Mat*)addrGray;
// 定义 int 和 jint 类型来接收函数返回值
int conv;
jint retVal;
// 此处调用了toGray函数来对图片进行灰度化处理
conv = toGray(mRgb, mGray);
retVal = (jint)conv;
return retVal;
}
看注释可以知道, 我们还需要实现toGray函数来实现单幅图片的灰度化, 这个实现代码很简单, 为啥非得再麻烦些折腾些呢, 因为大项目往往会有好多源文件, 相互调用, 所以之前最好有多源文件编译的机会来锻炼一下, nao, 自己创造机会。
接下来,新建gray.h、gray.cpp文件来实现toGray这个函数。按照刚才新建.c/.cpp文件的操作来新建:
// this is gray.h
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
bool toGray(Mat img, Mat& gray);
// this is gray.cpp
#include "gray.h"
bool toGray(Mat img, Mat& gray)
{
if (img.channels() == 3)
cvtColor(img, gray, CV_BGR2GRAY);
else if (img.channels() == 4)
cvtColor(img, gray, CV_BGRA2GRAY);
else if (img.channels() == 1)
gray = img;
if(gray.rows == img.rows && gray.cols == img.cols)
return true;
else
return false;
}
然后再修改下com_hoos_ndkopencvtest_OpencvNativeClass.h文件就好了,添加如下代码(在哪里添加应该都懂):
#include "gray.h"
using namespace cv;
using namespace std;
至此,C/C++源文件搞定。
5)写 Android.mk 和 Application.mk 文件
因为本文采用的gradle的方式来进行NDK开发的, 所以我们会用到 Android.mk 和 Application.mk文件,开始新建这两个文件:
# this is Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#opencv
OPENCVROOT:= D:/Usual/Android/OpenCV-2.4.9-android-sdk #这句代码等号右边写上自己的OpenCV安卓开发包的位置
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED
#思考下,参考我的修改一下就好了
include D:/Usual/Android/OpenCV-2.4.9-android-sdk/sdk/native/jni/OpenCV.mk
#这里写上自己的开发需要的源文件, 我这里需要两个
LOCAL_SRC_FILES := com_hoos_grayprocessing_OpencvClass.cpp gray.cpp
#设置生成的so库的名字 前面的lib和后缀名不用写
LOCAL_MODULE := MyLibs
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
# this is Application.mk
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
#这句是设置生成的cpu指令类型,可以根据自己需求来设置生成的平台, 一般这两个就够了
APP_ABI := armeabi-v7a x86
APP_PLATFORM := android-8 #这句是设置最低安卓平台,可以不弄
6)开始生成so库
源内容如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.hoos.grayprocessing.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>
根据实际情况, 我们不需要TextView, 而需要一个JavaCameraView来显示摄像头帧, 控件ID为"java_camera_view", 修改如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.hoos.grayprocessing.MainActivity">
<org.opencv.android.JavaCameraView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/java_camera_view"
/>
</RelativeLayout>
2)AndroidManifest.xml
原内容是:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hoos.grayprocessing">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
因为应用需要摄像头权限, 所以需要在这个文件添加摄像头权限:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hoos.grayprocessing">
<uses-permission android:name="android.permission.CAMERA"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3)gradle.propertise
需要添加对以前版本的支持,在此文件内容的最后添加一行:
android.useDepredcatedNdk=true
4)build.gradle
原内容:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "com.hoos.grayprocessing"
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
testCompile 'junit:junit:4.12'
compile project(':openCVLibrary249')
}
添加内容, 指明生成的so库的路径:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "com.hoos.grayprocessing"
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets.main{
jniLibs.srcDirs = ['src/main/libs']
jni.srcDirs = []
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
testCompile 'junit:junit:4.12'
compile project(':openCVLibrary249')
}
5)MainActivity.java
部分注释在代码中,
package com.hoos.grayprocessing;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2{
private static String TAG = "MainActivity";
// 初始化一个实例,用来获取摄像头帧
JavaCameraView javaCameraView;
Mat mRgba; // 用来存储原始摄像头数据
Mat mGray; // 用来存储灰度化后的帧数据
// 静态加载之前生成的so库——Mylins
static{
System.loadLibrary("MyLibs");
}
// 回调开初始化帧数据
BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch(status){
case BaseLoaderCallback.SUCCESS:
javaCameraView.enableView();
break;
default:
super.onManagerConnected(status);
break;
}
super.onManagerConnected(status);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 与java_camera_view控件绑定
javaCameraView = (JavaCameraView)findViewById(R.id.java_camera_view);
javaCameraView.setVisibility(View.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
}
@Override
protected void onPause(){
super.onPause();
if(javaCameraView!=null)
javaCameraView.disableView();
}
protected void onDestroy(){
super.onDestroy();
if(javaCameraView!=null)
javaCameraView.disableView();
}
// 判断是否加载成功OpenCV库
protected void onResume(){
super.onResume();
if(OpenCVLoader.initDebug()){
Log.i(TAG, "Opencv loaded successfully");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
else{
Log.i(TAG, "Opencv not loaded");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_9, this, mLoaderCallback);
}
}
@Override
public void onCameraViewStarted(int width, int height) {
// 初始化这两个Mat类型
mRgba = new Mat(height, width, CvType.CV_8UC4);
mGray = new Mat(height, width, CvType.CV_8UC1);
}
@Override
public void onCameraViewStopped() {
mRgba.release();
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
// 得到摄像头原始帧数据
mRgba = inputFrame.rgba();
// 调用OpencvClass类的方法来对帧数据进行灰度化
OpencvClass.convertGray(mRgba.getNativeObjAddr(), mGray.getNativeObjAddr());
return mGray;
}
}
此外, 出现了如下提示"Gradle files have changed ...", 请单击右边的"Sync Now"来进行更新。
木有问题
建议创建x86的虚拟机):
简单的调用OpenCV库的Android NDK开发 工具Android Studio的更多相关文章
- Android中开发工具Android Studio修改created用户(windows环境)
最近经常有朋友反馈说我的安卓项目中,在一些类中会出现Created by panchengjia on 2016/12/30的字样,是如何自动实现的(默认一般为Administrator),如下图: ...
- Android NDK开发之Android.mk文件
Android NDK开发指南---Android.mk文件 博客分类: Android NDK开发指南 Android.mk文件语法详述 介绍: ------------ 这篇文档是用来描述你的 ...
- Android NDK开发及调用标准linux动态库.so文件
源:Android NDK开发及调用标准linux动态库.so文件 预备知识及环境搭建 1.NDK(native development Kit)原生开发工具包,用来快速开发C.C++动态库,并能自动 ...
- !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
http://hujiaweibujidao.github.io/blog/2013/11/18/android-ndk-and-opencv-development-3/ Android Ndk a ...
- Android NDK开发及OpenCV初步学习笔记
https://www.jianshu.com/p/c29bb20908da Android NDK开发及OpenCV初步学习笔记 Super_圣代 关注 2017.08.19 00:55* 字数 6 ...
- windows下Qt Creator5.1.0编写程序以及调用OpenCV库
系统说明 最近使用opencv编写程序,程序编的差不多就学习使用QT加个界面,首先声明下本人的系统和使用的软件版本, 系统: windows xp QT IDE:QT Creator5.1.0 Ope ...
- Android NDK开发初识
神秘的Android NDK开发往往众多程序员感到兴奋,但又不知它为何物,由于近期开发应用时,为了是开发的.apk文件不被他人解读(反编译),查阅了很多资料,其中有提到使用NDK开发,怀着好奇的心理, ...
- Android NDK 开发(二) -- 从Hlello World学起【转】
转载请注明出处:http://blog.csdn.net/allen315410/article/details/41805719 上篇文章讲述了Android NDK开发的一些基本概念,以及NDK ...
- Android NDK开发Hello Word!
在之前的博客中已经为大家介绍了,如何在win环境下配置DNK程序,本篇我将带大家实现一个简单的Hello jni程序,让大家真正感受一下NDK开发的魅力.这里我们选择使用C+JAVA开发Android ...
随机推荐
- 【开源】【前后端分离】【优雅编码】分享我工作中的一款MVC+EF+IoC+Layui前后端分离的框架——【NO.1】框架概述
写博客之前总想说点什么,但写的时候又忘了想说点什么,算了,不说了,还是来送福利吧. 今天是来分享我在平时工作中搭建的一套前后端分离的框架. 平时工作大多时候都是在做管理类型的软件开发,无非就是增.删. ...
- POI不同版本替换Word模板时的问题
一.问题描述 通过POI,把Word中的占位符替换为实际的值,以生成复杂结构的业务报告. 在POI 3.9上,功能正常.由于某些原因升级到POI 3.10.1后,项目组反馈说Word模板出错,无法生成 ...
- SpringBoot下配置FreeMarker配置远程模版
需求产生原因 要求在同一个接口中,根据不同的参数,返回不同的视图结果 所有的视图中的数据基本一致 要求页面能静态化,优化SEO 例如:A接口返回客户的信息 客户A在调用接口时,返回其个性化定制的页面A ...
- .NET MVC 二级域名路由的实现
.NET MVC 5以下版本: http://www.cnblogs.com/luanwey/archive/2009/08/12/1544444.html http://blog.maartenba ...
- 【持续更新】JavaScript常见面试题整理
[重点提前说]这篇博客里的问题涉及到了了JS中常见的的基础知识点,也是面试中常见的一些问题,建议初入职场的园友Mark收藏,本文会持续更新~ 1. 引入JS的三种方式 1.在HTML标签中直接使用,直 ...
- day2--命令总结
1.mkdir 创建目录 -p递归(用来创建层级目录,底层目录不存在) 2.touch 创建文件 3.ls ...
- windows中更换Jdk版本不生效
本机已经安装了jdk1.7,而比较早期的项目需要依赖jdk1.6,于是同时在本机安装了jdk1.6和jdk1.7. 安装jdk1.6前,执行java -version得到 C:\Users\liuxi ...
- 检测应用的内存泄漏情况(shell)
写代码--调试--修BUG 改来改去可能还存在一些没发现的问题,在工程量大的时候更容易出现,例如内存泄漏这样的问题,严重影响着系统性能. 网上有些检测C程序是否存在内存泄漏的工具还不错的,例如valg ...
- 从零一起学Spring Boot之LayIM项目长成记(五)websocket
前言 距离上一篇已经比较久的时间了,项目也是开了个头.并且,由于网上的关于Spring Boot的websocket讲解也比较多.于是我采用了另外的一个通讯框架 t-io 来实现LayIM中的通讯功能 ...
- 1)C语言简介(C自考学习)
C语言历史由来 世界上第一个高级语言是"ALFOL",而C的前身是ALGOL语言.1970年美国贝尔实验室的肯·汤普逊对BCPL(基本复合程序设计语言)进行了进一步的简化,突出了硬 ...