1. 程式人生 > >基於OpenCV的讀取攝像頭實現單個人臉驗證MFC程式

基於OpenCV的讀取攝像頭實現單個人臉驗證MFC程式

與上一篇部落格類似,這篇部落格介紹使用OpenCV實現的MFC程式,可以實現單個人臉的驗

證,並在影象和介面給出識別結果。效果圖如下:


置信度一欄可以填寫判定的閾值,預設為70。開啟攝像頭才能進行驗證或拍照,拍照之前可以清除之前拍攝的訓練圖片,可以拍攝多張用於識別。其中mfc中的影象顯示需要用到CvImage.cpp和CvImage.h兩個檔案,該程式碼在比較新的OpenCV內已經沒有了,所以可以直接用我程式碼裡的。

有人說程式碼的檢測率不高,其實可以歸結為兩方面的原因,第一人臉檢測率不高,這個可以通過巢狀檢測嘴角、眼睛等來降低,或者背景、光照固定的話可以通過影象差分來解決;第二是識別方法本身的問題,如果想提高識別率,可以新增多張不同姿態、光照下的人臉作為訓練的樣本,如果有時間的話可以在採集影象時給出一個人臉框,引導使用者對齊人臉進行採集,三星手機解除鎖屏就有這麼一個功能。

下面貼一下主要的程式碼:

VideoMFCDlg.cpp

// VideoMFCDlg.cpp : implementation file
//

#include "stdafx.h"
#include "VideoMFC.h"
#include "VideoMFCDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


CvCapture* capture;
CRect rect;
CDC *pDC;
HDC hDC;
CWnd *pwnd;
CvVideoWriter* writer = 0;
IplImage *resizeRes;//存放檢測到的人臉
IplImage* faceGray;//存放檢測到的人臉 灰度影象
bool bool_cameOpen = false;//全域性變數 標誌攝像頭是否開啟
bool bool_picNum = false;//全域性變數 標誌訓練圖片是否為空
bool bool_detec_reco = false;//全域性變數 
double dConfidence = 0;//置信度
int predictedLabel = 100000;

CvMemStorage* storage = 0;
CvHaarClassifierCascade* cascade = 0;
CvHaarClassifierCascade* nested_cascade = 0;
int use_nested_cascade = 0;
const char* cascade_name =
    "../data/haarcascades/haarcascade_frontalface_alt.xml";
const char* nested_cascade_name =
    "../data/haarcascade_eye_tree_eyeglasses.xml";
double scale = 1;
int num_components = 9;
double facethreshold = 9.0;
//cv::Ptr<cv::FaceRecognizer> model = cv::createFisherFaceRecognizer();
cv::Ptr<cv::FaceRecognizer> model = cv::createLBPHFaceRecognizer();//LBP的這個方法在單個人臉驗證方面效果最好
//cv::Ptr<cv::FaceRecognizer> model = cv::createEigenFaceRecognizer();
vector<Mat> images;
vector<int> labels;

IplImage *frame, *frame_copy = 0;
IplImage *image = 0;
const char* scale_opt = "--scale="; // 分類器選項指示符號 
int scale_opt_len = (int)strlen(scale_opt);
const char* cascade_opt = "--cascade=";
int cascade_opt_len = (int)strlen(cascade_opt);
const char* nested_cascade_opt = "--nested-cascade";
int nested_cascade_opt_len = (int)strlen(nested_cascade_opt);
int i;
const char* input_name = 0;

// CAboutDlg dialog used for App About
CString strConfidence = "70";
CEdit* pEdtConfidence;
CString strTip = "";
CEdit* pTip;

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// Dialog Data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CVideoMFCDlg dialog




