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

簡介

本教程是通過本人屢次踩坑,並參考網上衆多OpenCV On Android的配置教程總結而來,盡但願能幫助學習移動圖像處理的朋友們少走彎路。
這也是本人第一次在簡書上發佈文章,若有不足,但願各位dalao可以指正,我也將及時修改。配置上若是遇到問題,也能夠在評論裏留言,我將盡力幫助解決。
若有轉載,請標明出處http://www.jianshu.com/p/b260ebb1c180
若是您使用的是android studio,請參考下一章OpenCV On Android環境配置最新&最全指南(Android studio篇)java

環境

電腦: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項目)linux

注:以上配置基本上爲最新版本android


1、建立OpenCV Demo

這裏只需建立普通Eclipse Android項目工程,沒什麼重要的地方可說的,若是這都不會,建議先把Android基礎學好。這裏我建立的是名爲OpenCVDemo的項目,包名爲com.demo.opencvc++


2、選擇您所須要的環境

一、若是你只想用在應用層編寫程序的話,請直接閱讀下面OpenCV Java庫使用指南
二、若是隻想在原生層快速進行圖像處理的話,請直接跳到下面OpenCV NDK庫使用指南
三、若是想同時使用OpenCV Java庫和Native庫的話,請參考OpenCV 混合使用指南
接下來我簡要介紹二者的區別與優缺點。程序員

一、使用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 混合使用指南則提供一個更爲簡便的方法。編程

二、使用OpenCV Java庫和NDK庫的優缺點

Java庫的缺點:
若是使用Java庫的話,手機上除了安裝你編寫的應用外,還須要額外安裝OpenCV Manager文件,其位於OpenCV Android SDK/apk/目錄下,請根據你編寫的apk架構(若是你僅使用Java開發,則能夠不用考慮apk架構)和你手機cup架構選擇對應的apk文件,通常這兩個選擇armeabi架構便能兼容絕大多數的手機。
Java庫的優勢:
一、不須要C/C++的功底便能輕鬆實如今Android客戶端的圖像處理,很是適合只會Java語言的編程人員。
二、由於有兩個應用(本身編寫的程序和OpenCV Manager),你將得到更多的內存處理空間(一個Android應用默認分配內存上限爲24M)。
三、Java庫封裝了幾個攝像頭顯示控件,如:JavaCameraView,NativeCameraView(目前OpenCV3.2裏該控件已經被取消),咱們使用少許代碼就能實現攝像頭數據採集->處理->顯示等一系列操做。
NDK庫的優勢:
一、若是使用Native庫的話,你能夠徹底拋棄OpenCV Manager的安裝,同時因爲在底層進行圖像處理,其處理速度會增長(但因爲Android高版本的優化,加上OpenCV Java庫其實也是在調用其原始層的庫,因此這種差距並不明顯)。
二、使用NDK庫的話,需採用C\C++進行編程,此方法更便於從Visual Studio進行程序移植。
NDK庫的缺點:
須要必定的C\C++功底,若是涉及到調用攝像頭,需自行編寫緩衝框架,避免數據幀堵塞。同時必須熟悉JNI的函數操做,什麼時候釋放內存和引用等等。若是你想在底層進行OpenGL ES渲染圖像,還需對Android底層和OpenGL ES有必定了解(但前者並非必須的)。windows


3、OpenCV Java庫使用指南

環境配置

第一步:Eclipse菜單->File->Import->Android->Existing Android Code Into Workspace(若是沒此選項,請確保你的eclipse 上ADT是否配置正確),而後導入OpenCV Android SDK\ sdk\java這個項目。架構


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函數了。eclipse


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-sdk\apk目錄下,選擇合適的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-sdk\sdk\native\libs\[架構]目錄下(這裏選擇合適的架構,通常用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文件的大小是固定的,此時採用此方法將是一個不錯的選擇。


4、OpenCV NDK庫使用指南

環境配置

第一步:配置NDK環境
進入菜單->Window->Preferences->Android->NDK,設置NDK Location路徑,注:請確保該路徑下存在ndk-build.cmd文件。以下圖:


7.png

第二步:配置JNI環境
這裏我將使用簡便方法快速配置:
一、在你的項目右鍵Android Tools->Add native support->輸入合適的名稱->肯定,這裏我就直接使用默認的"OpenCVDemo"
二、本地新建一個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>

三、項目右鍵選擇最後一個Properties,而後選擇C/C++ General -> Path and Symbols,如圖所示:


8.png

點擊Import Settings->Browse,在電腦本地選擇上面的xml文件,點擊Finish,環境就導入成功了。
接下來查看環境是否正確。
一、查看路徑是否存在:按照下圖,將編譯器切換到C/C++編輯器模式:


9.png


展開項目,查看Include目錄下路徑是否爲灰色(以下圖),而且都能展開,若是都知足,表示路徑都存在。


10.png


二、查看環境是否正常。進入jni目錄,打開OpenCVDemo.cpp,在#include<jni.h>這句代碼上使用Ctrl+鼠標左鍵,若是eclipse能打開jni.h這個文件,說明你的JNI環境就搭建成功了。
第二步:配置OpenCV NDK環境
一、將下面內容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)

二、在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,圖像處理時間本人並無計算,應該相差無幾。


5、OpenCV 混合使用指南

假設你已經看了前兩種配置,習慣於Java開發,但又以爲OpenCV Java庫提供的方法不夠,但願使用混合開發,那恭喜你,這一部分就是你所須要的。這部份內容不多,總結起來就兩部分:
一、導入OpenCV Java庫並關聯你的應用,參考OpenCV Java庫使用指南(記得寫靜態加載so庫的內容,但so文件不須要你手動導入)。
二、配置JNI環境與NDK環境,參考OpenCV NDK庫使用指南
三、修改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的代碼必須有


6、總結

本教程致力於幫助OpenCV新人快速配置,可能有不足之處,接受你們的建議與批評,後續將進行補充和改進。同時歡迎你們一塊兒探討Android圖像處理的知識,共同進步。

相關文章
相關標籤/搜索