c# - 使用 EmguCV 3.1.0.1 从视频捕获中检测

标签 c# winforms computer-vision emgucv face-detection

我正在尝试使用 EmguCV 3.1.0.1 库通过视频捕获实现人脸检测,该库由 VS15 的 NuGet 包安装在 PC Windows 10 64 位操作系统的 WinForms 桌面应用程序中。

我的目标是从摄像机检测和跟踪人脸并检测微笑,但对于下面的示例,我将仅使用人脸 HaarCascade .xml 和 CascadeClassifier

因此,我将 DirectShowLib 库用于 comboBox1_SelectedIndexChanged SelectedItemvideoDevice:

using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using DirectShowLib;

HaarCascade xml-s 的路径:

    string facePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "dir\\haarcascade_frontalface_default.xml");

计时器:

    private void timer1_Tick(object sender, EventArgs e)
    {
        detectFace();
    }

尝试 1:

    private void detectFace()
    {
        CascadeClassifier face = new CascadeClassifier(facePath);
        Image<Bgr, Byte> currentframe = null;
        Image<Gray, byte> grayFrame = null;
        Capture grabber;
        grabber = new Capture(videoDevice);
        currentframe = grabber.QueryFrame().Resize(500, 320, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC);
        if (currentframe != null)
        {
            grayFrame = currentframe.Convert<Gray, Byte>();
            Rectangle[] faceDetected = face.DetectMultiScale(grayFrame, 1.1, 10, Size.Empty, Size.Empty);
            foreach (Rectangle faceFound in faceDetected)
            {
                currentframe.Draw(faceFound, new Bgr(Color.Red), 2);
            }
            pictureBox1.Image = currentframe.ToBitmap();
        }
    }

currentframe = grabber.QueryFrame().Resize(500, 320, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC); 说:

Error CS0234 The type or namespace name 'INTER' does not exist in the namespace 'Emgu.CV.CvEnum' (are you missing an assembly reference?)

相反,我尝试使用 currentframe = grabber.QueryFrame().Resize(500, 320, Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed);,与 grabber.QueryFrame ().MatchTemplategrabber.QueryFrame().Retrieve,但另一个错误仍然在同一行:

Error CS1061 'Mat' does not contain a definition for 'Resize' and no extension method 'Resize' accepting a first argument of type 'Mat' could be found (are you missing a using directive or an assembly reference?)

我不确定我必须在哪里下载所需的 dll-s(如果它是丢失的原因?)以及我应该将哪些 dll-s 添加到引用中。

尝试 2:

    private Capture _capture;
    private CascadeClassifier _cascadeClassifier;
    private void detectFace()
    {
        _capture = new Capture(videoDevice); 
        _cascadeClassifier = new CascadeClassifier(facePath);
        using (var imageFrame = _capture.QueryFrame().ToImage())
        {
            if (imageFrame != null)
            {
                var grayframe = imageFrame.Convert();
                var faces = _cascadeClassifier.DetectMultiScale(grayframe, 1.1, 10, Size.Empty);
                foreach (var face in faces)
                {
                    imageFrame.Draw(face, new Bgr(Color.Red), 3);
                }
            }
            pictureBox1.Image = imageFrame.ToBitmap();
        }
    }

using (var imageFrame = _capture.QueryFrame().ToImage()):

Error CS0411 The type arguments for method 'Mat.ToImage(bool)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

var grayframe = imageFrame.Convert();:

Error CS0411 The type arguments for method 'Image.Convert()' cannot be inferred from the usage. Try specifying the type arguments explicitly.

imageFrame.Draw(face, new Bgr(Color.Red), 3);:

Error CS1503 Argument 2: cannot convert from 'Emgu.CV.Structure.Bgr' to 'TColor'

任何指南、建议或示例都会有所帮助

Michal Nawrocik 编辑以下回答:

方法一:

private void detectFace()
        {
            CascadeClassifier face = new CascadeClassifier(facePath);
            Image<Bgr, Byte> currentframe = null;
            Image<Gray, byte> grayFrame = null;
            Capture grabber;
            grabber = new Capture(videoDevice);  
            var dstMat = new Mat();
            var frame = grabber.QueryFrame();
            CvInvoke.Resize(frame, dstMat, new Size(500, 320), interpolation: Emgu.CV.CvEnum.Inter.Cubic);
            currentframe = dstMat.ToImage<Bgr, byte>();
            if (currentframe != null)
            {
                grayFrame = currentframe.Convert<Gray, Byte>();
                Rectangle[] faceDetected = face.DetectMultiScale(grayFrame, 1.1, 10, Size.Empty, Size.Empty);
                foreach (Rectangle faceFound in faceDetected)
                {
                    currentframe.Draw(faceFound, new Bgr(Color.Red), 2);
                }
                pictureBox1.Image = currentframe.ToBitmap();
            }
        }