CVideoMFCDlg::CVideoMFCDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CVideoMFCDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CVideoMFCDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CVideoMFCDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &CVideoMFCDlg::OnBnClickedButton1)
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_BUTTON2, &CVideoMFCDlg::OnBnClickedButton2)
	ON_WM_CLOSE()
	ON_EN_CHANGE(IDC_EdtConfidence, &CVideoMFCDlg::OnEnChangeEdtconfidence)
	ON_BN_CLICKED(IDC_Photograph, &CVideoMFCDlg::OnBnClickedPhotograph)
	ON_BN_CLICKED(IDC_Recognize, &CVideoMFCDlg::OnBnClickedRecognize)
	ON_BN_CLICKED(IDC_ClearPictures, &CVideoMFCDlg::OnBnClickedClearpictures)
END_MESSAGE_MAP()


// CVideoMFCDlg message handlers
BOOL CVideoMFCDlg::OnDestroy()
{
	cvReleaseImage( &resizeRes );
	cvReleaseImage( &faceGray );
	return TRUE; 
}
BOOL CVideoMFCDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	
	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here
	pwnd = GetDlgItem(IDC_ShowImage);
	//pwnd->MoveWindow(35,30,352,288);
    pDC =pwnd->GetDC();
	//pDC =GetDC();
    hDC= pDC->GetSafeHdc();
	pwnd->GetClientRect(&rect);

	GetDlgItem(IDC_BUTTON2)->EnableWindow(false);
	GetDlgItem(IDC_Photograph)->EnableWindow(false);
	GetDlgItem(IDC_Recognize)->EnableWindow(false);
	pEdtConfidence = (CEdit*) GetDlgItem(IDC_EdtConfidence);
	pTip = (CEdit*) GetDlgItem(IDC_Tip);
	pEdtConfidence->SetWindowText("70");
	pEdtConfidence->GetWindowText(strConfidence);
	pTip->SetWindowText( strTip );
	if(read_img_number()>0)
		bool_picNum = true;
	else
		bool_picNum = false;
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CVideoMFCDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CVideoMFCDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CVideoMFCDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


/*****************************************開啟攝像頭*******************************************/
void CVideoMFCDlg::OnBnClickedButton1()
{
	// TODO: Add your control notification handler code here
	//AfxMessageBox("OK");
	if(!capture)
	{
		capture = cvCaptureFromCAM(0);
		//AfxMessageBox("OK");
	}

	if (!capture)
	{
		AfxMessageBox("無法開啟攝像頭");
		return;
	}
	//writer=cvCreateVideoWriter("MyVideo.avi",CV_FOURCC('x','v','I','D'),25,cvSize(640,480));
	// 測試
	IplImage* m_Frame;
	m_Frame=cvQueryFrame(capture);
	CvvImage m_CvvImage;
	m_CvvImage.CopyOf(m_Frame,1);	
	if (true)
	{
		m_CvvImage.DrawToHDC(hDC, &rect);
		//cvWaitKey(10);
	}

	// 設定計時器,每10ms觸發一次事件
	SetTimer(1,10,NULL);

	
	cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 ); // 載入分類器 
    if( !cascade ) 
    {
        MessageBox("無法載入分類器檔案,請確認!");
    }
    storage = cvCreateMemStorage(0); // 建立記憶體儲存器 
    
    //if( !input_name || (isdigit(input_name[0]) && input_name[1] == '\0') ) // 判斷輸入引數是視訊序號,還是檔案 
        capture = cvCaptureFromCAM( !input_name ? 0 : input_name[0] - '0' ); // 建立視訊讀取結構 
	/*
    else if( input_name )
    {
        image = cvLoadImage( input_name, 1 ); // 如果是影象則載入 
        if( !image )
		{
            capture = cvCaptureFromAVI( input_name ); // 不是影象則嘗試視訊讀取 
			cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 640); 
			cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 480); ////////////////////////////////////////////////////////////////////
		}
    }*/
    //else
    //    image = cvLoadImage( "lena.bmp", 1 ); //都沒有則呼叫程式所在目錄的lena.jpg圖片 
    //cvNamedWindow( "result", 1 );
	
	GetDlgItem(IDC_BUTTON1)->EnableWindow(false);
	GetDlgItem(IDC_BUTTON2)->EnableWindow(true);
	GetDlgItem(IDC_Photograph)->EnableWindow(true);
	GetDlgItem(IDC_Recognize)->EnableWindow(true);
	bool_detec_reco = false;
	bool_cameOpen = true;
}

