android中利用opencv進行影象識別
阿新 • • 發佈:2019-01-05
之前開發的時候老大讓研究下影象識別的功能,同事推薦看看opencv,發現對於移動端來說opencv的資料和demo都比較少,現在整理下之前的工作成果。
首先是進行配置工作,先匯入opencv的一個程式碼模組
之後是匯入opencv的具體的演算法,當然是c++寫的
配置完畢後開始進行正式的程式碼階段,首先許可權設定,主要就是攝像頭許可權的獲取
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.front"
android:required="false" />
<uses-feature
android:name ="android.hardware.camera.front.autofocus"
android:required="false" />
接下來佈局方面
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:opencv="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_img_recognition"
android:layout_width="match_parent"
android:layout_height="match_parent" tools:context="com.example.imgrecognition.ImgRecognitionActivity">
<org.opencv.android.JavaCameraView
android:id="@+id/jcv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
opencv:camera_id="any"
opencv:show_fps="true" />
</RelativeLayout>
我們只需要新增一個JavaCameraView控制元件就可以,這個控制元件是opencv提供的,繼承了SurfaceView。
回到Activity中,在初始化JavaCameraView後,我們需要為其設定一個監聽事件
JavaCameraView jcv = (JavaCameraView) findViewById(R.id.jcv);
jcv.setVisibility(SurfaceView.VISIBLE);
jcv.setCvCameraViewListener(this);
需要實現的方法為
@Override
public void onCameraViewStarted(int width, int height) {
}
@Override
public void onCameraViewStopped() {
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
return null;
}
從名字上我們可以看出來,前兩個方法分別是在攝像頭獲取影像和停止獲取時呼叫,而第三個方法則是攝像頭獲取的每一幀畫面後回撥,我們進行影象識別就是主要在第三個方法中實現。
在開始使用opencv的功能前還要進行它的初始化的工作
@Override
protected void onResume() {
super.onResume();
OpenCVLoader.initDebug();
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS: {
jcv.enableView();
}
break;
default: {
super.onManagerConnected(status);
}
break;
}
}
};
@Override
protected void onPause() {
super.onPause();
if (jcv != null)
jcv.disableView();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (jcv != null)
jcv.disableView();
}
接著我們在onCameraViewStarted方法中設定下我們需要識別的圖片,我就隨便找了幾張卡牌遊戲的圖片放進去了,當然了我們也可以從網上來獲取圖片
// The filters.
private Filter[] mImageDetectionFilters;
private int[] mImgs = {R.mipmap.a, R.mipmap.b, R.mipmap.c, R.mipmap.d, R.mipmap.e};
@Override
public void onCameraViewStarted(int width, int height) {
int i = 0;
mImageDetectionFilters = new Filter[mImgs.length];
for (int mImg : mImgs) {
Filter starryNight = null;
try {
starryNight = new ImageDetectionFilter(this, mImg);
} catch (IOException e) {
e.printStackTrace();
}
mImageDetectionFilters[i] = starryNight;
i++;
}
}
接著在onCameraFrame中進行影象對比
private long lastTime;
/**
* describe 管理髮送訊息的執行緒池
*/
private ExecutorService mThreadPool = Executors.newCachedThreadPool();
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
final Mat rgba = inputFrame.rgba();
long time = System.currentTimeMillis() / 1000;
if (time > lastTime) {
if (mImageDetectionFilters != null) {
for (int i = 0; i < mImageDetectionFilters.length; i++) {
final int y = i;
mThreadPool.execute(new Runnable() {
@Override
public void run() {
if (mImageDetectionFilters[y].match(rgba, rgba)) {
h.sendEmptyMessage(y);
}
}
});
}
}
}
lastTime = time;
return rgba;
}
因為圖片識別需要大量的計算,十分消耗記憶體,不注意的話很容易造成卡頓甚至是程式崩潰,所以在這裡我讓他一秒進行一次匹配,而且具體的計算過程是利用執行緒池進行,如果匹配成功的話,則利用Handler傳送訊息
boolean isFinish;
private Handler h = new Handler() {
@Override
public void handleMessage(Message msg) {
if (!isFinish) {
int i = msg.what;
Intent intent = new Intent();
intent.putExtra("data", i);
setResult(RESULT_OK, intent);
isFinish = true;
finish();
}
super.handleMessage(msg);
}
};
經過測試,識別的準確率應該在70%~80%左右。但是對於它使用的具體演算法還是沒有太搞懂,需要進一步學習
Demo只能識別在資源目錄下的那幾張圖片,執行Demo後攝像頭裡要有那幾張圖片才會發生作用
點選下載Demo