[1]OpenCV4Android機器視覺應用入門-JNI編寫HelloWorld

OpenCV4Android機器視覺應用入門-引言

OpenCV是開源的C++機器視覺框架。OpenCV整體模塊分明,演算法優秀且速度很快,是很優秀的機器視覺框架。

Android業務程序一般採用Java編寫,但是在以下情境下需要C++的配合:

  1. 公司的C++代碼遺產,希望在移動平台繼續使用;
  2. IOS和Android的跨平台需求;
  3. 安全性考慮,C++編譯出的so庫很難被反編譯;
  4. 速度考慮,C++的運行速度可能比Java在JVM上快(以前的說法了,現在JVM優化的很好);
  5. 突破系統的限制,例如 如何突破24M內存的限制,為Android程序分配到更多內存
  6. 與底層硬體交互,下面是android中Camera一些代碼片段

android.hardware.Camera;nn//自動對焦功能npublic final void autoFocus(AutoFocusCallback cb)n {n synchronized (mAutoFocusCallbackLock) {n mAutoFocusCallback = cb;n }n native_autoFocus(); //調用本地方法,實現攝像機自動對焦功能n }n private native final void native_autoFocus(); //本地方法,用C/C++實現n

JNI內容有很多,由於OpenCV所利用的JNI技術只要會寫HelloWorld就可以啦,先推薦大家一本書Pro Android C++ with the NDK(密碼:g4F4),有興趣的童鞋可以看一看,這裡的文章我們只如何用JNI寫出HelloWorld~

-----------------------昏割線----------------------

1. Android的java代碼

我們實現最基本的功能,點一下按鈕顯示HelloWorld,再點一下復原,HelloWorld的文字從C++代碼中獲取。阿軍就利用Android Studio開發,基本都是默認設置給大家演示一下啦~

頁面布局:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>n<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"n xmlns:tools="http://schemas.android.com/tools"n android:layout_width_="match_parent"n android:layout_height="match_parent"n android:paddingBottom="@dimen/activity_vertical_margin"n android:paddingLeft="@dimen/activity_horizontal_margin"n android:paddingRight="@dimen/activity_horizontal_margin"n android:paddingTop="@dimen/activity_vertical_margin"n android:orientation="vertical"n tools:context="demo.hello.MainActivity">nn <Buttonn android:id="@+id/send_hello"n android:layout_width_="wrap_content"n android:layout_height="wrap_content"n android:text="Hello"/>nn <TextViewn android:id="@+id/say_hello"n android:layout_width_="wrap_content"n android:layout_height="wrap_content"n android:layout_marginTop="10dp" />nn</LinearLayout>n

MainActivity.java

