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

声明:由于简书写作不便,后续将在掘金上更新和发布文章,包括本文。

掘金账号:徒步青云

注:本篇文章作者已不再维护,因为现在大都以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库使用指南

环境配置

第一步: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以上的版本即可,然后OK,这里我选择的是5.0.1。

3.png

第二步:在你的项目右键也进入上图的页面,Library选择Add,添加OpenCV库,完成后,你的项目便可以调用OpenCV Java函数了。

4.png

Demo编写

创建一个Java图像处理类OpenCV_Java .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.java内容:

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

其位置位于OpenCV-android-sdk\apk目录下,选择合适的apk程序,一般选择OpenCV_3.2.0_Manager_3.20_armeabi.apk即可。

运行Demo

将项目编译打包成apk,安装到手机,运行,点击按钮show后,其效果左所示,
点击process按钮后,将彩图灰度化,如右图所示:


5.png

注意:

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

抛弃OpenCV Manager:

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

思路:Java库实际上只是对NDK库进行java封装,将so文件放在OpenCV Manager内,通过AIDL进行数据交流,并实现图像处理。

解决办法:如果我们将OpenCV Manager里面的NDK库放到我们的应用里,不就能抛弃OpenCV Manager并实现图像处理了吗?答案是YES,我们将OpenCV-android-sdk\sdk\native\libs\[架构]目录下(这里选择合适的架构,一般用armeabi-v7a即可)的libopencv_java3.so放在你的项目libs\[架构]\目录(需自己手动创建)下,项目结构如图:

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");
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    ......下面内容不变

编译并安装,这时候,你就可以将你的OpenCV Manager卸载掉了。运行结果与之前的无异。
此方法并不是完美的,因为在这个程序里,你只实现了彩色图像变灰度图像,但你却安装了这个原生库,apk足足有4.39M,相比之前的194k来说,接近增长了20倍。但如果你的应用比较大,而这个so文件的大小是固定的,此时采用此方法将是一个不错的选择。


四、OpenCV NDK库使用指南

环境配置

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

7.png

第二步:配置JNI环境
这里我将使用简便方法快速配置:
1、在你的项目右键Android Tools->Add native support->输入合适的名称->确定,这里我就直接使用默认的"OpenCVDemo"
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>

3、项目右键选择最后一个Properties,然后选择C/C++ General -> Path and Symbols,如图所示:

8.png

点击Import Settings->Browse,在电脑本地选择上面的xml文件,点击Finish,环境就导入成功了。
接下来查看环境是否正确。
1、查看路径是否存在:按照下图,将编译器切换到C/C++编辑器模式:

9.png

展开项目,查看Include目录下路径是否为灰色(如下图),并且都能展开,如果都满足,表示路径都存在。

10.png

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

第二步:配置OpenCV NDK环境
1、将下面内容copy进Android.mk文件,其中OPENCV_ANDROID_SDK请设置为正确路径,同时请注意我在清单文件里的注释

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)

2、在jni目录下增加Application.mk文件
其内容如下:

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := all
APP_PLATFORM := android-15
APP_OPTIM := debug

到此为止,NDK环境配置完毕。

Demo编写

布局文件与上面的Java 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;
            }
        }
    }
}

这里可以看出并无太大的改变。其中OpenCV_NDK.java内容如下:

public class OpenCV_NDK {

    static{
        System.loadLibrary("OpenCVDemo");
    }
    
    native void toGary(Object bitmap);
}

是不是特别简单^_^。然后在控制台使用javah命令将OpenCV_NDK.java生成.h的c/c++头文件,此处将生成com_demo_opencv_OpenCV_NDK.h(这个文件名跟你包名和类名有关,具体方法请百度)。
将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,图像处理时间本人并没有计算,应该相差无几。


五、OpenCV 混合使用指南

假设你已经看了前两种配置,习惯于Java开发,但又觉得OpenCV Java库提供的方法不够,希望使用混合开发,那恭喜你,这一部分就是你所需要的。这部分内容很少,总结起来就两部分:
1、导入OpenCV Java库并关联你的应用,参考OpenCV Java库使用指南(记得写静态加载so库的内容,但so文件不需要你手动导入)。
2、配置JNI环境与NDK环境,参考OpenCV NDK库使用指南
3、修改Android.mk文件内容:OPENCV_BULID_TYPE := NDKOPENCV_BULID_TYPE := JAVA_AND_NDK
然后编译,这时候Android.mk会打包你的原生代码成libOpenCVDemo.so文件,并将其与libopencv_java3.so同时放入你的libs目录。
如图:

12.png

这样就轻易实现免安装OpenCV Manager,进行java和c++混合处理图像,同时免于手动导入libopencv_java3.so,注意:静态导入so的代码必须有


六、总结

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容