微軟在雲端已經推出了透過上傳影像檔案,進行人臉辨識的雲端服務
但是由於每辨識一次都是要花錢的,所以就想說,是不是可以先在本地端進行人臉的辨識,當有辨識出人臉之後再往雲端送
所以,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只是在人臉辨識中最一開始要使用的功能,而真正重要的,是後面服務的串接與應用。
Github程式碼下載:
https://github.com/madukapai/maduka-FaceDetect