当前位置: 代码网 > it编程>编程语言>Java > ubuntu22.04@laptop OpenCV Get Started: 014_simple_background_estimation_in_videos

ubuntu22.04@laptop OpenCV Get Started: 014_simple_background_estimation_in_videos

2024年08月04日 Java 我要评论
在许多计算机视觉应用中,可用的处理能力有限。在这种情况下,必须使用简单但有效的技术。在本文中,将介绍一种针对场景背景估计的技术,当摄像头静止对场景中的移动对象进行检测。这种场景并不罕见,例如:许多交通和监控摄像头都是固定不动的。

1. 源由

在许多计算机视觉应用中,可用的处理能力有限。在这种情况下,必须使用简单但有效的技术。

在本文中,将介绍一种针对场景背景估计的技术,当摄像头静止对场景中的移动对象进行检测。这种场景并不罕见,例如:许多交通和监控摄像头都是固定不动的。

2. 应用demo

014_simple_background_estimation_in_videos是opencv背景分析技术,在这种特定场景中,对移动物体进行检测的技术。

2.1 c++应用demo

c++应用demo工程结构:

014_simple_background_estimation_in_videos/cpp$ tree .
.
├── cmakelists.txt
└── remove_video_bg.cpp

0 directories, 2 files

确认opencv安装路径:

$ find /home/daniel/ -name "opencvconfig.cmake"
/home/daniel/opencv/installation/opencv-4.9.0/lib/cmake/opencv4/
/home/daniel/opencv/opencv/build/opencvconfig.cmake
/home/daniel/opencv/opencv/build/unix-install/opencvconfig.cmake


$ export opencv_dir=/home/daniel/opencv/installation/opencv-4.9.0/lib/cmake/opencv4/

c++应用demo工程编译执行:

$ mkdir build
$ cd build
$ cmake ..
$ cmake --build . --config release
$ cd ..
$ ./build/remove_video_bg

2.2 python应用demo

python应用demo工程结构:

014_simple_background_estimation_in_videos/python$ tree .
.
├── remove_video_bg_save.py
└── remove_video_bg.py

0 directories, 2 files

python应用demo工程执行:

$ workoncv-4.9.0
$ python remove_video_bg.py
$ python remove_video_bg_save.py

3. 时间中值滤波

让我们考虑一个在一维空间中的简单问题。假设我们每 10 毫秒估计一个数量(比如房间的温度)。

假设房间的温度是华氏 70 度。

在这里插入图片描述在上图中,我们展示了来自两个温度计的测量结果 — 一个好的温度计和一个坏的温度计。

左侧显示的好的温度计报告的是 70 度,带有一定程度的高斯噪声。为了获得更准确的温度估计,我们可以简单地在几秒钟内对数值进行平均。由于噪声是高斯噪声,具有正负值,因此平均值将消除噪声。实际上,在这种特定情况下,平均值为 70.01 度。

另一方面,坏的温度计大部分时间的行为类似于好的温度计,但偶尔,数字完全错误。

事实上,如果我们取坏温度计报告的数字的平均值,我们得到 71.07 度。这显然是一个过高的估计。

我们是否仍然可以获得一个良好的温度估计?

答案是肯定的。当数据中包含异常值时,中值是我们试图估计的值的更稳健的估计。

中值是将数据按升序或降序排序后的中间值。

上述曲线的中值是 70.05 度,这比 71.07 度要好得多的估计。

唯一的缺点是相对于平均值/平均值而言,中值的计算成本更高。

4. 使用中值进行背景估计

在计算机视觉中,背景估计是许多应用的关键步骤之一。而使用中值进行背景估计是一种常见且有效的技术。

当摄像头处于静止状态时,我们可以假设大多数时间每个像素看到的是相同的背景,因为摄像头没有移动。只是偶尔会有汽车或其他移动物体进入前景,遮挡了背景。

对于一个视频序列,我们可以随机采样几帧(比如 25 帧)。这意味着对于每个像素,我们现在有 25 个背景的估计值。只要一个像素在这 25 帧中被汽车或其他移动物体遮挡的时间不超过 50%,那么在这 25 帧中该像素的中值将给出该像素的背景的良好估计。

我们可以对视频中的每个像素重复这个过程,并恢复整个背景。这种方法相对简单且计算效率高,因此在许多计算机视觉应用中被广泛采用。

4.1 背景评估

通过背景评估图像,可以给出路上无车时,整个画面情况。

在这里插入图片描述

c++:

	std::string video_file;
	// read video file
	if(argc > 1) {
		video_file = argv[1];
	} else {
		video_file = "../input/video.mp4";
	}

	videocapture cap(video_file);

	if(!cap.isopened())
		cerr << "error opening video file\n";

	// randomly select 25 frames
	default_random_engine generator;
	uniform_int_distribution<int>distribution(0, cap.get(cap_prop_frame_count));

	vector<mat> frames;
	mat frame;

	for(int i=0; i<25; i++) {
		int fid = distribution(generator);
		cap.set(cap_prop_pos_frames, fid);
		mat frame;
		cap >> frame;
		if(frame.empty())
			continue;
		frames.push_back(frame);
	}

	// calculate the median along the time axis
	mat medianframe = compute_median(frames);

	// display median frame
	imshow("frame", medianframe);
	waitkey(0);

python:

# open video
cap = cv2.videocapture('../input/video.mp4')

