简体   繁体   English

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

[英]Detection from video capture with EmguCV 3.1.0.1

I'm trying to implement face detection with video capture using EmguCV 3.1.0.1 library, installed by NuGet packages of VS15 on PC Windows 10 64-bit OS in WinForms desktop application. 我正在尝试使用EmguCV 3.1.0.1库进行视频捕获的面部检测, 库由VS15的NuGet软件包安装在WinForms桌面应用程序的PC Windows 10 64位操作系统上。

My goal is to get detection and tracking of human face from videocamera and detect smile, but for examples below I will use only face HaarCascade .xml with CascadeClassifier . 我的目标是从摄像机中检测和跟踪人脸并检测微笑,但是在下面的示例中,我将仅使用带有CascadeClassifier HaarCascade .xml。

So, my using with DirectShowLib library for videoDevice from comboBox1_SelectedIndexChanged SelectedItem : 所以,我从comboBox1_SelectedIndexChanged SelectedItem使用DirectShowLib库为videoDevice

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

Path to HaarCascade xml-s: HaarCascade xml-s的路径:

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

Timer: 计时器:

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

Attempt 1: 尝试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();
        }
    }

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

Error CS0234 The type or namespace name 'INTER' does not exist in the namespace 'Emgu.CV.CvEnum' (are you missing an assembly reference?) 错误CS0234命名空间“Emgu.CV.CvEnum”中不存在类型或命名空间名称“INTER”(您是否缺少程序集引用?)

Instead I've tried to use currentframe = grabber.QueryFrame().Resize(500, 320, Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed); 相反,我尝试使用currentframe = grabber.QueryFrame().Resize(500, 320, Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed); , with grabber.QueryFrame().MatchTemplate or grabber.QueryFrame().Retrieve , but another error still keeps in same line as: ,使用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?) 错误CS1061'Mat'不包含'Resize'的定义,并且没有扩展方法'Resize'接受类型'Mat'的第一个参数可以找到(你是否缺少using指令或汇编引用?)

I'm not sure where I have to download required dll-s (if it is a reason of missing?) and which dll-s exactly I should add to references additionally. 我不知道我在哪里下载所需的dll-s(如果它是丢失的原因?)以及哪些dll-s我应该另外添加到引用中。

Attempt 2: 尝试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();
        }
    }

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

Error CS0411 The type arguments for method 'Mat.ToImage(bool)' cannot be inferred from the usage. 错误CS0411无法从用法推断出方法“Mat.ToImage(bool)”的类型参数。 Try specifying the type arguments explicitly. 尝试显式指定类型参数。

line var grayframe = imageFrame.Convert(); line var grayframe = imageFrame.Convert(); :

Error CS0411 The type arguments for method 'Image.Convert()' cannot be inferred from the usage. 错误CS0411无法从用法中推断出方法“Image.Convert()”的类型参数。 Try specifying the type arguments explicitly. 尝试显式指定类型参数。

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

Error CS1503 Argument 2: cannot convert from 'Emgu.CV.Structure.Bgr' to 'TColor' 错误CS1503参数2:无法从'Emgu.CV.Structure.Bgr'转换为'TColor'

Any guide, advice or example would be helpful 任何指南,建议或示例都会有所帮助

Edit by Michal Nawrocik answer below: Michal Nawrocik编辑回答如下:

Method 1: 方法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);  
            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();
            }
        }

Unhandled exception: 未处理的异常:

System.AccessViolationException' occurred in Emgu.CV.World.dll Additional information: Attempted to read or write protected memory. Emgu.CV.World.dll中发生System.AccessViolationException附加信息:尝试读取或写入受保护的内存。 This is often an indication that other memory is corrupt 这通常表明其他内存已损坏

Method 2: 方法2:

 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 在Emgu.CV.World.dll中发生Emgu.CV.Util.CvException'附加信息:OpenCV:无法识别或不支持的数组类型

As your error message says, Mat class does not have a Resize() instance method, some quick googling reveals that you need to call the static method of CvInvoke class. 正如您的错误消息所示, Mat类没有Resize()实例方法,一些快速的googling显示您需要调用CvInvoke类的静态方法。 The enum value for cubic interpolation has also been changed by developers of EmguCV at some point in time. 在某些时间点,EmguCV的开发人员也改变了三次插值的枚举值。

Here is the working code corresponding to your attempt 1: 以下是与您的尝试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>();

Notice that I save references to Mat objects in order to dispose them later. 请注意,我保存了对Mat对象的引用,以便稍后处理它们。 using statements can also be used instead. 也可以使用using语句。

Your other problems are similar. 你的其他问题很相似。 There are no parameterless non-generic methods like Mat.ToImage() or Image.Convert() . 没有无参数的非泛型方法,如Mat.ToImage()Image.Convert() They would not even make much sense because you need to specify what format you want to convert to. 它们甚至没有多大意义,因为您需要指定要转换为的格式。 In your case you can use: 在您的情况下,您可以使用:

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

The last error in Image.Draw() call will disappear then. Image.Draw()调用中的最后一个错误将消失。

Your problems suggest that you might benefit from taking a closer look at classes metadata available eg via Object Browser built in Visual Studio. 您的问题表明,您可以通过仔细查看可用的类元数据(例如,通过Visual Studio内置的对象浏览器)来获益。

Edit: 编辑:

In the first method you forgot to call Dispose() for some objects and this caused the exception. 在第一种方法中,您忘记为某些对象调用Dispose() ,这导致异常。 Here is the corrected code, tested and working: 这是经过修正的代码,经过测试和运行:

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();
}

I made the least possible fixes to make it work. 我尽可能少地修复它以使其工作。 There is quite some room for improvement. 还有一些改进的余地。 You can create Capture and CascadeClassifier just once instead of each timer tick, this will improve performance considerably. 您只需创建一次CaptureCascadeClassifier ,而不是每个计时器滴答,这将大大提高性能。

I also noticed that the version of Emgu package you are using is quite old even though NuGet does not show newer version for this very package. 我也注意到你使用的Emgu包的版本已经很老了,尽管NuGet没有为这个包显示更新的版本。 The reason is it's been renamed to EMGU.CV. 原因是它已被重命名为EMGU.CV.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM