DSST折腾笔记(二):在安卓手机下的移植

NDK环境配置好的基础上移植DSST到安卓所需的改动并不多,主要还是编译的配置。

版权声明
原创文章,转载请说明出处:sheng-blog.cn
原文出处

博文结构:

    • 移植环境
    • 前提条件
    • 移植过程及所遇问题
    • 调试补充
    • 跟踪效果
    • 参考资料

移植环境

  • android studio 2.2.3
  • opencv4android 3.2.0
  • 测试机:荣耀7(arm64-v8a)、台电X98 Air III(x86)

前提条件

顺利运行需要目标机器cpu架构是x86或x86_64的(直接使用SSE)或者arm64-v8a(原因上篇博文说过的)。自己的渣机还是armeabi-v7a的,跑不了,在室友清明去浪的前夜赶紧测试了一下他的手机和平板,正是我想要的!果断扣下平板留着后面测试啊哈哈!
啥?白天出去浪晚上回旅馆要拿平板看电影?
诶诶诶,一堆人晚上玩点别的多好,狼人杀啥的,还一个人看电影?
室友对曰:一堆人看电影最好~
我:……
我:要看你就还是拿手机看吧2333~

查看自己手机或平板的cpu架构方法:

adb shell  
cat  /proc/cpuinfo

如图为室友平板的信息,对于flag一栏,需有sse。

ARM架构的长这样(Features这一栏需有neon,当然这还不够,ARMv7还是32位机器,这个工程需要64位机器才能正常运行):

移植过程及所遇问题

首先在/app/src/main下新建一个include文件夹用于保存c头文件,将DSST的那几个头文件拷贝进来,而cpp文件则和native-lib.cpp放一块,即/app/src/main/cpp。然后在CMakeLists里加入头文件路径,并把其余cpp文件也编译添加到native-lib中来:

include_directories(src/main/include)
add_library( 
             native-lib
             SHARED
             src/main/cpp/native-lib.cpp src/main/cpp/fdssttracker.cpp src/main/cpp/fhog.cpp)

如图:

然后同样的,如果目标机器是arm架构,sse.hpp里需做相应头文件的调整,编译,得到熟悉的报错:

error: "NEON support not enabled"

解决方案:在app下的build.gradle的externalNativeBuild{cmake{}}下加入如下代码(注意大括号的包含关系):

arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

编译,搞定,移植工作就算完成了。java层实现一下CameraBridgeViewBase.CvCameraViewListener2这个接口。控制逻辑上,在用户框选了跟踪目标后,将目标框和此帧画面喂给native层对tracker进行初始化,随后的每一帧都喂给native层另一个函数对tracker进行更新并返回新的目标位置给java层更新。

调试补充

平时调试C/C++程序一般习惯用printf来掌握程序走向,判断可能的逻辑错误,NDK下自然也有这个需求。找到一种方法可以像java层使用Log类在logcat里打印消息:
首先是编译配置,在app下的build.gradle的android{defaultConfig{ndk{}}}下加入ldLibs “log”(加完如下,注意大括号的包含关系):

ndk{
      ldLibs "log"
      abiFilters 'armeabi-v7a'
}

此外在CMakeLists中也要添加对log库的链接,如图:

在代码中使用如下:

#include"android/log.h"
//打印一个简单Info级别的日志 对应Java的Log.i("JNI","This is log")

__android_log_print(ANDROID_LOG_INFO,"JNI","This is log"); 

//打印格式化字符串 这里使用的是C语言中printf中的格式。关于C中的printf格式化输出可自行百度,文档非常多。

int i=5;
__android_log_print(ANDROID_LOG_INFO,"JNI","i=%d",i); 

利用参考资料4提供的更优雅地使用方式是新建一个头文件,拷贝以下代码:

#include "android/log.h"

#ifndef LOG_TAG
    #define LOG_TAG "JNI"
#endif
#ifndef IS_DEBUG
    #define IS_DEBUG true
#endif

#define LOG_NOOP (void) 0
//__FILE__ 输出文件名
//__LINE__ 输出行数
//__PRETTY_FUNCTION__  输出方法名
//可以按需选取 %s %u %s 分别与之对应
#define LOG_PRINT(level,fmt,...) __android_log_print(level,LOG_TAG,"(%s:%u) %s: " fmt,__FILE__,__LINE__,__PRETTY_FUNCTION__,##__VA_ARGS__)
//通过IS_DEBUG来控制是否输出日志
#if IS_DEBUG
    #define LOGI(fmt,...) LOG_PRINT(ANDROID_LOG_INFO,fmt,##__VA_ARGS__)
#else
    #define LOGI(...) LOG_NOOP
#endif

#if IS_DEBUG
    #define LOGW(fmt,...) LOG_PRINT(ANDROID_LOG_WARN,fmt ,##__VA_ARGS__)
#else
    #define LOGW(...) LOG_NOOP
#endif

#if IS_DEBUG
    #define LOGD(fmt,...) LOG_PRINT(ANDROID_LOG_DEBUG,fmt ,##__VA_ARGS__)
#else
    #define LOGD(...) LOG_NOOP
#endif

#if IS_DEBUG
    #define LOGE(fmt,...) LOG_PRINT(ANDROID_LOG_ERROR,fmt ,##__VA_ARGS__)
#else
    #define LOGE(...) LOG_NOOP
#endif

#if IS_DEBUG
    #define LOGF(fmt,...) LOG_PRINT(ANDROID_LOG_FATAL,fmt ,##__VA_ARGS__)
#else
    #define LOGF(...) LOG_NOOP
#endif

然后在需要使用的cpp文件中include即可

#include"LogUtils.h"

int func(int i)
  {
    LOGW("this is a warning");
    LOGE("i = %d",i);
  }

跟踪效果

以下是用室友的荣耀8跟踪鹅厂公仔的效果录屏:

参考资料

  • CMake | Android Developers-Using CMake variables in Gradle
  • 向您的项目添加 C 和 C++ 代码
  • NDK Programmer’s Guide
  • Android Studio NDK 入门教程(4)–优雅的在C++中输出Logcat
  • java - ROI in OpenCV from Android - Stack Overflow

你可能感兴趣的:(opencv,tracking,安卓)