安卓OpenCV開發(fā)(二)人臉檢測(cè)

重點(diǎn)是人臉檢測(cè),檢測(cè),檢測(cè)。

就是把人臉檢測(cè)出來(lái),不是識(shí)別,不是識(shí)別,不是識(shí)別。識(shí)別的意思,就是檢測(cè)到人臉,并且通過(guò)數(shù)據(jù)比對(duì),算法分析后得出人臉相識(shí)度的過(guò)程。而檢測(cè),僅僅是檢測(cè)出來(lái)。

針對(duì)全網(wǎng)關(guān)于安卓OpenCV識(shí)別XXX之類的標(biāo)題,而實(shí)際只做了檢測(cè)的相關(guān)文章,在此表示呵呵

回到正題

如何預(yù)覽視頻并進(jìn)行人臉檢測(cè)?

(一)預(yù)覽視頻

可直接使用OpenCV庫(kù)中的JavaCameraView控件,進(jìn)行視頻的預(yù)覽。

1、布局中聲明該對(duì)象:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <org.opencv.android.JavaCameraView
        android:id="@+id/activity_main_camera_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

2、在布局中,直接調(diào)用該對(duì)象的enableView()方法,即可預(yù)覽。

備注:權(quán)限申請(qǐng)之類的老生常談,這里不再啰嗦。

(二)構(gòu)建檢測(cè)分類器

OpenCV中,CascadeClassifier是用于分類器進(jìn)行數(shù)據(jù)處理的。首先,構(gòu)建一個(gè)分類器,需要數(shù)據(jù)源,而人臉檢測(cè)的分類器數(shù)據(jù)源,OpenCV官方已經(jīng)有一個(gè)可以直接用了,這里可以直接拿過(guò)來(lái)用。而文件的路徑,就在下載的資源文件中的OpenCV-android-sdk\sdk\etc\lbpcascades目錄下,關(guān)于構(gòu)建項(xiàng)目的流程,不懂可看我上一篇文章
OpenCV導(dǎo)入

把分類器數(shù)據(jù)復(fù)制到主項(xiàng)目的res-raw目錄下,沒(méi)有該目錄就新建,復(fù)制后如下圖:


raw文件目錄

然后,在應(yīng)用檢測(cè)前,進(jìn)行分類器數(shù)據(jù)復(fù)制到本地,并初始化分類器,代碼如下:

try {
            InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
            File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
            File 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();
            cascadeClassifier = new CascadeClassifier(mCascadeFile.getAbsolutePath());
        } catch (Exception e) {
            Log.e("OpenCVActivity", "Error loading cascade", e);
        }

這樣就完成了分類器的構(gòu)建了。

(三)監(jiān)聽視頻數(shù)據(jù)

對(duì)JavaCameraView設(shè)置CameraBridgeViewBase.CvCameraViewListener即可對(duì)視頻數(shù)據(jù)進(jìn)行監(jiān)聽

(四)人臉檢測(cè),基于第三部,在監(jiān)聽回調(diào)方法onCameraFragment()中,對(duì)回調(diào)的視頻數(shù)據(jù)進(jìn)行監(jiān)聽,實(shí)現(xiàn)代碼如下:

   @Override
    public Mat onCameraFrame(Mat aInputFrame) {
        Imgproc.cvtColor(aInputFrame, grayscaleImage, Imgproc.COLOR_RGBA2RGB);
        MatOfRect faces = new MatOfRect();
        if (cascadeClassifier != null) {
            cascadeClassifier.detectMultiScale(grayscaleImage, faces, 1.1, 3, 2,
                    new Size(absoluteFaceSize, absoluteFaceSize), new Size());
        }
        Rect[] facesArray = faces.toArray();
        for (int i = 0; i <facesArray.length; i++){
            Imgproc.rectangle(aInputFrame, facesArray[i].tl(), facesArray[i].br(), new Scalar(0, 255, 0, 255), 3);
        }
        return aInputFrame;
    }

首先,這里對(duì)傳入的圖片幀,進(jìn)行了一個(gè)色值轉(zhuǎn)換的才做,然后再通過(guò)分類器的detectMultiScale方法,進(jìn)行人臉檢測(cè),最后通過(guò)Imgproc.rectangle()方法,進(jìn)行繪制人臉。

對(duì)于detectMultiScale方法的傳入?yún)?shù),析意如下:

1.image表示的是要檢測(cè)的輸入圖像
2.objects表示檢測(cè)到的人臉目標(biāo)序列
3.scaleFactor表示每次圖像尺寸減小的比例
4.minNeighbors表示每一個(gè)目標(biāo)至少要被檢測(cè)到3次才算是真的目標(biāo)(因?yàn)橹車南袼睾筒煌拇翱诖笮《伎梢詸z測(cè)到人臉)
5.minSize為目標(biāo)的最小尺寸
6.minSize為目標(biāo)的最大尺寸

最后附上完整代碼:

package com.north.light.libopencv;

import android.Manifest;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;

import androidx.appcompat.app.AppCompatActivity;


import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener{

    private CameraBridgeViewBase openCvCameraView;
    private CascadeClassifier cascadeClassifier;
    private Mat grayscaleImage;
    private int absoluteFaceSize;
    private void initializeOpenCVDependencies() {
        try {
            InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
            File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
            File 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();
            cascadeClassifier = new CascadeClassifier(mCascadeFile.getAbsolutePath());
        } catch (Exception e) {
            Log.e("OpenCVActivity", "Error loading cascade", e);
        }
        openCvCameraView.enableView();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);   // 為該活動(dòng)設(shè)置布局
        openCvCameraView = findViewById(R.id.activity_main_camera_view);
        openCvCameraView.setCvCameraViewListener(this);

    }
    @Override
    public void onCameraViewStarted(int width, int height) {
        grayscaleImage = new Mat(height, width, CvType.CV_8UC4);
        absoluteFaceSize = (int) (height * 0.2);
    }

    @Override
    public void onCameraViewStopped() {
    }

    @Override
    public Mat onCameraFrame(Mat aInputFrame) {
        Imgproc.cvtColor(aInputFrame, grayscaleImage, Imgproc.COLOR_RGBA2RGB);
        MatOfRect faces = new MatOfRect();
        if (cascadeClassifier != null) {
            cascadeClassifier.detectMultiScale(grayscaleImage, faces, 1.1, 3, 2,
                    new Size(absoluteFaceSize, absoluteFaceSize), new Size());
        }
        Rect[] facesArray = faces.toArray();
        for (int i = 0; i <facesArray.length; i++){
            Imgproc.rectangle(aInputFrame, facesArray[i].tl(), facesArray[i].br(), new Scalar(0, 255, 0, 255), 3);
        }
        return aInputFrame;
    }
    @Override
    public void onResume() {
        super.onResume();

        if (!OpenCVLoader.initDebug()) {
        }
        initializeOpenCVDependencies();
        final String[] permissions = {
                Manifest.permission.CAMERA
        };
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(permissions,0);
        }
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        Log.d("返回鍵","back back back");
    }
}

至此,人臉檢測(cè)已經(jīng)完成,不過(guò)目前檢測(cè)人臉的預(yù)覽顯示,還是橫屏了,官方的demo也是橫屏顯示的,而對(duì)于豎屏顯示,請(qǐng)看我下一篇文章。
that's all---------------------------------------------------------------------------------

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容