一个人脸检测器
本文使用到Emgu.CV库,该库是C#语言对OpenCV的封装,以下是一个列子程序的改正版本。
using System;
using System.Collections.Generic;
using System.Text;
using Emgu.CV.Structure;
using Emgu.CV;
namespace VSL.Plugin.TrackingSystem.SimpleTrackingSystemExample
{
public class FaceDetector
{
//private HaarCascade _faceCascade;
Emgu.CV.HaarCascade face;// = new HaarCascade(".\\haarcascades\\haarcascade_frontalface_alt_tree.xml");
Emgu.CV.HaarCascade eye;// = new HaarCascade(".\\haarcascades\\haarcascade_frontaleye.xml");
public FaceDetector()
{
// _faceCascade = new HaarCascade(".\\haarcascades\\haarcascade_frontalface_alt_tree.xml");
face = new Emgu.CV.HaarCascade(".\\haarcascades\\haarcascade_frontalface_alt_tree.xml");
eye = new Emgu.CV.HaarCascade(".\\haarcascades\\haarcascade_frontaleye.xml");
}
public List<Face<D>> Detect<D>(Emgu.CV.Image<Emgu.CV.Bgr, D> img)
{
using (Emgu.CV.Image<Emgu.CV.Gray, D> gray = img.Convert<Emgu.CV.Gray, D>())
{
MCvAvgComp[][] objects = gray.DetectHaarCascade(face);
List<Face<D>> res = new List<Face<D>>();
System.Drawing.Rectangle rect = img.ROI; //保存操作区域
//通过判断脸部是否含有眼睛来进一步检测是否是正确的人脸
foreach (MCvAvgComp f in objects[0])
{
//检测眼睛
gray.ROI = f.rect;
MCvAvgComp[][] eyesDetected = gray.DetectHaarCascade(eye, 1.1, 1,
Emgu.CV.CvEnum.HAAR_DETECTION_TYPE.
DO_CANNY_PRUNING,
new System.Drawing.Size(20, 20));
gray.ROI = System.Drawing.Rectangle.Empty;
//if there is no eye in the specific region, the region shouldn't contains a face
//note that we might not be able to recoginize a person who ware glass in this case
if (eyesDetected[0].Length == 0) continue;
img.ROI = f.rect; //设定操作区域
res.Add(new Face<D>(img.Copy(), f.rect));
}
img.ROI = rect; //恢复操作区域
return res;
}
}
public void Dispose()
{
face.Dispose();
}
}
public class Eye<D>
{
private Emgu.CV.Image<Emgu.CV.Bgr, D> _image;
public Eye(Emgu.CV.Image<Emgu.CV.Bgr, D> img, System.Drawing.Rectangle rect)
{
_image = img;
}
public Emgu.CV.Image<Emgu.CV.Bgr, D> RGB
{
get
{
return _image;
}
}
}
public class Face<D>
{
private Emgu.CV.Image<Emgu.CV.Bgr, D> _image;
private Emgu.CV.Image<Emgu.CV.Gray, D> _imageGray;
private Emgu.CV.Image<Emgu.CV.Hsv, D> _imageHSV;
private Emgu.CV.Image<Emgu.CV.Gray, D> _h;
private Emgu.CV.Image<Emgu.CV.Gray, D> _s;
private Emgu.CV.Image<Emgu.CV.Gray, D> _v;
private Emgu.CV.Histogram _hueHtg;
//private Seq<MCvContour> _skinContour;
private System.Drawing.Rectangle _rect;
private Emgu.CV.HaarCascade _eyeCascade;
public Face(Emgu.CV.Image<Emgu.CV.Bgr, D> img, System.Drawing.Rectangle rect)
{
_image = img;
_rect = rect;
_eyeCascade = new Emgu.CV.HaarCascade(".\\haarcascades\\eye_12.xml");
}
public List<Eye<D>> DetectEye()
{
MCvAvgComp[][] objects = Gray.DetectHaarCascade(_eyeCascade);
List<Eye<D>> res = new List<Eye<D>>();
foreach (MCvAvgComp o in objects[0])
{
_image.ROI = o.rect;
res.Add(new Eye<D>(_image.Copy(), o.rect));
}
_image.ROI = System.Drawing.Rectangle.Empty;
return res;
}
public System.Drawing.Rectangle Rectangle
{
get { return _rect; }
}
public Emgu.CV.Image<Emgu.CV.Bgr, D> Bgr
{
get
{
return _image;
}
}
public Emgu.CV.Image<Emgu.CV.Gray, D> Gray
{
get
{
if (_imageGray == null) _imageGray = _image.Convert<Emgu.CV.Gray, D>();
return _imageGray;
}
}
public Emgu.CV.Image<Emgu.CV.Hsv, D> Hsv
{
get
{
if (_imageHSV == null) _imageHSV = _image.Convert<Emgu.CV.Hsv, D>();
return _imageHSV;
}
}
public Emgu.CV.Image<Emgu.CV.Gray, D> H
{
get
{
if (_h == null)
{
Emgu.CV.Image<Gray, D>[] imgs = Hsv.Split();
_h = imgs[0];
_s = imgs[1];
_v = imgs[2];
}
return _h;
}
}
public Image<Gray, D> S
{
get
{
if (_s == null)
{
Image<Gray, D>[] imgs = Hsv.Split();
_h = imgs[0];
_s = imgs[1];
_v = imgs[2];
}
return _s;
}
}
public Image<Gray, D> V
{
get
{
if (_h == null)
{
Image<Gray, D>[] imgs = Hsv.Split();
_h = imgs[0];
_s = imgs[1];
_v = imgs[2];
}
return _v;
}
}
public Histogram HueHistogram
{
get
{
if (_hueHtg == null)
{
int size = 60;
_hueHtg = new Histogram(new int[1] { size }, new float[1] { 0.0f }, new float[1] { 180.0f });
}
return _hueHtg;
}
}
public Image<Gray, D> SkinMask
{
get
{
Image<Gray, D> skinMask = Gray.CopyBlank();
//skinMask.Draw(SkinContour, new Gray(255.0), new Gray(120.0), -1);
return skinMask;
}
}
/*
public Seq<MCvContour> SkinContour
{
get
{
if (_skinContour == null)
{
Histogram htg = HueHistogram;
htg.Accumulate(new Image<Gray, D>[1] { H });
double[] arr = new double[htg.BinSize[0]];
for (int i = 0; i < htg.BinSize[0]; i++)
arr[i] = htg.Query(new int[1] { i });
System.Array.Sort<double>(arr);
System.Array.Reverse(arr);
htg.Threshold(arr[2]);
using (Image<Gray, D> bpj = htg.BackProject(new Image<Gray, D>[1] { H }))
{
Seq<MCvContour> cList = bpj.FindContours( CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST, new MemStorage());
Seq<MCvContour> maxAreaContour = cList;
foreach (Seq<MCvContour> ct in cList)
{
if (ct.Area > maxAreaContour.Area)
maxAreaContour = ct;
}
_skinContour = GRAY.Snake(maxAreaContour, 1.0f, 1.0f, 1.0f, new Point2D<int>(5, 5), new Emgu.CV.MCvTermCriteria(20, 1.0), new MemStorage());
}
}
return _skinContour;
}
}*/
public void Dispose()
{
_image.Dispose();
if (_imageGray != null) _imageGray.Dispose();
if (_imageHSV != null) _imageHSV.Dispose();
if (_h != null) _h.Dispose();
if (_s != null) _s.Dispose();
if (_v != null) _v.Dispose();
//if (_skinContour != null) _skinContour.Dispose();
}
}
}