基于opencv實(shí)現(xiàn)人臉檢測(cè)

原文地址:

https://blog.csdn.net/qq_34902522/article/details/82464516


基于opencv實(shí)現(xiàn)人臉檢測(cè)

opencv簡(jiǎn)述

opencv是一個(gè)開源的計(jì)算機(jī)視覺庫(kù),它有著C++,Python,Java等接口,支持Windows,Linux,Mac OS,IOS 和 Android平臺(tái).Opencv 是使用C/C++所寫的,可以利用多核處理.通過OpenCL啟用,它可以利用底層異構(gòu)計(jì)算平臺(tái)的硬件加速。關(guān)于Opencv的詳細(xì)介紹可以去其官網(wǎng)查看.Opencv 官網(wǎng)

注意

1.如果對(duì)opencv還沒有接觸過的,可以先參考參考這篇文章,了解如何在Android項(xiàng)目接入opencv.android 接入opencv的3種方式 .建議結(jié)合Opencv 的Tutorials看,效果更加.
2.這邊接入使用的opencv library是利用github上面opencv和opencv-contrib庫(kù)里的源碼來編譯的適用于Android平臺(tái)的庫(kù).這個(gè)庫(kù)的地址在opencv+opencv-contrib-lib4Android 編的這個(gè)庫(kù)是3.4.2版本,Android的各個(gè)平臺(tái)都有.還是很全的.
如果想自己編譯的話,可以參考我之前的文章編譯opencv+opencv-contrib 遇到的坑 .
當(dāng)然你也可以直接從opencv的官網(wǎng)下載人家已經(jīng)編譯好的庫(kù).但是從其官網(wǎng)下載的庫(kù)內(nèi)容不全,比如Tracker這一塊的內(nèi)容,就沒有.(PS:后面會(huì)更一篇利用OpenCV實(shí)現(xiàn)物體追蹤功能的文章。就是需要tracker)這是在opencv-contrib庫(kù)里的.
那什么是opencv-contrib庫(kù)?opencv-contrib庫(kù)是一個(gè)額外庫(kù),里面包含的是一些較新,高級(jí)些的功能模塊.

目標(biāo)

利用Opencv的分類器CascadeClassifier,對(duì)從Camera讀取的yuv數(shù)據(jù)進(jìn)行實(shí)時(shí)檢測(cè),并且把結(jié)果顯示在屏幕上.

CascadeClassifier介紹#####

CascadeClassifier不僅僅可以檢測(cè)人臉,也可以檢測(cè)眼睛,身體,嘴巴等.通過加載一個(gè)想要檢測(cè)的.xml的分類器文件就可以.

開擼####

要實(shí)現(xiàn)在相機(jī)預(yù)覽畫面的實(shí)時(shí)人臉檢測(cè),那么關(guān)于Android Camera的部分肯定要先弄起來.這邊為了方便演示,簡(jiǎn)單封裝了一個(gè)CameraModule庫(kù).來實(shí)現(xiàn)相機(jī)預(yù)覽,Camera 數(shù)據(jù)回調(diào).

            CameraApi.getInstance().setCameraId(CameraApi.CAMERA_INDEX_BACK);
            CameraApi.getInstance().initCamera(this, this);
            CameraApi.getInstance().setPreviewSize(new Size(previewWidth, previewHeight));
            CameraApi.getInstance().setFps(30).configCamera();
            CameraApi.getInstance().startPreview(holder);

在成功獲取Camera 的preview數(shù)據(jù)之后,我們開始處理opencv部分的邏輯.
1.首先我們需要?jiǎng)?chuàng)建CascadeClassifier.
在Activity的onResume回調(diào)中,先加載Opencv的library.

@Override
    protected 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_4_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

在加載成功的回調(diào)中,創(chuàng)建需要的屬于opencv的對(duì)象.代碼如下:

private LoaderCallbackInterface mLoaderCallback = new LoaderCallbackInterface() {
        @Override
        public void onManagerConnected(int status) {
            if (status == LoaderCallbackInterface.SUCCESS) {
                init();
                isLoadSuccess = true;
                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();

                    mFaceCascade = new CascadeClassifier(mCascadeFile.getAbsolutePath());
                    if (mFaceCascade.empty()) {
                        Log.e(TAG, "Failed to load cascade classifier");
                        mFaceCascade = null;
                    } else {
                        Log.e(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());
                    }


                    cascadeDir.delete();

                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
                }


            }
        }

        @Override
        public void onPackageInstall(int operation, InstallCallbackInterface callback) {

        }
    };
