0. 开发环境
之前一直是用ADT bundle自带的Eclipse来做OpenCV4Android的开发,其实AS(Android Studio)经过几年的发展已经逐渐完善,对Android开发的支持也好过Eclipse,所以痛下决心,这一次就花点时间把OpenCV4Android在AS中配置好。我会尽量把配置过程写得详细点,因为自己在配置过程中也遇到了很多坑。
为什么要选择face-detection这个project呢?相信熟悉OpenCV4Android的同学都知道fd的人脸识别模块调用的是jni的库,所以正好在借这个机会把jni在Android Studio里面的配置也搞清楚。
下面是我的开发环境清单:
- 操作系统: Win7
- Android Studio 2.2.2
- JDK&JRE: 1.8
- OpenCV4Android-2.4.10
- NDK: android-ndk-r10d
1. 导入eclipse程序
1.1 确定SdkVersion
在导入程序之前,我们需要先确定待会的OpenCv工程中的一些和SdkVersion有关的配置,最好的办法就是先用AS建一个HelloWorld,也可以顺便熟悉一下Android Studio的开发流程。在工程中打开build.gradle,可以得到我们需要的信息:
#这两个很重要
compileSdkVersion 25
buildToolsVersion "25.0.2"
#这个随意,和开发的APP的兼容性有关
minSdkVersion 21
targetSdkVersion 25
1.2 导入samples\face-detection
在Android Studio的界面上,选择File->New->Import Project:
在弹出的框里面选择OpenCV-2.4.10-android-sdk下的samples\face-detection,点击OK:
选择Next:
下一步请按下图勾选,然后点击Finish:
此时Android Studio会新打开一个窗口实例来导入工程,不出意外会出现如下错误:
此时,我们在AS左侧的Project窗口的打开如下的配置文件:
openCVSamplefacedetection->src->build.gradle
修改如下4个配置信息:
compileSdkVersion 14
buildToolsVersion "25.0.2"
minSdkVersion 8
targetSdkVersion 8
按照之前的HelloWorld工程里的配置进行修改:
在修改完成之后,需要重新进行”Gradle project sync”,点击Tools->Android->Sync Project with Gradle Files:
Oops,貌似又报错了:
Error:Failed to find target with hash string 'android-14' in: D:\develop\Android_Studio\sdk
<a href="install.android.platform">Install missing platform(s) and sync project</a>
原因在于,工程openCVSamplefacedetection依赖于库工程openCVLibrary2410,而库工程openCVLibrary2410的build.gradle配置也需要修改。这里不再赘述,找到openCVLibrary2410下的build.gradle进行修改即可。
修改完成后再次进行”Gradle project sync”,这一次”Gradle Sync”没有报错,随后AS会进行”Gradle build”,也顺利完成。
接下来我们尝试进行工程构建,点击工具栏中的“锤子”图案进行构建:
这一次出现以下错误,分析可以知道是fd工程的jni C++部分没有被正确编译。
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':openCVSamplefacedetection:compileDebugNdk'.
> Error: Your project contains C++ files but it is not using a supported native build system.
1.3 jni的配置
怎样解决上面出现的问题?
首先,我们需要先配置NDK的路径,点击File->Project Structure,在如下的界面上配置NDK的路径。
然后,在左侧的项目窗口中选中openCVSamplefacedetection,右键点击”Link C++ Project with Gradle”,在弹出的窗口中按照下图选择,点击”OK”。
接下来,在左右的项目窗口中的“External Build Files”下,选择Android.mk,修改OpenCV.mk的路径:
include ../../sdk/native/jni/OpenCV.mk
修改后的路径为,当然你需要根据自己电脑上的OpenCV4Android路径进行配置:
include D:\develop\OpenCV-2.4.10-android-sdk\sdk\native\jni\OpenCV.mk
最后一个需要修改的地方,修改对应于当前module(openCVSamplefacedetection)的build.gradle:
在第12行的ndk部分加入以下的声明:
abiFilters "armeabi-v7a"
最终得到:
ndk {
abiFilters "armeabi-v7a"
moduleName "detection_based_tracker"
}
至此,整个jni部分的配置就全部完成,这时再点击”构建“,可以发现已经可以生成成功。
BUILD SUCCESSFUL
2. 剔除OpenCV Manager依赖
在上一小节中,我们已经可以成功地配置fd工程,并且编译生成都成功,此时我们可以将apk部署到手机上进行运行。但是等等,你还想被OpenCV Manager所困扰吗?说实话,每次我想要找到适合自己手机的OpenCV Manager,都要上网查一大堆资料,费时又费劲。
那么接下来我就基于face-detection工程,给大家分享一个去掉OpenCV Manager依赖的方法。
找到mainActivity,在我们这里是FdActivity,如下两段代码是载入OpenCV Manager的核心代码:
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
// Load native library after(!) OpenCV initialization
System.loadLibrary("detection_based_tracker");
try {
// load cascade file from application resources
InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
FileOutputStream os = new FileOutputStream(mCascadeFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
is.close();
os.close();
mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
if (mJavaDetector.empty()) {
Log.e(TAG, "Failed to load cascade classifier");
mJavaDetector = null;
} else
Log.i(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());
mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0);
cascadeDir.delete();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
}
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
@Override
public void onResume()
{
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
}
在onResume method中进行OpenCVLoader的初始化,然后在回调函数onManagerConnected中,当OpenCV Manager载入成功,则进行后续的初始化。
要完全剔除OpenCV Manager的依赖,我们在编译构建apk时就选择将当前依赖的.so库进行静态链接,而不是动态地在app运行时再动态链接。
首先,将OpenCV-2.4.10-android-sdk\sdk\native下的libs文件夹拷贝至face-detection\openCVSamplefacedetection\src\main,并且重新命名为jniLibs;
然后修改代码,移除前面两段代码,在当前的activity中加入如下的静态声明:
static{
System.loadLibrary("opencv_java");
}
将下面这段代码搬移到onCreate函数的最后部分。
// Load native library after(!) OpenCV initialization
System.loadLibrary("detection_based_tracker");
try {
// load cascade file from application resources
InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
FileOutputStream os = new FileOutputStream(mCascadeFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
is.close();
os.close();
mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
if (mJavaDetector.empty()) {
Log.e(TAG, "Failed to load cascade classifier");
mJavaDetector = null;
} else
Log.i(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());
mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0);
cascadeDir.delete();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
}
mOpenCvCameraView.enableView();
这时,我们可以惊喜地发现,在手机上没有安装OpenCV Manager的情况下,face-detection也可以顺利运行。
3. 运行效果
在手机上实测的人脸识别效果如下所示: