OpenCV android sdk配置OpenCV android NDK开发实例
OpenCV android sdk配置OpenCV android NDK開發實例
? ? ? ?【尊重原創,轉載請注明出處】http://blog.csdn.net/guyuealian/article/details/78374708
? ? ? ? 在Android應用中調用OpenCV進行圖像處理的方法有很多種,考慮到性能問題,本人推薦使用NDK進行開發,畢竟C/C++要比Java性能好的多。博客會給出詳細的例子和配置方法,并給出本人Github的Demo代碼,親們只需git clone,并在本地Android Studio Build一下,即可以在本地Android手機上直接運行了。
Demo Github地址:https://github.com/PanJinquan/OpenCV-Android-Demo(NDK和Java都支持OpenCV開發)
老鐵要是覺得不錯,記得給個“Star”哈
? ? ? PS :Opencv3.x以后,已經把很多功能模塊放在contrib中,要想移植opencv contrib到Android需要自己編譯,這個過程還是相當麻煩的。如果你想支持opencv contrib開發,可以下載本人已經編譯且移植好的Android Demo:
?OpenCV-Contrib-Android-Demo: https://github.com/PanJinquan/OpenCV-Contrib-Android-Demo(NDK和Java都支持OpenCV contrib開發)
? ? ? 整個工程代碼都給親們啦,是不是很照顧大家呢?題外話:之前本人在Android Studio和Eclipse配置OpenCV NDK開發時,踩了不知多少坑,網上教程一大堆,偏偏放在本人機器上就是報各種錯誤,各種無法解析……。好不容易配置好了,后面又來了個大坑:在Java中Bitmap圖像與JNI的OpenCV的Mat圖像的數據轉換時,出現了各種各樣問題,如通道順序問題,顏色失真問題,數據轉換問題等等等等等等……。好在,經過一番折騰,終于把問題解決了。
? ? ?言歸正傳,配置前,先保證版本一樣:
- (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:與時俱進,目前Github的OpenCV版本已經升級到:opencv-3.4.2-android-sdk,NDK:android-ndk-r14b
目錄
OpenCV android sdk配置OpenCV android NDK開發實例
?
一、OpenCV android sdk配置
1.第一步:
2.第二步:
3.第三步:
4.第四步:CMakeLists.txt配置
?二、OpenCV android的圖像數據轉換
第一種:通過JNI的整型數組傳遞圖像數據:
第二種:通過自定義圖像對象傳遞圖像數據:
第三種:使用OpenCV的Java包實現NDK開發
三、三種方法相比
一、OpenCV android sdk配置
1.第一步:
? ? 新建工程一定要勾選“Include C++ support”,這樣新建的Android工程會直接支持NDK開發,避免各種配置問題,如果提示沒有NDK,請下載NDK,并在工程“Project Structure”中導入即可:
2.第二步:
? 新建工程勾選了“Include C++ support”,就已經支持NDK開發了(即native-lib),我們需要做的是,根據自己項目需要,增加JNI接口。
3.第三步:
? 將下載的“OpenCV-android-sdk”放在工程的根目錄下:
4.第四步:CMakeLists.txt配置
? ? ?類似于Visual Studio的工程配置,我們需要告訴NDK去哪里查找OpenCV的頭文件路徑和依賴文件,Android Studio現在已經支持Cmake配置了,修改app/CMakeLists.txt如下:
#設置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. #聲明庫名稱、類型、源碼文件 #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庫鏈接到native庫中,這樣native庫才能調用NDK庫中的函數 #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() - 指定關聯的頭文件目錄?? clean一下,并重新編譯,這樣NDK就支持OpenCV開發了。
?PS:到這里,只是表示NDK C++層可以支持OpenCV開發了,要是想在Java層中,直接使用OpenCV的Java包,還需要導入“OpenCV-android-sdk”的Java,方法是可以參考:http://blog.csdn.net/u010097644/article/details/56849758
?二、OpenCV android的圖像數據轉換
? ?Android開發中,圖像類型一般是Bitmap,而在OpenCV是Mat,兩者數據的轉換需要進行特殊的處理。本OpenCVDemo實現了三種方法,可以現實Android的Bitmap圖像與OpenCV的Mat類型的互轉。
第一種:通過JNI的整型數組傳遞圖像數據:
(1)定義JNI接口
public native int[] ImageBlur(int[] pixels,int w,int h);說明:
pixels:整型數組,存儲圖像的像素值;
?w:圖像的寬度;
?h:圖像的高度;
?返回整型數組,處理后的圖像像素值;
? ? ? Android的Bitmap圖像需要利用getPixels方法,將Bitmap轉為整型數組pixels。再調用ImageBlur接口進行OpenCV的圖像處理,處理完后,返回的圖像數據需要用setPixels轉為Bitmap圖像進行顯示。問題來了:Bitmap.Config有多個屬性,用哪個呢?
public static final Bitmap.Config ?ARGB_4444 //4個4位組成即16位 public static final Bitmap.Config ?ARGB_8888//4個8位組成即32位 public static final Bitmap.Config ?RGB_565//R為5位,G為6位,B為5位共16位? ? ?一開始,腦殘,直接選了RGB_565,心思細膩測試部門的妹子,發現一個巨坑Bug:原圖與OpenCV接收的圖像總是有細微的差異,即出現了失真問題。后來,調試發現,就是RGB_565導致轉換到CV_8UC3出現數據丟失的情況。不難得出,應該用ARGB_8888類型,畢竟OpenCV中,常用的類型是8位無符號類型:CV_8UC4、CV_8UC3等。
/*** 調用JNI的ImageBlur(int[] pixels,int w,int h)接口實現圖像模糊*/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數組轉為bitmap類型。Bitmap desImage=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);//faceall為返回的int數組desImage.setPixels(image,0,w,0,0,w,h);return desImage;}(2)JNI數據封裝:
? ? 需要注意的是,上層Java傳遞進來的圖像數據是四通道的,即ARGB,而在OpenCV圖像處理中,我們往往希望處理的圖像數據是BGR三通道的。特別需要注意的是,OpenCV處理圖像數據的通道順序是B、G、R,而不是R、G、B,不然很容易出現圖像變色的問題。?
第二種:通過自定義圖像對象傳遞圖像數據:
? ? ? JNI接口參數除了可以傳遞基本數據類型,如:int、 float 、char等基本類型,實質上還可以是引用類型,如:類、實例、數組。第一種方法中,傳遞的數組就是引用類型(不管是對象數組還是基本類型數組,都作為reference types存在)。詳見:http://blog.csdn.net/qinjuning/article/details/7599796
? ? ? 既然可以傳遞對象,為什么不直接傳遞一個圖像對象,每次都要傳遞圖像的寬度w和高度h,這不是很麻煩么。OK,we do...!
(1)定義JNI接口;?
說明:?ImageData是本人定義好的圖像數據類,并提供了構造方法,getBitmap和getImageData方法方便數據之間的轉換:
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類型轉為int數組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數組轉為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;} }? ? ? ?有了這個類和方法,Android的Bitmap圖像與OpenCV的Mat數據,就可以很方便地進行數據的轉換啦,接口調用方法如下:
/*** 調用JNI的ImageProJNI(ImageData image_data)接口實現圖像模糊*/public Bitmap ImageProcess(Bitmap origImage) {ImageData imageData=new ImageData(origImage);//創建圖像數據imageData對象 // ImageData imageData=new ImageData(); // imageData.getImageData(origImage);Log.i(TAG, "input image size:"+imageData.w+","+imageData.h);ImageData out_image=ImageProJNI(imageData);//直接傳入圖像數據imageData對象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;}? ? ? ? 有了這個類和方法,Android的Bitmap圖像與OpenCV的Mat數據,就可以很方便地進行數據的轉換啦,接口調用方法如下:
相比第一種方法,第二種方法僅需傳入ImageData對象即可,是不是簡單明了很多了啦。
(2)JNI數據封裝:
? ?OK,Java層面簡單了,但JNI接口封裝就麻煩了,畢竟現在傳入的參數是對象,類似與Java的反射機制,在JNI中同樣可以獲取
? ?Java類的屬性和方法。方法如下:
第三種:使用OpenCV的Java包實現NDK開發
? ? ? 首先需要給項目添加OpenCV的java依賴模塊,可參考:http://blog.csdn.net/u010097644/article/details/56849758 配置過程。利用bitmapToMat將BitMap圖像轉為Java OpenCV的Mat圖像,再利用Mat的getNativeObjAddr()方法獲得圖像的Native對象地址,利用Native的對象地址,從而實現C++和Java層的圖像數據傳遞。
(1)定義JNI接口
? ?Java層實現過程:
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轉OpenCV的MatjniImagePro3(origMat.getNativeObjAddr(), destMat.getNativeObjAddr());Bitmap bitImage = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Utils.matToBitmap(destMat, bitImage);//OpenCV的Mat轉Bitmap顯示Log.i(TAG, "jniImagePro3 called successfully");return bitImage;}(2)JNI數據封裝:
? ?利用OpenCV的Native對象地址作為參數傳遞,其JNI封裝過程就十分簡單了。?
? ? 與第二種方法相比,第三種方法借助OpenCV android SDK的方法,其JNI封裝過程就簡單很多了。
三、三種方法相比
? ?(1)性能比較:就運行時間,耗時長久而言:第一種>第二種≈第三種,即第一種最耗時,性能最差,第二種和第三種差不多,不過第三種還是稍微快一點,但相差不大,畢竟是人家官方推薦使用的。
? ?(2)兼容性:第三種方法需要依賴OpenCV的jar包,而第一種和第二種方法只需要OpenCV的jni就可以,不需要上層Java對OpenCV的支持。因此兼容性好。
? ?(3)擴展性:顯然,第二種方法,通過自定義圖像對象傳遞圖像數據的方法,可以在不改變接口參數的情況下,非常方便的新增屬性變量,或者刪除。
運行處理效果:
?
如果你覺得該帖子幫到你,還望貴人多多支持,鄙人會再接再厲,繼續努力的~
總結
以上是生活随笔為你收集整理的OpenCV android sdk配置OpenCV android NDK开发实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决Error: undefined r
- 下一篇: OpenCV isContinuous(