/********************************************設定定時器*********************************************/
void CVideoMFCDlg::OnTimer(UINT_PTR nIDEvent)
{
	//顯示攝像頭
	IplImage* m_Frame;
	m_Frame=cvQueryFrame(capture);
	//AllocConsole();
	//判斷是檢測還是識別人臉
	if(bool_cameOpen)
	{
		if(!bool_detec_reco)//false只為識別
		{
			detect_and_draw(m_Frame);//檢測人臉
			//_cprintf("%s\n", "jiance");
		}
		else if(bool_picNum)//false代表訓練圖片為空
			recog_and_draw(m_Frame);//檢測和識別人臉
	}
	CvvImage m_CvvImage;
	m_CvvImage.CopyOf(m_Frame,1);	
	if (true)
	{
		m_CvvImage.DrawToHDC(hDC, &rect);
		//cvWriteFrame(writer,m_Frame); //將幀影象通過writer寫入檔案
		//cvWaitKey(10);
	}
	if(bool_detec_reco)
	{
		if(predictedLabel <= dConfidence)
		{
			CString tipPhoto = strTip + "\r\n驗證成功!!";
			pTip->SetWindowText( tipPhoto );
		}
		else
		{
			CString tipPhoto = strTip + "\r\n驗證失敗!!";
			pTip->SetWindowText( tipPhoto );
		}
	}

	CDialogEx::OnTimer(nIDEvent);
}

//關閉攝像頭按鈕
void CVideoMFCDlg::OnBnClickedButton2()
{
	// TODO: Add your control notification handler code here
	cvReleaseVideoWriter(&writer);
	cvReleaseCapture(&capture);
	CDC MemDC; 
	CBitmap m_Bitmap1;
	m_Bitmap1.LoadBitmap(IDB_BITMAP1); 
	MemDC.CreateCompatibleDC(NULL);
	MemDC.SelectObject(&m_Bitmap1);
	pDC->StretchBlt(rect.left,rect.top,rect.Width(),rect.Height(),&MemDC,0,0,48,48,SRCCOPY); 
	GetDlgItem(IDC_BUTTON1)->EnableWindow(true);
	GetDlgItem(IDC_BUTTON2)->EnableWindow(false);
	GetDlgItem(IDC_Photograph)->EnableWindow(false);
	GetDlgItem(IDC_Recognize)->EnableWindow(false);
	bool_cameOpen = false;
}

//關閉窗體
void CVideoMFCDlg::OnClose()
{
	// TODO: Add your message handler code here and/or call default
	cvReleaseCapture(&capture);
	CDialogEx::OnClose();
}



void CVideoMFCDlg::OnEnChangeEdtconfidence()
{
}

//拍照按鈕
void CVideoMFCDlg::OnBnClickedPhotograph()
{
	// TODO: 在此新增控制元件通知處理程式程式碼
	if (!faceGray)
	{
		pTip->GetWindowText(strTip);
		CString tipPhoto = strTip + "\r\n拍照失敗,請將攝像頭對準人臉";
		pTip->SetWindowText( tipPhoto );
		return;
	}
	Mat img(faceGray,0);
	stringstream ss;
	ss << (read_img_number()+1);
	string faceImgName = "..//einfacedata//trainingdata//"+ss.str()+".jpg";
	imwrite(faceImgName,img);

	//pTip->GetWindowText(strTip);
	CString tipPhoto = strTip + "\r\n拍照成功!已存為" + ("/einfacedata/trainingdata/"+ss.str()+".jpg").c_str();
	pTip->SetWindowText( tipPhoto );
	//MessageBox("OK");
}

