1. 程式人生 > >android中利用opencv進行影象識別

android中利用opencv進行影象識別

之前開發的時候老大讓研究下影象識別的功能,同事推薦看看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