基于opencv實現(xiàn)人臉檢測

原文地址:

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


基于opencv實現(xiàn)人臉檢測

opencv簡述

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

注意

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

目標(biāo)

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

CascadeClassifier介紹#####

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

開擼####

要實現(xiàn)在相機預(yù)覽畫面的實時人臉檢測,那么關(guān)于Android Camera的部分肯定要先弄起來.這邊為了方便演示,簡單封裝了一個CameraModule庫.來實現(xiàn)相機預(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.首先我們需要創(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的對象.代碼如下:

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

2.對相機預(yù)覽數(shù)據(jù)的處理
這邊設(shè)置的預(yù)覽的FPS是30,因為人臉檢測是耗時操作,為了不影響預(yù)覽的畫面流暢度.這邊采用了,子線程+隊列的處理方式.通過創(chuàng)建兩個隊列,來保證對相機數(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.從隊列獲取數(shù)據(jù),進行檢測

/**
     * 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)建子線程,在里面進行face detect處理.
首先我們從隊列中獲取Camera data.接著為mSrcMat對象賦值.接著利用Opencv 的convert方法,對源數(shù)據(jù)進行灰度化處理.

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

這里主要看下第三個參數(shù).因為我這邊設(shè)置的image format 是NV21 ,所用了這個YUV420 to Gray的flag.我們進去可以看到:


這里寫圖片描述

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

我們接著看這行代碼:

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

這就是檢測的核心代碼,這里說一下各個參數(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.這三個參數(shù).
通過這三個參數(shù)可以控制檢測的精確度.
minNeighbors 值越大,檢測的準(zhǔn)確度越高,不過耗時也越久.酌情調(diào)整.
minSize 可以根據(jù)Screen 尺寸的一定比例來設(shè)置,別設(shè)置太小,不然會有一些錯誤干擾結(jié)果.
maxSize 最大可檢測尺寸,酌情調(diào)整.

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

這里寫圖片描述

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

效果圖

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

結(jié)語

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市趋厉,隨后出現(xiàn)的幾起案子寨闹,更是在濱河造成了極大的恐慌,老刑警劉巖君账,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件繁堡,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機椭蹄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門闻牡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來只盹,“玉大人宰啦,你說我怎么就攤上這事褐着∷腋眨” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵奈搜,是天一觀的道長接癌。 經(jīng)常有香客問我本讥,道長应媚,這世上最難降的妖魔是什么严沥? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮中姜,結(jié)果婚禮上消玄,老公的妹妹穿的比我還像新娘。我一直安慰自己扎筒,他們只是感情好莱找,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布酬姆。 她就那樣靜靜地躺著嗜桌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辞色。 梳的紋絲不亂的頭發(fā)上骨宠,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音相满,去河邊找鬼层亿。 笑死,一個胖子當(dāng)著我的面吹牛立美,可吹牛的內(nèi)容都是我干的匿又。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼建蹄,長吁一口氣:“原來是場噩夢啊……” “哼碌更!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起洞慎,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤痛单,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后劲腿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旭绒,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了挥吵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片重父。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖忽匈,靈堂內(nèi)的尸體忽然破棺而出坪郭,到底是詐尸還是另有隱情,我是刑警寧澤脉幢,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布歪沃,位于F島的核電站,受9級特大地震影響嫌松,放射性物質(zhì)發(fā)生泄漏沪曙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一萎羔、第九天 我趴在偏房一處隱蔽的房頂上張望液走。 院中可真熱鬧,春花似錦贾陷、人聲如沸缘眶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巷懈。三九已至,卻和暖如春慌洪,著一層夾襖步出監(jiān)牢的瞬間顶燕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工冈爹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涌攻,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓频伤,卻偏偏與公主長得像恳谎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子憋肖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載請注明出處(http://www.reibang.com/p/5f538820e370),您的打賞是小編繼續(xù)...
    福later閱讀 26,885評論 8 70
  • 1 實驗?zāi)康?目前計算機視覺技術(shù)已經(jīng)比較成熟因痛,相關(guān)的開源項目與算法很多,可以將這些開源算法進行整合瞬哼,進而做成一個小...
    YOUNG_FAN閱讀 6,677評論 0 50
  • 有一天晚上我夢見了雨 它落在了屋檐上耗盡之前所有的喧囂像是一位匆匆過客卻余音繞梁 它落在了田野上成為廣袤中的無數(shù)鱗...
    不安分大叔閱讀 189評論 6 3
  • 在昨晚她進屋洗澡婚肆,反鎖門的時候,我就意識到坐慰。她已經(jīng)把我拒之門外了较性。所有一切都證明用僧,我還是在她心門之外。 我覺得再多...
    d3993c4b32f3閱讀 157評論 0 0
  • 一只鳥赞咙, 落在輸電線上责循, 四處張望, 塔桿下的我攀操, 不知所措院仿。
    野_方閱讀 121評論 1 0