zoukankan      html  css  js  c++  java
  • WINDOWS系统Eclipse+NDK+Android + OpenCv

    WINDOWS系统Eclipse+NDK+Android + OpenCv

    参考文档博客

    1 NDK环境搭建

    http://jingyan.baidu.com/article/5d6edee22d908799eadeec9f.html

    2 官方文档

    Android.mk与Application.mk如何编写,OpenCV库如何调用

    http://docs.opencv.org/trunk/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.html#dev-with-ocv-on-android

    3 OpenCv测试代码来源

    http://www.verydemo.com/demo_c131_i131095.html

    4 NDK OpenCv测试入门程序(有问题)

    http://blog.csdn.net/pwh0996/article/details/8957764

    5 NDK测试入门程序(有问题)

    http://blog.csdn.net/redoffice/article/details/6654714

    6 出现部分问题的解法

    http://blog.csdn.net/houshunwei/article/details/17217695

     

    目前Android平台下调用OpenCv库函数有两种方式:

    1使用封装的OpenCV API开发android:这种方式调用Java的包中的类实现类似。

    2 利用JNI接口通过NDK平台调用OpenCV API函数(这种方式比较普遍)。

    本文将实现这两种方式的调用。

    第一部分 准备工作:

    1.1本文所使用的软件版本如下:

    ADT版本:ADT-22.6.3 ,开发工具ADT(Android Development Tool),集成了最新的ADT和NDK插件以及Eclipse,还有一个最新版本SDK。解压之后就可以用了,下载地址http://developer.android.com/tools/sdk/ndk/index.html

    NDK: android-ndk-r10, NDK插件:用于开发Android NDK的插件,ADT版本在20以上,就能安装NDK插件,另外NDK集成了CDT插件,NDK版本在r7以上之后就集成了Cygwin,而且还是十分精简版。下载链接见:http://developer.android.com/tools/sdk/ndk/index.html

    OpenCV:OpenCV-2.4.4-android-sdk,下载地址http://opencv.org/downloads.html

     

    1.2 配置NDK的环境变量

    1.2.1解压NDK压缩包,配置环境变量。

    将解压的地址写入环境变量PATH中。在命令提示符下输入ndk-build如果弹出如下的错误,而不是说ndk-build not found,就说明ndk环境已经安装成功了。特别提示一下,搜索引擎中会告诉一些早期的NDK版本的使用,是在命令提示符下输入build/host- setup.sh;但是NDK经过更新,这个文件已经没有了。只需要输入ndk-build就可以了。

    Android NDK: Could not find application project directory !   
    Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.   
    /home/braincol/workspace/android/android-ndk-r5/build/core/build-local.mk:85: *** Android NDK: Aborting    .  Stop.

    1.2.2 Eclipse配置NDK环境

    打开Eclipse,点Window->Preferences->Android->NDK,设置NDK路径,例如当前的NDK版本是是E:android-ndk-r10。

    1.3 配置OpenCv

    下载OpenCV-2.4.4-android-sdk解压出来,可以看到文件下有apk文件,doc文档,sample例子程序,以及OpenCV4Android-sdk。

    其 中,OpenCV4Android-sdk目录即是我们开发opencv所需要的类库;samples目录中存放着若干opencv应用示例(包括人脸检测等),可为我们进行 android下的opencv开发提供参考;doc目录为opencv类库的使用说明及api文档等;而apk目录则存放着对应于各内核版本的。

     

    1.3.1 安装OpenCV_Manager

        OpenCV_2.4.3.2_Manager_2.4应用安装包。此应用用来管理手机设备中的opencv类库,在运行opencv应用之前,必须确保手机中已经安装了OpenCV_2.4.4.4_Manager_2.6_*.apk,否则opencv应用将会因为无法加载opencv类库而无法运行。

    可以根据自己的手机类型来安装OpenCV_2.4.4.4_Manager.apk.

    通过查询自己的手机CPU信息

    adb shell

    cat /proc/cpuinfo

     

    我的手机是arm v7 –neon 架构的,Android版本为4.4.4选择第3个,第四个是适用于低版本Android系统的手机。

    安装完毕后,可以测试OpenCV_2.4.4.4_Manager.apk.是否正确。

    在samples文件架中找个例子安装包,如example-15-puzzle.apk,安装到手机测试这个拼图游戏是否正常运行。

     

    1.3.2 OpenCV4Android-sdk引入工作空间

    (1) 将OpenCV4Android-sdk目录copy至后面的Android工程的工作空间;

    (2)打开eclipse,将OpenCV-SDK引入到工作空间中,如下图所示:

     

    第二部分 开始实战---使用封装的OpenCV API开发android(不通过jni接口)

     

    1.1 创建工程

    (1) 打开eclipse,创建android应用工程API_OPENCV;

    (2) 在手机的的SD卡根目录/sdcard上新建一个目录andimg ,放入图像img1.jpg

    (3) 在Package Explorer中选择项目API_OPENCV,单击右键在弹出菜单中选择Properties,

    然后在弹出的Properties窗口中左侧选择 Android,然后点击右下方的Add按钮,选择OpenCV Library 2.4.4并点击OK,

    操作完成后,会将OpenCV类库添加到API_OPENCV的Android Dependencies中,如下图所示:

     

    1.2 工程代码:

     

    (1)布局文件:main.xml

    <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"
    
    tools:context="${packageName}.${activityClass}" >
    
    <Button
    
    android:id="@+id/imagegray"
    
    android:layout_width="fill_parent"
    
    android:layout_height="wrap_content"
    
    android:text="图像灰度化" />
    
     
    
    <ImageView
    
    android:id="@+id/imageview"
    
    android:layout_width="wrap_content"
    
    android:layout_height="wrap_content"
    
    android:layout_below="@+id/imagegray"
    
    android:layout_centerHorizontal="true"
    
    android:layout_marginTop="86dp" />
    
    </RelativeLayout> 

     

     

    (2) 在AndroidManifest.xml文件中加入

     

     <!-- 在SDCard中创建与删除文件权限 -->
    
    <uses-permission
    
    android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    
    <!-- 往SDCard写入数据权限 -->
    
    <uses-permission
    
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

     

     

    如图所示


     (3) MainActivity.java

     

    package com.example.api_opencv;
    
     
    
    import org.opencv.android.BaseLoaderCallback;
    
    import org.opencv.android.LoaderCallbackInterface;
    
    import org.opencv.android.OpenCVLoader;
    
    import org.opencv.android.Utils;
    
    import org.opencv.core.Mat;
    
    import org.opencv.imgproc.Imgproc;
    
     
    
    import android.annotation.SuppressLint;
    
    import android.app.Activity;
    
    import android.graphics.Bitmap;
    
    import android.graphics.Bitmap.Config;
    
    import android.graphics.BitmapFactory;
    
    import android.os.Bundle;
    
    import android.view.Menu;
    
    import android.view.MenuItem;
    
    import android.view.View;
    
    import android.view.View.OnClickListener;
    
    import android.widget.Button;
    
    import android.widget.ImageView;
    
     
    
    public class MainActivity extends Activity {
    
     
    
             private Button btngray;
    
         private ImageView imageview;
    
         private Bitmap bmp;
    
          
    
         //OpenCV类库加载并初始化成功后的回调函数,在此我们不进行任何操作
    
            private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) {
    
         @Override
    
         public void onManagerConnected(int status) {
    
         switch (status) {
    
         case LoaderCallbackInterface.SUCCESS:
    
         {
    
         } break;
    
         default:
    
         {
    
         super.onManagerConnected(status);
    
         } break;
    
         }
    
         }
    
         };
    
          
    
            @SuppressLint("SdCardPath")
    
            @Override
    
            protected void onCreate(Bundle savedInstanceState) {
    
                super.onCreate(savedInstanceState);
    
                setContentView(R.layout.activity_main);
    
                this.setTitle("Android API OpenCV");
    
                btngray = (Button)findViewById(R.id.imagegray);
    
         imageview = (ImageView)this.findViewById(R.id.imageview);
    
         bmp=BitmapFactory.decodeFile("/sdcard/andimg/img1.jpg");
    
         imageview.setImageBitmap(bmp);
    
     
    
         btngray.setOnClickListener(new OnClickListener() {    
    
                    @Override
    
                    public void onClick(View v) {
    
             Mat rgbMat = new Mat();
    
             Mat grayMat = new Mat();
    
             //获取lena彩色图像所对应的像素数据
    
             Utils.bitmapToMat(bmp, rgbMat);
    
             //将彩色图像数据转换为灰度图像数据并存储到grayMat中
    
             Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);
    
             //创建一个灰度图像
    
             Bitmap grayBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Config.RGB_565);
    
             //将矩阵grayMat转换为灰度图像
    
             Utils.matToBitmap(grayMat, grayBmp);
    
             imageview.setImageBitmap(grayBmp);
    
                    }
    
                });
    
          
    
            }
    
     
    
            @Override
    
         public void onResume()
    
         {
    
         //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是
    
         //OpenCV_2.4.4_Manager_2.4_*.apk程序包,存在于OpenCV安装包的apk目录中
    
         super.onResume();
    
         OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_4, this, mOpenCVCallBack);
    
         }
    
     
    
    } 

     


    1.3 运行结果

    第三部分 开始实战--- 利用JNI接口通过NDK平台调用OpenCV API函数

    1. 新建一个Android工程 NDK_OPENCV(后面将会看到这种命名方式不是很好)

      默认使用MainActivitiy,包名com.example.ndk_opencv

       

    2. 构建Android的Java层代码

       

    (1)布局文件:main.xml

     

    <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"
    tools:context="${packageName}.${activityClass}" >
    <Button
    android:id="@+id/imagegray"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="图像灰度化" />
     
    <ImageView
    android:id="@+id/imageview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/imagegray"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="86dp" />
    </RelativeLayout> 

     

     

     

     

    (2) 在AndroidManifest.xml文件中加入

     

     <!-- 在SDCard中创建与删除文件权限 -->
    
    <uses-permission
    
    android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    
    <!-- 往SDCard写入数据权限 -->
    
    <uses-permission
    
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

     

     

     (3) MainActivity.java

     

    package com.example.ndk_opencv;
    
     
    
    import android.annotation.SuppressLint;
    
    import android.app.Activity;
    
    import android.graphics.Bitmap;
    
    import android.graphics.Bitmap.Config;
    
    import android.graphics.BitmapFactory;
    
    import android.os.Bundle;
    
    import android.view.View;
    
    import android.view.View.OnClickListener;
    
    import android.widget.Button;
    
    import android.widget.ImageView;
    
     
    
     
    
    @SuppressLint("SdCardPath")
    
    public class MainActivity extends Activity {
    
     
    
    private Button btngray;
    
    private ImageView imageview;
    
    private Bitmap bmp;
    
     
    
        @SuppressLint("SdCardPath")
    
        @Override
    
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
    
            this.setTitle("Android NDK OpenCV");
    
            btngray = (Button)findViewById(R.id.imagegray);
    
    //btnProc.setOnClickListener(this);
    
    imageview = (ImageView)this.findViewById(R.id.imageview);
    
    bmp=BitmapFactory.decodeFile("/sdcard/andimg/img1.jpg");
    
    imageview.setImageBitmap(bmp);
    
    //点击按钮,调用Opencv接口实现SD卡中的图片灰度化
    
    btngray.setOnClickListener(new OnClickListener() {    
    
                @Override
    
                public void onClick(View v) {
    
             bmp=BitmapFactory.decodeFile(stringNDKOPENCV());
    
             imageview.setImageBitmap(bmp);
    
                }
    
            });
    
     
    
        }
    
        
    
        //通过jni接口实现的本地函数
    
        public native String stringNDKOPENCV();
    
        static {
    
                System.loadLibrary("NDK_OPENCV");
    
    }
    
     
    
    }
    
     

     

    3.2 配置工程的NDK编译环境

     

    3.2.1在工程上右键点击Android Tools->Add Native Support...,然后给我们的.so文件取个名字,例如: NDK_OPENCV(默认为工程名字)

    这时候工程就会多一个jni的文件夹,jni下有Android.mk和NDK_OPENCV.cpp文件。

    3.2.2工程右键,点Properties->C/C++ Build的Building Settings中去掉Use default build command,然后输入${NDKROOT}/ndk-build.cmd

    3.2.3在C/C++ Build中点击Environment,点Add...添加环境变量NDKROOT,值为NDK的根目录E:projectAndroidadt-bundle-windows-x86-20140321android-ndk-r10

    3.3 编写NDK_OPENCV.cpp文件

    3.3.1生成.h文件

    在编写NDK_OPENCV.cpp文件前,需要利用javah这个工具生成相应的.h文件,然后根据这个.h文件编写相应的C/C++代码。用eclipse编译该工程,生成相应的.class文件,这步必须在下一步之前完成,因为生成.h文件需要用到相应的.class文件。暂时不考虑报错信息。

    进入到刚才建立的NDK_OPENCV工程目录中,在工程目录下执行: 

    Javah -classpath binclasses -d jni com.example.ndk_opencv.MainActivity

    这里-classpath表示类的路径;-d jni表示生成的.h文件存放的目录com.example.ndk_opencv.MainActivity则是完整的类名。现在可以在jni目录下看到多了一个.h文件:com_example_ndk_opencv_MainActivity.h;

    打开后,可以看到.h的内容:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    
    #include <jni.h>
    
    /* Header for class com_example_ndk_opencv_MainActivity */
    
     
    
    #ifndef _Included_com_example_ndk_opencv_MainActivity
    
    #define _Included_com_example_ndk_opencv_MainActivity
    
    #ifdef __cplusplus
    
    extern "C" {
    
    #endif
    
    /*
    
    * Class: com_example_ndk_opencv_MainActivity
    
    * Method: stringNDKOPENCV
    
    * Signature: ()Ljava/lang/String;
    
    */
    
    JNIEXPORT jstring JNICALL Java_com_example_ndk_1opencv_MainActivity_stringNDKOPENCV
    
    (JNIEnv *, jobject);
    
     
    
    #ifdef __cplusplus
    
    }
    
    #endif
    
    #endif 

     

    代码解释:

    1. JNI接口的命名规范是:Java_ + 调用该方法的包名(包名的点用_代替) + _ + 调用该接口的类名 + _ + 方法名。函数名比较长但是完全按照:java_pacakege_class_mathod 形式来命名。也就是说:MainActivity.java中stringNDKOPENCV () 方法对应于 C/C++中的 Java_com_example_ndk_1opencv_MainActivity_stringNDKOPENCV () 方法。
    2. 对于实例方法, JNIEXPORT 和 JNICALL 是jni的宏,在android的jni中不需要,当然写上去也不会有错。注意下其中的注释:Signature: ()Ljava/lang/String表示函数的参数为空,这里为空是指除了JNIEnv *, jobject 这两个参数之外没有其他参数,JNIEnv*, jobject是所有jni函数必有的两个参数,分别表示jni环境和对应的java类(或对象)本身,Ljava/lang/String表示函数的返回值是java的String对象。
    3. ndk_1opencv 代码中的红色标记处多了个1,这个1应该表示ndk_opencv为一个整体而非继承结构,因此不建议包名出现空格,以免出错。

    3.3.2 编写NDK_OPENCV.cpp

    #include <jni.h>
    
    #include <string.h>
    
    #include <opencv2/core/core.hpp>
    
    #include <opencv2/features2d/features2d.hpp>
    
    #include <opencv2/highgui/highgui.hpp>
    
    #include <opencv2/imgproc/imgproc.hpp>
    
    #include <opencv2/calib3d/calib3d.hpp>
    
    #include <opencv2/imgproc/imgproc_c.h>
    
     
    
    extern "C" jstring
    
    Java_com_example_ndk_1opencv_MainActivity_stringNDKOPENCV( JNIEnv* env,
    
    jobject thiz )
    
    {
    
        //return env->NewStringUTF("/sdcard/andimg/img2.jpg");
    
         const char* file="/sdcard/andimg/img1.jpg";
    
         IplImage* object = cvLoadImage(file,CV_LOAD_IMAGE_GRAYSCALE);
    
         std::string filePath="/sdcard/andimg/img1_result.jpg";
    
         jstring filePath1=env->NewStringUTF(filePath.c_str());
    
         const char * resultpath=env->GetStringUTFChars(filePath1, 0);
    
         cvSaveImage(resultpath,object);
    
         //env->ReleaseStringUTFChars(path, file);
    
         env->ReleaseStringUTFChars(filePath1, resultpath);
    
         return filePath1;
    
    } 

     

    代码解释:这段代码首先读取了SD卡中的一张图像,接着调用env->GetStringUTFChars,以灰度形式读取图像并保存在SD卡中,最后返回灰度图像的路径。

    3.4利用NDK平台编译C++语言函数成.so文件

    3.4.1首先需要编写Android.mk文件

    LOCAL_PATH := $(call my-dir)

    include$(CLEAR_VARS)

    # OpenCV

    OPENCV_CAMERA_MODULES:=on

    OPENCV_INSTALL_MODULES:=on

    OPENCV_LIB_TYPE:=STATIC

    include E:projectJavaWorkJavaExistWorksdk ativejniOpenCV.mk

    LOCAL_MODULE := NDK_OPENCV

    LOCAL_SRC_FILES := NDK_OPENCV.cpp

    include$(BUILD_SHARED_LIBRARY)

     

    3.4.2同时建立Application.mk文件

    APP_STL := gnustl_static

    APP_CPPFLAGS := -frtti -fexceptions

    APP_ABI := armeabi-v7a

    APP_PLATFORM := android-9

     

    4测试运行

        如果在Eclipse下面打开NDK_OPEC.cpp文件会出现很多错误,需要将错误删除掉,否则无法编译。

    "Eclipse可能报一堆的错误提示 由于eclipse的语法检查,当你打开一个源码,尤其是引入外面开发完成的项目,因为源码不是在工程中管理的,大部分情况会报一堆的错误提示,而你是明 确这些问题实际上是不存在的,那么就可以这样子做了,设置项目属性,把eclipse多管的这些闲事给免了它的职,如下图,保存后,你会发现你的 problems窗口下非常清爽了:"

    解决方法之一:需要将错误删除掉,否则无法编译。

    解决方法之二:打开工程属性,选择C/C++ General 中的Code Analysis ,接着选择Useproject setting 去掉Syntax and Semantic……,这是Eclipse在检测一些不符合java中的符号报错,其实代码没有错的。

     

     

    运行结果

  • 相关阅读:
    动态加载配置文件
    Split Full Name as First and Last
    MM03物料主数据视图中某些视图或者某些字段的控制方法
    如何用《老友记》学英语?
    FOR ALL ENTRIES IN的用法
    [转载]MBEWH表数据更新逻辑
    如何学习《六人行》??
    英语口语练习方法
    SAP收货时自动创建采购订单
    BAPI_PO_CREATE1 创建采购订单时价格的处理函数
  • 原文地址:https://www.cnblogs.com/adong7639/p/3925347.html
Copyright © 2011-2022 走看看