//開始驗證按鈕
void CVideoMFCDlg::OnBnClickedRecognize()
{
	// TODO: 在此新增控制元件通知處理程式程式碼
	images.clear();
	labels.clear();
	pEdtConfidence->GetWindowText(strConfidence);
	
	try
	{
		dConfidence = atoi((const char *)strConfidence);	
	}
	catch(cv::Exception &e)
	{
		MessageBox("置信度請輸入整數!");
		return;
	}
	
	model->set("threshold", dConfidence);
	//string output_folder;
	//output_folder = string("../einfacedata");

	//讀取你的CSV檔案路徑
	//string fn_csv = string("../einfacedata/at.txt");

	//兩個容器來存放影象資料和對應的標籤
	/*
	try
	{
		read_csv(fn_csv, images, labels);	
	}
	catch(cv::Exception &e)
	{
		cerr<<"Error opening file "<<fn_csv<<". Reason: "<<e.msg<<endl;
		exit(1);
	}
	*/
	if(!read_img(images, labels))
	{
		AfxMessageBox("Error in reading images!");
		//MessageBox("Error in reading images!");
		images.clear();
		labels.clear();
		return;
	}

	//如果沒有讀到足夠的圖片,就退出
	if(images.size() < 1)
	{
		MessageBox("This demo needs at least 1 images to work!");
		return;
	}
	//training
	model->train(images, labels);

	bool_detec_reco = true;
	bool_picNum = true;

}

//清除訓練圖片
void CVideoMFCDlg::OnBnClickedClearpictures()
{
	// TODO: 在此新增控制元件通知處理程式程式碼
	if(delete_img())
	{
		//pTip->GetWindowText(strTip);
		CString tipPhoto = strTip + "\r\n刪除成功!";
		pTip->SetWindowText( tipPhoto );
		bool_detec_reco = false;
		bool_picNum = false;
	}
	else
	{
		//pTip->GetWindowText(strTip);
		CString tipPhoto = strTip + "\r\n刪除失敗!";
		pTip->SetWindowText( tipPhoto );
	}
}

detect_recog.cpp和上一篇部落格類似