private void init() {
        mSrcMat = new Mat(previewHeight, previewWidth, CvType.CV_8UC1);
        mDesMat = new Mat(previewHeight, previewWidth, CvType.CV_8UC1);
        matOfRect = new MatOfRect();
        initQueue();
    }

我們分析一下上面的代碼,首先我們創(chuàng)建了一些必須的對(duì)象,比如Mat對(duì)象.
Mat - The Basic Image Container 在opencv里,對(duì)圖像的處理,大都是先把圖像數(shù)據(jù)轉(zhuǎn)化成Mat對(duì)象,Mat對(duì)象就像是一個(gè)容器,對(duì)圖像的處理就是對(duì)Mat的處理.
然后,我們從raw文件夾里讀取了opencv訓(xùn)練好的,用于檢測(cè)人臉的分類器文件lbpcascade_frontalface.xml.xml分類器文件,可以從opencv下載的包里面找到.你會(huì)發(fā)現(xiàn),里面有兩種類型的分類器文件,一種是haar的,一種是lbp的.關(guān)于這兩種的不同.可以參考haar-vs-lbp .
這里我們選擇的是lbp的.分類器文件獲取到后,我們通過分類器文件,創(chuàng)建了我們想要的CascadeClassifier.

2.對(duì)相機(jī)預(yù)覽數(shù)據(jù)的處理
這邊設(shè)置的預(yù)覽的FPS是30,因?yàn)槿四槞z測(cè)是耗時(shí)操作,為了不影響預(yù)覽的畫面流暢度.這邊采用了,子線程+隊(duì)列的處理方式.通過創(chuàng)建兩個(gè)隊(duì)列,來保證對(duì)相機(jī)數(shù)據(jù)的管理.

@Override
    public void onPreviewFrameCallback(byte[] data, Camera camera) {
        mCamera.addCallbackBuffer(data);
        if (isStart) {
            CameraRawData rawData = mFreeQueue.poll();
            if (rawData != null) {
                rawData.setRawData(data);
                rawData.setTimestamp(System.currentTimeMillis());
                mFrameQueue.offer(rawData);
            }
        }

    }

3.從隊(duì)列獲取數(shù)據(jù),進(jìn)行檢測(cè)

/**
     * face detect thread
     */
    private class DetectThread extends Thread {
        DetectThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            while (isStart && isLoadSuccess) {
                synchronized (mLock) {
                    try {
                        mCameraRawData = mFrameQueue.poll(20, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    if (mCameraRawData == null) {
                        continue;
                    }
                    frameDatas = mCameraRawData.getRawData();
                    mSrcMat.put(0, 0, frameDatas);
                    Imgproc.cvtColor(mSrcMat, mDesMat, Imgproc.COLOR_YUV2GRAY_420);
                    mFaceCascade.detectMultiScale(mDesMat, matOfRect, 1.1, 5
                            , 2, mMinSize, mMaxSize);
                    if (matOfRect.toArray().length != 0) {
                        Rect rect = getBiggestFace(matOfRect.toArray());
                        mResultView.showFace(rect);
                    } else {
                        mResultView.clear();
                    }
                    mFreeQueue.offer(mCameraRawData);
                    mCamera.addCallbackBuffer(frameDatas);
                }

            }
        }
    }

如上面的代碼所示,我們通過創(chuàng)建子線程,在里面進(jìn)行face detect處理.
首先我們從隊(duì)列中獲取Camera data.接著為mSrcMat對(duì)象賦值.接著利用Opencv 的convert方法,對(duì)源數(shù)據(jù)進(jìn)行灰度化處理.

 Imgproc.cvtColor(mSrcMat, mDesMat, Imgproc.COLOR_YUV2GRAY_420);

這里主要看下第三個(gè)參數(shù).因?yàn)槲疫@邊設(shè)置的image format 是NV21 ,所用了這個(gè)YUV420 to Gray的flag.我們進(jìn)去可以看到:


這里寫圖片描述

我們發(fā)現(xiàn),只要是YUV420的無論是P還是SP都是用的同一個(gè)Flag.還挺省事,省的格式轉(zhuǎn)化了O(∩_∩)O.

我們接著看這行代碼:

mFaceCascade.detectMultiScale(mDesMat, matOfRect, 1.1, 5
                            , 2, mMinSize, mMaxSize);

