【Android】【opencv】實現攝像頭拍照和錄影

weixin_34405925發表於2018-08-01

後面可以疊加物體檢測,參考:https://github.com/kongqw/OpenCVForAndroid

1.0 需求

在安卓開發板上實現視訊監控功能,並能後臺監控。
由於後期可能跑視訊識別,所以考慮用OpenCV實現:通過OpenCV Manager進行動態庫的連結,實現幀的預覽和儲存。
android版本:5.0.1
開發平臺:Android Studio 3.1.2
OpenCV版本:opencv4android 3.2.0
OpenCV Manager:OpenCV_3.2.0_Manager_3.20_armeabi.apk

2.0 步驟

2.1 下載opencv-3.2.0-android-sdk.zip

可以直接去官網下載。如果下載很慢,可以切換源試試
解壓後,會看到OpenCV-android-sdk目錄下有如下目錄:

8810995-6ca41067bd3688e4.png

2.2 開發板安裝對應的OpenCV Manage版本

OpenCV_3.2.0_Manager_3.20_armeabi.apk

2.3 新建工程VideoMonitor並匯入Opencv Module

具體步驟: File --> New --> Import Module選擇opencv-3.2.0-android-sdk下的java目錄

8810995-eff2cacfd9e0db48.png

選擇步驟2.1下載的opencv-3.2.0-android-sdk目錄下的如下目錄:


8810995-11ccb01caf2fb9c0.png

工程匯入完會看到project下有openCVLibrary320:

8810995-53b393380a5da554.png

2.4 新增工程依賴(opencv)

點選Project Structure:


8810995-14bf2fca476eadb9.png

點選Dependencies --> 點選+ --> 選擇module dependency

8810995-e0bab265ea6ed429.png

2.5 把opencv要用到的動態庫放到專案中

將下載解壓得到的檔案中的“sdk\native\libs\”複製一份到專案的“app\src\main\”並重新命名為jniLibs:

8810995-fb05cc066bcd459f.png

2.6 修改佈局檔案

佈局修改如下:

8810995-4644d4d1eb6fcda3.png

這裡的JavaCameraView就是OpenCV裡的預覽部件,然後我們再新增了一個拍照的按鍵。
注意:之前忘記新增opencv,導致R檔案生成失敗,MainActivity中R類就找不到了。

2.7 新增許可權

開啟AndroidManifest.xml,新增我們需要的許可權,攝像頭和內部儲存,這個也十分重要:

<uses-permission android:name="android.permission.CAMERA"/>  
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2.8 正式編寫MainActivity

具體程式碼如下:

package cn.com.suredata.videomonitor;

import android.content.pm.ActivityInfo;
import android.icu.text.SimpleDateFormat;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.io.File;
import java.util.Date;

public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {

    private String TAG = "OpenCV_Test";
    //OpenCV的相機介面
    private CameraBridgeViewBase mCVCamera;
    //快取相機每幀輸入的資料
    private Mat mRgba;
    private Button button;

    /**通過OpenCV管理Android服務,初始化OpenCV**/
    BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                    Log.i(TAG, "OpenCV loaded successfully");
                    mCVCamera.enableView();
                    break;
                default:break;
            }
        }
    };
    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            super.handleMessage(msg);
            if(msg.what == 1){
                button.performClick();
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //初始化並設定預覽部件
        mCVCamera = (CameraBridgeViewBase) findViewById(R.id.camera_view);
        mCVCamera.setCvCameraViewListener(this);
        
        //拍照按鍵
        button = (Button) findViewById(R.id.deal_btn);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mRgba != null) {
                    if(!mRgba.empty()) {
                        Mat inter = new Mat(mRgba.width(), mRgba.height(), CvType.CV_8UC4);
                        Log.e("Mat","...............1...............");
                        //將四通道的RGBA轉為三通道的BGR,重要!!
                        Imgproc.cvtColor(mRgba, inter, Imgproc.COLOR_RGBA2BGR);
                        Log.e("Mat","...............2...............");
                        File sdDir = null;
                        //判斷是否存在機身記憶體
                        boolean sdCardExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
                        if(sdCardExist) {
                            //獲得機身儲存根目錄
                            sdDir = Environment.getExternalStorageDirectory();
                            Log.e("Mat","...............3...............");
                        }
                        //將拍攝準確時間作為檔名
                        java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
                        String filename = sdf.format(new Date());
                        String savepath=sdDir + "/Pictures/OpenCV/";
                        File f=new File(savepath);
                        if(!f.exists()){
                            f.mkdirs();
                        }
                        String filePath = sdDir + "/Pictures/OpenCV/" + filename + ".png";
                        Log.e("Mat","..............."+filePath+"...............");
                        //將轉化後的BGR矩陣內容寫入到檔案中
                        Imgcodecs.imwrite(filePath, inter);
                        Toast.makeText(MainActivity.this, "圖片儲存到: "+ filePath, Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });

       // moveTaskToBack(true);
//        new test().start();
    }
    private class test extends Thread{
        @Override
        public void run() {
            super.run();
            while (true){
                try {
                    Thread.sleep(2000);
                    Log.e("begin","kaishi.........");

                    Message message = new Message();
                    message.what = 1;
                    handler.sendMessage(message);
                }catch (Exception e){
                    Log.e("error",e.getMessage());
                }
            }
        }
    }
    @Override
    protected void onResume() {
        /***強制橫屏***/
        if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        } else {
            //橫屏後才載入部件,否則會FC
            if(!OpenCVLoader.initDebug()) {
                Log.d(TAG, "OpenCV library not found!");
            } else {
                Log.d(TAG, "OpenCV library found inside package. Using it!");
                mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
            }
        }
        super.onResume();
    }

    @Override
    protected void onDestroy() {
        if(mCVCamera != null) {
            mCVCamera.disableView();
        }
        super.onDestroy();
    }

    //物件例項化及基本屬性的設定,包括長度、寬度和影象型別標誌
    public void onCameraViewStarted(int width, int height) {
        Log.e("Mat","...............4...............");
        mRgba = new Mat(height, width, CvType.CV_8UC4);
    }

    /**影象處理都寫在這裡!!!**/
    @Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        mRgba = inputFrame.rgba();  //一定要有!!!不然資料儲存不進MAT中!!!
        //直接返回輸入視訊預覽圖的RGB資料並存放在Mat資料中
        Log.e("Mat","...............5...............");
        return mRgba;
    }

    //結束時釋放
    @Override
    public void onCameraViewStopped() {
        Log.e("Mat","...............6...............");
        mRgba.release();
       // mTmp.release();
    }
}

相關文章