OpenCv - 从网络摄像头捕获帧时发生内存泄漏

Posted

技术标签:

【中文标题】OpenCv - 从网络摄像头捕获帧时发生内存泄漏【英文标题】:OpenCv - Memory leak when capturing frames from webcam 【发布时间】:2015-01-04 04:54:07 【问题描述】:

我正在编写一个 C 应用程序,它使用 OpenCv 从网络摄像头捕获图像,然后将图像保存到文件中。它在针对 OpenCv 2.3.1-11 的 Raspian OS Wheezy 上运行。

如果我只是像这样打开和关闭网络摄像头,就没有内存泄漏,所以我认为我不会遇到关于“网络摄像头开放税”的旧错误:

CvCapture* capture;
while (1) 
  // No increase in memory consumption at all
  capture = cvCreateCameraCapture(0);
  cvReleaseCapture(&capture);

但是,当我真正开始获取图像时,内存消耗会激增,并且每次迭代都会消耗另外 2MB 的内存。我在手动触发图像采集时在命令行通过free -s 2 确认,并注意到最终,我的应用程序抱怨没有足够的内存来采集图像。

capture = cvCreateCameraCapture(0);
while (1) 
   if (capture) 
      frame = cvQueryFrame(capture);
   
   if (frame) 
      CvSize size = cvSize(100, 100);
      tmp = cvCreateImage(size, IPL_DEPTH_8U, 3);
      cvResize(frame, tmp, CV_INTER_CUBIC);
      // Do some stuff with tmp
   

cvReleaseCapture(&capture);

我在网上查了很多帖子,都说我不支持修改frame中存储的数据。如果我在frame 上使用cvReleaseImage,它没有效果。事实上,内存使用量仅因cvQueryFrame 调用而爆炸式增长。即使是下面的示例也会导致此问题弹出:

capture = cvCreateCameraCapture(0);
while (1) 
   if (capture) 
      frame = cvQueryFrame(capture);
   

cvReleaseCapture(&capture);

我该如何解决这个问题?

我真的必须使用 C++ API(目前对我来说不是一个真正的选择),还是有其他方法可以解决这个问题?这是一个 Gtk+-2.0 应用程序,我包含的唯一标题是:

/*******************************************************************************
 * Preprocessor Directives
 ******************************************************************************/
#include "opencv/cxcore.h"
#include "opencv2/highgui/highgui_c.h"
#include "opencv2/imgproc/imgproc_c.h"

#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

那里似乎混合了opencvopencv2,但我找不到只有opencv 标题并在没有它们的情况下访问相机的方法。我担心这可能会导致某些仅 C++ 的代码以未定义的方式初始化。解决内存泄漏的任何帮助,或者如果需要,在 C++ 中创建没有泄漏的等效代码将非常有帮助。

感谢您的帮助。

【问题讨论】:

cvReleaseImage on frame, it has no effect你怎么知道没有效果?如果您使用任务管理器或其他一些操作系统工具来确定内存是否泄漏,请不要使用它。使用此类工具不会确定内存泄漏。 @PaulMcKenzie 无论这些工具的工作情况如何,如果我在控制台上监控free -s 2 的输出,每次手动触发另一次图像采集时,我都会看到可用系统内存总量下降.我还可以分析应用程序并确认它正在消耗更多内存。 tmp 曾经发布过吗? @PaulMcKenzie 是的,tmp 最终通过一个单独的函数发布,该函数只调用 cvReleaseImage(IplImage** imgToFree)。我还添加了另一个仅调用 cvQueryFrame 导致相同问题的示例。谢谢! 您的线程是否在不释放 tmp 等的情况下中断/取消/重新启动/修改? 【参考方案1】:

这个不会泄露给我的:

#include <opencv2\opencv.hpp>
int main()

    CvCapture* capture;
    IplImage* frame;

    capture = cvCreateCameraCapture(0);
    while (1) 
    
       if (capture) 
       
          frame = cvQueryFrame(capture);
       
    
    cvReleaseCapture(&capture);

这个明显漏了:

int main()

    CvCapture* capture;
    IplImage* frame;
    IplImage* tmp;

    capture = cvCreateCameraCapture(0);
    while (1) 
       if (capture) 
          frame = cvQueryFrame(capture);
       
       if (frame) 
          CvSize size = cvSize(100, 100);
          tmp = cvCreateImage(size, IPL_DEPTH_8U, 3);
          cvResize(frame, tmp, CV_INTER_CUBIC);
          // Do some stuff with tmp
       
    
    cvReleaseCapture(&capture);

虽然这个没有泄漏:

int main()

    CvCapture* capture;
    IplImage* frame;
    IplImage* tmp;

    capture = cvCreateCameraCapture(0);
    while (1) 
       if (capture) 
          frame = cvQueryFrame(capture);
       
       if (frame) 
          CvSize size = cvSize(100, 100);
          tmp = cvCreateImage(size, IPL_DEPTH_8U, 3);
          cvResize(frame, tmp, CV_INTER_CUBIC);
          // Do some stuff with tmp

          // release tmp:
          cvReleaseImage(&tmp);
       
    
    cvReleaseCapture(&capture);

你能试试最后一个例子吗?如果它确实泄漏,您可能确实遇到了错误的标头或链接库的问题。

【讨论】:

显然,因为 while(1) 从未调用过 cvReleaseCapture(&amp;capture),这也是某种泄漏,但对于最后一个示例,内存使用量不应随时间增加。 我在顶部发布了额外的 cmets。当我在main() 中将其作为循环运行时,您的示例有效,但是当它在单独的pthread 中运行时,内存泄漏行为会恢复。【参考方案2】:

我今天 [01/04/2015] 进行了检查,在当前的 wheezy/main 存储库中,opencv(gpu 除外)的开发包链接到 openCV 2.4.1。这里测试步骤:

安装包:

sudo apt-get install cmake build-essential libopencv-core-dev libcv-dev libcvaux-dev libhighgui-dev libopencv-calib3d-dev libopencv-contrib-dev libopencv-core-dev libopencv-dev libopencv-features2d-dev libopencv-flann-dev libopencv-highgui-dev libopencv-imgproc-dev libopencv-legacy-dev libopencv-ml-dev libopencv-objdetect-dev libopencv-video-dev

验证 openCv 库安装版本:

sudo ldconfig -v | grep opencv

结果:

libopencv_imgproc.so.2.4 -> libopencv_imgproc.so.2.4.1
libopencv_highgui.so.2.4 -> libopencv_highgui.so.2.4.1
libopencv_legacy.so.2.4 -> libopencv_legacy.so.2.4.1
libopencv_objdetect.so.2.4 -> libopencv_objdetect.so.2.4.1
libopencv_calib3d.so.2.4 -> libopencv_calib3d.so.2.4.1
libopencv_videostab.so.2.4 -> libopencv_videostab.so.2.4.1
libopencv_ml.so.2.4 -> libopencv_ml.so.2.4.1
libopencv_core.so.2.4 -> libopencv_core.so.2.4.1
libopencv_ts.so.2.4 -> libopencv_ts.so.2.4.1
libopencv_stitching.so.2.4 -> libopencv_stitching.so.2.4.1
libopencv_photo.so.2.4 -> libopencv_photo.so.2.4.1
libopencv_flann.so.2.4 -> libopencv_flann.so.2.4.1
libopencv_features2d.so.2.4 -> libopencv_features2d.so.2.4.1
libopencv_video.so.2.4 -> libopencv_video.so.2.4.1
ibopencv_contrib.so.2.4 -> libopencv_contrib.so.2.4.1

测试程序

#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <errno.h>

#include "opencv/cxcore.h"
#include "opencv2/highgui/highgui_c.h"
#include "opencv2/imgproc/imgproc_c.h"

int main(int argc, char * const argv[])

    struct rusage usage;
    long max_resident_set_size = 0;
    long frame_no = 0;

    CvCapture* capture;
    IplImage*  frame;
    capture = cvCreateCameraCapture(0);

    if (!capture) 
        // error getting webcam
        return 1;
    

    //  test 100 frames to check memory usage
    while (frame_no < 100) 
        frame = cvQueryFrame(capture);
        frame_no++;

        errno = 0;
        getrusage(RUSAGE_SELF, &usage);
        if (errno == EFAULT)
            printf("Error: EFAULT\n");
        else if (errno == EINVAL)
            printf("Error: EINVAL\n");
        else if (max_resident_set_size != usage.ru_maxrss) 

            printf("frame %ld maximum resident set size: %ld\n", frame_no, usage.ru_maxrss);
            printf("frame %ld maximum resident set size diff : %ld\n", frame_no, (usage.ru_maxrss - max_resident_set_size));

            max_resident_set_size = usage.ru_maxrss;
        
    

    cvReleaseCapture(&capture);
    return 0;

在 OpenCV 2.4 中使用上述代码没有内存问题。

我建议你卸载 openCV 2.3.1 包,更新系统并安装最新的并使用 openCV2 符号。

列出已安装的包:

sudo dpkg --get-selections | grep -v deinstall | grep cv

希望对您有所帮助。

【讨论】:

【参考方案3】:

经过大量测试,事实证明 OpenCv 2.3.1-11 在使用 pthreads 时有一些奇怪的行为。

如果我只是在 main() 的循环中使用以下内容,则没有问题。

int main()

    CvCapture* capture;
    IplImage* frame;
    IplImage* tmp;

    capture = cvCreateCameraCapture(0);
    while (1) 
       if (capture) 
          frame = cvQueryFrame(capture);
       
       if (frame) 
          CvSize size = cvSize(100, 100);
          tmp = cvCreateImage(size, IPL_DEPTH_8U, 3);
          cvResize(frame, tmp, CV_INTER_CUBIC);
          // Do some stuff with tmp

          // release tmp:
          cvReleaseImage(&tmp);
       
    
    cvReleaseCapture(&capture);

如果我在pthread 中使用相同的循环,每次迭代都会导致至少 2MB 的内存丢失。但是,如果在pthread 中,我只调用capture = cvCreateCameraCapture(0)cvReleaseCapture(&amp;capture) 一次,则问题不再存在。

此外,如果我在 main 中调用 cvCreateCameraCapture(0) 并将其分配给可从线程访问的东西(即:全局变量,或将其存储在上下文变量中,我在生成线程时将指针传递给线程),我在 pthreadcvQueryFrame() 的每个调用中都会出现内存泄漏行为。

因此,简而言之,如果在多线程程序中使用 C API(我知道已弃用),有多种方法可以让您自食其果。谢谢大家的意见。

【讨论】:

以上是关于OpenCv - 从网络摄像头捕获帧时发生内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV:使用 cvWriteFrame 从网络摄像头写入视频时出现内存泄漏

MacOS 上的视频捕获

使用 OpenCV Python 从 Android 智能手机捕获视频

从网络摄像头捕获帧,它在 opencv cv2.VideoCapture() 中仅返回 1 帧

洞察使用 python 和 OpenCV 与 VideoCapture 进行多网络摄像头捕获

如何在 OpenCV 中使用网络摄像头捕获 Mat 图像的像素值