#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <time.h>
#include <ctype.h>
#include "detect_recog.h"
#include <opencv2\contrib\contrib.hpp>  
#include <opencv2\core\core.hpp>  
#include <opencv2\highgui\highgui.hpp> 
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdio.h>
#include <io.h>  
#include <direct.h> 
#include <sys/types.h>
#include <conio.h>
using namespace std;
using namespace cv;
void detect_and_draw( IplImage* img ) // 只是檢測,並圈出人臉
{
    static CvScalar colors[] = 
    {
        {{0,0,255}},
        {{0,128,255}},
        {{0,255,255}},
        {{0,255,0}},
        {{255,128,0}},
        {{255,255,0}},
        {{255,0,0}},
        {{255,0,255}}
    };
    IplImage *gray, *small_img;
    int i, j;
    gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );
    small_img = cvCreateImage( cvSize( cvRound (img->width/scale),
                         cvRound (img->height/scale)), 8, 1 );
    cvCvtColor( img, gray, CV_BGR2GRAY ); // 彩色RGB影象轉為灰度影象 
    cvResize( gray, small_img, CV_INTER_LINEAR );
    cvEqualizeHist( small_img, small_img ); // 直方圖均衡化 
    cvClearMemStorage( storage );
    if( cascade )
    {
        double t = (double)cvGetTickCount(); 
        CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,
                                            1.1, 2, 0
                                            //|CV_HAAR_FIND_BIGGEST_OBJECT
                                            |CV_HAAR_DO_ROUGH_SEARCH
                                            //|CV_HAAR_DO_CANNY_PRUNING
                                            //|CV_HAAR_SCALE_IMAGE
                                            ,
                                            cvSize(30, 30) );
        t = (double)cvGetTickCount() - t; // 統計檢測使用時間 
        //printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
        for( i = 0; i < (faces ? faces->total : 0); i++ )
        {
            CvRect* r = (CvRect*)cvGetSeqElem( faces, i ); // 將faces資料從CvSeq轉為CvRect 
            CvMat small_img_roi;
            CvSeq* nested_objects;
            CvPoint center,recPt1,recPt2;
            CvScalar color = colors[i%8]; // 使用不同顏色繪製各個face,共八種色 
            int radius;
            center.x = cvRound((r->x + r->width*0.5)*scale); // 找出faces中心 
            center.y = cvRound((r->y + r->height*0.5)*scale);
			recPt1.x = cvRound((r->x)*scale);
			recPt1.y = cvRound((r->y)*scale);
			recPt2.x = cvRound((r->x + r->width)*scale);
			recPt2.y = cvRound((r->y + r->height)*scale);
            radius = cvRound((r->width + r->height)*0.25*scale); 
				
			cvGetSubRect( small_img, &small_img_roi, *r );
			
			IplImage *result;
			CvRect roi;
			roi = *r;
			result = cvCreateImage( cvSize(r->width, r->height), img->depth, img->nChannels );
			cvSetImageROI(img,roi);
			// 建立子影象
			cvCopy(img,result);
			cvResetImageROI(img);
			
			//IplImage *resizeRes;
			CvSize dst_cvsize;
			dst_cvsize.width=(int)(100);
			dst_cvsize.height=(int)(100);
			resizeRes=cvCreateImage(dst_cvsize,result->depth,result->nChannels);
			cvResize(result,resizeRes,CV_INTER_NN);
			faceGray = cvCreateImage(cvGetSize(resizeRes), IPL_DEPTH_8U, 1);//建立目標影象	
			cvCvtColor(resizeRes,faceGray,CV_BGR2GRAY);//cvCvtColor(src,des,CV_BGR2GRAY)
            cvShowImage( "resize", resizeRes );
			cvRectangle(img,recPt1,recPt2,color,1, 8,0);
			//rectangle(img,recPt1,recPt2,color,1,8,0);
			//cvCircle( img, center, radius, color, 3, 8, 0 ); // 從中心位置畫圓,圈出臉部區域
            if( !nested_cascade )
                continue;
            nested_objects = cvHaarDetectObjects( &small_img_roi, nested_cascade, storage,
                                        1.1, 2, 0
                                        //|CV_HAAR_FIND_BIGGEST_OBJECT
                                        //|CV_HAAR_DO_ROUGH_SEARCH
                                        //|CV_HAAR_DO_CANNY_PRUNING
                                        //|CV_HAAR_SCALE_IMAGE
                                        ,
                                        cvSize(0, 0) );
            for( j = 0; j < (nested_objects ? nested_objects->total : 0); j++ )
            {
                CvRect* nr = (CvRect*)cvGetSeqElem( nested_objects, j );
                center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale);
                center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale);
                radius = cvRound((nr->width + nr->height)*0.25*scale);
                cvCircle( img, center, radius, color, 3, 8, 0 );
            }
        }
    }
    //cvShowImage( "result", img );
    cvReleaseImage( &gray );
    cvReleaseImage( &small_img );
}
//檢測並識別人臉,並在每幀圖片上寫入結果
void recog_and_draw( IplImage* img )
{
    static CvScalar colors[] = 
    {
        {{0,0,255}},
        {{0,128,255}},
        {{0,255,255}},
        {{0,255,0}},
        {{255,128,0}},
        {{255,255,0}},
        {{255,0,0}},
        {{255,0,255}}
    };
    IplImage *gray, *small_img;
    int i, j;
    gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );
    small_img = cvCreateImage( cvSize( cvRound (img->width/scale),
                         cvRound (img->height/scale)), 8, 1 );
    cvCvtColor( img, gray, CV_BGR2GRAY ); // 彩色RGB影象轉為灰度影象 
    cvResize( gray, small_img, CV_INTER_LINEAR );
    cvEqualizeHist( small_img, small_img ); // 直方圖均衡化 
    cvClearMemStorage( storage );
    if( cascade )
    {
        double t = (double)cvGetTickCount(); 
        CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,
                                            1.1, 2, 0
                                            //|CV_HAAR_FIND_BIGGEST_OBJECT
                                            //|CV_HAAR_DO_ROUGH_SEARCH
                                            |CV_HAAR_DO_CANNY_PRUNING
                                            //|CV_HAAR_SCALE_IMAGE
                                            ,
                                            cvSize(30, 30) );
        t = (double)cvGetTickCount() - t; // 統計檢測使用時間 
        //printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
        for( i = 0; i < (faces ? faces->total : 0); i++ )
        {
            CvRect* r = (CvRect*)cvGetSeqElem( faces, i ); // 將faces資料從CvSeq轉為CvRect 
            CvMat small_img_roi;
            CvSeq* nested_objects;
            CvPoint center,recPt1,recPt2;
            CvScalar color = colors[i%8]; // 使用不同顏色繪製各個face,共八種色 
            int radius;
            center.x = cvRound((r->x + r->width*0.5)*scale); // 找出faces中心 
            center.y = cvRound((r->y + r->height*0.5)*scale);
			recPt1.x = cvRound((r->x)*scale);
			recPt1.y = cvRound((r->y)*scale);
			recPt2.x = cvRound((r->x + r->width)*scale);
			recPt2.y = cvRound((r->y + r->height)*scale);
            radius = cvRound((r->width + r->height)*0.25*scale); 
				
			cvGetSubRect( small_img, &small_img_roi, *r );
			
			IplImage *result;
			CvRect roi;
			roi = *r;
			result = cvCreateImage( cvSize(r->width, r->height), img->depth, img->nChannels );
			cvSetImageROI(img,roi);
			// 建立子影象
			cvCopy(img,result);
			cvResetImageROI(img);
			
			//IplImage *resizeRes;
			CvSize dst_cvsize;
			dst_cvsize.width=(int)(100);
			dst_cvsize.height=(int)(100);
			resizeRes=cvCreateImage(dst_cvsize,result->depth,result->nChannels);
			cvResize(result,resizeRes,CV_INTER_NN);

			faceGray = cvCreateImage(cvGetSize(resizeRes), IPL_DEPTH_8U, 1);//建立目標影象	
			cvCvtColor(resizeRes,faceGray,CV_BGR2GRAY);//cvCvtColor(src,des,CV_BGR2GRAY)
            cvShowImage( "resize", resizeRes );
			cvRectangle(img,recPt1,recPt2,color,3, 8,0);
			//cvCircle( img, center, radius, color, 3, 8, 0 ); // 從中心位置畫圓,圈出臉部區域
			
			Mat test = faceGray;
			//images[images.size() - 1] = test;
			model->train(images, labels);
			//predictedLabel = model->predict(test);
			double predicted_confidence = 0.0;
			model->predict(test,predictedLabel,predicted_confidence);
			stringstream strStream;
			strStream<<predicted_confidence;
			string ss = strStream.str(); 
			cvText(img, ss.c_str(), r->x+r->width*0.5, r->y); 
			if(predicted_confidence <= dConfidence)
				cvText(img, "Result:YES", 0, 30); 
			else
				cvText(img, "Result:NO", 0, 30);  
			//cout << "predict:"<<model->predict(test) << endl;
			//cout << "predict:"<< predictedLabel << "\nconfidence:" << predicted_confidence << endl;

            if( !nested_cascade )
                continue;
            
            nested_objects = cvHaarDetectObjects( &small_img_roi, nested_cascade, storage,
                                        1.1, 2, 0
                                        //|CV_HAAR_FIND_BIGGEST_OBJECT
                                        //|CV_HAAR_DO_ROUGH_SEARCH
                                        //|CV_HAAR_DO_CANNY_PRUNING
                                        //|CV_HAAR_SCALE_IMAGE
                                        ,
                                        cvSize(0, 0) );
            for( j = 0; j < (nested_objects ? nested_objects->total : 0); j++ )
            {
                CvRect* nr = (CvRect*)cvGetSeqElem( nested_objects, j );
                center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale);
                center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale);
                radius = cvRound((nr->width + nr->height)*0.25*scale);
                cvCircle( img, center, radius, color, 3, 8, 0 );
            }
        }
    }
    //cvShowImage( "result", img );
    cvReleaseImage( &gray );
    cvReleaseImage( &small_img );
}
void cvText(IplImage* img, const char* text, int x, int y)  
{  
    CvFont font;  
    double hscale = 1.0;  
    double vscale = 1.0;  
    int linewidth = 2;  
    cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX | CV_FONT_ITALIC,hscale,vscale,0,linewidth);  
    CvScalar textColor =cvScalar(0,255,255);  
    CvPoint textPos =cvPoint(x, y);  
    cvPutText(img, text, textPos, &font,textColor);  
}

Mat norm_0_255(cv::InputArray _src)
{
	Mat src = _src.getMat();
	Mat dst;

	switch(src.channels())
	{
	case 1:
		cv::normalize(_src, dst, 0, 255, cv::NORM_MINMAX, CV_8UC1);
		break;
	case 3:
		cv::normalize(_src, dst, 0, 255, cv::NORM_MINMAX, CV_8UC3);
		break;
	default:
		src.copyTo(dst);
		break;
	}

	return dst;
}

void read_csv(const string &filename, vector<Mat> &images, vector<int> &labels, char separator)
{
	std::ifstream file(filename.c_str(), ifstream::in);
	if(!file)
	{
		string error_message = "No valid input file was given.";
		CV_Error(CV_StsBadArg, error_message);
	}

	string line, path, classlabel;
	while(getline(file, line))
	{
		stringstream liness(line);
		getline(liness, path, separator);  //遇到分號就結束
		getline(liness, classlabel);     //繼續從分號後面開始,遇到換行結束
		if(!path.empty() && !classlabel.empty())
		{
			images.push_back(imread(path, 0));
			labels.push_back(atoi(classlabel.c_str()));
		}
	}
}
//實現了從trainningdata 目錄下直接讀取jpg檔案作為訓練集
bool read_img(vector<Mat> &images, vector<int> &labels)
{
	long file;  
    struct _finddata_t find; 
	//AllocConsole();
	string path = "..//einfacedata//trainingdata/";
	char filepath[60];
    //_chdir("..//einfacedata//trainingdata");  
    if((file=_findfirst("..//einfacedata//trainingdata/*.jpg", &find))==-1L) 
	{  
		AfxMessageBox("Cannot find the dir");
        return false;  
    }  
	int i = 0;
    images.push_back(imread(path+find.name, 0));
	labels.push_back(0); 
    while(_findnext(file, &find)==0)  
    {  
		//_cprintf("%s\n", path+find.name);
		//_cprintf("%d\n", i++);
		images.push_back(imread(path+find.name, 0));
		labels.push_back(0); 
    }  
    _findclose(file);
	return true;
}
//實現了從trainningdata 目錄下讀取jpg檔案數目
int read_img_number()
{
	long file;  
	int i = 0;
    struct _finddata_t find; 
	//AllocConsole();
	string path = "..//einfacedata//trainingdata/";
	char filepath[60];
    if((file=_findfirst("..//einfacedata//trainingdata/*.jpg", &find))==-1L) 
        return i;
	i++;
    while(_findnext(file, &find)==0)  
    {  
		i++;
    }  
    _findclose(file);
	return i;
}
bool delete_img()
{
	system( "del ..\\einfacedata\\trainingdata\\" ); 
	return true;
}