폴킴 (Paul Kim) -你都記得 You Remember

曾經有那樣深刻的感情,因為家裡反對和當時自己的懦弱 在那之後幾乎看到廟宇就許下:願把自己下半輩子的幸福都給你,只願你幸福喜樂 所以看到這段歌詞非常有感,年紀即將過40,所以真的得若無其事地活下去  네가 아주 행복했음 좋겠어 要是你過得幸福就好了 대신 내가 불행하면 좋겠어 要是換成我變得不幸就好了 기억나지 않았으면 좋겠어 如果什麼都不記得就好了 다시 돌아갈 수 있음 좋겠어 如果能夠再次回頭就好了 너의 찰나와 영원들이 願你所有的剎那和永恆 너만의 것이 되길 都變成專屬於你自己的時刻 사실 난 행복을 잘 몰라 但其實我真的不懂所謂的幸福是什麼 너는 아무렇지 않게 살아가야 하니까 因為你得若無其事地活下去

臉部偵測經典之作!! 「Robust Real-Time Face Detection」...未完

出處:
http://cg2010studio.wordpress.com/2011/04/26/opencv-人臉偵測-face-detection/

最先看到有人臉偵測的設備是什麼呢?當然是「數位相機」!!

只要real-time找到人臉,接著加以追蹤,直到按下快門前,

焦點都會在這個人臉上頭,如此一來就不太會發生失焦的狀況。

這篇 「Robust Real-Time Face Detection」,被引用7000多次了!!

這是一篇2004年在IJCV上發表的論文,裡頭有

Haar-FeaturesDetection FrameworkIntegral Image

Feature SelectionLearning the ClassifierAdaBoost

Detection ProcedureCascade Method

Face Detection基本作法就是先做Feature Extraction,接著做Cascade Detection。

特徵擷取(Feature Extraction)沒有問題,那麼瀑布偵測(Cascade Detection)是什麼呢?

其實就只是一連串的檢查動作,通過第一關才能進入第二關,

通過第二關才能進入地三關……直到通過最後一關,才會將此特徵辨識為人臉。

Cascade Architecture
Cascade Architecture
paper所使用的特徵型有四種,在偵測階段時,因為大部分的window沒有包含人臉,

於是提早拒絕負面的window,來加快偵測速度。

又因為我們人臉不一定都是「正」的,有時候會坐著、躺著、手會支撐著臉、

情人依偎等等,臉可能會有高達45度角的狀況,

此時檢測視窗(如下圖的A、B、C、D特徵型態) 

就會改變角度來掃描搜索視窗(如下圖的正方形)。

Feature Types
Feature types used by Viola and Jones


偵測人臉是個非常實用的技術,在照相、攝影、監視器等各種追蹤人的設備上都相當需要,能想到這idea的作者真是厲害呢!

下面是用OpenCV的程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
    Theme: Face Detection
    Compiler: Dev C++ 4.9.9.2
    Date: 100/04/26
    Author: ShengWen
