溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

android studio如何使用jni編譯opencv

發(fā)布時間:2021-11-06 18:17:12 來源:億速云 閱讀:166 作者:柒染 欄目:建站服務(wù)器

android studio如何使用jni編譯opencv,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

1,過程感慨(想直接看教程,請?zhí)^此部分)

       在寫具體內(nèi)容之前,我先說下我搞這個東西的過程,由于導(dǎo)師之前說過要搞個圖像匹配的androi APP,具體就是匹配前后兩張圖片的相似度,類似 安卓5.0 引入的刷臉解鎖。

       當(dāng)時覺得,要實(shí)現(xiàn)這樣一個東西,肯定沒現(xiàn)成的API 可供使用,第一時間想到的 無疑就是opencv,這個擁有一套強(qiáng)大的圖像處理函數(shù)的庫,它的開發(fā)語言主要是C++,但是,也有 jar 包可供android開發(fā)使用,如果單單是使用里面已經(jīng)寫好了的效果的話,肯定是不能完成圖像匹配的。

       也就是說,我必須要調(diào)用它里面的函數(shù)再結(jié)合自己算法重新去實(shí)現(xiàn)這樣一個功能,再使用 ndk 環(huán)境去實(shí)現(xiàn) jni 編程,把我自己寫好的 c++ 代碼,在生成 .so 動態(tài)庫的基礎(chǔ)上,引入并使用。

       剛開始,思路很清晰,然后便著手百度 android studio(下面簡稱 as) 的 opencv jni編程使用教程,十分遺憾,所能搜到的,關(guān)于 as 和 opencv、jni 搭邊的例子 幾乎為0,很多的例子是 eclipse。沒辦法,只有自己親手搞了。

       剛動手的時候,很快地把所有裝備工作都搞定了,.so 動態(tài)庫文件(下面會介紹)也編譯出來了,但是,就在此時,我遇到了一個 令我第一階段切底放棄的 bug!!

       這個 bug 是:(下面我會說明白,它的真實(shí)起因和解決方法)

       fatal error: opencv2/opencv.hpp: No such file or directory, 意思是 我所要編譯的 cpp文件中的 頭文件 opencv2/opencv.hpp 找不到。當(dāng)時,無論是自己請教別人、百度、google 還是查書,都無法解決,足足耗時 一星期??!

       android studio如何使用jni編譯opencv

      逐保留項(xiàng)目信息,放棄不搞。

      直到 2 天前,開始決定重新嘗試,并于今天正式解決后,現(xiàn)發(fā)表此文。

2,運(yùn)行環(huán)境

      win 7, 系統(tǒng);

      android studio 版本 0.8.0 beta,使用  build:gradle:0.12.+,tools版本:21.1.2,api 21;

      opencv for android 包,我使用的版本是 OpenCV-3.0.0-android-sdk,2.4.9的也可以,可以到 opencv 官網(wǎng)下載,我這里提供個鏈接

http://downloads.sourceforge.net/project/opencvlibrary/opencv-android/3.0.0/OpenCV-3.0.0-android-sdk-1.zip?r=http%3A%2F%2Fopencv.org%2F&ts=1436167636&use_mirror=nchc

      編譯.so 動態(tài)庫 使用 cygwin,安裝了所有包,這里提示,不一定要用它,可以直接使用 cmd 進(jìn)行編譯;

      ndk 為 android-ndk-r10d(強(qiáng)烈建議使用 r9 或 r10 系列,因?yàn)檫@兩個能在 cmd 中編譯出 .so),r10d 能夠支持的 android api 最高到 21,如果你的是 22 的請修改,否則會有會編譯不出 jni.h 頭文件,或者其他的頭文件,你會發(fā)現(xiàn),別人的源碼在你這編譯不出了。

