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

Posted

技术标签:

【中文标题】使用 EmguCV 3.1.0.1 从视频捕获中检测【英文标题】:Detection from video capture with EmguCV 3.1.0.1 【发布时间】:2019-08-07 11:25:05 【问题描述】:

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

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

所以,我使用 DirectShowLib 库为 videoDevice 来自 comboBox1_SelectedIndexChanged SelectedItem

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); 行说:

错误 CS0234 类型或命名空间名称“INTER”不存在于 命名空间“Emgu.CV.CvEnum”(您是否缺少程序集引用?)

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

错误 CS1061 'Mat' 不包含定义 “调整大小”并且没有扩展方法“调整大小”接受第一个参数 可以找到“Mat”类型的(您是否缺少 using 指令或 汇编参考?)

我不确定我必须在哪里下载所需的 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()):

错误 CS0411 无法从用法推断方法“Mat.ToImage(bool)”的类型参数。尝试指定 显式输入参数。

线var grayframe = imageFrame.Convert();:

错误 CS0411 方法“Image.Convert()”的类型参数无法从 用法。尝试明确指定类型参数。

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

错误 CS1503 参数 2:无法从 'Emgu.CV.Structure.Bgr' 转换 到“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();
            
        

未处理的异常:

在 Emgu.CV.World.dll 中发生 System.AccessViolationException' 附加信息:试图读取或写入受保护的内存。 这通常表明其他内存已损坏

方法二:

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

例外:

Emgu.CV.Util.CvException' 发生在 Emgu.CV.World.dll 附加 信息:OpenCV:无法识别或不支持的数组类型

【问题讨论】:

【参考方案1】:

正如您的错误消息所说,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。

【讨论】:

您好,感谢您的反馈。我已经根据您上面的回答编辑了代码,但是两种方法都会引发未处理的异常,第一种是: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,第二种方法:Emgu.CV.Util.CvException' occurred in Emgu.CV.World.dll Additional information: OpenCV: Unrecognized or unsupported array type @viktor80 我编辑了我的答案并添加了您的第一个方法中缺少的代码。 我确信这个解决方案应该可以工作,但至少在我这边似乎有问题。图像移动不顺畅,而是挂起,并且跟踪矩形 faceFound.X 或 Y 的坐标取值,冻结和移动之间的时间间隔很大。我使用了更老的 EmguCV 2,它具有等效的代码,并且工作正常,对我来说缺少旧的库版本是新的 haarcascade xml-s 不适合它。在哪里可以下载最新版本的 EMGU.CV 并找到有用的示例? EMGU.CV 在 NuGet 上可用,它只是一个不同的包。我很确定有一些方法可以使您的代码在新版本中比在 2.x 中执行得更好。在我的编辑中提到的小修改(创建一次CaptureCascadeClassifier)后,在我的 PC 上,该程序以全相机 fps 工作,消耗约 30% 的 CPU。在需要运行此程序的机器上是否有支持 OpenCL 的 GPU?如果是这样,也许您可​​以从 Emgu.CV 中的 OpenCL 中受益,如以下答案:***.com/a/32035912/6866539 您也可以尝试减小捕获的帧大小。 一些示例可在github.com/emgucv/emgucv/tree/master/Emgu.CV.Example获得

以上是关于使用 EmguCV 3.1.0.1 从视频捕获中检测的主要内容,如果未能解决你的问题,请参考以下文章

无法从 emgucv 中的视频中检测到人脸

EMgucv 保存从网络摄像头检测到的多张人脸

从视频中提取帧的最快方法

EmguCV 类型初始化异常

Emgu CV 图像捕获使用 C#

[C#]emgucv教程1:摄像头读取