OpenCV On Android最佳环境配置指南(Eclipse篇)

·  阅读 700

注:本篇文章作者已不再维护,因为现在大都以Android Studio进行开发。后续只维护Android Studio篇
如果你坚持使用Eclipse,请确保所有环境与本文一致。

简介

本教程是经过本人多次踩坑,并结合网上众多OpenCV On Android的配置教程总结而来,尽希望能帮助学习OpenCV的朋友们少走弯路。如果配置上遇到问题,可在评论中留言,我将尽力帮助解决。

如果您使用的是Android Studio,请参考下一章OpenCV On Android最佳环境配置指南(Android studio篇)

如有转载,请标明出处

环境

  • 电脑:Windows10
  • Java:jdk1.8.0_172
  • Eclipse:Photon Release (4.8.0)
  • ADT:ADT-24.0.2
  • NDK:Android Studio自带的最新NDK
  • OpenCV:V3.4.1
  • SDK:25.2.5(由24.4.1更新)

注:以上配置基本上为最新版本。其中,Eclipse可与Android Studio共用一个NDK,但SDK不能通用,否则你将不能在eclipse上正确创建Android项目。


一、创建OpenCV Demo

首先创建一个普通的Android应用,需要注意的是,我们需要将Minimum Required SDK设置为API15及以上,这样既能兼容市面上95%的Android手机,又不会引入潜在的错误。如果此处出现错误,请确保你的JDK、ADT和SDK是否配置正确。

这里我创建名为OpenCVDemo的项目,包名为com.demo.opencv,OK。

1.png


二、OpenCV Java库使用指南

2.1、环境配置

第一步Eclipse菜单->File->Import->Android->Existing Android Code Into Workspace,然后导入OpenCV Android SDK\sdk\java 这个目录。

2.png

为了防止误操作OpenCV库,建议勾选Copy project into workspace,将该库copy到你的工作文件夹,然后点击Finish。

如果导入后出现错误,请将Project build Target设置为Android5.0以上(因为一般是Camera2报错,Camera2只存在于Android5.0+)。具体步骤为:

项目鼠标右键,选择Properties -> Android,勾选 Android5.0 以上的版本即可,然后Apply and Close

这里我选择的是5.0.1。

3.png

第二步:还是进入上一步的页面,点击Library里的Add按钮,添加 OpenCV 库,完成后,你的项目便可以调用到 OpenCV Java 函数了。

4.png

2.2、Demo编写

创建一个Java类,名为OpenCV_Java.java,内容如下:

public class OpenCV_Java extends BaseLoaderCallback {
    private boolean isInit = false;
    private Context context;

    public OpenCV_Java(Context context) {
        super(context);
        this.context = context;
    }

    @Override
    public void onManagerConnected(int status) {
        switch (status) {
            case LoaderCallbackInterface.SUCCESS:
                isInit = true;
                break;
            default:
                isInit = false;
                super.onManagerConnected(status);
                break;
        }
    }

    public void toGary(Bitmap bitmap) {
        if (isInit) {
            Mat mat = new Mat();
            Utils.bitmapToMat(bitmap, mat);
            Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGBA2GRAY);
            Utils.matToBitmap(mat, bitmap);
        } else {
            Toast.makeText(context, "OpenCV init error", Toast.LENGTH_LONG).show();
        }
    }
}
复制代码

然后是MainActivity,内容如下:

public class MainActivity extends Activity implements OnClickListener {
    private ImageView imageView;
    private Bitmap bitmap;
    private Button showBtn, processBtn;
    private OpenCV_Java javaUtil = new OpenCV_Java(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.imageView);
        showBtn = (Button) findViewById(R.id.show);
        showBtn.setOnClickListener(this);
        processBtn = (Button) findViewById(R.id.process);
        processBtn.setOnClickListener(this);
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION, this, javaUtil);
        } else {
            javaUtil.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    @Override
    public void onClick(View v) {
        if (v == showBtn) {
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
            imageView.setImageBitmap(bitmap);
        } else {
            if (bitmap != null) {//避免二次处理
                javaUtil.toGary(bitmap);
                imageView.setImageBitmap(bitmap);
                bitmap = null;
            }
        }
    }
}
复制代码

布局内容:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/show"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1" 
            android:text="show"/>

        <Button
            android:id="@+id/process"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1" 
            android:text="process"/>
    </LinearLayout>
</RelativeLayout>
复制代码

代码编写完毕,但此时还不能运行,因为手机还未安装OpenCV Manager.apk

2.3、安装OpenCV Manager

位于OpenCV-android-sdk\apk目录下,选择合适的apk程序安装(一般选择OpenCV_3.2.0_Manager_3.20_armeabi.apk)。

2.4、运行Demo

将项目编译打包成apk,安装到手机,运行,点击按钮show后,对图像进行灰度处理,效果如下图所示:

5.png

