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

注:本篇文章做者已再也不維護,由於如今大都以Android Studio進行開發。後續只維護Android Studio篇
若是你堅持使用Eclipse,請確保全部環境與本文一致。java

簡介

本教程是通過本人屢次踩坑,並結合網上衆多OpenCV On Android的配置教程總結而來,盡但願能幫助學習OpenCV的朋友們少走彎路。若是配置上遇到問題,可在評論中留言,我將盡力幫助解決。
linux

若是您使用的是Android Studio,請參考下一章OpenCV On Android最佳環境配置指南(Android studio篇)android

若有轉載,請標明出處c++

環境

  • 電腦: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項目。程序員


1、建立OpenCV Demo

首先建立一個普通的Android應用,須要注意的是,咱們須要將Minimum Required SDK設置爲API15及以上,這樣既能兼容市面上95%的Android手機,又不會引入潛在的錯誤。若是此處出現錯誤,請確保你的JDK、ADT和SDK是否配置正確。編程

這裏我建立名爲OpenCVDemo的項目,包名爲com.demo.opencv,OK。windows

1.png


2、OpenCV Java庫使用指南

2.一、環境配置

第一步Eclipse菜單->File->Import->Android->Existing Android Code Into Workspace,而後導入OpenCV Android SDK\sdk\java 這個目錄。markdown

2.png

爲了防止誤操做OpenCV庫,建議勾選Copy project into workspace,將該庫copy到你的工做文件夾,而後點擊Finish。架構

若是導入後出現錯誤,請將Project build Target設置爲Android5.0以上(由於通常是Camera2報錯,Camera2只存在於Android5.0+)。具體步驟爲:eclipse

項目鼠標右鍵,選擇Properties -> Android,勾選 Android5.0 以上的版本便可,而後Apply and Close

這裏我選擇的是5.0.1。

3.png

第二步:仍是進入上一步的頁面,點擊Library裏的Add按鈕,添加 OpenCV 庫,完成後,你的項目即可以調用到 OpenCV Java 函數了。

4.png

2.二、Demo編寫

建立一個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,內容以下:

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.apk

2.三、安裝OpenCV Manager

位於OpenCV-android-sdk\apk目錄下,選擇合適的apk程序安裝(通常選擇OpenCV_3.2.0_Manager_3.20_armeabi.apk)。

2.四、運行Demo

將項目編譯打包成apk,安裝到手機,運行,點擊按鈕show後,對圖像進行灰度處理,效果以下圖所示:

5.png

注意:

  1. 若是出現OpenCV was not initialised correctly.Application will be shut down,多是你的OpenCV Manager程序與你的cpu架構不一樣,選擇合適的apk便可。
  2. 若是手機版本較高,可能會出現調用不到OpenCV Manager的狀況,這是由於手機廠商作了限制,防止惡意軟件相互喚醒(如百度全家桶),解決方法請自行百度(由於沒有統一的方法)。

2.五、拋棄OpenCV Manager:

安裝一個額外的apk對用戶來講很是不友好,但使用C/C++編程,又對一些Java程序員又提升了實現難度,故咱們應該想一個一箭雙鵰的方法,即幫助開發人員使用 Java 快速開發,又無需讓用戶安裝額外的軟件。

思路:

Java庫實際上只是對NDK庫進行java封裝,將so文件放在OpenCV Manager內,經過AIDL進行交互,從而實現圖像處理。

解決方案:

若是咱們將OpenCV Manager裏面的so文件直接打包到咱們的應用裏,不就能拋棄OpenCV Manager了嗎?

沒錯,就是這樣,而且 OpenCV 官方也給咱們提供了現成的so文件,只需將OpenCV-android-sdk\sdk\native\libs\[ARM架構]\ 目錄下(通常採用armeabi-v7a便可)的libopencv_java3.so,放到你的項目libs\[ARM架構]\目錄下,如圖所示:

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");
    }

    ......下面內容不變
}
複製代碼

