OpenCV的Android开发
该教程会教会您在Android项目中如何使用OpenCV库。
教程在Windows 7环境编写,当然可以运行在OpenCV For Android SDK所支持的其他操作系统之上。
该教程假设您已经完成了如下的安装和配置:
- JDK
- Android SDK and NDK
- Eclipse IDE
- ADT and CDT plugins for Eclipse
如果你需要上述环境搭建、配置的帮助,你可以参考《Android开发介绍》。
该教程还会假设你已经在你的电脑上安装了OpenCV For Android,并且OpenCV Manager已经运行在你的测试设备上了。如果你需要这方面的帮助你参考《OpenCV For Android SDK》。
如果在实践下面的教程中出了问题,你可以通过OpenCV For Android 讨论组联系我们,或这OpenCV通过Q&A forum。我们会尽快的为您解决问题。
在你的Android项目中使用OpenCV库
Java
程序开发中使用异步初始化
在应用开过过程中异步初始化是被推荐使用的一个初始化方式。它通过OpenCV Manager来访问安装在目标系统中的OpenCV库。
- 添加OpneCV项目到你的workspace中。菜单File -> Import -> Existing project in your workspace。
点击浏览按钮定位到OpenCV4Android SDK(OpenCV-2.4.9-android-sdk/sdk)。 - 在程序内添加一个OpenCV Java SDK的关联(reference)Project -> Properties -> Android -> Library -> Add select OpenCV Library-2.4.9。
在大多数情况下OpenCV Manager可能已经通过你的Google Play进行了自动安装。另外一种情况就是Google Play没有被安装,或者模拟器、开发板。你可以通过adb工具进行手动安装。
这段代码很简单的实现了异步初始化。详细参看sample ‘15-puzzle’
public class Sample1Java extends Activity implements CvCameraViewListener {
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
@Override
public void onResume()
{
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback);
}
...
}
在这种情况下应用程序的工作通过OpenCV Manager异步完成。当初始化完成之后,回调OnManagerConnected会被UI线程调用。请注意回调返回之前,不允许调用OpenCV和OpenCV本地依赖。OpenVC初始化完成之后,在加载依赖于OpenCV的本地库。默认情况下BaseLoaderCallback实现需要使用Activity的Context来创建,因为在初始化失败的时候会调用 Activity.finish()方法。撤销这行为你需要重写BaseLoaderCallback 类中的finish()方法,实现你自己的完成方法。
程序开发中使用静态初始化
根据这个方式所有的OpenCV库都需要包含在你应用程序中。它的设计主要是以开发为目的,这种方式是过时的代码生产方式,发布的包推荐使用上述方式,去链接OpenCV Manager实现异步初始化。
- 使用与异步初始化相同的方式添加OpenCV库项目到你的workspace里面。用户菜单File->Import->Existing project in your workspace,点击浏览按钮选择OpenCV SDK的路径 在 (Opencv-2.4.9-android-sdk/sdk)。
- 在程序内添加一个OpenCV Java SDK的关联(reference)Project -> Properties -> Android -> Library -> Add select OpenCV Library-2.4.9。
- 如果你的应用程序不包含JNI部分,在
<OpenCV-2.4.9-android-sdk>/sdk/native/libs/<target_arch>
拷贝相应的OpenCV本地库到你项目的libs/<target_arch>
。
在项目包含JNI part的情况,取代手动拷贝你需要修改你的Android.mk文件:在"include $(CLEAR_VARS)"
后面,和在` “include path_to_OpenCV-2.4.9-android-sdk/sdk/native/jni/OpenCV.mk”签名添加如下两行代码。
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
修改完成后应该类似于下面这样:
include $(CLEAR_VARS)
# OpenCV
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
include ../../sdk/native/jni/OpenCV.mk
在JNI build期间OpenCV库将被拷贝到你程序的libs
目录,Eclipse会自动的将这些库放在APK中。
- 最后一步是在应用程序调用OpenCV API之前,在Java代码中开启OpenCV。这是一个例子,在Activity类的static段里面:
static {
if (!OpenCVLoader.initDebug()) {
// Handle initialization error
}
}
如果你的应用程序包含其他OpenCV依赖的本地库,你应当先加载他们,再初始化OpenCV。
static {
if (!OpenCVLoader.initDebug()) {
// Handle initialization error
} else {
System.loadLibrary("my_jni_lib1");
System.loadLibrary("my_jni_lib2");
}
}
Native/C++
将OpenCV作为你应用程序的一部分来编译Android程序,应采用一下的步骤:
- 你可以使用一个环境变量指定你的OpenCV的位置,或者在jni/Android.mk中将相对地址或者绝对地址写死。
- 使用共同的规则,文件jni/Android.mk为现在的程序而写。详细信息参看Android NDK文档,该文档在
<path_where_NDK_is_placed>/docs/ANDROID-MK.html
。 - 这面这行
include C:\Work\OpenCV4Android\OpenCV-2.4.9-android-sdk\sdk\native\jni\OpenCV.mk
应当插入到jni/Android.mk文件的这一行之后:
include $(CLEAR_VARS)
- OpenCV有多个自定义变量,当你使用OpenCV Manager API进行异步加载的时候,并不需要使用他们。
提示:这个变量应当设置在”include …/OpenCV.mk”之前:
OPENCV_INSTALL_MODULES:=on
拷贝所需要的OpenCV动态库到项目libs目录中,因为APK需要包含他们。
OPENCV_CAMERA_MODULES:=off
不将OpenCV camera发布的库拷贝到项目目录。
OPENCV_LIB_TYPE:=STATIC
执行静态链接OpenCV。默认的是动态链接,项目的JNI会依赖于libopencv_java.so.
5. Application.mk应当存在,并且需要包含下面这几行:
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
像这样一行:
APP_ABI := armeabi-v7a
需要指定应用程序的目标平台。
当编译应用程序的时候,JNI库会出现链接错误,例如:(like "In function 'cv::toUtf16(std::basic_string<...>... undefined reference to 'mbstowcs'"
)。Application.mk里面的这一行就可以对其修正:
APP_PLATFORM := android-9
- 无论使用ndk-build手册调用还是安装Eclipse CDT Builder来编译本地JNI库,编译java部分,都会创建一个APK。
Hello OpenCV Sample
这里是一个基础教程,指导你创建一个OpenCV-centric应用程序的simple。他能访问照相机的输出,执行它并显示出来。
- 打开Eclipse IDE创建一个干净的workspace,创建一个Android项目File->New->Android Project。
- 设置名称、目标、包名和minSDK版本。OpenCV4AndroidSDK支持的最低Android编译版本是11.最低设备API版本是8.
- 允许Eclipse图创建默认的activity。将Activity名称定义为HelloOpenCvActivity。
- 选择Blank Activity全屏layout。设置layout的名称为HelloOpenCvLayout。
- 引入OpenCV库到你的项目workspace中。
- 关联OpenCV库到你的项目属性中。
- 编译你的layout xml文件并且粘贴如下的内容:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:opencv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<org.opencv.android.JavaCameraView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone"
android:id="@+id/HelloOpenCvView"
opencv:show_fps="true"
opencv:camera_id="any" />
</LinearLayout>
- 在AndroidManifest.xml文件中添加如下权限:
</application>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
<uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
- 在AndroidManifest.xml中设置应用程序的风格,隐藏title和系统按钮。
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
- 在Activity中添加OpenCV库的初始化。
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
@Override
public void onResume()
{
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback);
}
- 你的Activity类需要实现CvCameraViewListener2接口
YourActivity extends Activity implements CvCameraViewListener2
,onCreate、onDestroy、onPause实现下面的代码。
private CameraBridgeViewBase mOpenCvCameraView;
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.HelloOpenCvLayout);
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.HelloOpenCvView);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
public void onCameraViewStarted(int width, int height) {
}
public void onCameraViewStopped() {
}
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
return inputFrame.rgba();
}
- 运行你的用于程序。
来讨论一下非常重要的步骤。每个Android程序必须实现Activity和View。在第一步我们创建了Blank Activity并且定义了view的layout。OpenCV-centric必须实现OpenCV的初始化,创建自己的view来预览相机并且实现CvCameraViewListener2接口来获取相机的帧数据。
首先我们创建了我们自己的应用程序,view使用layout。我自己的layout内容仅仅是一个全屏的组件类org.opencv.android.JavaCameraView. 。这个类通过OpenCV library来实现。它继承CameraBridgeViewBase,extends SurfaceView 使用Android的相机API。
然后创建了laoyout我们需要实现Activity类。OpenCV的初始化在上面已经讨论过了。在这个sample里面使用了异步初始化。实现CvCameraViewListener接口,允许你添加执行抓取帧数据,并且将数据显示在屏幕上。必须实现函数onCameraFrame。它是一个回调函数,并且它会取回相机的帧数据。回调的输入对象是CvCameraViewFrame类。
提示 不要保存或者使用CvCameraViewFrame对象的onCameraFrame回调。这个对象没有它自己的状态和行为。
它有rgba()和gray()方法允许得到RGBA帧数据,和一个灰度通道的Mat。它希望通过onCameraFrame方法返回RGBA真,这样就可以绘制到屏幕上了。