*/
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include <iostream>
using namespace std;
// the minimum object size
int min_face_height = 50;
int min_face_width = 50;
int main( int argc , char ** argv )
{
    string image_name="../image/XD2.jpg";
    // Load image

    IplImage* image_detect=cvLoadImage(image_name.c_str(), 1);
    string cascade_name=
"../haarcascades/haarcascade_frontalface_default.xml";
    // Load cascade
    CvHaarClassifierCascade* classifier=
(CvHaarClassifierCascade*)cvLoad(cascade_name.c_str(), 0, 0, 0);
    if(!classifier)
{
        cerr<<"ERROR: Could not load classifier cascade."<<endl;
        return -1;
    }

    CvMemStorage* facesMemStorage=cvCreateMemStorage(0);

    IplImage* tempFrame=cvCreateImage
(cvSize(image_detect->width, image_detect->height), IPL_DEPTH_8U, image_detect->nChannels);
    
if(image_detect->origin==IPL_ORIGIN_TL)
{
        cvCopy(image_detect, tempFrame, 0);    
}
    else
{
        cvFlip(image_detect, tempFrame, 0);  
  }
    cvClearMemStorage(facesMemStorage);
CvSeq* faces=cvHaarDetectObjects
(tempFrame, classifier, facesMemStorage, 1.1, 3
, CV_HAAR_DO_CANNY_PRUNING, cvSize
(min_face_width, min_face_height));
    if(faces){
        for(int i=0; i<faces->total; ++i){
            // Setup two points that define the extremes of the rectangle,
            // then draw it to the image
            CvPoint point1, point2;
            CvRect* rectangle = (CvRect*)cvGetSeqElem(faces, i);
            point1.x = rectangle->x;
            point2.x = rectangle->x + rectangle->width;
            point1.y = rectangle->y;
            point2.y = rectangle->y + rectangle->height;
            cvRectangle(tempFrame, point1, point2, CV_RGB(255,0,0), 3, 8, 0);
        }
    }
    // Save the image to a file
    cvSaveImage("../result/result.jpg", tempFrame);
    // Show the result in the window
    cvNamedWindow("Face Detection Result", 1);
    cvShowImage("Face Detection Result", tempFrame);
    cvWaitKey(0);
    cvDestroyWindow("Face Detection Result");
    // Clean up allocated OpenCV objects
    cvReleaseMemStorage(&facesMemStorage);
    cvReleaseImage(&tempFrame);
    cvReleaseHaarClassifierCascade(&classifier);
    cvReleaseImage(&image_detect);
    return EXIT_SUCCESS;
}





cvHaarDetectObjects(const CvArr* image, CvHaarClassifierCascade* cascade,
CvMemStorage* storage, double scale_factor=1.1,
int min_neighbors=3, int flags=0,
CvSize min_size=cvSize(0,0) );
說明:
  • image: 要偵測的圖片。
  • cascade: 要使用的分類器。
  • storage: 偵測到的物件所儲存的記憶體區塊。
  • scale_factor: 搜索視窗成長比率。
  • min_neighbors: 最少鄰近偵測視窗。一個臉可能重複偵測好幾次,但我們只要取一次,如果設0的話,所有偵測的視窗都會畫出來。
  • flag: 演算法模式。
  • min_size檢測視窗的最小尺寸。因為AdaBoost的演算法,分成搜索視窗檢測視窗兩個部分,搜索視窗在整個影像中移動,檢測視窗搜索視窗中移動並計算特徵值。當檢測視窗越小,則計算特徵值的單位就越小,需要的運算量就越高,但是結果不一定會更為精確。
結論:Haar Features的效果非常好,然而在影像長寬不一樣(如上)時,會有不一樣的偵測結果,我想這跟我設定的偵測框架大小有關係,像素多的影像可以擷取較細節的特徵,而像素少的則相反,看來還是得根據需要來調整影像大小偵測框架大小。因為影像有縮小,但依然可以看到,左邊也就是像素大的影像紅框比較細,而右邊則比較粗。
我將參數調整恰到好處:
  • int min_face_height = 50;
  • int min_face_width = 50;
  • CvSeq* faces=cvHaarDetectObjects(tempFrame, classifier, facesMemStorage, 1.1, 3, CV_HAAR_DO_CANNY_PRUNING, cvSize(min_face_width, min_face_height));
在此需要特別注意的地方是路徑,接著就是classifier的選擇,我所使用的是OpenCV 2.0,訓練好的分類器放在C:\OpenCV2.0\data\haarcascades,這次人臉偵測實驗使用haarcascade_frontalface_default.xml,效果比較好的則是haarcascade_frontalface_alt.xml。其它的還有可以偵測眼睛、嘴巴、鼻子、上半身、下半身等等,但並不是所有分類器都能運作的相當好。這次實做相當好玩,有興趣的人趕快來玩玩看吧~

留言

ALIN寫道…
您好,請問cascade detection的目的是為了要做到real time嗎?

這個網誌中的熱門文章

C# 裡 List用法

"需要有物件參考才能使用非靜態欄位、方法或屬性"的問題排除

達因筆 & 表面能 原理