編譯並安裝,這時候,你就能夠將你的 OpenCV Manager 卸載掉了。運行結果與以前的無異。

注意: 此方法並非完美的,由於在這個程序裏,你只實現了圖像的灰度處理,但卻引入了libopencv_java3.so這個so文件,apk足足有4.39M,相比以前的194k來講,接近增加了20倍。除非你的應用比較大,而這個so文件的大小是固定的,此時採用此方法也是一個不錯的選擇。


3、OpenCV NDK庫使用指南

3.一、環境配置

第一步: 配置NDK路徑

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

7.png

第二步: 配置JNI環境

這裏咱們經過導入配置文件進行配置:

  1. 在你的項目右鍵Android Tools->Add native support->輸入合適的名稱->肯定,這裏我直接使用默認名稱。
  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>
複製代碼
  1. 項目右鍵選擇最後一個Properties,而後選擇C/C++ General -> Path and Symbols,如圖所示:

8.png

點擊Import Settings->Browse,在電腦本地選擇上面的xml文件,點擊Finish,環境就導入成功了。

第三步: 檢查環境是否正確

  1. 將編譯器切換到C/C++編輯器模式:

9.png

  1. 檢查Includes路徑是否都存在:確保路徑不爲灰色(以下圖),而且都能展開

10.png

  1. 查看環境是否正常。

進入jni目錄,打開OpenCVDemo.cpp,在#include<jni.h>上使用Ctrl+鼠標左鍵,若是eclipse能打開jni.h文件,說明你的JNI環境就搭建成功了。

第四步: 配置OpenCV NDK環境

  1. 將下面內容 copy 到Android.mk文件,注意修改OPENCV_ANDROID_SDK爲你的OpenCV路徑,並閱讀我添加的註釋。
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)
複製代碼
  1. 在jni目錄下增長Application.mk文件,內容以下:
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := all
APP_PLATFORM := android-15
APP_OPTIM := debug
複製代碼

至此,NDK環境就配置完畢。

3.二、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;
            }
        }
    }
}
複製代碼

能夠看到,MainActivity與以前修改不大。

而後是 OpenCV_NDK.java 內容以下:

public class OpenCV_NDK {
    static {
        System.loadLibrary("OpenCVDemo");
    }
    native void toGary(Object bitmap);
}
複製代碼

是否是很是簡單!

如今只須要爲toGary()這個native方法生成頭文件,這一步須要使用到javah命令,具體用法,請參考百度。

而後將 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,我也比較推薦使用NDK開發OpenCV應用程序。


4、OpenCV混合使用指南

假設你已經看了前兩種配置,習慣於Java開發,但又以爲OpenCV Java庫提供的方法不夠,但願使用混合開發,那恭喜你,這一部分就是你所須要的。

這部份內容不多,總結起來就兩部分:

  1. 導入OpenCV Java庫並關聯你的應用,參考OpenCV Java庫使用指南(記得寫加載so文件的代碼,但so文件不須要你手動導入)。
  2. 配置NDK環境與OpenCV環境,參考OpenCV NDK庫使用指南
  3. 修改Android.mk文件內容:將OPENCV_BULID_TYPENDK修改成JAVA_AND_NDK

通過上述步驟,在編譯時,系統會將你的native代碼生成libOpenCVDemo.so文件,同時也會將libopencv_java3.so放入你的libs目錄下。如圖:

12.png

這樣就輕易實現了java和c\c++混合處理圖像,同時不用安裝OpenCV Manager,而且自動導入libopencv_java3.so。注意:加載so文件的代碼必須寫


6、總結

本教程致力於幫助OpenCV新人快速配置,可能有不足之處,接受你們的建議與批評,後續將進行補充和改進。

同時歡迎你們一塊兒探討Android圖像處理的知識,共同進步。

OpenCV On Android學習羣.png

相關文章
相關標籤/搜索