1. 程式人生 > >Android之OpenCV學習

Android之OpenCV學習

OpenCV learn

這裡寫圖片描述

這裡寫圖片描述
測試原始碼

環境:android studio 配置好NDK
* download source from offical 下載的是3.3.0
* 執行sample需要先安裝OpenCVManager,這個讓人無語,不過從開發者的角度看,他們是 把OpenCV做成一個系統支援框架,這樣需要先安裝就很好理解了
* 發現OpenCV/sdk/native 裡提供的 xx.a的靜態庫,所以考慮直接使用靜態庫

直接使用OpenCV 提供的 Java 庫

  1. 新建一個工程, File > New > New Module, 選擇Import Eclipse ADT Project, openCV-android-sdk/sdk/java 下的專案匯入到專案裡, 自動命名為 openCVLibrary330,
  2. 新建 src/main/jniLibs , 複製 OpenCV-android-sdk/sdk/native/libs 下所有檔案放在jniLibs下,這樣就可以不需要安裝OpenCVManager, 直接在java層使用OpenCV
  3. opencvjava 裡是openCV提供的demo

使用C++協同開發 Cmake 我是這麼一個步驟,有些可以不用這樣

  1. 新建一個工程,勾選或者不勾選 include C++都可以
  2. 在新建一個openCVCode module
  3. 新建一個資料夾 比如我的叫 NativeDependLibs 用來存放相關的依賴包
  4. 複製 OpenCV-android-sdk/sdk/native/jni 下檔案放在NativeDependLibs/OpenCV下
  5. 新建 src/main/jniLibs , 複製 OpenCV-android-sdk/sdk/native/libs 下檔案放在jniLibs下,我只編譯armeabi ,所以只複製了 armeabi
  6. 新建一個cpp資料夾 src/main/cpp,並暫時隨便新建一個.cpp檔案
  7. 新建 CMakeLists.txt
  8. 編輯檔案如下

    cmake_minimum_required(VERSION 3.4.1)
    
    
    
    #配置載入native依賴
    
    set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
    include_directories(
            NativeDependLibs/OpenCV/jni/include
    ) #CPP資料夾下帶編譯的cpp檔案 file(GLOB_RECURSE ALL_PROJ_SRC "src/main/cpp/*.cpp" "src/main/cpp/*.h") add_library( ${ANDROID_BUILD_TARGET} SHARED ${ALL_PROJ_SRC}) #C++日誌 find_library( log-lib log ) file(GLOB OPENCV "${libs}/${ANDROID_ABI}/libopencv_java3.so" ) target_link_libraries( ${ANDROID_BUILD_TARGET} android ${log-lib} ${OPENCV} )
  9. 修改build.gradle 支援Cmake編譯

    android {
        compileSdkVersion 25
        buildToolsVersion "25.0.2"
    
        defaultConfig {
            minSdkVersion 19
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
    
            externalNativeBuild {
                cmake {
                    arguments "-DANDROID_TOOLCHAIN=gcc",
                            "-DANDROID_STUDIO=TRUE",
                            "-DANDROID_BUILD_TARGET=opencv-lib",// lib name
                            "-DANDROID_ABI=armeabi",
                            "-DBUILD_TYPE=debug",
                            "-DFOR_SDK=NO"
    
                    cppFlags "-std=c++11 -frtti -fexceptions -w"
                    abiFilters 'armeabi'//, 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
                }
            }
    
        }
    
        externalNativeBuild {
            cmake {
                path "CMakeLists.txt"
            }
        }
    
    }
  10. build 一下專案,去那個臨時的cpp檔案裡測試一下,可以成功引入OpenCV的庫,下面就可以直接在C程式碼裡使用OpenCV

大致檔案結構

|---app
|---core(module)
|     |---NativeDependLibs/OpenCV
                |---jni/include
|     |___build.gradle
|     |___CMakeLists.txt 
|     |___src
|          |___main
|               |___AndroidManifest.xml
|               |___cpp (cpp src)
|               |___java (java src)
|               |___jniLibs (libs)

