欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

关于opencv读取摄像头的未解之谜

程序员文章站 2022-05-17 20:35:37
...

    前段时间做项目需要用opencv读usb摄像头的视频数据,遇到很多无解的问题,虽然后来没有用到,但是还是记录下来,也许以后就知道答案了呢。

1、无论摄像头的实际分辨率是多少,opencv读进来的视频分辨率都是640*480大小的,网上说可以用内置的函数修改分辨率的大小,我试了下下面的函数语句,虽然分辨率确实变成了720p,但是清晰度并没有提升甚至有些模糊,看起来像是强行插值扯大的,没有真正得到清晰度720p的图像。

	cap.set(CV_CAP_PROP_FRAME_WIDTH, 1280);//不起作用
	cap.set(CV_CAP_PROP_FRAME_HEIGHT, 720);

     另外,看网上有资料说可以修改opencv的默认值,找到了设置640*480的地方在modules/highgui/src/cap_v4l.cpp,但是修改之后编译并没有什么变化,不知道编译的方式是不是不对。

2、无论摄像头的帧率多少,用opencv内置的函数读到的帧率永远是0.

	double fps = cap.get(CV_CAP_PROP_FPS);//帧率总是得到0

    后来用内置语句对帧率进行设置,没有明显的变化,但是再次输出时,帧率的确是1.也许是显示的原因,设置waitkey=1000ms就可以真正达到一秒一帧。

cap.set(CV_CAP_PROP_FPS, 1);

3、最大的谜团是关于循环读摄像头,最终会内存泄漏这个问题。解决这个问题引申出很多其他问题,比如opencv读摄像头的缓存机制,抓取和显示的延迟,内存的释放等等。

    刚开始没有考虑释放内存,因为觉得Mat图像有自己的释放机制,但是往往只读到7-8万帧程序就崩溃了。实际上,Mat的析构函数只有在程序退出时才会起作用,所以while循环读图时没有调用析构函数,需要每用完一帧手动释放一帧,也就是加了一句frame.release,这样程序可以一直运行(只测试了一天一夜)。程序如下:

int main()
{
	VideoCapture cap(0);
	if (!cap.isOpened())
	{
		return -1;
	}

	Mat frame;
	long currentFrame = 0;//计数
	bool stop = false;
	char image_name[100];//用来存储保存的图片名字

	while (!stop)
	{
		 cap >> frame; //存储每一帧图像	  
		 if ( !frame.empty())   //注意要判断图像是否为空!!
		{
			//处理这张图..........算法	
			sprintf(image_name, "%d%s", currentFrame, ".bmp");//保存的图片名
			imwrite(image_name, frame);//保存图片

			imshow("【检测结果】", frame);//视频显示 	
			cout << "正在判断的帧数为:" << currentFrame << "判断结果为:" << "正常" << endl;
		}
		if (waitKey(30) >= 0)
			stop = true;

		currentFrame++;//帧数+1
		frame.release();
	}
	return 0;
}

       我以为这样就能够及时释放内存,然而在while里面加了算法的处理程序以后,依然会出现动态分配内存空间不足的问题,说明已经没有足够连续的内存能用了。当然,由于我的算法只能达到1秒一帧,不能实时的读取-处理-释放,所以想每隔一定的帧数处理一帧,但是要考虑帧率和算法的时间等问题,也有可能是这个原因导致内存不能及时释放。程序如下:

int main()
{
	VideoCapture cap(0);
	if (!cap.isOpened())
	{
		return -1;
	}

	Mat frame;
	Mat edges;
	long currentFrame = 0;//计数
	int interval = 20;//每隔20帧处理一帧,具体间隔可根据算法时间、摄像头帧率、传送带速度配合修改
	double t = 0;
	bool stop = false;
	char image_name[100];//用来存储保存的图片名字

	while (!stop)
	{
		 cap >> frame; //存储每一帧图像	  
		 if ( currentFrame % interval == 0  &&!frame.empty())   //每隔50帧且图片不为空时,处理这一帧.
		{
			//处理这张图..........算法	
			cvtColor(frame, edges, CV_BGR2GRAY);
			GaussianBlur(edges, edges, Size(7, 7), 1.5, 1.5);
			Canny(edges, edges, 0, 30, 3);
			sprintf(image_name, "%d%s", currentFrame, ".bmp");//保存的图片名
			imwrite(image_name, frame);//保存图片

			imshow("【检测结果】", frame);//视频显示 	
			cout << "正在判断的帧数为:" << currentFrame << "判断结果为:" << "正常" << endl;
		}

		if (waitKey(30) >= 0)
			stop = true;

		currentFrame++;//帧数+1
		frame.release();
	}
	return 0;
}

       看遍了所有的博客都没有找到有效的解决办法,后来看到外国论坛上很多人讨论这个问题,较多的解决办法是使用多线程,一个线程去抓取只保留当前帧,另一个线程去处理。多线程在实际中很多地方都用到,比如这次公司影音部门的人就提出,将一张图划分为4块,每块并行处理以加快速度。以后会慢慢去了解这一块。

4、OpenCV读取摄像头会产生一定的滞后,目前显示或者处理的图像,有可能是之前的图像帧,没有找到什么规律去知道到底处理的哪一帧。论坛上有人说,相机有一定的缓存机制,每次缓存五张图,只保存新的一张到硬盘里。帧的缓冲存在于硬件层面,无法避免。