注意:

  1. 如果出现OpenCV was not initialised correctly.Application will be shut down,可能是你的OpenCV Manager程序与你的cpu架构不同,选择合适的apk即可。
  2. 如果手机版本较高,可能会出现调用不到OpenCV Manager的情况,这是因为手机厂商做了限制,防止恶意软件相互唤醒(如百度全家桶),解决方法请自行百度(因为没有统一的方法)。

2.5、抛弃OpenCV Manager:

安装一个额外的apk对用户来说非常不友好,但使用C/C++编程,又对一些Java程序员又提高了实现难度,故我们应该想一个两全其美的方法,即帮助开发人员使用 Java 快速开发,又无需让用户安装额外的软件。

思路:

Java库实际上只是对NDK库进行java封装,将so文件放在OpenCV Manager内,通过AIDL进行交互,从而实现图像处理。

解决方案:

如果我们将OpenCV Manager里面的so文件直接打包到我们的应用里,不就能抛弃OpenCV Manager了吗?

没错,就是这样,并且 OpenCV 官方也给我们提供了现成的so文件,只需将OpenCV-android-sdk\sdk\native\libs\[ARM架构]\ 目录下(一般采用armeabi-v7a即可)的libopencv_java3.so,放到你的项目libs\[ARM架构]\目录下,如图所示:

6.png

然后在你的MainActivity.java里面主动加载这个so库

public class MainActivity extends Activity implements OnClickListener {
    private ImageView imageView;
    private Bitmap bitmap;
    private Button showBtn, processBtn;
    private OpenCV_Java javaUtil = new OpenCV_Java(this);

    static{
            System.loadLibrary("opencv_java3");
    }

    ......下面内容不变
}
复制代码

编译并安装,这时候,你就可以将你的 OpenCV Manager 卸载掉了。运行结果与之前的无异。

注意: 此方法并不是完美的,因为在这个程序里,你只实现了图像的灰度处理,但却引入了libopencv_java3.so这个so文件,apk足足有4.39M,相比之前的194k来说,接近增长了20倍。除非你的应用比较大,而这个so文件的大小是固定的,此时采用此方法也是一个不错的选择。


三、OpenCV NDK库使用指南

3.1、环境配置

第一步: 配置NDK路径

进入菜单->Window->Preferences->Android->NDK,设置NDK Location,注:请确保该路径下存在ndk-build.cmd文件。如下图:

7.png

第二步: 配置JNI环境

这里我们通过导入配置文件进行配置:

  1. 在你的项目右键Android Tools->Add native support->输入合适的名称->确定,这里我直接使用默认名称。
  2. 本地新建一个xml文件,将下面内容 copy 进去,并修改其中的路径为你ndk库对应的路径
<?xml version="1.0" encoding="UTF-8"?>
<cdtprojectproperties>
    <section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths">
        <language name="c,cpp">
            <includepath>D:\AndroidSDK\AndroidStudio\ndk-bundle\sysroot\usr\include</includepath>
            <includepath>D:\AndroidSDK\AndroidStudio\ndk-bundle\sources\cxx-stl\gnu-libstdc++\4.9\include</includepath>
            <includepath>D:\OpenCV\OpenCV-android-sdk\sdk\native\jni\include</includepath>
            <includepath>D:\AndroidSDK\AndroidStudio\ndk-bundle\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\lib\gcc\arm-linux-androideabi\4.9.x\include</includepath>
        </language>
    </section>
    <section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.Macros">
        <language name="c,cpp">
        </language>
    </section>
</cdtprojectproperties>
复制代码
  1. 项目右键选择最后一个Properties,然后选择C/C++ General -> Path and Symbols,如图所示:

8.png

点击Import Settings->Browse,在电脑本地选择上面的xml文件,点击Finish,环境就导入成功了。

第三步: 检查环境是否正确

  1. 将编译器切换到C/C++编辑器模式:

9.png

  1. 检查Includes路径是否都存在:确保路径不为灰色(如下图),并且都能展开

10.png

  1. 查看环境是否正常。 进入jni目录,打开OpenCVDemo.cpp,在#include<jni.h>上使用Ctrl+鼠标左键,如果eclipse能打开jni.h文件,说明你的JNI环境就搭建成功了。

第四步: 配置OpenCV NDK环境

  1. 将下面内容 copy 到Android.mk文件,注意修改OPENCV_ANDROID_SDK为你的OpenCV路径,并阅读我添加的注释。
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
# ###########################################################
#这里改成你的路径,分割线内其余内容不变
OPENCV_ANDROID_SDK := D:/OpenCV/OpenCV-android-sdk

#OPENCV_BULID_TYPE := NDK #默认NDK环境,不会自动导入openCV_java3.so,故不支持OpenCV Java库
OPENCV_BULID_TYPE := JAVA_AND_NDK #将自动导入openCV_java3.so,来支持Java库(无需安装OpenCV Manager)

