(原+轉)Eclipse中Android調用OpenCv


大部分都是參考下面的網址,如果感覺看起來不舒服,可以直接查看原網址。最后遇到了一點問題:

Description      Resource Path Location   Type

E:/~\code\Eclipse\opencvV4sdk\native\jni\/../libs/armeabi-v7a/libopencv_core.a(persistence.cpp.o): in function cvOpenFileStorage:persistence.cpp(.text.cvOpenFileStorage+0x1ac): error: undefined reference to 'gzrewind'         ~       C/C++ Problem

可以直接拖到最后查看。

 

參考文章: http://www.cnblogs.com/adong7639/p/3925347.html(WINDOWS系統Eclipse+NDK+Android + OpenCv)

 

使用的ADT:

 

使用的NDK:android-ndk-r10c

使用的opencv:OpenCV-2.4.9-android-sdk

具體的配置就不說了,由於自己是NDK中直接調用opencv的函數,因而直接說明此步驟。

1. 將OpenCV-2.4.9-android-sdk 中的sdk文件夾放到Eclipse工程中,為了方便查看,可以改名成opencvV4sdk。

2. 安裝OpenCV_Manager,沒有測試如果不安裝這個會怎么樣(卸載后繼續運行程序,可以正常運行。因而不確定是否一定需要這步)。

3. 利用JNI接口通過NDK平台調用OpenCV API函數

  1. 新建一個Android工程 NDK_OPENCV(后面將會看到這種命名方式不是很好)

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

  1. 構建Android的Java層代碼

 (1)布局文件:main.xml 

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2 xmlns:tools="http://schemas.android.com/tools"
 3 android:layout_width="match_parent"
 4 android:layout_height="match_parent"
 5 tools:context="${packageName}.${activityClass}" >
 6 <Button
 7 android:id="@+id/imagegray"
 8 android:layout_width="fill_parent"
 9 android:layout_height="wrap_content"
10 android:text="圖像灰度化" />
11  
12 <ImageView
13 android:id="@+id/imageview"
14 android:layout_width="wrap_content"
15 android:layout_height="wrap_content"
16 android:layout_below="@+id/imagegray"
17 android:layout_centerHorizontal="true"
18 android:layout_marginTop="86dp" />
19 </RelativeLayout>

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

1 <!-- 在SDCard中創建與刪除文件權限 -->
2 <uses-permission
3 android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
4 <!-- 往SDCard寫入數據權限 -->
5 <uses-permission
6 android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

 (3) MainActivity.java

 1 package com.example.ndk_opencv;
 2 
 3 import android.annotation.SuppressLint;
 4 import android.app.Activity;
 5 import android.graphics.Bitmap;
 6 import android.graphics.Bitmap.Config;
 7 import android.graphics.BitmapFactory;
 8 import android.os.Bundle;
 9 import android.view.View;
10 import android.view.View.OnClickListener;
11 import android.widget.Button;
12 import android.widget.ImageView;
13 
14 @SuppressLint("SdCardPath")
15 public class MainActivity extends Activity {
16 private Button btngray;
17 private ImageView imageview;
18 private Bitmap bmp;
19 
20     @SuppressLint("SdCardPath")
21     @Override
22     protected void onCreate(Bundle savedInstanceState) {
23         super.onCreate(savedInstanceState);
24         setContentView(R.layout.activity_main);
25         this.setTitle("Android NDK OpenCV");
26         btngray = (Button)findViewById(R.id.imagegray);
27 //btnProc.setOnClickListener(this);
28 
29 imageview = (ImageView)this.findViewById(R.id.imageview);
30 
31 bmp=BitmapFactory.decodeFile("/sdcard/andimg/img1.jpg");
32 
33 imageview.setImageBitmap(bmp);
34 
35 //點擊按鈕,調用Opencv接口實現SD卡中的圖片灰度化
36 
37 btngray.setOnClickListener(new OnClickListener() {    
38             @Override
39             public void onClick(View v) {
40          bmp=BitmapFactory.decodeFile(stringNDKOPENCV());
41          imageview.setImageBitmap(bmp);
42             }
43         });
44     }
45 
46     //通過jni接口實現的本地函數
47 
48     public native String stringNDKOPENCV();
49 
50     static {
51             System.loadLibrary("NDK_OPENCV");
52 }
53 }

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:\project\Android\adt-bundle-windows-x86-20140321\android-ndk-r10 

