Android之OpenCV學習
阿新 • • 發佈:2019-01-08
OpenCV learn
環境:android studio 配置好NDK
* download source from offical 下載的是3.3.0
* 執行sample需要先安裝OpenCVManager,這個讓人無語,不過從開發者的角度看,他們是 把OpenCV做成一個系統支援框架,這樣需要先安裝就很好理解了
* 發現OpenCV/sdk/native 裡提供的 xx.a的靜態庫,所以考慮直接使用靜態庫
直接使用OpenCV 提供的 Java 庫
- 新建一個工程, File > New > New Module, 選擇Import Eclipse ADT Project, openCV-android-sdk/sdk/java 下的專案匯入到專案裡, 自動命名為 openCVLibrary330,
- 新建 src/main/jniLibs , 複製 OpenCV-android-sdk/sdk/native/libs 下所有檔案放在jniLibs下,這樣就可以不需要安裝OpenCVManager, 直接在java層使用OpenCV
- opencvjava 裡是openCV提供的demo
使用C++協同開發 Cmake 我是這麼一個步驟,有些可以不用這樣
- 新建一個工程,勾選或者不勾選 include C++都可以
- 在新建一個openCVCode module
- 新建一個資料夾 比如我的叫 NativeDependLibs 用來存放相關的依賴包
- 複製 OpenCV-android-sdk/sdk/native/jni 下檔案放在NativeDependLibs/OpenCV下
- 新建 src/main/jniLibs , 複製 OpenCV-android-sdk/sdk/native/libs 下檔案放在jniLibs下,我只編譯armeabi ,所以只複製了 armeabi
- 新建一個cpp資料夾 src/main/cpp,並暫時隨便新建一個.cpp檔案
- 新建 CMakeLists.txt
編輯檔案如下
cmake_minimum_required(VERSION 3.4.1) #配置載入native依賴 set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs") include_directories( NativeDependLibs/OpenCV/jni/include
修改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" } } }
- 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
- onResume 里加載OpenCV的庫,回撥裡面載入人臉識別庫
- 在 onCameraFrame 裡拿到每一幀Camera Preview 的資料,進行人臉檢測,
OpenCV 人臉檢測用的是harr或LBP特徵,分類演算法用的是adaboost演算法。這種演算法需要提前訓練大量的圖片,非常耗時,因此opencv已經訓練好了,把訓練結果存放在一些xml檔案裡面。練好的檔案放在 /sdk/etc 資料夾下,有兩個資料夾haarcascades和lbpcascades,前者存放的是harr特徵訓練出來的檔案,後者存放的是lbp特徵訓練出來的檔案,比如
- 人臉檢測主要用到的是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());
- 人臉檢測主要用到的是CascadeClassifier這個類,以及該類下的detectMultiScale函式
- 總結起來就是 OpenCV有一個自己的org.opencv.android.JavaCameraView自定義控制元件,它迴圈的從攝像頭抓取資料,在回撥方法中,我們能獲取到Mat資料,然後通過呼叫檢測當前是否有人臉,我們會獲取到一個MatOfRect 是一個Rect陣列,裡面會有人臉資料,最後將人臉畫在螢幕上
- 拿到人臉後,繪製人臉框使用的是 Imgproc.rectangle
/**
* Mat型別的圖上繪製矩形
* rectangle(Mat img, //影象
* Point pt1, //矩形的一個頂點
* Point pt2, //矩形對角線上的另一個頂點
* Scalar color, //線條顏色 (RGB) 或亮度(灰度影象 )
* int thickness, //組成矩形的線條的粗細程度。取負值時(如 CV_FILLED)函式繪製填充了色彩的矩形
* int lineType, //線條的型別
* int shift //座標點的小數點位數
* )
*/
這位小哥的人臉眼睛檢測 感覺檢測的效果,模擬來說夠了,但是實用就不行了
下面是在官方文件中列出的最重要的模組。C層和java層都有相應的庫
- core:簡潔的核心模組,定義了基本的資料結構,包括稠密多維陣列 Mat 和其他模組需要的基本函式。
- imgproc:影象處理模組,包括線性和非線性影象濾波、幾何影象轉換 (縮放、仿射與透視變換、一般性基於表的重對映)、顏色空間轉換、直方圖等等。
- video:視訊分析模組,包括運動估計、背景消除、物體跟蹤演算法。
- calib3d:包括基本的多視角幾何演算法、單體和立體相機的標定、物件姿態估計、雙目立體匹配演算法和元素的三維重建。
- features2d:包含了顯著特徵檢測演算法、描述運算元和運算元匹配演算法。
- objdetect:物體檢測和一些預定義的物體的檢測 (如人臉、眼睛、杯子、人、汽車等)。
- ml:多種機器學習演算法,如 K 均值、支援向量機和神經網路。
- highgui:一個簡單易用的介面,提供視訊捕捉、影象和視訊編碼等功能,還有簡單的 UI 介面
- … and so on
- 前後攝像頭切換(需要判斷是否存在改相機,部分手機只有一個攝像頭)
預覽畫布調整 :
- 前置:預設是橫屏的,需要轉成豎屏顯示, 前置映象
- 後置:預設是橫屏的,需要轉成豎屏顯示
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 歸一化相關係數匹配法
* )
*/
識別的思路參考眼睛的識別
- 檢測到眼睛,獲得眼睛的模板 Mat 見 com.cl.slack.opencv.facedetect.FaceDetectHelperImpl#getTemplate
- 呼叫 Imgproc.matchTemplate 識別 見 com.cl.slack.opencv.facedetect.FaceDetectHelperImpl#match_eye
TODO:
1. 這個是橫屏可以檢測,豎屏是無法檢測的
1. 沒有嘴巴,鼻子 眉毛,上下嘴脣的檢測
1. 檢測效率略低