简 介: 本文给出了一个估计视频背景 的简便可靠的算法,对于固定镜头下的视频,包含有少量移动物体,可以通过中值滤波的方式来获得图像的背景图像。再借助于帧间差可以获得图像中移动物体的掩膜,进一步进行物体检测、跟踪和识别。
关键词 : 背景建模,帧间差
简 介
目 录
Contents
时间中值滤波
中值背景估计
利用中值进
行背景估计
背景估计代码
帧间差
总 结
本文根据 Simple Background Estimation in Videos using OpenCV (C++/Python) 中的内容整理而得。
?
§00 简??介
??在很多计算机视觉应用场合,你所能支配的计算机算力不够用的。这种情况下,我们需要使用有效简单的图像处理技术。
??针对于固定相机,且镜头中存在移动物体的场景中对背景进行估计应用中,本文将会讨论相关的技术。这种应用场景实际并不少见。举例来说,对于很多交通监控摄像头都是固定不同的。
注意,本文中的代码在OpenCV 4.4中进行测试的。
0.1 时间中值滤波
??为了便于理解本文后面算法,我们先考虑一个 1D 的简单问题。
??假定我们需要估计一个每隔10ms的物理量(比如房间内的温度)。比如,房间温度是70华氏度。
▲ 图1.1 房间温度测量曲线
??在上面图表中,我们显示了两个温度计测量曲线一个温度计是好的,另外一个有点毛病。
??左边曲线显示了来自于好的温度计测量曲线,其中包含了定义水平的高斯噪声。为了得到更加准确的温度值,我们可以把几秒钟之内的数值求平均。既然高速噪声具有正和负的取值,所以简单的平均计算便可以消除噪声的影响。实际上,这个特例中平均值为70.01华氏度。
??存在故障的温度计,则不然,它在大多数情况下输出的数值和好的温度计是相同的。但有时候抽风,输出完全错误的数值。
??实际上,如果我们仍然采用取平均的算法,坏的温度计输出数值为71.07华氏度,显然温度值估计偏高了。
??这种情况下还能够获得温度的精确估计吗?
??答案是:可以的。如果数据存在“野值”,也就是完全错误的数值,使用数据的中值可以对需要估计的数值产生更加可靠稳定的估计。
??所谓的中值就是对所有数据按照升序,或者降序进行排列的时候的中间取值。
??上述坏的温度计数据如果使用中值滤波所得到的温度为70.05华氏度,比71.07的平均数值要好。
??中值计算的唯一确定就是比起平均算法所需要的算力更多。
?
§01 中值背景估计
1.1 利用中值进行背景估计
??现在我们回到对于静止相机所拍摄的视频背景估计的问题上来。我们可以假设在大多数情况下每个像素都看到相同地点的北京,这是因为相机是固定的。偶然情况下,汽车或者其他移动物体经过前面挡住了背景。
??对于视频序列,我们可以随机选择一些视频帧(比如25帧,大约1秒钟)。换而言之,对于每个图像中的像素我们都有了25次的估计值。由于图像中每个像素没有被移动物体或者车辆遮挡的时间超过50%,所以25帧图像中所有像素中值就是对于背景的最好估计。
??对于图像中每个像素都使用中值进行估计,便可以得到一个中值图像,它代表了场景的背景。
1.2 背景估计代码
??下面让我们看看实际的代码。
import numpy as np
import cv2
from skimage import data, filters
cap = cv2.VideoCapture('video.mp4')
frameIds = cap.get(cv2.CAP_PROP_FRAME_COUNT) * np.random.uniform(size=25)
frames = []
for fid in frameIds:
cap.set(cv2.CAP_PROP_POS_FRAMES, fid)
ret, frame = cap.read()
frames.append(frame)
medianFrame = np.median(frames, axis=0).astype(dtype=np.uint8)
cv2.imshow('frame', medianFrame)
cv2.waitKey(0)
using namespace std;
using namespace cv;
??下面我们需要创建几个函数来计算中值图像帧。
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;
}
int main(int argc, char const *argv[])
{
std::string video_file;
// Read video file
if(argc > 1)
{
video_file = argv[1];
} else
{
video_file = "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);
}
??正如你所看到的,我们随机选择了25帧图片, 计算每个像素在25帧中的中值,这个中值帧就是对于背景的很好的估计。这是因为每个像素至少在50%的时间中是背景图像。
??下面显示了背景结果。
▲ 图1.1 通过计算式25帧每个像素对应的中值组成了对于背景的估计
?
§02 帧间差
??很明显,下一个问题就是我们是否可以对每一帧获得一个移动物体的掩膜(mask)。
??可以通过以下步骤来完成: ??1. 将背景帧转换成灰度图; ??2. 对于视频中的所有帧, 转换成对应的灰度图; ??3. 计算机当前帧的灰度图与背景帧之间的差异的绝对值; ??4. 通过阈值判断、噪声去除、二值化最终获得输出结果。
??下面是对应的程序代码:
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
grayMedianFrame = cv2.cvtColor(medianFrame, cv2.COLOR_BGR2GRAY)
ret = True
while(ret):
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
dframe = cv2.absdiff(frame, grayMedianFrame)
th, dframe = cv2.threshold(dframe, 30, 255, cv2.THRESH_BINARY)
cv2.imshow('frame', dframe)
cv2.waitKey(20)
cap.release()
cv2.destroyAllWindows()
// Reset frame number to 0
cap.set(CAP_PROP_POS_FRAMES, 0);
// Convert background to grayscale
Mat grayMedianFrame;
cvtColor(medianFrame, grayMedianFrame, COLOR_BGR2GRAY);
// Loop over all frames
while(1)
{
// Read frame
cap >> frame;
if (frame.empty())
break;
// Convert current frame to grayscale
cvtColor(frame, frame, COLOR_BGR2GRAY);
// Calculate absolute difference of current frame and the median frame
Mat dframe;
absdiff(frame, grayMedianFrame, dframe);
// Threshold to binarize
threshold(dframe, dframe, 30, 255, THRESH_BINARY);
// Display Image
imshow("frame", dframe);
waitKey(20);
}
cap.release();
return 0;
}
?
※ 总??结 ※
??本文给出了一个估计视频背景 的简便可靠的算法,对于固定镜头下的视频,包含有少量移动物体,可以通过中值滤波的方式来获得图像的背景图像。再借助于帧间差可以获得图像中移动物体的掩膜,进一步进行物体检测、跟踪和识别。
??对于图像中移动物体比较多,频繁,可以中值滤波无法准确获得背景。则可以采用:
■ 相关文献链接:
● 相关图表链接:
|