3.3 編寫NDK_OPENCV.cpp文件

3.3.1生成.h文件

(其實此步驟可以不管,即便不生成.h文件,也可以正常使用.so文件)

在編寫NDK_OPENCV.cpp文件前,需要利用javah這個工具生成相應的.h文件,然后根據這個.h文件編寫相應的C/C++代碼。用eclipse編譯該工程,生成相應的.class文件,這步必須在下一步之前完成,因為生成.h文件需要用到相應的.class文件。暫時不考慮報錯信息。

進入到剛才建立的NDK_OPENCV工程目錄中,在工程目錄下執行: 

Javah -classpath bin\classes -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的內容:

 1 /* DO NOT EDIT THIS FILE - it is machine generated */
 2 #include <jni.h>
 3 /* Header for class com_example_ndk_opencv_MainActivity */
 4 
 5 #ifndef _Included_com_example_ndk_opencv_MainActivity
 6 #define _Included_com_example_ndk_opencv_MainActivity
 7 #ifdef __cplusplus
 8 extern "C" {
 9 #endif
10 
11 /*
12 
13 * Class: com_example_ndk_opencv_MainActivity
14 
15 * Method: stringNDKOPENCV
16 
17 * Signature: ()Ljava/lang/String;
18 
19 */
20 
21 JNIEXPORT jstring JNICALL Java_com_example_ndk_1opencv_MainActivity_stringNDKOPENCV
22  (JNIEnv *, jobject);
23 
24 #ifdef __cplusplus
25 
26 }
27 #endif
28 #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

 1 #include <jni.h>
 2 #include <string.h>
 3 #include <opencv2/core/core.hpp>
 4 #include <opencv2/features2d/features2d.hpp>
 5 #include <opencv2/highgui/highgui.hpp>
 6 #include <opencv2/imgproc/imgproc.hpp>
 7 #include <opencv2/calib3d/calib3d.hpp>
 8 #include <opencv2/imgproc/imgproc_c.h>
 9 
10 extern "C" jstring
11 
12 Java_com_example_ndk_1opencv_MainActivity_stringNDKOPENCV( JNIEnv* env,
13 jobject thiz )
14 {
15     //return env->NewStringUTF("/sdcard/andimg/img2.jpg");
16      const char* file="/sdcard/andimg/img1.jpg";
17      IplImage* object = cvLoadImage(file,CV_LOAD_IMAGE_GRAYSCALE);
18      std::string filePath="/sdcard/andimg/img1_result.jpg";
19      jstring filePath1=env->NewStringUTF(filePath.c_str());
20      const char * resultpath=env->GetStringUTFChars(filePath1, 0);
21      cvSaveImage(resultpath,object);
22      //env->ReleaseStringUTFChars(path, file);
23      env->ReleaseStringUTFChars(filePath1, resultpath);
24      return filePath1;
25 }

代碼解釋:這段代碼首先讀取了SD卡中的一張圖像,接着調用env->GetStringUTFChars,以灰度形式讀取圖像並保存在SD卡中,最后返回灰度圖像的路徑。

3.4利用NDK平台編譯C++語言函數成.so文件

3.4.1首先需要編寫Android.mk文件

 1 LOCAL_PATH := $(call my-dir) 
 2 include$(CLEAR_VARS) 
 3 # OpenCV 
 4 OPENCV_CAMERA_MODULES:=on 
 5 OPENCV_INSTALL_MODULES:=on 
 6 OPENCV_LIB_TYPE:=STATIC 
 7 include E:\project\JavaWork\JavaExistWork\sdk\native\jni\OpenCV.mk 
 8 LOCAL_MODULE := NDK_OPENCV 
 9 LOCAL_SRC_FILES := NDK_OPENCV.cpp 
10 include$(BUILD_SHARED_LIBRARY) 

3.4.2同時建立Application.mk文件

1 APP_STL := gnustl_static 
2 APP_CPPFLAGS := -frtti -fexceptions 
3 APP_ABI := armeabi-v7a 
4 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中的符號報錯,其實代碼沒有錯的。

運行結果

 

問題:按照示例編譯代碼沒問題,但是自己的代碼調用opencv則出現如下錯誤:

之后,參考:

http://stackoverflow.com/questions/10858055/opencv2-4-with-android-native-activity

在Android.mk中加上了

LOCAL_LDLIBS += -lz # Compression library

之后,錯誤消失。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM