[Cognitive] 在C#中使用Emgu.CV進行簡易的人臉辨識

微軟在雲端已經推出了透過上傳影像檔案,進行人臉辨識的雲端服務
但是由於每辨識一次都是要花錢的,所以就想說,是不是可以先在本地端進行人臉的辨識,當有辨識出人臉之後再往雲端送

所以,Emgu.CV這個套件就派上用場了

Emgu.CV是OpenCV在.NET平台上的套件,讓.NET的開發者也可以很快的使用OpenCV的功能,要在.NET專案中加入Emgu.CV的套件並作到人臉辨識也非常的簡單,依照下面的步驟,或是參考最後附上的Github網址專案就可以很快的作出簡易的Client端人臉辨識功能了

1.建立一個新的.NET專案,並在表單上拉出一個PictureBox以及一個Button,如下圖

2.在專案中,加入[EMGU.CV]的Buget套件

3.接著用Google搜尋"Emgu.CV download"的字串,下載Emgu.CV的安裝包,或是壓縮包,這裡最主要的目的是取得下面三個檔案
cvextern.dll
haarcascade_eye.xml
haarcascade_frontalface_default.xml
如果不想下載,或是不想安裝Emgu.CV的安裝包在電腦中的話,從Github的專案檔中下載也可以

在下載完的壓縮包,並解壓縮之後,可以在libs目錄中找到"cvextern.dll"

在etc\haarcascades資料夾下可以找到xml兩個檔案

接著將這三個檔案都複製到專案之中,並將這三個檔案的屬性設定為"有更新時才複製"

4.這樣環境都已經準備好了,接下來把下面的程式碼放入到表單之中

using Emgu.CV;
using Emgu.CV.Structure;

VideoCapture objVideoCapture;
Mat objMat;

private void frmMain_Load(object sender, EventArgs e)
{
    // 這裡設定WebCam的index,如果有多個WebCam,就更改不同的index數字
    objVideoCapture = new VideoCapture(0);
    objVideoCapture.ImageGrabbed += ProcessFrameAsync;
}

private void btnStartCamera_Click(object sender, EventArgs e)
{
    blCameraOpen = !blCameraOpen;

    if (blCameraOpen)
    {
        objMat = new Mat();
        // 啟動照相機
        btnStartCamera.Text = "Stop";
        objVideoCapture.Start();
    }
    else
    {
        // 停止照相機
        btnStartCamera.Text = "Start";
        objVideoCapture.Stop();
    }
}

這一段程式碼主要的內容,就是在程式一執行起來之後,建立一個VideoCapture的物件,以及Mat的影像容器物件,並且指定當VideoCapture開始運作的時候,執行ProcessFrameAsync這一個程序

接著再將下面程式碼放入,ProcessFrameAsync就是在表單一啟動時所宣告,當VideoCapture執行時要執行的影格處理程序

/// <summary>
/// 處理影格的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ProcessFrameAsync(object sender, EventArgs e)
{
    try
    {
        // 從OpenCV取得影像,並辨識人臉的存在
        objVideoCapture.Retrieve(objMat);
        OpenCVResult result = this.CaptureFace(objMat);

        // 在影像上進行框線的繪圖
        for (int f = 0; f < result.faces.Count; f++)
            CvInvoke.Rectangle(objMat, result.faces[f], new Bgr(Color.Red).MCvScalar, 2);

        for (int y = 0; y < result.eyes.Count; y++)
            CvInvoke.Rectangle(objMat, result.eyes[y], new Bgr(Color.Yellow).MCvScalar, 1);

        // 將圖片放到PictureBox之中
        if (picRender.Image != null)
            picRender.Image.Dispose();
        picRender.Image = Image.FromHbitmap(objMat.Bitmap.GetHbitmap());

        Thread.Sleep(5);
    }
    catch(Exception ex)
    {
        string strMsg = ex.Message;
        txtMessage.Text = strMsg;
    }
}

ProcessFrameAsync這個程序主要的目的,就是將WebCam的畫面取出後,將取出的影格畫面送入至CaptureFace的副程式,進行人臉的辨識,再將辨識出來的結果繪製出畫框,最後將整張圖放到PictureBox之中,而CaptureFace的副程式可以參考下面的程式碼內容所示

/// <summary>
/// 透過OpenCV進行人臉是否存在的辨識
/// </summary>
/// <param name="objMat"></param>
/// <param name="index"></param>
/// <returns></returns>
private OpenCVResult CaptureFace(Mat objMat)
{
    long detectionTime;
    List<Rectangle> faces = new List<Rectangle>();
    List<Rectangle> eyes = new List<Rectangle>();

    DetectFace.Detect(
        objMat, "haarcascade_frontalface_default.xml", "haarcascade_eye.xml",
        faces, eyes,
        out detectionTime);

    // 重新計算比例
    decimal diWidth = decimal.Parse(picRender.Width.ToString()) / decimal.Parse(objMat.Bitmap.Width.ToString());
    decimal diHeight = decimal.Parse(picRender.Height.ToString()) / decimal.Parse(objMat.Bitmap.Height.ToString());

    List<Rectangle> objDraw = new List<Rectangle>();

    for (int i = 0; i < faces.Count; i++)
    {
        objDraw.Add(new Rectangle(
            (int)(faces[i].X * diWidth),
            (int)(faces[i].Y * diHeight),
            (int)(faces[i].Width * diWidth),
            (int)(faces[i].Height * diHeight)
            ));
    }

    OpenCVResult result = new OpenCVResult()
    {
        eyes = eyes,
        faces = faces,
    };

    return result;
}

public class OpenCVResult
{
    public List<Rectangle> faces { get; set; }
    public List<Rectangle> eyes { get; set; }
}

在CaptureFace的副程式中,載入了haarcascade_eye.xml與haarcascade_frontalface_default.xml這兩個xml,並呼叫DetectFace.Detect這個副程式,然後取得臉部以及眼睛在這個網格中的數量以及座標,並回傳得到的結果。DetectFace.Detect的程式內容因為很長,所以建立參考剛剛下載Emgu.CV的壓縮包中的範例。或是直接參考文末Github中的程式碼也可以

5.這樣,就完成的簡單的Client端人臉辨識的功能了,程式碼非常的少,但是效果非常的好

在Client端能夠快速的取得人臉辨識的結果當然很方便,不過我在這個專案中主要的目的是為了減少對雲端API呼叫的次數,所以將部份的負擔轉移到Client端,當Client端有辨識出畫面存在人臉時,才將影像傳入至雲端進行人臉辨識,最後取得需要的人臉資訊及對應的person Guid。所以Emgu.CV只是在人臉辨識中最一開始要使用的功能,而真正重要的,是後面服務的串接與應用。

Emgu.CV在進行人臉辨識時會需要運用到GPU的資源,如果電腦本身不包含運算能力較佳的GPU,執行起來會有明顯的Lag狀況

Github程式碼下載:
https://github.com/madukapai/maduka-FaceDetect