Face Detection

opencvSample/facedetect/FaceDetectionActivity.java

  1. onResume 里加載OpenCV的庫,回撥裡面載入人臉識別庫
  2. 在 onCameraFrame 裡拿到每一幀Camera Preview 的資料,進行人臉檢測,
  3. OpenCV 人臉檢測用的是harr或LBP特徵,分類演算法用的是adaboost演算法。這種演算法需要提前訓練大量的圖片,非常耗時,因此opencv已經訓練好了,把訓練結果存放在一些xml檔案裡面。練好的檔案放在 /sdk/etc 資料夾下,有兩個資料夾haarcascades和lbpcascades,前者存放的是harr特徵訓練出來的檔案,後者存放的是lbp特徵訓練出來的檔案,比如

    1. 人臉檢測主要用到的是CascadeClassifier這個類,以及該類下的detectMultiScale函式
      /**
               * detectMultiScale(
               *                  Mat image, //輸入影象,一般為灰度圖
               *                  MatOfRect objects, //檢測到的Rect[],存放所有檢測出的人臉,每個人臉是一個矩形
               *                  double scaleFactor, //縮放比例,對圖片進行縮放,預設為1.1
               *                  int minNeighbors, //合併視窗時最小neighbor,每個候選矩陣至少包含的附近元素個數,預設為3
               *                  int flags,  //檢測標記,只對舊格式的分類器有效,與cvHaarDetectObjects的引數flags相同,在3.0版本中沒用處, 預設為0,
               *            可能的取值為CV_HAAR_DO_CANNY_PRUNING(CANNY邊緣檢測)、CV_HAAR_SCALE_IMAGE(縮放影象)、
               *            CV_HAAR_FIND_BIGGEST_OBJECT(尋找最大的目標)、CV_HAAR_DO_ROUGH_SEARCH(做粗略搜尋);
               *            如果尋找最大的目標就不能縮放影象,也不能CANNY邊緣檢測
               *                  Size minSize, //檢測出的人臉最小尺寸
               *                  Size maxSize //檢測出的人臉最大尺寸
               *                  )
               */
                  mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE
                          new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());
      
  4. 總結起來就是 OpenCV有一個自己的org.opencv.android.JavaCameraView自定義控制元件,它迴圈的從攝像頭抓取資料,在回撥方法中,我們能獲取到Mat資料,然後通過呼叫檢測當前是否有人臉,我們會獲取到一個MatOfRect 是一個Rect陣列,裡面會有人臉資料,最後將人臉畫在螢幕上
  5. 拿到人臉後,繪製人臉框使用的是 Imgproc.rectangle

    /**
    * Mat型別的圖上繪製矩形
    * rectangle(Mat img, //影象
    * Point pt1, //矩形的一個頂點
    * Point pt2, //矩形對角線上的另一個頂點
    * Scalar color, //線條顏色 (RGB) 或亮度(灰度影象 )
    * int thickness, //組成矩形的線條的粗細程度。取負值時(如 CV_FILLED)函式繪製填充了色彩的矩形
    * int lineType, //線條的型別
    * int shift //座標點的小數點位數
    * )
    */
  6. 這位小哥的人臉眼睛檢測 感覺檢測的效果,模擬來說夠了,但是實用就不行了

  7. 下面是在官方文件中列出的最重要的模組。C層和java層都有相應的庫

    1. core:簡潔的核心模組,定義了基本的資料結構,包括稠密多維陣列 Mat 和其他模組需要的基本函式。
    2. imgproc:影象處理模組,包括線性和非線性影象濾波、幾何影象轉換 (縮放、仿射與透視變換、一般性基於表的重對映)、顏色空間轉換、直方圖等等。
    3. video:視訊分析模組,包括運動估計、背景消除、物體跟蹤演算法。
    4. calib3d:包括基本的多視角幾何演算法、單體和立體相機的標定、物件姿態估計、雙目立體匹配演算法和元素的三維重建。
    5. features2d:包含了顯著特徵檢測演算法、描述運算元和運算元匹配演算法。
    6. objdetect:物體檢測和一些預定義的物體的檢測 (如人臉、眼睛、杯子、人、汽車等)。
    7. ml:多種機器學習演算法,如 K 均值、支援向量機和神經網路。
    8. highgui:一個簡單易用的介面,提供視訊捕捉、影象和視訊編碼等功能,還有簡單的 UI 介面
    9. … and so on
  8. 前後攝像頭切換(需要判斷是否存在改相機,部分手機只有一個攝像頭)
  9. 預覽畫布調整 :

    1. 前置:預設是橫屏的,需要轉成豎屏顯示, 前置映象
    2. 後置:預設是橫屏的,需要轉成豎屏顯示
      protected Mat rotateMat(Mat src) {
      
          if(mCameraIndex == CAMERA_ID_FRONT) {
              // 豎屏需要選擇 rotate
              /**
               * transpose : 矩陣轉置
               * 矩陣轉置是將矩陣的行與列順序對調(第i行轉變為第i列)形成一個新的矩陣
               */
              Core.transpose(src, mRgbaT); //轉置函式,可以水平的影象變為垂直
              Imgproc.resize(mRgbaT,src, src.size(), 0.0D, 0.0D, 0); //將轉置後的影象縮放為src的大小
              // 左右映象
              /**
               * flip(Mat src, //輸入矩陣
               *      Mat dst, //翻轉後矩陣,型別與src一致
               *      int flipCode //翻轉模式,flipCode==0垂直翻轉(沿X軸翻轉),flipCode>0水平翻轉(沿Y軸翻轉),
               *                  flipCode<0水平垂直翻轉(先沿X軸翻轉,再沿Y軸翻轉,等價於旋轉180°)
               *      )
               */
              Core.flip(src, src, -1);
          } else {
              Core.transpose(src, mRgbaT);
              Imgproc.resize(mRgbaT,src, src.size(), 0.0D, 0.0D, 0);
              Core.flip(src, src, 1);
          }
          return super.rotateMat(src);
      }

    不過OpenCV的庫,貌似是橫屏是識別的,我改成豎屏後檢測效果不好,不過橫過來,人臉眼睛檢測還是不錯的

