• OpenCV On Android环境配置最新&最全指南(Eclipse篇)


    简介

    本教程是经过本人多次踩坑,并参考网上众多OpenCV On Android的配置教程总结而来,尽希望能帮助学习移动图像处理的朋友们少走弯路。
    这也是本人第一次在简书上发布文章,如有不足,希望各位dalao能够指正,我也将及时修改。配置上如果遇到问题,也可以在评论里留言,我将尽力帮助解决。
    如有转载,请标明出处http://www.jianshu.com/p/b260ebb1c180
    如果您使用的是android studio,请参考下一章OpenCV On Android环境配置最新&最全指南(Android studio篇)

    环境

    电脑:Windows10
    Java:jdk1.8.0_131
    Eclipse:Neon.3 Release (4.6.3)
    ADT:ADT-23.0.7
    NDK:Android Studio2.3自带的最新NDK
    OpenCV:V3.2.0
    SDK:25.2.5 (单独下载,请不要与Android Studio同用,否则你将不能在eclipse上正确创建Android项目)

    注:以上配置基本上为最新版本


    一、创建OpenCV Demo

    这里只需创建普通Eclipse Android项目工程,没什么重要的地方可说的,如果这都不会,建议先把Android基础学好。这里我创建的是名为OpenCVDemo的项目,包名为com.demo.opencv


    二、选择您所需要的环境

    1、如果你只想用在应用层编写程序的话,请直接阅读下面OpenCV Java库使用指南
    2、如果只想在原生层快速进行图像处理的话,请直接跳到下面OpenCV NDK库使用指南
    3、如果想同时使用OpenCV Java库和Native库的话,请参考OpenCV 混合使用指南
    接下来我简要介绍两者的区别与优缺点。

    1、使用OpenCV Java库和NDK库的区别

    简而言之,如果使用OpenCV Java库,你只需在应用层使用Java语言编写代码,由OpenCV Java库通过AIDL连接OpenCV Manager进行图像处理。
    如果你使用的OpenCV NDK库,就能在原生层使用C/C++编写代码,此时就涉及到JNI。
    虽然两者使用的编程语言不同,但最终结果都是在原生层实现图像处理功能,因为OpenCV Java库只是将原生代码(C/C++)用Java封装(JNI)好,并打包成so文件,存入OpenCV Manager里,外部应用通过AIDL进行调用,这就是为什么使用Java库时必须要额外安装OpenCV Manager的原因了。不过我将会在OpenCV Java库使用指南中介绍一个不需要安装OpenCV Manager的方法,弥补其不足,同时OpenCV 混合使用指南则提供一个更为简便的方法。

    2、使用OpenCV Java库和NDK库的优缺点

    Java库的缺点:
    如果使用Java库的话,手机上除了安装你编写的应用外,还需要额外安装OpenCV Manager文件,其位于OpenCV Android SDK/apk/目录下,请根据你编写的apk架构(如果你仅使用Java开发,则可以不用考虑apk架构)和你手机cup架构选择对应的apk文件,一般这两个选择armeabi架构便能兼容绝大多数的手机。
    Java库的优点:
    1、不需要C/C++的功底便能轻松实现在Android客户端的图像处理,非常适合只会Java语言的编程人员。
    2、因为有两个应用(自己编写的程序和OpenCV Manager),你将获得更多的内存处理空间(一个Android应用默认分配内存上限为24M)。
    3、Java库封装了几个摄像头显示控件,如:JavaCameraView,NativeCameraView(目前OpenCV3.2里该控件已经被取消),我们使用少量代码就能实现摄像头数据采集->处理->显示等一系列操作。
    NDK库的优点:
    1、如果使用Native库的话,你可以完全抛弃OpenCV Manager的安装,同时由于在底层进行图像处理,其处理速度会增加(但由于Android高版本的优化,加上OpenCV Java库其实也是在调用其原始层的库,所以这种差距并不明显)。
    2、使用NDK库的话,需采用CC++进行编程,此方法更便于从Visual Studio进行程序移植。
    NDK库的缺点:
    需要一定的CC++功底,如果涉及到调用摄像头,需自行编写缓冲框架,避免数据帧堵塞。同时必须熟悉JNI的函数操作,何时释放内存和引用等等。如果你想在底层进行OpenGL ES渲染图像,还需对Android底层和OpenGL ES有一定了解(但前者并不是必须的)。


    三、OpenCV Java库使用指南

    环境配置

    第一步:Eclipse菜单->File->Import->Android->Existing Android Code Into Workspace(如果没此选项,请确保你的eclipse 上ADT是否配置正确),然后导入OpenCV Android SDK sdkjava这个项目。


    1.png

    为了防止误操作OpenCV库,建议勾选Copy project into workspace,将该库copy到你的工作文件夹。然后Finish,如果导入后出现报错,请将Project build Target设置为Android5.0以上(因为一般是Camera2报错,Camera2只存在于Android5.0+),具体方法是项目鼠标右键,选择Properties,然后点击Android,选择Android5.0以上的版本即可,然后OK,这里我选择的是6.0。


    2.png

    第二步:在你的项目右键也进入上图的页面,Library选择Add,添加OpenCV库(如下图),OK后,你的项目将可以使用OpenCV Java函数了。


    3.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_3_2_0, 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-sdkapk目录下,选择合适的apk程序,一般选择OpenCV_3.2.0_Manager_3.20_armeabi.apk即可。

    运行Demo

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


    4.png

    点击按钮process,将其转化为灰度图,如下所示:


    5.png
    • 注意:

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

      抛弃OpenCV Manager:

      安装一个额外的apk对用户来说非常不友好,但使用C/C++编程,对一些Java程序员又提高了实现难度,故我们应该想一个两全其美的方法,即帮助开发人员使用java快速开发,又给用户一良好体验。
      思路:
      Java库实际上只是对NDK库进行java封装,将so文件放在OpenCV Manager内,通过AIDL进行数据交流,并实现图像处理。
      解决办法:
      如果我们将OpenCV Manager里面的NDK库放到我们的应用里,不就能抛弃OpenCV Manager并实现图像处理了吗?答案是YES,我们将OpenCV-android-sdksdk ativelibs[架构]目录下(这里选择合适的架构,一般用armeabi即可)的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进去,修改其中的路径。

    <?xml version="1.0" encoding="UTF-8"?>
    <cdtprojectproperties>
    <section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths">
    <language name="c,cpp">
    <includepath>D:/Android/android-sdk/ndk-bundle/platforms/android-9/arch-arm/usr/include</includepath>
    <includepath>D:/Android/android-sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/include</includepath>
    <includepath>D:/Android/android-sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi/include</includepath>
    <includepath>D:/Android/android-sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include</includepath>
    <includepath>D:/Android/android-sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/include</includepath>
    <includepath>D:/OpenCV/OpenCV-android-sdk/sdk/native/jni/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 := armeabi
    APP_PLATFORM := android-8
    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命令将其生成jni头文件,此处我用的一键生成(参考了《Android C高级编程 使用NDK》),Eclipse配置如下:
    菜单Run->External Tools->External Tools Configurations,双击Program,右侧将产生新界面。配置如下:


    Main选项卡里
    Name:Generate C and C++ HeaderFile
    Location:${system_path:javah}
    Working Directory:${project_loc}/jni
    Arguments:-classpath "${project_classpath};
    ${env_var:ANDROID_SDK_HOME}/platforms/android-14/android.jar" ${java_type_name}

    切换到Refresh选项卡:
    选中Refresh resource up completion,并在列表里选择The project containing the selected resource

    切换到Common选项卡,勾选上External Tools。

    配置完成(上面的配置按照实际情况修改,本人建议还是使用javah命令生成头文件)
    其中ANDROID_SDK_HOME是我在电脑环境配置里创建的一个变量,指向的是sdk路径。


    然后将你的鼠标点中OpenCV_NDK.java,然后点击如图所示位置:


    11.png

    几秒后,将生成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新人快速配置,可能有不足之处,接受大家的建议与批评,后续将进行补充和改进。同时欢迎大家一起探讨Android图像处理的知识,共同进步。

  • 相关阅读:
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(9)各种常用辅助类
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(7)声音播放、硬件信息、键盘模拟及钩子、鼠标模拟及钩子等设备相关
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(6)全屏截图、图标获取、图片打印、页面预览截屏、图片复杂操作等
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(4)CSV、Excel、INI文件、独立存储等文件相关
    DevExpress控件使用经验总结
    Winform开发框架之通用自动更新模块
    详解在数据查看界面中增加记录导航功能,你应该需要的
    使用Aspose.Cell控件实现多个Excel文件的合并
    厚积薄发,丰富的公用类库积累,助你高效进行系统开发(8)非对称加密、BASE64加密、MD5等常用加密处理
    WCF开发框架形成之旅如何实现X509证书加密
  • 原文地址:https://www.cnblogs.com/antflow/p/7340721.html
Copyright © 2020-2023  润新知