C# 使用OpenCV 类VideoCapture 和 Mat的正确方法

发布于:2024-08-25 ⋅ 阅读:(148) ⋅ 点赞:(0)

在做WPF桌面程序时候,要调用USB摄像头,因此接触了OpenCV , 遇到了各种坑,严重烧肝终于找到原因。 涉及的类包括VideoCapture , Mat. 

问题1.内存泄漏 

运行以下 代码片段,会发现内存持续飙升

                for (int i = 0; i < 1000; i++)
                {
                    using (Mat image = new Mat())
                    using (VideoCapture capture = VideoCapture.FromCamera(1))
                    { 
                        capture.Read(image);
                        Cv2.WaitKey(1000);
                        capture.Release();
                        image.Release();
                    } 
                }

解决办法:

把代码行:

using (VideoCapture capture = VideoCapture.FromCamera(1))

改成以下(指定API格式),内存立即平稳:

using (VideoCapture capture = VideoCapture.FromCamera(1,VideoCaptureAPIs.DSHOW))

在github OpenCV的项目讨论中找到的灵感,不得不说洋人讨论问题还是比较用心和无私。

问题2.尝试读取或写入受保护的内存。这通常指示其他内存已损坏。

这个问题出现的很隐蔽,直到我从网上一片文章找到的观点“非托管内存对象,实例化最好当场用掉,不要通过回调/事件等在线程之间传来传去” ,才知道可能的问题: 我源代码有把Mat对象传给百度人脸SDK处理(寻找人脸,画框之类的),然后通过回调函数把Mat对象交给UI线程显示。因此,我首先保守的把Mat 变量用using方式使用(用完立即回收),然后把要传给UI线程的Mat对象克隆一份交给视图使用(也许不是最佳的效率),然后问题迎刃而解。修改后的代码片段如下:

using (Mat image = new Mat())
{
    isWorking = true;
    cap.Read(image); // same as cvQueryFrame
    if (!image.Empty())
    {

        BDFaceTrackInfo[] track_info = new BDFaceTrackInfo[ilen];
        for (int i = 0; i < ilen; i++)
        {
            track_info[i].box = new BDFaceBBox();
            track_info[i].box.score = 0;
            track_info[i].box.width = 0;
            track_info[i].landmark.data = new float[144];
            track_info[i].face_id = 0;
        } 
        int faceSize = ilen;//返回人脸数  分配人脸数和检测到人脸数的最小值
        int curSize = ilen;//当前人脸数 输入分配的人脸数,输出实际检测到的人脸数
        int type = 0;
        IntPtr ptT = Marshal.AllocHGlobal(sizeTrack * ilen);

        faceSize = track(ptT, image.CvPtr, type);
        for (int index = 0; index < faceSize; index++)
        {
            IntPtr ptr = new IntPtr();
            if (8 == IntPtr.Size)
            {
                ptr = (IntPtr)(ptT.ToInt64() + sizeTrack * index);
            }
            else if (4 == IntPtr.Size)
            {
                ptr = (IntPtr)(ptT.ToInt32() + sizeTrack * index);
            }

            track_info[index] = (BDFaceTrackInfo)Marshal.PtrToStructure(ptr, typeof(BDFaceTrackInfo));
        }

        Marshal.FreeHGlobal(ptT);
        FaceDraw.draw_rects( image, faceSize, track_info);

        uiThread(image.Clone()); 

        Console.WriteLine("mat not empty");
    }
    else
    {
        Console.WriteLine("mat is empty");
    }
}

注意:

using (Mat image = new Mat()) 

uiThread(image.Clone());

最后祝同学们debug愉快,也许我的一小步,是码友们一大步:)


网站公告

今日签到

点亮在社区的每一天
去签到