package demo.hello;nnimport android.support.v7.app.AppCompatActivity;nimport android.os.Bundle;nimport android.view.View;nimport android.widget.Button;nimport android.widget.TextView;nnpublic class MainActivity extends AppCompatActivity {nn private Button bnSendHello;n private TextView tvSayHello;n private boolean isSayHello = false;nn @Overriden protected void onCreate(Bundle savedInstanceState) {n super.onCreate(savedInstanceState);n setContentView(R.layout.activity_main);nn bnSendHello = (Button)findViewById(R.id.send_hello);n tvSayHello = (TextView) findViewById(R.id.say_hello);n final HelloWorld helloWorld = new HelloWorld();nn bnSendHello.setOnClickListener(new View.OnClickListener() {n @Overriden public void onClick(View v) {n if(!isSayHello) {n //從本地方法中獲取字元串n tvSayHello.setText(helloWorld.getString());n bnSendHello.setText("RETURN");n isSayHello = true;n }else{n tvSayHello.setText(" ");n bnSendHello.setText("HELLO");n isSayHello = false;n }n }n });n }n}n

HelloWorld.java

package demo.hello;nn/**n * Created by wcjzj on 2016/6/10.n */npublic class HelloWorld {nnstatic {ntry {n//載入編譯好的動態庫n System.loadLibrary("HelloWorld");n }catch (Exception e){n e.printStackTrace();n }n }nn//本地方法,獲取字元串n public native String getString();n}n

以上完成了Java層代碼的編寫,HelloWorld.java里完成動態庫的載入和本地方法的聲明。

2. HelloWorld.java中本地方法頭文件生成

  • 在工程local.properties文件中設定ndk地址(需要自己提前下載解壓),目前我的ndk和sdk位於同一目錄下

    ndk.dir=C:UserswcjzjAppDataLocalAndroidNdkn

    在gradle.properties中輸入

    android.useDeprecatedNdk=truen

    這樣就可以使用ndk了;

  • make一下現在的代碼,讓其生成build文件;
  • 編譯好的HelloWorld.class文件位置如下:

  • 利用javah工具生成頭文件,在cmd窗口輸入javah可以看到如下用法說明

在Android Studio的termial窗口輸入

  • cd app/build/intermediates/classes/debugn

    按enter,接著輸入

    javah -jni demo.hello.HelloWorldn

    然後就生成了jni介面頭文件,demo_hello_HelloWorld.h,這裡有兩點需要注意的:

一是:如果在HelloWorld.java中import了其他類(比如import android.graphics.Bitmap;),則需要-classpath <path>參數設定載入類的路徑,比如說我現在的項目應該設置為

set classpath=C:UserswcjzjDocumentsworkandroidHelloappsrcmainjavan

然後再生成h文件

javah -classpath . -jni demo.hello.HelloWorldn

否則,會提示無法載入類之類的錯誤

二是:如果提示沒有找到javah之類的,需要把jdk的bin地址添加到環境路徑path中

3. 建立jni文件夾存放c++/c文件

  • 默認情況下jni應該放到[module]/src/main/java/jni中,也可以自定義文件地址,build.gradle在android節點下添加如下代碼

sourceSets.main {n jni.srcDirs src/main/java/myjnin }n

或者點擊右鍵new->Folder->JNI Folder會自動在build.gradle添加如上代碼,這裡我們使用默認的配置,在java下建立jni文件夾,把上一步生成的demo_hello_HelloWorld.h剪切到jni文件夾中。

  • 接下來我們看一下demo_hello_HelloWorld.h中都有哪些內容

    /* DO NOT EDIT THIS FILE - it is machine generated */n#include <jni.h>n/* Header for class demo_hello_HelloWorld */nn#ifndef _Included_demo_hello_HelloWorldn#define _Included_demo_hello_HelloWorldn#ifdef __cplusplusnextern "C" {n#endifn/*n * Class: demo_hello_HelloWorldn * Method: getStringn * Signature: ()Ljava/lang/String;n */nJNIEXPORT jstring JNICALL Java_demo_hello_HelloWorld_getStringn (JNIEnv *, jobject);nn#ifdef __cplusplusn}n#endifn#endifn

    我們不要編輯這個文件,在Cpp中編寫實現代碼

    //n// Created by wcjzj on 2016/6/10.n//n#include "demo_hello_HelloWorld.h"n/*n * Class: demo_hello_HelloWorldn * Method: getStringn * Signature: ()Ljava/lang/String;n */nJNIEXPORT jstring JNICALL Java_demo_hello_HelloWorld_getStringn(JNIEnv *env, jobject obj){n return env->NewStringUTF("HelloWorld!");n}n

    由於java和C++的數據通信需要JVM作為中介,所以這裡涉及到Java和C++的數據轉換,這裡面的內容非一時半會可以說完,想仔細研究的童鞋可以閱讀我上文推薦的那本書。

  • 接著就是需要編譯這些代碼,android studio的gradle初步支持jni的編譯,不需要再自己編寫Android.mk和Application.mk文件了,由於這裡的編譯比較簡單,我們直接利用gradle進行編譯就好了,在build.gradle的android節點下defaultConfig子節點添加如下代碼

    ndk {n moduleName "HelloWorld" // 這個就是上面java中載入的動態鏈接庫的名稱 }n

    或者可以進行更為詳細的配置(xi:

    ndk { n moduleName "myEpicGameCode"n cFlags "-DANDROID_NDK -D_DEBUG DNULL=0 " // Define some macrosn ldLibs "EGL", "GLESv3", "dl", "log" // Link with these libraries! 在這裡添加你原先在makefile里ldlibs所鏈接的庫n stl "stlport_shared" // Use shared stlport libraryn}n

  • 然後build(如果出現clean失敗的提示,那麼進terminal窗口輸入exit;結束其佔用),如下圖可以看到生成了各平台的libHelloWorld.so動態鏈接庫

4. 編譯運行效果如下

---------------------昏割線--------------------

阿軍終於寫完了,原來寫新手教程這麼累的啊....

這個是入門篇哦,如果對jni感興趣最好把我推薦的那本書看了哦~我今天辛苦一下,還能再更一篇呢~^ ^

[2]OpenCV4Android機器視覺應用入門-不使用OpenCV Manager的三種方法-法1:純java層開發 - 王傳軍的文章 - 知乎專欄

[3]OpenCV4Android機器視覺應用入門-不使用OpenCV Manager的三種方法-法2:java和native混編 - 行深般若 - 知乎專欄

[4]OpenCV4Android機器視覺應用入門-不使用OpenCV Manager的三種方法-法3:純native方法 - 王傳軍的文章 - 知乎專欄

[5]OpenCV4Android機器視覺應用入門-拍照預覽界面動態繪圖

[6]OpenCV4IOS靜態庫製作


推薦閱讀:

ImagePy 簡介
1.5【OpenCV圖像處理】讀寫像素
在 MFC 框架中,有什麼方法能直接將 OpenCV 2.0 庫中 Mat 格式的圖片傳遞到 Picture Control(圖片控制項)顯示?
想用OpenCV做AR該如何入手?

TAG:OpenCV | 机器视觉 | Android开发 |