識別

Imgproc.matchTemplate

/**
     * 可以用來做 識別
     * 模板匹配是一種在影象中定位目標的方法
     * 通過把輸入影象在實際影象上逐畫素點滑動,計算特徵相似性,以此來判斷當前滑塊影象所在位置是目標影象的概率。
     * 在目標特徵變化不是特別快的情況下,跟蹤效果還可以
     * matchTemplate(Mat image, 搜尋物件影象
     *              Mat templ, 模板影象,小於image,並且和image有相同的資料型別
     *              Mat result, 比較結果
     *              int method 比較演算法總共有六種
     *              TM_SQDIFF 平方差匹配法:該方法採用平方差來進行匹配;最好的匹配值為0;匹配越差,匹配值越大。
     *              TM_CCORR 相關匹配法:該方法採用乘法操作;數值越大表明匹配程度越好。
     *              TM_CCOEFF 相關係數匹配法:1表示完美的匹配;-1表示最差的匹配。
     *              TM_SQDIFF_NORMED 歸一化平方差匹配法
     *              TM_CCORR_NORMED 歸一化相關匹配法
     *              TM_CCOEFF_NORMED 歸一化相關係數匹配法
     *              )
     */

識別的思路參考眼睛的識別

  1. 檢測到眼睛,獲得眼睛的模板 Mat 見 com.cl.slack.opencv.facedetect.FaceDetectHelperImpl#getTemplate
  2. 呼叫 Imgproc.matchTemplate 識別 見 com.cl.slack.opencv.facedetect.FaceDetectHelperImpl#match_eye

TODO:
1. 這個是橫屏可以檢測,豎屏是無法檢測的
1. 沒有嘴巴,鼻子 眉毛,上下嘴脣的檢測
1. 檢測效率略低