最近因爲需要實現了一下人臉識別,實現了這個功能之後,就想着把踩過的坑總結出來。參考過許多博客,發現主要有兩種形式,一種是基於 android SDK 實現人臉檢測,而另一種是利用openCvManager來實現。這兩種方式都實現了動態的人臉檢測,當然網上也有許多關於檢測靜態圖片中的人臉的文章。這裏我就不詳細介紹了。我這裏實現的功能主要是啓動Camera之後,當照相機中有人臉出現時,Camera界面動態的繪製出人臉矩形框,獲取到人臉框之後,Camera拍照,獲取到圖片,將圖片傳送到後臺來驗證人臉。這裏需要區分一下人臉檢測和人臉識別。人臉檢測是檢測camera中是否有人臉存在,如果存在的話會繪製出矩形框將人臉標誌出來,而人臉識別則是根據兩張人臉驗證是否是一個人的概念。
先大概講一下第一種實現動態人臉檢測的方式.這是Camera基於Google自帶的FaceDetectionListener進行人臉檢測,當Camera拍攝到人臉後,會將人臉矩形繪製出來。這個具體大家可以參考這位大神的博客,http://blog.csdn.net/yanzi1225627/article/details/38098729/
他已經講的很細緻了。但感覺google自帶檢測算法在測試的時候檢測人臉比較慢,而且有些機型不支持這種人臉檢測接口,不過他的文章還是很具有參考價值的。那麼我就在這裏就具體介紹一下使用openCv實現動態人臉檢測的具體方法。
先提供下opencv下載地址http://opencv.org/ 我下載的是最新的3.2版本,本來用的是2.4,但會發現比較卡頓。公司一直使用的eclipse,而且demo中的工程也是eclipse項目,所以直接使用eclipse來進行調試。當然網上也有許多在As中使用的教程,大家可以去多找找。
在我們下載sdk後解壓後,進入到samples目錄下會見到如下結構:
我們直接運行example-face-detection.apk後,會發現提示要安裝opencv-manager,在sdk/apk目錄下,我們會發現不同種類的opencvmanager,我們根據不同的機型選擇對應的manager安裝,我選擇的是OpenCV_3.2.0_Manager_3.20_armeabi-v7a.apk這個包,安裝之後,我們打開應用後就可以發現攝像頭進行動態的人臉檢測了。
運行完官方的apk後,我們開始自己進行調試。我們導入OpenCV-android-sdk/samples的face-detection工程,然後新建個工程作爲Library,在我們的library中導入OpenCV-android-sdk/java中相關類和資源文件,
然後對face-detection進行關聯,這時候我的工程仍然報錯,發現下面的錯誤:
沒有找到ndk-build.cmd.原來是沒有配置ndk-build路徑,配置完NDKROOT即可。這裏就是右鍵properties-C/C++build,如下圖:
這裏的ndk我下載的是android-ndk-r9d,在下載完畢之後我們直接解壓就行,這裏的NDKROOT就是我們解壓後的ndk的路徑。附帶上Ndk的下載路徑:
http://dl.google.com/android/ndk/android-ndk-r9d-windows-x86.zip
之後又碰見如下錯誤:
這是因爲在jni/Android.mk中沒配置opencv的Opencv.mk的根路徑,同理和ndk配置方式一樣,我們配置完我們自己的${OPENCV_ANDROID_SDK}就行了
至此,我們運行官方的demo,可直接就運行出來,但我們發現總需要安裝opencvmanager,很明顯,有時候這是不合適的。所以接下來我介紹下不需要安裝opencvmanager的方法,很簡單,分下面4步:
1.修改Android.mk前幾行 爲下列形式,
include $(CLEAR_VARS)
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED
2.打開FdActivity.java文件,在其中添加一個靜態初始化塊代碼,
static {
Log.i(TAG, "OpenCV library load!");
if (!OpenCVLoader.initDebug()) {
Log.i(TAG, "OpenCV load not successfully");
} else {
System.loadLibrary("detection_based_tracker");// load other libraries
}
}
它是用來加載OpenCV_java庫的,如果沒有加回報類似於native method not found的錯誤
3.註釋掉onResume中的initAsync那句,讓程序不去訪問OpenCV Manager。
@Override
public void onResume()
{
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
// OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
4.修改FdActivity.java的OnCreate()方法,從上面的private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this)代碼塊中拷貝try-catch塊放到OnCreate的setContentView()之後。
try {
// load cascade file from application resources
InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
FileOutputStream os = new FileOutputStream(mCascadeFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
is.close();
os.close();
mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
if (mJavaDetector.empty()) {
Log.e(TAG, "Failed to load cascade classifier");
mJavaDetector = null;
} else
Log.i(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());
mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0);
cascadeDir.delete();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
}
之後運行,即可發現不用在依賴OpenCvManager,程序也可以直接運行了。
這裏上述進行的主要是camera動態監測出人臉,其實這基本上已經算完成了。現在我們集成到我們自己的工程中,通過利用百度的人臉識別,來判斷人臉是否一致,從而實現人臉識別。
首先,我們可以在百度AI開放平臺上 http://ai.baidu.com/ 創建我們自己的人臉識別應用
![這裏寫圖片描述](https://img-blog.csdn.net/20170712160653919? /2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjg5MzE2MjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
創建完後 我們的應用會有一個APPiD和ApI key我們利用這兩個屬性值可生成access_token具體的調用方式我們可參考
https://cloud.baidu.com/doc/FACE/Face-API.html生成access_token後,當發送post請求時,附帶上這個token即可,我們可以進行人臉識別了。我這裏調用的是人臉註冊和人臉認證接口,通過認證返回的分數值,我們可判斷是否是同一個人。具體請求參數和返回參數,大家可以參考百度人臉識別的API。
好了,現在開始創建我們的應用。在創建完工程後,我們把face-detection中的FdActivity和DetectionBasedTracker導入到我們自己的工程中,這之中的FdActivity相當於相機界面,這裏注意不要改動包名,否則可能會出現找不到本地方法的錯誤。同時,我們要將jni和Lib armead目錄複製到我們的目錄裏,之後我們要利用Eclipse自動編譯NDK/JNI。下面簡單介紹一下自動編譯的方法:
1.將Android項目轉換爲C/C++項目,如下圖,New -> Other -> C/C++ -> Convert to a C/C++ Project.
2. 配置NDK編譯路徑,Project->Properties,如下圖,其中Build-Command中ANDROID_NDK爲環境變量中配置的Android-NDK路徑;Build-Directory爲當前工程目錄
3.Project->Properties,CNU C和GNU C++中配置OpenCV的鏈接庫
配置完指後,我們需要稍微修改一下我們自己創建的opencvLibrary的內容。因爲camera中出現人臉框後需要拍照功能,我們需要簡單的修改OpenCvLibrary中的JavaCameraView,在這個類裏面我們可以調用camera的takepicture方法。同時通過PictureCallback將我們拍的照片保存到我們指定的路徑。
mCamera.takePicture(mShutterCallback, null, mJpegPictureCallback);
ShutterCallback mShutterCallback = new ShutterCallback()
{
public void onShutter() {
}
};
PictureCallback mJpegPictureCallback = new PictureCallback()
{
public void onPictureTaken(byte[] data, Camera camera) {
Log.d("hr", "拍照回調");
Bitmap b = null;
if (null != data) {
b = BitmapFactory.decodeByteArray(data, 0, data.length);
mCamera.stopPreview();
}
if (null != b) {
Bitmap rotaBitmap = ImageUtil.getRotateBitmap(b, 00.0f);
boolean bitmap = FileUtil.saveBitmap(rotaBitmap,idTag);
if (bitmap) {
if (handler!=null) {
handler.sendEmptyMessage(2000);
}
}
}
mCamera.startPreview();
}};
在FdActivity的 onCameraFramed中 facesArray 得長度 即是人臉顯示的數目
Rect[] facesArray = faces.toArray();
for (int i = 0; i < facesArray.length; i++)
Imgproc.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
當相機捕捉到人臉後,我們可以通過handle自動拍照也可以手動拍照,這就看具體的需求了。保存圖片之後,我們調用百度的接口,根據返回值來判斷人體的相似度。
url = new URL(urlPath);
Bitmap bmp = FileUtil.getValidateBitmap();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
try {
baos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] buffer = baos.toByteArray();
// 將圖片的字節流數據加密成base64字符輸出
String photo = Base64.encodeToString(buffer, 0, buffer.length,Base64.DEFAULT);
HashMap<String, String> map=new HashMap<String, String>();
map.put("uid", uid);
map.put("group_id", groupid);
map.put("image", photo);
map.put("ext_fields", "faceliveness");
String str=HttpClientUtil.doPost(urlPath, map);
獲取驗證和註冊的方式代碼基本上一樣,就是發送普通的網絡請求,我們根據返回值可以判斷出人臉是否一致。至此整個人臉驗證流程就算結束了。我這裏就光展示一下動態檢測人臉的效果
當然還有認證的流程,因爲感覺難點主要在動態識別人臉這塊,所以調用百度的接口,實現人臉識別的過程就不再詳細介紹了。當然 ,還有一些小問題,當豎屏時,檢測不準確或者Camera界面沒有全屏, 或者是前置攝像頭的開啓。大家有興趣的話可以去研究一下。
最後附帶上我的demo地址,http://download.csdn.net/download/qq_28931623/10167057