OpenCV android sdk配置OpenCV android NDK开发实例
OpenCV android sdk配置OpenCV android NDK開(kāi)發(fā)實(shí)例
? ? ? ?【尊重原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處】http://blog.csdn.net/guyuealian/article/details/78374708
? ? ? ? 在Android應(yīng)用中調(diào)用OpenCV進(jìn)行圖像處理的方法有很多種,考慮到性能問(wèn)題,本人推薦使用NDK進(jìn)行開(kāi)發(fā),畢竟C/C++要比Java性能好的多。博客會(huì)給出詳細(xì)的例子和配置方法,并給出本人Github的Demo代碼,親們只需git clone,并在本地Android Studio Build一下,即可以在本地Android手機(jī)上直接運(yùn)行了。
Demo Github地址:https://github.com/PanJinquan/OpenCV-Android-Demo(NDK和Java都支持OpenCV開(kāi)發(fā))
老鐵要是覺(jué)得不錯(cuò),記得給個(gè)“Star”哈
? ? ? PS :Opencv3.x以后,已經(jīng)把很多功能模塊放在contrib中,要想移植opencv contrib到Android需要自己編譯,這個(gè)過(guò)程還是相當(dāng)麻煩的。如果你想支持opencv contrib開(kāi)發(fā),可以下載本人已經(jīng)編譯且移植好的Android Demo:
?OpenCV-Contrib-Android-Demo: https://github.com/PanJinquan/OpenCV-Contrib-Android-Demo(NDK和Java都支持OpenCV contrib開(kāi)發(fā))
? ? ? 整個(gè)工程代碼都給親們啦,是不是很照顧大家呢?題外話:之前本人在Android Studio和Eclipse配置OpenCV NDK開(kāi)發(fā)時(shí),踩了不知多少坑,網(wǎng)上教程一大堆,偏偏放在本人機(jī)器上就是報(bào)各種錯(cuò)誤,各種無(wú)法解析……。好不容易配置好了,后面又來(lái)了個(gè)大坑:在Java中Bitmap圖像與JNI的OpenCV的Mat圖像的數(shù)據(jù)轉(zhuǎn)換時(shí),出現(xiàn)了各種各樣問(wèn)題,如通道順序問(wèn)題,顏色失真問(wèn)題,數(shù)據(jù)轉(zhuǎn)換問(wèn)題等等等等等等……。好在,經(jīng)過(guò)一番折騰,終于把問(wèn)題解決了。
? ? ?言歸正傳,配置前,先保證版本一樣:
- (1)Android Studio 2.3.3 以上
- (2)android-ndk-r10d 以上,下載地址:https://developer.android.google.cn/ndk/downloads/index.html
- (3)OpenCV-3.1.0-android-sdk 以上,下載地址:https://opencv.org/releases.html
PS:與時(shí)俱進(jìn),目前Github的OpenCV版本已經(jīng)升級(jí)到:opencv-3.4.2-android-sdk,NDK:android-ndk-r14b
目錄
OpenCV android sdk配置OpenCV android NDK開(kāi)發(fā)實(shí)例
?
一、OpenCV android sdk配置
1.第一步:
2.第二步:
3.第三步:
4.第四步:CMakeLists.txt配置
?二、OpenCV android的圖像數(shù)據(jù)轉(zhuǎn)換
第一種:通過(guò)JNI的整型數(shù)組傳遞圖像數(shù)據(jù):
第二種:通過(guò)自定義圖像對(duì)象傳遞圖像數(shù)據(jù):
第三種:使用OpenCV的Java包實(shí)現(xiàn)NDK開(kāi)發(fā)
三、三種方法相比
一、OpenCV android sdk配置
1.第一步:
? ? 新建工程一定要勾選“Include C++ support”,這樣新建的Android工程會(huì)直接支持NDK開(kāi)發(fā),避免各種配置問(wèn)題,如果提示沒(méi)有NDK,請(qǐng)下載NDK,并在工程“Project Structure”中導(dǎo)入即可:
2.第二步:
? 新建工程勾選了“Include C++ support”,就已經(jīng)支持NDK開(kāi)發(fā)了(即native-lib),我們需要做的是,根據(jù)自己項(xiàng)目需要,增加JNI接口。
3.第三步:
? 將下載的“OpenCV-android-sdk”放在工程的根目錄下:
4.第四步:CMakeLists.txt配置
? ? ?類似于Visual Studio的工程配置,我們需要告訴NDK去哪里查找OpenCV的頭文件路徑和依賴文件,Android Studio現(xiàn)在已經(jīng)支持Cmake配置了,修改app/CMakeLists.txt如下:
#設(shè)置OpenCV的路徑 set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../OpenCV-android-sdk/sdk/native/jni) find_package(OpenCV REQUIRED) #包含OpenCV的頭文件 include_directories( ${CMAKE_SOURCE_DIR}/../OpenCV-android-sdk/sdk/native/jni/include)? ?此外,還需要在target_link_libraries添加${OpenCV_LIBS}?
target_link_libraries( # Specifies the target library.imagePro-lib# Links the target library to the log library# included in the NDK.${log-lib}${OpenCV_LIBS})? ? 完整的?CMakeLists.txt文件:
# For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.cmake_minimum_required(VERSION 3.4.1)set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../OpenCV-android-sdk/sdk/native/jni) find_package(OpenCV REQUIRED) include_directories( ${CMAKE_SOURCE_DIR}/../OpenCV-android-sdk/sdk/native/jni/include)# Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. #聲明庫(kù)名稱、類型、源碼文件 #add_library( # Sets the name of the library. # native-lib # # # Sets the library as a shared library. # SHARED # # # Provides a relative path to your source file(s). # src/main/cpp/native-lib.cpp)add_library( # Sets the name of the library.imagePro-lib# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).src/main/cpp/imagePro.cppsrc/main/cpp/AndroidDebug.cpp)# Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build.find_library( # Sets the name of the path variable.log-lib# Specifies the name of the NDK library that# you want CMake to locate.log )# Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. #將NDK庫(kù)鏈接到native庫(kù)中,這樣native庫(kù)才能調(diào)用NDK庫(kù)中的函數(shù) #target_link_libraries( # Specifies the target library. # native-lib # imagePro-lib # # # Links the target library to the log library # # included in the NDK. # ${log-lib} )target_link_libraries( # Specifies the target library.imagePro-lib# Links the target library to the log library# included in the NDK.${log-lib}${OpenCV_LIBS})#include_directories() - 指定關(guān)聯(lián)的頭文件目錄?? clean一下,并重新編譯,這樣NDK就支持OpenCV開(kāi)發(fā)了。
?PS:到這里,只是表示NDK C++層可以支持OpenCV開(kāi)發(fā)了,要是想在Java層中,直接使用OpenCV的Java包,還需要導(dǎo)入“OpenCV-android-sdk”的Java,方法是可以參考:http://blog.csdn.net/u010097644/article/details/56849758
?二、OpenCV android的圖像數(shù)據(jù)轉(zhuǎn)換
? ?Android開(kāi)發(fā)中,圖像類型一般是Bitmap,而在OpenCV是Mat,兩者數(shù)據(jù)的轉(zhuǎn)換需要進(jìn)行特殊的處理。本OpenCVDemo實(shí)現(xiàn)了三種方法,可以現(xiàn)實(shí)Android的Bitmap圖像與OpenCV的Mat類型的互轉(zhuǎn)。
第一種:通過(guò)JNI的整型數(shù)組傳遞圖像數(shù)據(jù):
(1)定義JNI接口
public native int[] ImageBlur(int[] pixels,int w,int h);說(shuō)明:
pixels:整型數(shù)組,存儲(chǔ)圖像的像素值;
?w:圖像的寬度;
?h:圖像的高度;
?返回整型數(shù)組,處理后的圖像像素值;
? ? ? Android的Bitmap圖像需要利用getPixels方法,將Bitmap轉(zhuǎn)為整型數(shù)組pixels。再調(diào)用ImageBlur接口進(jìn)行OpenCV的圖像處理,處理完后,返回的圖像數(shù)據(jù)需要用setPixels轉(zhuǎn)為Bitmap圖像進(jìn)行顯示。問(wèn)題來(lái)了:Bitmap.Config有多個(gè)屬性,用哪個(gè)呢?
public static final Bitmap.Config ?ARGB_4444 //4個(gè)4位組成即16位 public static final Bitmap.Config ?ARGB_8888//4個(gè)8位組成即32位 public static final Bitmap.Config ?RGB_565//R為5位,G為6位,B為5位共16位? ? ?一開(kāi)始,腦殘,直接選了RGB_565,心思細(xì)膩測(cè)試部門(mén)的妹子,發(fā)現(xiàn)一個(gè)巨坑Bug:原圖與OpenCV接收的圖像總是有細(xì)微的差異,即出現(xiàn)了失真問(wèn)題。后來(lái),調(diào)試發(fā)現(xiàn),就是RGB_565導(dǎo)致轉(zhuǎn)換到CV_8UC3出現(xiàn)數(shù)據(jù)丟失的情況。不難得出,應(yīng)該用ARGB_8888類型,畢竟OpenCV中,常用的類型是8位無(wú)符號(hào)類型:CV_8UC4、CV_8UC3等。
/*** 調(diào)用JNI的ImageBlur(int[] pixels,int w,int h)接口實(shí)現(xiàn)圖像模糊*/public Bitmap doImageBlur(Bitmap origImage) {int w = origImage.getWidth();int h = origImage.getHeight();int[] pixels = new int[w * h];origImage.getPixels(pixels, 0, w, 0, 0, w, h);int[] image=ImageBlur(pixels,w,h);//JNILog.i(TAG, "ImageBlur called successfully");//最后將返回的int數(shù)組轉(zhuǎn)為bitmap類型。Bitmap desImage=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);//faceall為返回的int數(shù)組desImage.setPixels(image,0,w,0,0,w,h);return desImage;}(2)JNI數(shù)據(jù)封裝:
? ? 需要注意的是,上層Java傳遞進(jìn)來(lái)的圖像數(shù)據(jù)是四通道的,即ARGB,而在OpenCV圖像處理中,我們往往希望處理的圖像數(shù)據(jù)是BGR三通道的。特別需要注意的是,OpenCV處理圖像數(shù)據(jù)的通道順序是B、G、R,而不是R、G、B,不然很容易出現(xiàn)圖像變色的問(wèn)題。?
第二種:通過(guò)自定義圖像對(duì)象傳遞圖像數(shù)據(jù):
? ? ? JNI接口參數(shù)除了可以傳遞基本數(shù)據(jù)類型,如:int、 float 、char等基本類型,實(shí)質(zhì)上還可以是引用類型,如:類、實(shí)例、數(shù)組。第一種方法中,傳遞的數(shù)組就是引用類型(不管是對(duì)象數(shù)組還是基本類型數(shù)組,都作為reference types存在)。詳見(jiàn):http://blog.csdn.net/qinjuning/article/details/7599796
? ? ? 既然可以傳遞對(duì)象,為什么不直接傳遞一個(gè)圖像對(duì)象,每次都要傳遞圖像的寬度w和高度h,這不是很麻煩么。OK,we do...!
(1)定義JNI接口;?
說(shuō)明:?ImageData是本人定義好的圖像數(shù)據(jù)類,并提供了構(gòu)造方法,getBitmap和getImageData方法方便數(shù)據(jù)之間的轉(zhuǎn)換:
package com.panjq.opencv.alg;import android.graphics.Bitmap;/*** Created by panjq1 on 2017/10/23.*/public class ImageData {// public Bitmap bitmap;public int[] pixels;public int w;public int h;ImageData(){}ImageData(Bitmap bitmap){this.w = bitmap.getWidth();this.h = bitmap.getHeight();//將bitmap類型轉(zhuǎn)為int數(shù)組this.pixels = new int[this.w * this.h];bitmap.getPixels(this.pixels, 0, this.w, 0, 0, this.w, this.h);}public Bitmap getBitmap( ){//int數(shù)組轉(zhuǎn)為bitmap類型。Bitmap desImage=Bitmap.createBitmap(this.w,this.h,Bitmap.Config.ARGB_8888);desImage.setPixels(this.pixels,0,this.w,0,0,this.w,this.h);return desImage;}public ImageData getImageData(Bitmap bitmap){this.w = bitmap.getWidth();this.h = bitmap.getHeight();this.pixels = new int[w * h];bitmap.getPixels( this.pixels, 0, w, 0, 0, w, h);return this;} }? ? ? ?有了這個(gè)類和方法,Android的Bitmap圖像與OpenCV的Mat數(shù)據(jù),就可以很方便地進(jìn)行數(shù)據(jù)的轉(zhuǎn)換啦,接口調(diào)用方法如下:
/*** 調(diào)用JNI的ImageProJNI(ImageData image_data)接口實(shí)現(xiàn)圖像模糊*/public Bitmap ImageProcess(Bitmap origImage) {ImageData imageData=new ImageData(origImage);//創(chuàng)建圖像數(shù)據(jù)imageData對(duì)象 // ImageData imageData=new ImageData(); // imageData.getImageData(origImage);Log.i(TAG, "input image size:"+imageData.w+","+imageData.h);ImageData out_image=ImageProJNI(imageData);//直接傳入圖像數(shù)據(jù)imageData對(duì)象Log.i(TAG, "return image size:"+out_image.w+","+out_image.h);Bitmap desImage=out_image.getBitmap();Log.i(TAG, "ImageProJNI called successfully");return desImage;}? ? ? ? 有了這個(gè)類和方法,Android的Bitmap圖像與OpenCV的Mat數(shù)據(jù),就可以很方便地進(jìn)行數(shù)據(jù)的轉(zhuǎn)換啦,接口調(diào)用方法如下:
相比第一種方法,第二種方法僅需傳入ImageData對(duì)象即可,是不是簡(jiǎn)單明了很多了啦。
(2)JNI數(shù)據(jù)封裝:
? ?OK,Java層面簡(jiǎn)單了,但JNI接口封裝就麻煩了,畢竟現(xiàn)在傳入的參數(shù)是對(duì)象,類似與Java的反射機(jī)制,在JNI中同樣可以獲取
? ?Java類的屬性和方法。方法如下:
第三種:使用OpenCV的Java包實(shí)現(xiàn)NDK開(kāi)發(fā)
? ? ? 首先需要給項(xiàng)目添加OpenCV的java依賴模塊,可參考:http://blog.csdn.net/u010097644/article/details/56849758 配置過(guò)程。利用bitmapToMat將BitMap圖像轉(zhuǎn)為Java OpenCV的Mat圖像,再利用Mat的getNativeObjAddr()方法獲得圖像的Native對(duì)象地址,利用Native的對(duì)象地址,從而實(shí)現(xiàn)C++和Java層的圖像數(shù)據(jù)傳遞。
(1)定義JNI接口
? ?Java層實(shí)現(xiàn)過(guò)程:
public Bitmap ImageProcess3(Bitmap origImage) {Log.i(TAG, "called JNI:jniImagePro3 ");int w=origImage.getWidth();int h=origImage.getHeight();Mat origMat = new Mat();Mat destMat = new Mat();Utils.bitmapToMat(origImage, origMat);//Bitmap轉(zhuǎn)OpenCV的MatjniImagePro3(origMat.getNativeObjAddr(), destMat.getNativeObjAddr());Bitmap bitImage = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Utils.matToBitmap(destMat, bitImage);//OpenCV的Mat轉(zhuǎn)Bitmap顯示Log.i(TAG, "jniImagePro3 called successfully");return bitImage;}(2)JNI數(shù)據(jù)封裝:
? ?利用OpenCV的Native對(duì)象地址作為參數(shù)傳遞,其JNI封裝過(guò)程就十分簡(jiǎn)單了。?
? ? 與第二種方法相比,第三種方法借助OpenCV android SDK的方法,其JNI封裝過(guò)程就簡(jiǎn)單很多了。
三、三種方法相比
? ?(1)性能比較:就運(yùn)行時(shí)間,耗時(shí)長(zhǎng)久而言:第一種>第二種≈第三種,即第一種最耗時(shí),性能最差,第二種和第三種差不多,不過(guò)第三種還是稍微快一點(diǎn),但相差不大,畢竟是人家官方推薦使用的。
? ?(2)兼容性:第三種方法需要依賴OpenCV的jar包,而第一種和第二種方法只需要OpenCV的jni就可以,不需要上層Java對(duì)OpenCV的支持。因此兼容性好。
? ?(3)擴(kuò)展性:顯然,第二種方法,通過(guò)自定義圖像對(duì)象傳遞圖像數(shù)據(jù)的方法,可以在不改變接口參數(shù)的情況下,非常方便的新增屬性變量,或者刪除。
運(yùn)行處理效果:
?
如果你覺(jué)得該帖子幫到你,還望貴人多多支持,鄙人會(huì)再接再厲,繼續(xù)努力的~
總結(jié)
以上是生活随笔為你收集整理的OpenCV android sdk配置OpenCV android NDK开发实例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 解决Error: undefined r
- 下一篇: OpenCV isContinuous(