ifeq ($(OPENCV_BULID_TYPE), JAVA_AND_NDK) 
	OPENCV_LIB_TYPE := SHARED
	OPENCV_INSTALL_MODULES := on
else
	OPENCV_LIB_TYPE := STATIC
endif 

ifdef OPENCV_ANDROID_SDK
  ifneq ("","$(wildcard $(OPENCV_ANDROID_SDK)/OpenCV.mk)")
    include ${OPENCV_ANDROID_SDK}/OpenCV.mk
  else
    include ${OPENCV_ANDROID_SDK}/sdk/native/jni/OpenCV.mk
  endif
else
  include ../../sdk/native/jni/OpenCV.mk
endif
# ###########################################################

#动态链接日志库
LOCAL_LDLIBS += -llog -ljnigraphics

LOCAL_MODULE    := OpenCVDemo
LOCAL_SRC_FILES := OpenCVDemo.cpp

include $(BUILD_SHARED_LIBRARY)
复制代码
  1. 在jni目录下增加Application.mk文件,内容如下:
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := all
APP_PLATFORM := android-15
APP_OPTIM := debug
复制代码

至此,NDK环境就配置完毕。

3.2、Demo编写

布局文件还是采用之前的布局文件,修改MainActivity.java内容如下:

public class MainActivity extends Activity implements OnClickListener {
    private ImageView imageView;
    private Bitmap bitmap;
    private Button showBtn, processBtn;
    private OpenCV_NDK nativeUtil = new OpenCV_NDK ();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById (R.id.imageView);
        showBtn = (Button) findViewById (R.id.show);
        showBtn.setOnClickListener(this);
        processBtn = (Button) findViewById (R.id.process);
        processBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v == showBtn) {
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
            imageView.setImageBitmap(bitmap);
        } else {
            if (bitmap != null) {
                nativeUtil.toGary(bitmap);
                imageView.setImageBitmap(bitmap);
                bitmap = null;
            }
        }
    }
}
复制代码

可以看到,MainActivity与之前修改不大。

然后是 OpenCV_NDK.java 内容如下:

public class OpenCV_NDK {
    static {
        System.loadLibrary("OpenCVDemo");
    }
    native void toGary(Object bitmap);
}
复制代码

是不是非常简单!

现在只需要为toGary()这个native方法生成头文件,这一步需要使用到javah命令,具体用法,请参考百度。

然后将 OpenCVDemo.cpp 修改为如下内容:

#include "com_demo_opencv_OpenCV_NDK.h"
#include <opencv2/opencv.hpp>
#include <android/bitmap.h>

using namespace cv;

JNIEXPORT void JNICALL Java_com_demo_opencv_OpenCV_1NDK_toGary
  (JNIEnv *env, jobject thiz, jobject bitmap){
    AndroidBitmapInfo bitmapInfo;
    void* bitmapPixels;

    int width, height, ret;

    //解析bitmap
    if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0) {
            return;
    }
    if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
            return ;
    }
    if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0) {
            return;
    }

    width = bitmapInfo.width;
    height = bitmapInfo.height;

    Mat bgra(height, width, CV_8UC4, bitmapPixels);
    Mat gary;
    cvtColor(bgra,gary,COLOR_RGBA2GRAY);
    cvtColor(gary,bgra,COLOR_GRAY2BGRA);

    AndroidBitmap_unlockPixels(env, bitmap);
}
复制代码

注意:上面的方法名,需要跟生成的头文件中方法名保持一致,否则将无法被调用。

然后编译运行,效果与之前Java代码无异,但无需安装OpenCV Manager,此方法编译的安装包只有951kb,我也比较推荐使用NDK开发OpenCV应用程序。


四、OpenCV混合使用指南

假设你已经看了前两种配置,习惯于Java开发,但又觉得OpenCV Java库提供的方法不够,希望使用混合开发,那恭喜你,这一部分就是你所需要的。

这部分内容很少,总结起来就两部分:

  1. 导入OpenCV Java库并关联你的应用,参考OpenCV Java库使用指南(记得写加载so文件的代码,但so文件不需要你手动导入)。
  2. 配置NDK环境与OpenCV环境,参考OpenCV NDK库使用指南
  3. 修改Android.mk文件内容:将OPENCV_BULID_TYPENDK修改为JAVA_AND_NDK

经过上述步骤,在编译时,系统会将你的native代码生成libOpenCVDemo.so文件,同时也会将libopencv_java3.so放入你的libs目录下。如图:

12.png

这样就轻易实现了java和c\c++混合处理图像,同时不用安装OpenCV Manager,并且自动导入libopencv_java3.so。注意:加载so文件的代码必须写


六、总结

本教程致力于帮助OpenCV新人快速配置,可能有不足之处,接受大家的建议与批评,后续将进行补充和改进。

同时欢迎大家一起探讨Android图像处理的知识,共同进步。

OpenCV On Android学习群.png

分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改