未处理的异常:

System.AccessViolationException' occurred in Emgu.CV.World.dll Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt

方法二:

 private Capture _capture;
    private CascadeClassifier _cascadeClassifier;
    private void detectFace()
    {
            _capture = new Capture(videoDevice);   
            _cascadeClassifier = new CascadeClassifier(facePath);
            using (var imageFrame = _capture.QueryFrame().ToImage<Bgr, byte>())
            {
                if (imageFrame != null)
                {
                    var grayframe = imageFrame.Convert<Gray, byte>();
                    var faces = _cascadeClassifier.DetectMultiScale(grayframe, 1.1, 10, Size.Empty);  
                    foreach (var face in faces)
                    {
                        imageFrame.Draw(face, new Bgr(Color.Red), 3);
                    }
                }
                pictureBox1.Image = imageFrame.ToBitmap();
            }
        }           
    }

异常(exception):

Emgu.CV.Util.CvException' occurred in Emgu.CV.World.dll Additional information: OpenCV: Unrecognized or unsupported array type

最佳答案

如您的错误消息所述,Mat 类没有 Resize() 实例方法,一些快速谷歌搜索表明您需要调用 的静态方法>CvInvoke 类。 EmguCV 的开发人员在某个时间点也更改了三次插值的枚举值。

这是与您的尝试 1 相对应的工作代码:

var dstMat = new Mat();
var frame = grabber.QueryFrame();
CvInvoke.Resize(frame, dstMat, new Size(500, 320), interpolation: Emgu.CV.CvEnum.Inter.Cubic);
currentframe = dstMat.ToImage<Bgr, byte>();

请注意,我保存了对 Mat 对象的引用,以便稍后处理它们。 using 语句也可以代替。

你的其他问题类似。没有像 Mat.ToImage()Image.Convert() 这样的无参数非泛型方法。它们甚至没有多大意义,因为您需要指定要转换成的格式。 在您的情况下,您可以使用:

var imageFrame =_capture.QueryFrame().ToImage<Bgr, byte>();
var grayFrame = imageFrame.Convert<Gray, byte>();

Image.Draw() 调用中的最后一个错误将会消失。

您的问题表明您可能会从仔细查看可用的类元数据中受益,例如通过 Visual Studio 中内置的对象浏览器。

编辑:

在第一个方法中,您忘记为某些对象调用 Dispose(),这导致了异常。这是更正后的代码,经过测试和工作:

private void detectFace()
{
    CascadeClassifier face = new CascadeClassifier(facePath);
    Image<Bgr, Byte> currentframe = null;
    Image<Gray, byte> grayFrame = null;
    Capture grabber;
    grabber = new Capture(videoDevice);
    var dstMat = new Mat();
    var frame = grabber.QueryFrame();
    CvInvoke.Resize(frame, dstMat, new Size(500, 320), interpolation: Emgu.CV.CvEnum.Inter.Cubic);
    currentframe = dstMat.ToImage<Bgr, byte>();
    if (currentframe != null)
    {
        grayFrame = currentframe.Convert<Gray, Byte>();
        Rectangle[] faceDetected = face.DetectMultiScale(grayFrame, 1.1, 10, Size.Empty, Size.Empty);
        foreach (Rectangle faceFound in faceDetected)
        {
            currentframe.Draw(faceFound, new Bgr(Color.Red), 2);
        }

        var oldImage = panAndZoomPictureBox1.Image;
        panAndZoomPictureBox1.Image = currentframe.ToBitmap();
        if (oldImage != null)
        {
            oldImage.Dispose();
        }

        currentframe.Dispose();
        grayFrame.Dispose();
    }

    face.Dispose();

    grabber.Dispose();

    dstMat.Dispose();
    frame.Dispose();
}

我做了尽可能少的修复以使其正常工作。有相当大的改进空间。您可以只创建一次 CaptureCascadeClassifier 而不是每个计时器滴答,这将大大提高性能。

我还注意到您使用的 Emgu 包的版本很旧,即使 NuGet 没有显示这个包的更新版本。原因是它已重命名为 EMGU.CV。

关于c# - 使用 EmguCV 3.1.0.1 从视频捕获中检测,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55198568/

相关文章:

c# - MVC Razor AJAX 调用未命中 Controller

c# - Excel:取回选定的行

c# - 在类文件中创建控件

c# - 如何正确阻止方法直到对象计数器> 0?

c# - 使用 Fluent API 在 C# 中创建 Azure 存储帐户,且不允许 Blob 公共(public)访问

c# - 使用 Access 数据库中的数据填充组合框

c# - 当我使用快捷键时获取我的 ContextMenuStrip 的 SourceControl

python - 为什么 conv2d 在不同的批量大小下会产生不同的结果

python - 如何为SVC绘制训练数据和训练目标

python - OpenCV相机标定,无法控制畸变常数