目录
一、帧差法
1、概念
2、为什么帧差法可以检测运动的物体?
二、使用OpenCV配合帧差法实现车辆识别
1、加载视频
2、灰度处理+帧差计算
3、二值化
4、腐蚀
5、膨胀
6、框选出车辆
三、全部代码+实现效果
1、代码
2、车辆检测效果
四、帧差法存在不足之处
帧差法是一种通过对视频图像序列中 相邻两帧作差分运算 来获得运动目标轮廓的方法,它可以很好地适用于存在多个运动目标和摄像机移动的情况。
当监控场景中出现异常物体运动时,帧与帧之间会出现较为明显的差别,两帧相减,得到两帧图像 亮度差的绝对值 ,判断它是否大于 阈值 来分析视频或图像序列的运动特性,确定图像序列中有无物体运动。
还记得小时候的葫芦娃动画吗?每个人物其实都是一张剪纸,也被叫做“剪纸动画”,剪一张就是一帧,假设葫芦娃动画为每秒25帧,1秒内连续播放25张不同的剪纸。
因为每一帧之间是有差异的,所以我们可以看到剪纸 动 起来了。
博主有以下同一条鲨鱼的不同形态的png图片,使用图片查看来切换显示每一张图片
可以看到鲨鱼动起来了!!!
因此可以通过判断 前后两帧是否相同 ,来判断是否有运动的物体,即通过帧差法来检测运动的物体。
所以下面跟着博主来学习使用OpenCV通过帧差法来进行移动车辆的识别。当然不止可以识别车辆,其他移动的物体也可以识别。
Qt 5.8.0 + OpenCV
int main(int argc, char *argv[])
{
Mat frame;
VideoCapture cap("D:/QT-Project/image/carMove.mp4");
while(cap.read(frame))
{
//读取一帧显示一帧
imshow("frame", frame);
//延时
waitKey(25);
}
return 0;
}
因为彩色图片是3通道(RGB)24位深度的图像,而灰度图是单通道8位深度的图像,因此处理灰度图比彩色图效率快多了。
//灰度处理
cvtColor(frontMat, frontGray, CV_BGR2GRAY);
cvtColor(afterMat, afterGray, CV_BGR2GRAY);
//帧差处理 找到帧与帧之间运动的物体差异
absdiff(frontGray, afterGray, diffGray);
通过下图可以发现检测是检测出来了,但是画面非常的暗淡(不清晰),因此需要通过二值化来让图像更清晰点。
参数一为原图,参数二为处理后的图,直接将处理后的图覆盖掉原图即可
//二值化:黑白分明 会产生大量白色噪点
threshold(diffGray, diffGray, 25, 255, CV_THRESH_BINARY);
可以发现图像确实是变“清晰”了,因为二值化后的图像只有黑白两种颜色。并且我们还可以发现白色噪点非常多,因为摄像机抖动,风吹树叶等原因,因此还需要通过腐蚀来去除掉这些白色噪点。
腐蚀是针对图片的二值化数据进行操作的,主要是针对高亮部分。使用算法,将图像的边缘腐蚀掉。作用就是将目标的边缘的“毛刺”踢除掉。
如下图所示:
//腐蚀处理:去除白色噪点 噪点不能完全去除,反而主要物体会被腐蚀的图案都变得不明显
Mat element = cv::getStructuringElement(MORPH_RECT, Size(3, 3));
erode(diffGray, diffGray, element);
白色噪点确实是被去除了,但是我们的车辆也被腐蚀的不成车样(内部坑坑洼洼的),所以还需要通过膨胀将车辆进行进一步处理。
膨胀是针对图片的二值化数据进行操作的,主要是针对高亮部分。使用算法,将图像的边缘扩大些。作用就是将目标的边缘或者是内部的坑填掉。
如下图所示:
//膨胀处理:将白色区域变“胖”
Mat element2 = cv::getStructuringElement(MORPH_RECT, Size(20, 20));
dilate(diffGray, diffGray, element2);
我们的车辆变成一个个大方块了,做到这一步差不多就可以来标记运动的车辆了,只要画矩形将白色大方块框起来即可。
框选的原理就是找到白色方块最左边的点与最右边的点,得到之间的大小差距(矩形宽),找到白色方块最上边的点与最下边的点,得到之间的大小差距(矩形高)。
通过宽高即可画出一个把白色方块包含在内的矩形,矩形左上角坐标通过白色方块最上方的值和最左方的值来确定。
#include
#include
using namespace std;
using namespace cv;
//帧差法检测车辆
Mat MoveCheck(Mat &frontMat, Mat &afterMat)
{
Mat frontGray ,afterGray, diffGray;
Mat resframe = afterMat.clone();
//灰度处理
cvtColor(frontMat, frontGray, CV_BGR2GRAY);
cvtColor(afterMat, afterGray, CV_BGR2GRAY);
//imshow("GRAY", frontGray);
//帧差处理 找到帧与帧之间运动的物体差异
//缺点:会把其他运动物体也算进来
absdiff(frontGray, afterGray, diffGray);
//imshow("absdiff", diffGray);
//二值化:黑白分明 会产生大量白色噪点
threshold(diffGray, diffGray, 25, 255, CV_THRESH_BINARY);
//imshow("diff", diffGray);
//腐蚀处理:去除白色噪点 噪点不能完全去除,反而主要物体会被腐蚀的图案都变得不明显
Mat element = cv::getStructuringElement(MORPH_RECT, Size(3, 3));
erode(diffGray, diffGray, element);
//imshow("erode", diffGray);
//膨胀处理:将白色区域变“胖”
Mat element2 = cv::getStructuringElement(MORPH_RECT, Size(20, 20));
dilate(diffGray, diffGray, element2);
//imshow("dilate", diffGray);
//动态物体标记
vector> contours; //保存关键点
findContours(diffGray, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
//提取关键点
vector> contours_poly(contours.size());
vector boundRect(contours.size());
int x, y, w, h;
int num = contours.size();
for(int i =0; i< num; i++)
{
approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
boundRect[i] = boundingRect(Mat(contours_poly[i]));
x = boundRect[i].x;
y = boundRect[i].y;
w = boundRect[i].width;
h = boundRect[i].height;
//绘制
rectangle(resframe, Point(x, y), Point(x+w, y+h), Scalar(0, 255, 0), 4);
}
return resframe;
}
int main(int argc, char *argv[])
{
Mat frame;
Mat tempframe;
Mat res;
int count = 0;
VideoCapture cap("D:/QT-Project/image/carMove.mp4");
while(cap.read(frame))
{
count++;
if(count == 1)
{
res = MoveCheck(frame, frame);
}
else
{
res = MoveCheck(tempframe, frame);
}
tempframe = frame.clone();
imshow("frame", frame);//原视频帧
imshow("res", res);//框选后的视频帧
waitKey(25);
}
return 0;
}
帧差法虽然能够检测出运动的车辆,但是不仅包括车辆,任何运动的物体都会检测出来,就像上图所示,一旦相机抖动或者突然起大风,运动的物体就多了起来(两帧差异的地方很多),因此就会出现上面那种情况。
The end ……
原创不易,转载请标明出处。
对您有帮助的话可以一键三连,会持续更新的(嘻嘻)。