• Android下OpenCV的环境搭建


    目录(?)[-]

    1. 前言
    2. 系统环境
    3. 相关工具
    4. Android ADT环境搭建
      1. Android SDK环境变量的配置
      2. Android NDK的安装与配置
    5. OpenCV for Android 环境搭建
    6. 基于SDK的OpenCV开发
    7. 基于NDK的OpenCV开发
      1. Android上层程序的编写
        1. 来自CODE的代码片 activity_mainxml
        2. 来自CODE的代码片 MainActivityjava
        3. 来自CODE的代码片 ImageProcjava
      2. 生成C头文件
      3. 底层C程序的编写
        1. 来自CODE的代码片 Androidmk
        2. 来自CODE的代码片 Applicationmk
        3. 来自CODE的代码片 ImageProccpp
      4. 编译和运行程序
    8. 总结

      前言

          最近需要在Android移动设备上进行OpenCV开发,搭建环境是第一步,我在网上看了n个相关的中文和英文的教程,终于成功搭建环境,现在跟大家分享一下成果,希望为大家减少不必要的困惑和麻烦。

      系统环境

          win7/win8(注:我的系统是win8专业版)

      相关工具

      1.Android ADT Bundle for Windows(注: 我下的是adt-bundle-windows-x86_64-20131030)
      下载地址:https://developer.android.com/sdk/index.html?hl=sk
      2.Android NDK(注:我下的是android-ndk-r8e-windows-x86_64)
      下载地址:https://developer.android.com/tools/sdk/ndk/index.html
      3.OpenCV for Android(注:我下的是OpenCV-2.4.8-android-sdk)
      下载地址:http://sourceforge.net/projects/opencvlibrary/files/opencv-android/

      Android ADT环境搭建

          在下面的操作前,保证你的PC机上已经安装了JDK,关于JDK的安装及其环境变量的配置,在此不再介绍,网上资料很多。

      解压下载的Android ADT Bundle for Windows到某一目录下,我解压在D:MySoftware目录下。

      Android SDK环境变量的配置

              打开 计算机->属性->高级系统设置->高级->环境变量,在系统变量栏目下新建“ANDROID_SDK_HOME”变量,变量值为adt-bundle-windows目录下sdk目录的路径,我的是"D:MySoftwareadt-bundle-windows-x86_64-20131030sdk",如图:

          在Path变量值中添加"%ANDROID_SDK_HOME% ools;%ANDROID_SDK_HOME%platform-tools"

      进入cmd命令窗口,检查SDK是否安装成功。
      运行 android –h 如果有下面的输出,表明安装成功:

            打开%ANDROID_SDK_HOME%eclipse下的eclipse.exe,现在进行自己的安卓开发之旅了。

      Android NDK的安装与配置

         将下载下来的Android NDK解压到某一目录,这里我解压到的目录是“D:MySoftware”。

      打开%ANDROID_SDK_HOME%eclipse下的eclipse.exe,在eclipse中,选择windows->Preference->Android->NDK,然后添加刚才解压的NDK目录的路径,如图:

      点击"ok"按钮,至此NDK环境搭建完成。

      OpenCV for Android 环境搭建

          解压下载的OpenCV for Android到某一目录下。我解压到的目录是“D:MySoftware”。将解压后的目录下的sdk(我的是:D:MySoftwareOpenCV-2.4.8-android-sdksdk)目录拷贝到你的android的workspace目录下,改名为:OpenCV-SDK(也可以改成其它名)。

          在eclipse中,选择File->import,选择Existing project into Workspace:

          选择“next”,然后如下图,选择OpenCV-SDK目录的路径:

      点击“Finish”完成。

      基于SDK的OpenCV开发

          这种情形下,我们可以直接调用android为我们封装好的openCV接口进行开发,我们只需要上面的OpenCV for Android环境就可以进行开发。

          在eclipse中,选择File->import,选择Existing project into Workspace,选择...OpenCV-***-android-sdksample(我的是D:MySoftwareOpenCV-2.4.8-android-sdksamples)目录下的“OpenCV Tutorial 1 - Camera Preview”:

          点击“Finish”完成。此时工程会报错,需进行如下操作:

          选中工程,右击选择Properties,然后在左边选择Android,在Project build target栏中选择一个:

          在Library栏目中选择"add",增加OpenCV类库:

          选中OpenCV Library,点击OK

          点击ok完成操作,此时你会发现项目不再报错。

          如果此时依然出现错误,可能是项目太旧,与新的target不太兼容的缘故,此时按以下操作应该能解决问题:

          选中项目,右键选择build path->configure build path...

          选中Android Dependencies和 Android Private Libraries,点击“Remove”进行删除。点击“OK”完成。

          此时选中工程,右键选择 Android tools->fix project properties。错误消失。

      你可以使用真机或模拟器运行运行该程序,这是个基于openCV的摄像头调用程序。需要注意的是,在运行前你需要安装OpenCV_***_Manager_**_*.apk,否则opencv应用将会因为无法加载opencv类库而无法运行。OpenCV_***_Manager_**_*.apk存在于...OpenCV-***-android-sdkapk(我的是D:MySoftwareOpenCV-2.4.8-android-sdkapk)目录下。我安装的是“OpenCV_2.4.8_Manager_2.16_armeabi.apk”。

          当然,你也可以自己新建android工程,然后按上述方法进行openCV库的引用,然后调用相关接口,进行自己的项目开发。

          然而android只是对openCV的部分进行了封装,有许多openCV的功能并没有被封装,所以要实现很多复杂的功能还必须用C/C++进行实现,这就是我们下面要介绍的方式。

      基于NDK的OpenCV开发

      下面我将以一个实例向大家展示基于NDK的OpenCV开发。

          新建android工程NdkOpenCV,将一张图片添加到资源文件,并命名为"test.jpg",按上面所示将OpenCV Library添加到工程中。

      Android上层程序的编写

      1. activity_main.xml
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        
        <Button 
            android:id="@+id/btn"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/app_name"/>
        
        <ImageView
            android:id="@+id/image_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@string/app_name"/>
    
    
    </LinearLayout>

    2. MainActivity.java

    package my.example.ndkopencv;
    
    
    import org.opencv.android.BaseLoaderCallback;
    import org.opencv.android.LoaderCallbackInterface;
    import org.opencv.android.OpenCVLoader;
    import android.os.Bundle;
    import android.app.Activity;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Bitmap.Config;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.ImageView;
    
    
    public class MainActivity extends Activity implements OnClickListener{
    
    
        private Button btnProc;
        private ImageView imageView;
        private Bitmap bmp;
        
        //OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作
         private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
            @Override
            public void onManagerConnected(int status) {
                switch (status) {
                    case LoaderCallbackInterface.SUCCESS:{
                        System.loadLibrary("image_proc");
                    } break;
                    default:{
                        super.onManagerConnected(status);
                    } break;
                }
            }
        };
        
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btnProc = (Button) findViewById(R.id.btn);
            imageView = (ImageView) findViewById(R.id.image_view);
            //将测试图片加载程序中并进行显示
             bmp = BitmapFactory.decodeResource(getResources(), R.drawable.test);
            imageView.setImageBitmap(bmp);
            btnProc.setOnClickListener(this);
        }
    
    
        @Override
        public void onClick(View v) {
             
            int w = bmp.getWidth();
            int h = bmp.getHeight();
            int[] pixels = new int[w*h];     
            bmp.getPixels(pixels, 0, w, 0, 0, w, h);
            int[] resultInt = ImageProc.grayProc(pixels, w, h);
            Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);
            resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);
            imageView.setImageBitmap(resultImg);    
        }
        
        @Override
        public void onResume(){
            super.onResume();
            //通过OpenCV引擎服务加载并初始化OpenCV类库
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
        }
    }
    3. ImageProc.java
    package my.example.ndkopencv;
    
    
    public class ImageProc {
        public static native int[] grayProc(int[] pixels, int w, int h);
    }
    生成C++头文件

      打开cmd,进入android工程所在的目录下的bin/classes目录下,执行javah ****.ImageProc命令:

           在F:MyCodeandroidNdkOpenCVinclasses目录下多了个my_example_ndkopencv_ImageProc.h文件,将该文件重命名为ImageProc.h。

      底层C++程序的编写

      在项目中建立jni文件夹,将上面生成的ImageProc.h文件移动到jni文件夹下。然后分别新建Android.mk,Application.mk,ImageProc.cpp文件,这三个文件内容如下:

      1.Android.mk

      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      include ../OpenCV-SDK/native/jni/OpenCV.mk
      LOCAL_SRC_FILES  := ImageProc.cpp
      LOCAL_MODULE     := image_proc
      include $(BUILD_SHARED_LIBRARY)

      代码说明:

      第一行:指明当前编译路径;

      第二行:清空变量;

      第三行:将OpenCV类库中的编译文件包含进来,如此一来在我们的工程中即可使用OpenCV类库;

      第四行:指定需要编译的C++源文件;

      第五行:指定编译生成的类库名称;

      第六行:调用命令将源文件编译为静态库。

      2.Application.mk

      APP_STL := gnustl_static
      APP_CPPFLAGS := -frtti -fexceptions
      APP_ABI := armeabi-v7a
      APP_PLATFORM := android-8

      3.ImageProc.cpp

       
      #include <ImageProc.h>
      #include <opencv2/core/core.hpp>
      #include <string>
      #include <vector>
      
      
      using namespace cv;
      using namespace std;
      
      
      JNIEXPORT jintArray JNICALL Java_my_example_ndkopencv_ImageProc_grayProc(JNIEnv* env, jclass obj, jintArray buf, jint w, jint h){
          jint *cbuf;
          cbuf = env->GetIntArrayElements(buf, false);
          if(cbuf == NULL){
              return 0;
          }
      
      
          Mat imgData(h, w, CV_8UC4, (unsigned char*)cbuf);
      
      
          uchar* ptr = imgData.ptr(0);
          for(int i = 0; i < w*h; i ++){
              //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
              //对于一个int四字节,其彩色值存储方式为:BGRA
              int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
              ptr[4*i+1] = grayScale;
              ptr[4*i+2] = grayScale;
              ptr[4*i+0] = grayScale;
          }
      
      
          int size=w * h;
          jintArray result = env->NewIntArray(size);
          env->SetIntArrayRegion(result, 0, size, cbuf);
          env->ReleaseIntArrayElements(buf, cbuf, 0);
          return result;
      }

       

      上面操作完成后,选中工程,右键选择Android tools->add native support...

      点击“Finish”完成。

      然后选中工程,右键选择properties,在左侧选择C/C++ General->Paths and Symbols.

      在右侧选择"Add",然后选择File System,选择OpenCV-SDK所在目录下的“nativejniinclude”路径,并选择“ok”:

      点击“ok”,完成。

      编译和运行程序

      现在运行程序,eclipse会先调用ndk-build对C++程序进行编译,如果你使用的是NDK8以上的版本,当前可能会出现如下信息:

      Android NDK: WARNING:jni/Android.mk:detection_based_tracker: non-system libraries in linker flags: -lopencv_java
      Android NDK:     This is likely to result in incorrect builds. Try using LOCAL_S

      这是NDK9目前的问题(当前最新版本是android-ndk-r9d-windows-x86_64),希望在今后能够改进,这也正是我为什么要用NDK8的原因。

      点击“NdkOpenCV”按钮,对比两张图:

      至此,Android下OpenCV的环境搭建已经完成。

      总结

           在此需要说明一下,传统的编译底层C++方法是通过在Cygwin中进行的,由于新版ADT和NDK给我们带来的便捷,我们可以不安装Cygwin进行android下的openCV开发。如果你想了解Cygwin下编译上述程序的方法,我会在今后的博客中为大家介绍。

  • 相关阅读:
    Overloaded的方法是否可以改变返回值的类型
    parseXXX的用法
    java的类型转换问题。int a = 123456;short b = (short)a;System.out.println(b);为什么结果是-7616?
    UVA 10405 Longest Common Subsequence(简单DP)
    POJ 1001 Exponentiation(大数处理)
    POJ 2318 TOYS(计算几何)(二分)
    POJ 1265 Area (计算几何)(Pick定理)
    POJ 3371 Flesch Reading Ease (模拟题)
    POJ 3687 Labeling Balls(拓扑序列)
    POJ 1094 Sorting It All Out(拓扑序列)
  • 原文地址:https://www.cnblogs.com/yishujun/p/3628992.html
Copyright © 2020-2023  润新知