3,準(zhǔn)備工作

      1,---ndk 的下載、安裝和配置,此部分不說,網(wǎng)上教程很多,很多可行。

      2,---cygwin 的下載和安裝, 參照 http://blog.csdn.net/asmcvc/article/details/9311573,我上面說了,不一定要用它,win 自帶的 cmd 也可以編譯。如果使用 cygwin,要做好心理準(zhǔn)備,下載和安裝它,非常非常的久,文件總體積 20 多G!?。。∥沂怯昧?個多小時。

      3,---opencv for android 的sdk 下載完成后。打開 該文件夾,sdk/native/libs,里面有很多平臺的文件夾,能在里面出現(xiàn)的,證明你能夠在下面的 Application.mk 中設(shè)置生成對應(yīng)的架構(gòu)的 .so文件,我舉個例子,我的是:android studio如何使用jni編譯opencv 

      在下面介紹的 Application.mk 文件中有一句話 android studio如何使用jni編譯opencv,它是用來設(shè)置生成 對應(yīng)架構(gòu)的 .so 文件,我這里是armeabi-7a,如果要生所有的,寫出 :=all,注意,這樣很可能會報錯,錯誤信息是,某種架構(gòu)找不到,所以,我要你看清楚,上面文件夾里面有哪些架構(gòu),這些 坑是網(wǎng)上找不到,如果你要生成兩種,可以輪著來編譯,第二次的編譯,不同的架構(gòu)是不會覆蓋的。現(xiàn)在打開 sdk/native/jni,如無意外,里面肯定有個 文件叫做 OpenCV.mk,它就是我們在 android.mk 腳本文件中要引入 opencv C++庫所要參照的文件。請用記事本 或者Notepad++ 打開。

      4,---了解 Android.mk 和 Application.mk 文件的基本內(nèi)容信息:下面我使用默認(rèn)的 Android.mk 來說明,和我的例子的 Application.mk 來說明。

      它們都是腳本文件。

Android.mk android studio如何使用jni編譯opencv

Application.mk

android studio如何使用jni編譯opencv

4,編譯 .so

     使用你的 as 創(chuàng)建一個新項(xiàng)目,然后在你的 項(xiàng)目的 main 目錄下創(chuàng)建一個一個 jni 文件夾,這樣創(chuàng)建:android studio如何使用jni編譯opencv

創(chuàng)建好了之后,是這樣的:

android studio如何使用jni編譯opencv

   首先編譯 項(xiàng)目的頭文件 .h,一般編譯出來后,它的名字結(jié)構(gòu)是:包名_類名.h

  編譯命令如下,請在你的 as 下面的 Terminal 里面輸入:

    SourcePath:    D:\work\androidstudio\VisualRecognition\app\src\main\java (絕對路徑)

    TargetPath:    D:\work\androidstudio\VisualRecognition\visual\src\main\jni (絕對路徑)

    TargetClassName:    com.yf.visualrecognition.UnityPlayerActivity  (你的包名+類名)

    格式:  javah -d ${SourceFile} -classpath ${TargetPath} ${TargetClassName}

    控制臺指令:javah -d D:\work\androidstudio\VisualRecognition\visual\src\main\jni  -classpath                  D:\work\androidstudio\VisualRecognition\app\src\main\java io.github.froger.jni.MyActivity

然后在你的jni 文件夾下面 分別創(chuàng)建 Android.mk 、Application.mk 和你要編譯的 .cpp 或者.c 文件,前兩個的 內(nèi)容可以模仿我上面介紹的, .cpp 我這里提供一個。

android studio如何使用jni編譯opencv

   Android.mk 、Application.mk 、ImgFuncpp 分別如下,util.c 是空文件,之所以創(chuàng)建它是為了避免另外一個 bug,這不說:

Android.mk 文件如下


LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
OPENCV_LIB_TYPE:=STATIC
ifeq ("$(wildcard $(OPENCV_MK_PATH))","")
include E:\OpenCV-3.0.0-android-sdk-1\OpenCV-android-sdk\sdk\native\jni\OpenCV.mkelseinclude $(OPENCV_MK_PATH)
endif
LOCAL_MODULE := ImgFun
LOCAL_SRC_FILES := ImgFun.cpp
LOCAL_LDLIBS += -lm -llog
include $(BUILD_SHARED_LIBRARY)

Application.mk 文件如下

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a       #這句是設(shè)置生成的cpu指令類型,提示,目前絕大部分安卓手機(jī)支持armeabi,libs下太多類型,編譯進(jìn)去 apk 包會過大
APP_PLATFORM := android-8    #這句是設(shè)置最低安卓平臺,可以不弄