這就是檢測(cè)的核心代碼,這里說一下各個(gè)參數(shù)所代表的含義.
Parameters
image Matrix of the type CV_8U containing an image where objects are detected.
objects Vector of rectangles where each rectangle contains the detected object, the rectangles may be partially outside the original image.
scaleFactor Parameter specifying how much the image size is reduced at each image scale.
minNeighbors Parameter specifying how many neighbors each candidate rectangle should have to retain it.
flags Parameter with the same meaning for an old cascade as in the function cvHaarDetectObjects. It is not used for a new cascade.
minSize Minimum possible object size. Objects smaller than that are ignored.
maxSize Maximum possible object size. Objects larger than that are ignored. If maxSize == minSize model is evaluated on single scale.

參數(shù)的含義,來自官網(wǎng),懶得翻譯。

這里寫圖片描述

這里主要說下minNeighbors,minSize,maxSize.這三個(gè)參數(shù).
通過這三個(gè)參數(shù)可以控制檢測(cè)的精確度.
minNeighbors 值越大,檢測(cè)的準(zhǔn)確度越高,不過耗時(shí)也越久.酌情調(diào)整.
minSize 可以根據(jù)Screen 尺寸的一定比例來設(shè)置,別設(shè)置太小,不然會(huì)有一些錯(cuò)誤干擾結(jié)果.
maxSize 最大可檢測(cè)尺寸,酌情調(diào)整.

接著往下看,檢測(cè)結(jié)果出來后,是一個(gè)rect集合,檢測(cè)到的每一張臉是一個(gè)矩形....O__O "…
這邊做的處理選擇了一張臉最大的來顯示.

這里寫圖片描述

現(xiàn)在我們把代碼跑起來,看一下效果:

效果圖

通過效果gif演示,我們可以很明顯的看到,成功的檢測(cè)到了人臉。這里要說個(gè)缺點(diǎn),就是使用OpenCV的CascadeClassifier進(jìn)行人臉檢測(cè),檢測(cè)的結(jié)果是返回一個(gè)MatOfRect對(duì)象,可理解為rectangle集合,意思是只記錄著檢測(cè)到臉的位置。并沒有其他的信息了,并不能記住識(shí)別人臉。注意人臉識(shí)別人臉檢測(cè)的區(qū)別。關(guān)于OpenCV的人臉識(shí)別(FaceRecognizer)需要對(duì)目標(biāo)先進(jìn)行數(shù)據(jù)訓(xùn)練,訓(xùn)練好才能對(duì)目標(biāo)成功識(shí)別。關(guān)于OpenCV人臉識(shí)別的內(nèi)容,這邊先不說了,之后的文章再討論。

結(jié)語(yǔ)

關(guān)于使用OpenCV來進(jìn)行人臉檢測(cè),就說到這,后期想到什么要補(bǔ)充的會(huì)再補(bǔ)充。人臉檢測(cè)的實(shí)現(xiàn),也寫了一篇利用Firebase Vision ML Kit庫(kù)來實(shí)現(xiàn)的文章,建議大家去看看,做做對(duì)比,根據(jù)需要選擇合適的技術(shù)手段來實(shí)現(xiàn)。利用Google vision來實(shí)現(xiàn)人臉檢測(cè)
演示demo地址如下。
OpencvFaceDetect

最后編輯于
?著作權(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)容

  • 轉(zhuǎn)載請(qǐng)注明出處(http://www.itdecent.cn/p/5f538820e370),您的打賞是小編繼續(xù)...
    福later閱讀 29,719評(píng)論 8 73
  • 1 實(shí)驗(yàn)?zāi)康?目前計(jì)算機(jī)視覺技術(shù)已經(jīng)比較成熟,相關(guān)的開源項(xiàng)目與算法很多,可以將這些開源算法進(jìn)行整合,進(jìn)而做成一個(gè)小...
    YOUNG_FAN閱讀 7,068評(píng)論 0 50
  • 有一天晚上我夢(mèng)見了雨 它落在了屋檐上耗盡之前所有的喧囂像是一位匆匆過客卻余音繞梁 它落在了田野上成為廣袤中的無數(shù)鱗...
    不安分大叔閱讀 216評(píng)論 6 3
  • 在昨晚她進(jìn)屋洗澡,反鎖門的時(shí)候,我就意識(shí)到。她已經(jīng)把我拒之門外了。所有一切都證明,我還是在她心門之外。 我覺得再多...
    d3993c4b32f3閱讀 182評(píng)論 0 0
  • 一只鳥, 落在輸電線上, 四處張望, 塔桿下的我, 不知所措。
    野_方閱讀 139評(píng)論 1 0

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