• 【C#】人脸识别 视频数据转图片数据


    使用虹软人脸识别的开发过程中遇到了转换的问题

    因为不会用C#直接打开摄像头,就只能用第三方dll。一开始用Aforge,后来发现有个问题,关闭摄像头老是陷入等待,所以抛弃了。前一阵子开始用封装了OpenCV的Emgu,一路走来也是N声叹息。

    一、安装Emgu的叹息
    一开始自己下载并安装了Emgu,然后各种测试,发现还需要这个那个的,最后发现直接安装一个EMGU.CV的NuGet包就OK了。
    当然要打开视频,还需要引用System.ServiceModel。

    二、获取视频用Emgu获取视频真心十分方便
    1.定义两个变量

    VideoCapture _VideoCapture;
    Mat _Frame = new Mat();

    2.初始化视频

    _VideoCapture = new VideoCapture();
    //_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, 1024); //设置宽度
    //_VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, 768);//设置高度
    _VideoCapture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps, 10);//设置每秒钟的帧数
    _VideoCapture.Start();
    _VideoCapture.ImageGrabbed += _VideoCapture_ImageGrabbed; //视频事件

    3.视频显示

    private void _VideoCapture_ImageGrabbed(object sender, EventArgs e)
    {
    _VideoCapture.Retrieve(_Frame, 1);
    this.pictureBox1.Image = _Frame.Bitmap; //很神奇的pictureBox,居然不在UI线程也能显示
    }

    4.获取当前帧
    用于人脸比对,一般在另外一个线程

    Mat curFrame=_VideoCapture.QueryFrame();

    一切十分完美,就是_Frame.Bitmap似乎没有Dispose(后来发现Mat的地址是不变,不会发生内存泄漏),但运行起来也没问题。

    三、人脸识别的叹息
    看了一下,人脸识别需要Bitmap,方便

    Mat curFrame=_VideoCapture.QueryFrame();
    Bitmap bitmap=curFrame.Bitmap;

    var bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
    int width = (bitmap.Width + 3) / 4 * 4;
    var bytesCount = bmpData.Height * width * 3;
    IntPtr pImageData = Marshal.AllocCoTaskMem(bytesCount);
    if (width == bitmap.Width)
    CopyMemory(pImageData, bmpData.Scan0, bytesCount);
    else
    for (int i = 0; i < bitmap.Width; i++)
    CopyMemory(IntPtr.Add(pImageData, i * width * 3), IntPtr.Add(bmpData.Scan0, i * bmpData.Stride), bmpData.Stride);
    bitmap.UnlockBits(bmpData);

    得到了ArcFace所需的图片数据pImageData,测试一下挺好,能运行。时间一长,报错了:“试读取或写入受保护的内存。这通常指示其他内存已损坏。”
    估计人脸识别的线程和显示视频的线程冲突了,查看了Emgu的源代码,发现QueryFrame就是封装了Retrieve。
    好吧,克隆一下,Bitmap bitmap=(Bitmap)curFrame.Bitmap.Clone();问题依旧!查看地址发现Clone没卵用!

    四、最终解决的办法
    研究了Mat这个东东,发现GetData()就能返回图片数据,而且不会冲突,最后写成:
    1.定义

    IntPtr _PImageData;
    int _ImageWidth,_ImageHeight,_ImageSize;

    2.初始化

    _ImageWidth=_VideoCapture.Width;
    _ImageHeight=_VideoCapture.Height;
    _ImageSize = _VideoCapture.Width * _VideoCapture.Height * 3;
    _PImageData = Marshal.AllocCoTaskMem(_ImageSize);

    3.转换

    Marshal.Copy(_Frame.GetData(), 0, _PImageData, _ImageSize);

    ASFDetectFaces(pEngine,_ImageWidth, _ImageHeight,513,_PImageData, out var faceInfo);
    ...

    一切变得如此简单,长叹一声!

    五、其他
    1.视频图片的宽度一般是4的倍数,所以上述方式肯定没问题。
    2.经常有人问如何获取网络摄像头、ip摄像头的图像,其实就是

    videoCapture = new VideoCapture("string filename");

    如某tplink的IP摄像头的filename是这样的"rtsp://admin:admin@192.168.0.159/stream1",格式是rstp://用户名:密码@ip地址/...


    各位C#的亲,你们怎么转换的?

  • 相关阅读:
    样式的使用
    样式的使用
    jqurey基础一
    jQuery三天复习.md
    webstorm快捷键大全
    计算机的进制与编码
    2016-4-29HTML标记的使用
    HTML的基本概况
    Apache Maven 入门篇 ( 上 )
    ehcache.xml 分布试缓存
  • 原文地址:https://www.cnblogs.com/ccLqqy/p/10255092.html
Copyright © 2020-2023  润新知