ImgFun.cpp 文件如下

 1 #include <io_github_froger_jni_MyActivity.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <opencv2/opencv.hpp> 5 using namespace cv; 6 IplImage * change4channelTo3InIplImage(IplImage * src); 7  8 extern "C" { 9 JNIEXPORT jintArray JNICALL Java_io_github_froger_jni_MyActivity_ImgFun(10     JNIEnv* env, jobject obj, jintArray buf, int w, int h);11 JNIEXPORT jintArray JNICALL Java_io_github_froger_jni_MyActivity_ImgFun(12     JNIEnv* env, jobject obj, jintArray buf, int w, int h) {13 14   jint *cbuf;15   cbuf = env->GetIntArrayElements(buf, false);16   if (cbuf == NULL) {17     return 0;18   }19 20   Mat myimg(h, w, CV_8UC4, (unsigned char*) cbuf);21   IplImage image=IplImage(myimg);22   IplImage* image3channel = change4channelTo3InIplImage(&image);23 24   IplImage* pCannyImage=cvCreateImage(cvGetSize(image3channel),IPL_DEPTH_8U,1);25 26   cvCanny(image3channel,pCannyImage,50,150,3);27 28   int* outImage=new int[w*h];29   for(int i=0;i<w*h;i++)30   {31     outImage[i]=(int)pCannyImage->imageData[i];32   }33 34   int size = w * h;35   jintArray result = env->NewIntArray(size);36   env->SetIntArrayRegion(result, 0, size, outImage);37   env->ReleaseIntArrayElements(buf, cbuf, 0);38   return result;39 }40 }41 42 IplImage * change4channelTo3InIplImage(IplImage * src) {43   if (src->nChannels != 4) {44     return NULL;45   }46 47   IplImage * destImg = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);48   for (int row = 0; row < src->height; row++) {49     for (int col = 0; col < src->width; col++) {50       CvScalar s = cvGet2D(src, row, col);51       cvSet2D(destImg, row, col, s);52     }53   }54 55   return destImg;56 }

  上面 .cpp 文件的有幾句話要說明下,注意 .c 文件和 .cpp 文件是不一樣的:

  1,請用  extern "C" { } 包住 你要你的 c++ 函數(shù)體的定義和里面的變量,函數(shù)聲明可以在外面。

  2,JNIEXPORT jintArray JNICALL Java_io_github_froger_jni_MyActivity_ImgFun(JNIEnv* env, jobject obj, jintArray buf, int w, int h);

  3,jintArray 是你定義的函數(shù)的返回值,我這里的是int數(shù)組,它在類型的前面有一個 j ,如果是字符串,那么就是 jstring,數(shù)組加上Array;

  4,JNICALL Java 這句不變,所有都一樣,注意java的 j 是大寫;

  5,io_github_froger_jni 這里是你的包名;

  6,MyActivity 你的類名,要引用這個這里C++函數(shù)的類名;

  7,ImgFun  是你要在java中調(diào)用的函數(shù)名字,哪些不用直接被調(diào)用的,不用寫;

  8,JNIEnv* env, jobject obj, 這個固定不變,第一個的意思是虛擬機(jī)引用,第二個是項(xiàng)目;

  9,jintArray buf, int w, int h 函數(shù)的參數(shù)。

  好了,上面該介紹的已經(jīng)介紹完了,接下來是編譯 .so 的正式操作(我這里使用cmd做例子,因?yàn)樗唵尾僮?,cygwin也可以)。

你可以在 as 的 cmd 中或者 系統(tǒng)的 cmd框中實(shí)現(xiàn)編譯,首先使用命令進(jìn)入到當(dāng)前的 jni 文件夾的 目錄,例如,我的是 

D:asproject/JniDemo/app/main/jni,然后使用命令 ndk-build,(使用ndk-build命令這一步,需要你已經(jīng)配置好了 ndk 環(huán)境,請參照百度上面的教程)然后回車,如無意外,將會生成如下文件:

android studio如何使用jni編譯opencv

其中的 .so 文件就是我們所需要的,現(xiàn)在打開你項(xiàng)目app下的  build.gradle 文件,在 android{} 里面加入:

sourceSets {
  main() {
    jniLibs.srcDirs = ['src/main/libs']
  }
}

這樣是為了使用 .so文件,上面我們僅僅是生產(chǎn)!

  OK,到這里基本大功告成了,不過,筆者我就是在這一步之后,運(yùn)行程序的時候,出現(xiàn)的簡單的致命的 bug,導(dǎo)致我找了近2星期,現(xiàn)在想起來真是蠢..............

5,遇到的關(guān)鍵問題及其解決方法

  運(yùn)行程序,出現(xiàn),如下錯誤,這里聲明下,不僅僅是 opencv2/opencv.hpp,還可能是其他的 hpp。

android studio如何使用jni編譯opencv

  出現(xiàn)的原因:

     原來是這樣的,android studio 在我們編譯完 .so 文件后,我們在Android.mk 文件中設(shè)置引入的opencv 函數(shù)庫,是已經(jīng)被編譯進(jìn)去.so 動態(tài)庫里面了的,而我們編譯所需要的 cpp 文件,它在 jni 文件夾呢,自然就沒有 opencv 庫可依賴,所以。

  解決方法:

     在你編譯完.so 文件后,就可以把 cpp 或者 c 文件里面的內(nèi)容 注釋或者刪除了,不然在你運(yùn)行程序的時候就會拋出頭文件找不到的錯誤,哎,真是辛酸淚,這樣一個 bug 搞了我 那么多時間,不過還好,還是解決了。

6,實(shí)現(xiàn)效果

 1 package io.github.froger.jni; 2  3 import android.app.Activity; 4 import android.graphics.Bitmap; 5 import android.graphics.drawable.BitmapDrawable; 6 import android.os.Bundle; 7 import android.view.View; 8 import android.widget.Button; 9 import android.widget.ImageView;10 11 public class MyActivity extends Activity {12     /** Called when the activity is first created. */13     ImageView imgView;14     Button btnNDK, btnRestore;15     public static native int[] ImgFun(int[] buf, int w, int h);16     static {17         System.loadLibrary("ImgFun");18     }19     @Override20     public void onCreate(Bundle savedInstanceState) {21         super.onCreate(savedInstanceState);22         setContentView(R.layout.activity_my);23 24         this.setTitle("使用NDK轉(zhuǎn)換灰度圖");25         btnRestore = (Button) this.findViewById(R.id.btnRestore);26         //btnRestore.setText(ImgFun());27         btnRestore.setOnClickListener(new ClickEvent());28         btnNDK = (Button) this.findViewById(R.id.btnNDK);29         btnNDK.setOnClickListener(new ClickEvent());30         imgView = (ImageView) this.findViewById(R.id.ImageView01);31         Bitmap img = ((BitmapDrawable) getResources().getDrawable(32                 R.drawable.ic_launcher)).getBitmap();33         imgView.setImageBitmap(img);34     }35 36     class ClickEvent implements View.OnClickListener {37         public void onClick(View v) {38             //btnRestore.setText(ImgFun());39             if (v == btnNDK) {40                 long current = System.currentTimeMillis();41                 Bitmap img1 = ((BitmapDrawable) getResources().getDrawable(42                         R.drawable.ic_launcher)).getBitmap();43                 int w = img1.getWidth(), h = img1.getHeight();44                 int[] pix = new int[w * h];45                 img1.getPixels(pix, 0, w, 0, 0, w, h);46                 int[] resultInt = ImgFun(pix, w, h);47                 Bitmap resultImg = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);48                 resultImg.setPixels(resultInt, 0, w, 0, 0, w, h);49                 long performance = System.currentTimeMillis() - current;50                 imgView.setImageBitmap(resultImg);51             } else if (v == btnRestore) {52                 Bitmap img2 = ((BitmapDrawable) getResources().getDrawable(53                         R.drawable.ic_launcher)).getBitmap();54                 imgView.setImageBitmap(img2);55             }56         }57     }58 59 60 }

android studio如何使用jni編譯opencv android studio如何使用jni編譯opencv

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI