同步操作将从 zeners/dlib-android 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
在Android Studio工程中集成Dlib的方法流程,由于本人是Android小白,有错误之处还请指正。
编译环境准备(以我个人电脑为例)
创建一个dlib4android文件夹并在该文件夹下创建一个jni文件件
文件夹结构如下:
dlib4android.
└── jni
将解压的dlib-19.15文件夹拷贝到jni文件夹下,此时文件结构如下:
dlib4android.
└── jni
└── dlib-19.15
├── CMakeLists.txt
├── dlib
├── documentation.html
├── examples
├── ISSUE_TEMPLATE.md
├── MANIFEST.in
├── python_examples
├── README.md
├── setup.py
└── tools
虽然我们编译的时候只需要dlib-19.15下的dlib文件夹即可,但是为了保险方便期间都可以放进去。
在jni文件夹下创建Android.mk和Application.mk文件,此时文件夹结构如下:
dlib4android.
└── jni
├── Android.mk
├── Application.mk
└── dlib-19.15
├── CMakeLists.txt
├── dlib
├── documentation.html
├── examples
├── ISSUE_TEMPLATE.md
├── MANIFEST.in
├── python_examples
├── README.md
├── setup.py
└── tools
编辑Android.mk文件内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := dlib #定义最终输出的.so文件的名称
LOCAL_SRC_FILES := $(LOCAL_PATH)/dlib-19.15/dlib/all/source.cpp # 编译的源文件
include $(BUILD_SHARED_LIBRARY)
编辑Application.mk文件内容如下:
APP_OPTIM:=release #设置Dlib采用Release模式编译,虽然Dlib默认就是使用Release模式编译的,但是在这里强调一下省心
APP_STL := gnustl_static #为了和OpenCV兼容,STL设置为与OpenCV一直,至于OpenCV 4.0等到发布Android版本的时候再来验证
APP_CPPFLAGS += -std=c++11 #指定采用c++11编译器
APP_CPPFLAGS += -frtti #支持c++运行时定义,关闭可以节约内存,不过Android手机的内存都辣么大
APP_CPPFLAGS += -fexceptions #支持C++异常处理
APP_CPPFLAGS += -DDLIB_NO_GUI_SUPPORT=1 #交叉编译项 dlib提供了基于xquart的图形界面,该界面不支持Android,关闭该界面的支持
APP_CPPFLAGS += -DLIB_PNG_SUPPORT #交叉编译项 支持读取PNG图像
APP_CPPFLAGS += -DLIB_JPEG_SUPPORT #交叉编译项 支持读取JPG图像
APP_ABI := x86_64, x86, armeabi-v7a, arm64-v8a, mips64, mips#指定需要编译的ABI版本,目前暂不支持armeabi版本
APP_PLATFORM := android-16 #编译的SDK版本
NDK_TOOLCHAIN_VERSION := clang #工具链,如果是在Windows上编译,需要将此行注释掉
注意:
如果在Windows上编译,需要将 NDK_TOOLCHAIN_VERSION := clang注释掉,具体原因未知
目前编译armeabi平台会出现如下错误:
c++ dlib/all/../base64/../threads/thread_pool_extension.h:456:26: error: no type named 'exception_ptr' in namespace 'std'; did you mean 'exception'? mutable std::exception_ptr eptr; // non-null if the task threw an exception ~~~~~^~~~~~~~~~~~~ exception # 已经等等的类似错误
关于该错误的讨论可以见:https://github.com/davisking/dlib/issues/596
这是因为NDK所包含的libstd++并不是完成版本,不能支持C++11的所有特性,在交叉编译的时候容易出现问题。好在Android已经打算弃用armeabi,所以放心大胆的丢弃armeabi就行了。
编译
使用终端(Mac)或者CMD(Windows)cd进入jni文件夹,运行你的NDK安装目录下的ndk-build,此时ndk开始编译。
不过你很快就会遇到错误而终止,错误提示可能如下:
In file included from ../../../../src/main/cpp/dlib-19.15/dlib/opencv.h:10:
In file included from ../../../../src/main/cpp/dlib-19.15/dlib/opencv/cv_image.h:10:
In file included from ../../../../src/main/cpp/dlib-19.15/dlib/opencv/../pixel.h:7:
../../../../src/main/cpp/dlib-19.15/dlib/serialize.h:1635:30: error: no member named 'to_string' in namespace 'std'
std::to_string(objects_read+1) + "th object from the file " + filename +
~~~~~^
1 error generated.
ninja: build stopped: subcommand failed.
这时候不用担心,这是因为dlib默认使用的是的STL是c++_static,而我们为了配合opencv官方发布的Android SDK在Application.mk文件中将其指定为gnustl_static。而在gnustl_static中std::string是没有实现to_string的方法和round的方法。这个时候我们就需要在dlib-19.15/dlib下面创建一个.h文件来自己实现这个方法了。我在dlib-19.15/dlib下创建了一个opencv_dlib_bridge.h文件,并实现这两个方法。opencv_dlib_bridge.h的内容如下:
/**
* @file opencv_dlib_bridge.h
* @author SeventyThree
* @brief OpenCV使用的是gnustl_static模板编译的,Dlib使用的是c++_static,使用该文件将双方结合在一起
* @version 0.1
* @date 2018-10-30
*
* @copyright Copyright (c) 2018
*
*/
#include <string>
#include <sstream>
using namespace std;
namespace std{
template <typename T> std::string to_string(const T& n)
{
std::ostringstream stm;
stm << n;
return stm.str();
}
template <typename T> T round(T v)
{
return (v>0)?(v+0.5):(v-0.5);
}
}
然后将opencv_dlib_bridge.h文件include到serialize.h文件中即可。这样重新编译,就可以正常通过编译了。ndk会在dlib4android文件夹下面创建libs文件夹存放对应abi的libdlib.so文件.而obj只是临时文件不用理会。
dlib4android.
├── jni
| ├── Android.mk
| ├── Application.mk
| └── dlib-19.15
| ├── CMakeLists.txt
| ├── dlib
| ├── documentation.html
| ├── examples
| ├── ISSUE_TEMPLATE.md
| ├── MANIFEST.in
| ├── python_examples
| ├── README.md
| ├── setup.py
| └── tools
├── libs
| └── arm64-v8a
| └── libdlib.so
| └── armeabi-v7a
| └── libdlib.so
| └── x86
| └── libdlib.so
| └── x86_64
| └── libdlib.so
└── obj
8. APP_CPPFLAGS += -DDLIB_JPEG_STATIC带来错误
如果在Application.mk文件中添加了这句,在ndk编译的时候还会出现如下所示的找不到文件的错误:
```c++
undefined reference to 'jpeg_set_quality(jpeg_compress_struct*, int, int)'
jni/./dlib/all/../image_saver/save_jpeg.cpp:153: error: undefined reference to 'jpeg_start_compress(jpeg_compress_struct*, int)'
jni/./dlib/all/../image_saver/save_jpeg.cpp:158: error: undefined reference to 'jpeg_write_scanlines(jpeg_compress_struct*, unsigned char**, unsigned int)'
jni/./dlib/all/../image_saver/save_jpeg.cpp:161: error: undefined reference to 'jpeg_finish_compress(jpeg_compress_struct*)'
```
这也是gnustl_stactic造成的,我们需要在jpeg_loader.cpp文件中加上**extern "C"**
```c++
#ifdef DLIB_JPEG_SUPPORT
#include "../array2d.h"
#include "../pixel.h"
#include "../dir_nav.h"
#include "jpeg_loader.h"
#include <stdio.h>
#ifdef DLIB_JPEG_STATIC
extern "C"{
# include "../external/libjpeg/jpeglib.h"
}
#else
# include <jpeglib.h>
#endif
#include <sstream>
#include <setjmp.h>
```
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
#配置OpenCV
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
add_library( lib_opencv SHARED IMPORTED )
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so)
#配置dlib
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/dlib-19.15) #设置dlib的include文件路径,如果不设置话再native-lib.cpp文件中则无法引入dlib的头文件
add_library( lib_dlib SHARED IMPORTED ) # 这里可以随意命名
set_target_properties(lib_dlib PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libdlib.so) #将lib_dlib与libdlib.so文件对应
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
lib_opencv
lib_dlib #将lib_dlib与native-lib连接在一起
# Links the target library to the log library
# included in the NDK.
${log-lib})
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
abiFilters "x86", "x86_64", "armeabi-v7a", "arm64-v8a" #你用几个ABI加入几个ABI,但是前提得保证你编译的对应ABI的dlib和opencv
}
}
至此dlib已经集成完毕,现在外面使用dlib来检测人脸并绘制出来人脸区域。
#include <android/log.h>
#include <jni.h>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <dlib/opencv.h>
#include <dlib/image_processing.h>
#include <dlib/image_processing/frontal_face_detector.h>
#define LOG_TAG "Native-out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
using namespace std;
using namespace cv;
using namespace dlib;
dlib::frontal_face_detector faceDetector = dlib::get_frontal_face_detector();
extern "C" JNIEXPORT jstring JNICALL Java_com_seventythree_cvdlibdemo_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C" JNIEXPORT void JNICALL Java_com_seventythree_cvdlibdemo_MainActivity_cannyDetect(
JNIEnv *env,
jobject thiz,
jlong matAddr) {
Mat & grayMat = *(Mat * )matAddr;
matAddr;
cv::Canny(grayMat, grayMat, 50, 100);
}
extern "C" JNIEXPORT void JNICALL Java_com_seventythree_cvdlibdemo_MainActivity_faceDetect(
JNIEnv *env,
jobject thiz,
jlong matAddr) {
cv::Mat &img = *(cv::Mat *) matAddr; // 这里的img必须使用&定义,因为我们操作的是内存,否则修改之后的值无法传回给MainActivity
if (img.channels() ==4)
cv::cvtColor(img, img, CV_RGBA2BGR); // onCameraFrame返回的RGB图像是RGBA通道的,这与我们需要的BGR通道不相符,需要转换
cv::Mat rotatedMat = img.clone();
cv::rotate(rotatedMat, rotatedMat, ROTATE_90_COUNTERCLOCKWISE); // OpenCV本身的问题,造成获取的图像旋转了90°,而dlib只能识别竖直的人头,所以我们需要逆时针旋转回来
dlib::cv_image<bgr_pixel> dlibImg(rotatedMat); //dlib的facedetector只接受cv_image<bgr_pixel>图像格式
//
std::vector<dlib::rectangle> faceRects = faceDetector(dlibImg);
if (faceRects.size() > 0) {
LOGD("检测到%d个人脸", faceRects.size());
for (int i = 0; i < faceRects.size(); ++i) {
cv::rectangle(rotatedMat,
cv::Point((int)faceRects[i].left(), (int)faceRects[i].top()),
cv::Point((int)faceRects[i].right(), (int)faceRects[i].bottom()),
cv::Scalar(255, 0, 255),
2, LINE_8, 0);
}
img = rotatedMat.clone();
cv::rotate(img, img, ROTATE_90_CLOCKWISE);
} else
{
LOGD("未能检测到人脸");
}
cv::cvtColor(img, img, CV_BGR2RGBA); //JavaCameraView显示图像需要RGBA通道
}
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
Mat frame = inputFrame.rgba();
faceDetect(frame.getNativeObjAddr());
return frame;
}
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions -Os -O2"
arguments "-DCMAKE_BUILD_TYPE=Release .."
abiFilters "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
}
}
}
一般情况下我们设置arguments "-DCMAKE_BUILD_TYPE=Release .."就可以了,但是对于dlib并不其作用,需要在cppFlags上增加-Os,而且这一项与你编译的时候使用的ndk版本有关。 在你的NDK安装路径下的build/cmake/android.toolchain.cmake文件中有这样一段内容:
# Debug and release flags.
list(APPEND ANDROID_COMPILER_FLAGS_DEBUG -O0)
if(ANDROID_ABI MATCHES "^armeabi")
list(APPEND ANDROID_COMPILER_FLAGS_RELEASE -Os)
else()
list(APPEND ANDROID_COMPILER_FLAGS_RELEASE -O2)
endif()
list(APPEND ANDROID_COMPILER_FLAGS_RELEASE -DNDEBUG)
if(ANDROID_TOOLCHAIN STREQUAL clang)
list(APPEND ANDROID_COMPILER_FLAGS_DEBUG -fno-limit-debug-info)
endif()
我的ndk的release标志位-Os或者-O2,所以我选择了cppFlags "-std=c++11 -frtti -fexceptions -Os -O2".此时再build就一切正常了。不过此时还是回比较慢,大概15FPS左右,要想更快就要考虑图像对齐、降采样、跳帧了。
截止目前发现OpenCV和Dlib在Android上都有点问题。而且Android采用了大量开源的东西,组件之间不同的版本之间兼容性也是非常差的,有时候编译不回来的时候不妨按照别人编译成功的软件环境再试试。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。