# randomly select 25 frames
frameids = cap.get(cv2.cap_prop_frame_count) * np.random.uniform(size=25)

# store selected frames in an array
frames = []
for fid in frameids:
    cap.set(cv2.cap_prop_pos_frames, fid)
    ret, frame = cap.read()
    frames.append(frame)

# calculate the median along the time axis
medianframe = np.median(frames, axis=0).astype(dtype=np.uint8)    

# display median frame
cv2.imshow('frame', medianframe)
cv2.waitkey(0)

4.2 帧差法计算

显然的下一个问题是我们是否可以为每一帧创建一个遮罩,显示出运动中的图像部分。

这可以通过以下步骤来实现:

  1. 将中值帧转换为灰度图像。
  2. 循环遍历视频中的所有帧。提取当前帧并将其转换为灰度图像。
  3. 计算当前帧与中值帧之间的绝对差异。
  4. 对上述图像进行阈值化,以去除噪声并将输出进行二值化。

4.2.1 中值帧转换为灰度

c++:

	// convert background to grayscale
	mat graymedianframe;
	cvtcolor(medianframe, graymedianframe, color_bgr2gray);

python:

# convert background to grayscale
graymedianframe = cv2.cvtcolor(medianframe, cv2.color_bgr2gray)

4.2.2 遍历所有帧,并转换为灰度

c++:

		// read frame
		cap >> frame;

		if (frame.empty())
			break;

		// convert current frame to grayscale
		cvtcolor(frame, frame, color_bgr2gray);

python:

  # read frame
  ret, frame = cap.read()
  # convert current frame to grayscale
  frame = cv2.cvtcolor(frame, cv2.color_bgr2gray)

4.2.3 计算当前帧与中值帧差异

c++:

		// calculate absolute difference of current frame and the median frame
		mat dframe;
		absdiff(frame, graymedianframe, dframe);

python:

  # calculate absolute difference of current frame and 
  # the median frame
  dframe = cv2.absdiff(frame, graymedianframe)

4.2.4 进行阈值化去除噪声

c++:

		// threshold to binarize
		threshold(dframe, dframe, 30, 255, thresh_binary);
		
		// display image
		imshow("frame", dframe);

python:

  # treshold to binarize
  th, dframe = cv2.threshold(dframe, 30, 255, cv2.thresh_binary)
  # display image
  cv2.imshow('frame', dframe)

4.3 效果

基于中值背景估计运动检测

5. 总结

这里的中值法可以理解为取中间值。

hm…可能确实说的有点“拗口”,可以理解为去掉最大值,去掉最小值,取中间那个值。

python采用了numpy库中的median方法:
numpy.median(a, axis=none, out=none, overwrite_input=false, keepdims=false)

c++没有这个库,所以代码层面就需要自己实现:

int computemedian(vector<int> elements) {
	nth_element(elements.begin(), elements.begin()+elements.size()/2, elements.end());
	//sort(elements.begin(),elements.end());

	return elements[elements.size()/2];
}

cv::mat compute_median(std::vector<cv::mat> vec) {
	// note: expects the image to be cv_8uc3
	cv::mat medianimg(vec[0].rows, vec[0].cols, cv_8uc3, cv::scalar(0, 0, 0));

	for(int row=0; row<vec[0].rows; row++) {
		for(int col=0; col<vec[0].cols; col++) {
			std::vector<int> elements_b;
			std::vector<int> elements_g;
			std::vector<int> elements_r;

			for(int imgnumber=0; imgnumber<vec.size(); imgnumber++) {	
				int b = vec[imgnumber].at<cv::vec3b>(row, col)[0];
				int g = vec[imgnumber].at<cv::vec3b>(row, col)[1];
				int r = vec[imgnumber].at<cv::vec3b>(row, col)[2];
				
				elements_b.push_back(b);
				elements_g.push_back(g);
				elements_r.push_back(r);
			}

			medianimg.at<cv::vec3b>(row, col)[0] = computemedian(elements_b);
			medianimg.at<cv::vec3b>(row, col)[1] = computemedian(elements_g);
			medianimg.at<cv::vec3b>(row, col)[2] = computemedian(elements_r);
		}
	}
	return medianimg;
}

关于中值c++的api函数,可以参考下面具体链接:
void nth_element( randomit first, randomit nth, randomit last, compare comp )

实际情况可能更加复杂,比如:如下路口场景(中值方法获取的)

  1. 户外摄像头自动曝光调整亮度
  2. 摄像头收到外力影响而导致振动
  3. 全天候户外,日光会影响周边环境亮度(上述算法代码只是一次性去了25帧)

以上种种因素都会导致实际中值背景侦测移动物体受到干扰,详细情况下面效果。

注:后续,我们会再对这种实际场景进行分析和验证。

在这里插入图片描述

4k road traffic video for object detection and tracking

因此,面对实际问题,可能需要多种算法复合应用,甚至自研的算法来得到更加的效果。这里仅提供了熟悉入门的一个示例代码,希望能够让大家更好的理解和熟悉opencv。

6. 参考资料

【1】ubuntu22.04@laptop opencv get started
【2】ubuntu22.04@laptop opencv安装
【3】ubuntu22.04@laptop opencv定制化安装

7. 补充

学习是一种过程,对于前面章节学习讨论过的,就不在文中重复了。

有兴趣了解更多的朋友,请从《ubuntu22.04@laptop opencv get started》开始,一个章节一个章节的了解,循序渐进。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com