运动物体检测以及追踪
程序员文章站
2022-05-30 12:02:03
...
概括:运动物体检测是应用差分法实现,运动物体追踪是利用基于时间序列预测模型实现。
运动物体检测
常用的检测方法为背景减除法和帧差法,这两种方法原理基本上都是图片相减。两种方法各有优缺点:
帧差法适用于更多场景,如:摄像头移动以及多目标运动场景,缺点就是检测的人物之间容易出现空洞。
背景减除法,适用场景局限,只适用于第一帧是背景图的视频,但检测人物没有空洞。
以上这两种方法均不能很好的处理光照过强以及阴影的运动物体视频。
物体检测方法还有许多,有基于图像相减的三帧差法,还有对于电脑性能要求较高的光流法。
效果
背景减除法:(人物没有出现空洞现象,但对于光照过强区域效果不好)
帧差法:(人物出现空洞,对下一步处理造成影响)
运动物体追踪
之前一直对每一帧检测的运动物体进行标志就以为达到了追踪,但实时检测标志并不是追踪,若在多目标运动物体中没有做到区分不同的目标,就不算是追踪。
追踪思路
1.对每一帧进行运动目标检测
2.对检测到的物体进行判断,判断是否是存在的运动目标,是则添加运动轨迹,不是则当作新的目标。
3.画出运动轨迹等
如何判断当前已检测到的目标是否是已经存在的运动目标?
这里我刚开始选取的是简单的轮廓面积已经轮廓外接矩形等特征,因为轮廓获得的不稳定,显然效果不是很好。这里采用的是预测模型,在上一篇鼠标运动的预测中。简单来说就是用检测到的运动目标与预测的位置进行比较,若在一定误差内就算追踪成功。
运动视频下载
网上有很多的视频库,我下载视频库的链接
代码
头文件
#pragma once
#ifndef TrackObject
#define TrackObject
#include<opencv2\opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
class Blob {
public:
vector<Point>_contour;
vector<Point>centerPos;
Point preditNextCenter;
Point currentCenter;//first
Rect currentRect;//first
double currentArea;//first
double currentRate;//first
//bool isStillBeTracked;//是否还在被跟踪
//int FramesWithoutMatched;
Blob(vector<Point>con);
void Predit();
};
#endif // !TrackObject
Main
//问题:由于光照的影响很不稳定(对于寻找的轮廓),光照补偿(解决阴影)和去除光照
#include "ObjectTrack.h"
double DistanceBetwwenPoint(Point p1,Point p2)
{
int x =abs(p1.x - p2.x);
int y = abs(p1.y - p2.y);
double res = sqrt(pow(x, 2)) + sqrt(pow(y, 2));
//cout << "距离为:" << res << endl;
return res;
//sqrt(里面参数接受为double型)
}
vector<Blob>Blobs;
int main()
{
VideoCapture cap("Walk1.mpeg");//这个视频有轻微抖动现象,利用高斯滤波
//int framenum = cap.get(CAP_PROP_POS_FRAMES);
if (!cap.isOpened()) { cout << "读取视频错误" << endl; return 0; }
Mat cur, pre;
cap >> pre;
GaussianBlur(pre,pre,Size(3,3),0.8);
cvtColor(pre,pre,CV_BGR2GRAY);
while (1)
{
cap >> cur;
if (cur.empty()) { break; }//作用相当于判断是否到达视频最后一帧
GaussianBlur(cur,cur,Size(3,3),0.8);
cvtColor(cur,cur,CV_BGR2GRAY);
/*具体实现步骤*/
//1.背景减除法检测运动物体
Mat df;//difference frame
absdiff(cur, pre,df );
threshold(df,df,70,255,CV_THRESH_BINARY);
vector<vector<Point>>contours;//找到的轮廓均存在contours
findContours(df,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
vector<Blob>possibleBlobs;
for (size_t i = 0; i < contours.size(); i++)
{//筛选出可能的目标对象,根据视频中的背景设置筛选条件
Blob tem(contours[i]);
//if (tem.currentArea > 60&&tem.currentRate<1.0) {
if (tem.currentArea > 100) {
possibleBlobs.push_back(tem);
}
}
cout << "每次检测出来的possible个数" << possibleBlobs.size() << endl;
/*
2.跟踪队列为空则直接把possibleBlob全部放入跟踪队列
其他则进行判断,判断是已有的跟踪对象,则把中心点推入已有的跟踪对象,
若是新的跟踪对象直接放入跟踪队列里
*/
if (Blobs.size()==0) {//跟踪队列为空
for (auto &c : possibleBlobs)
{
Blobs.push_back(c);
}
cout << "目标队列为空时第一次加入目标队列追踪的个数"<<Blobs.size() << endl;
}
else {
for (auto &tem : Blobs)
{
tem.Predit();
}
/*
3.选作什么作为不同的跟踪目标之间的区分呢
首先尝试过直接利用已找到的轮廓作为区分,例如轮廓的面积和轮廓外界矩形特征等,但是区分效果很不好
这里利用的是鼠标运动检测中的预测目标点,若预测的目标点小于规定值则认为是同一个目标。
*/
for (auto &tem : possibleBlobs)
{
int flag = false;
for (auto &target : Blobs)
{
if (DistanceBetwwenPoint(tem.currentCenter, target.preditNextCenter) <50.0 )
{
target.centerPos.push_back(tem.currentCenter);
flag = true;
break;
}
}
if (flag == false)Blobs.push_back(tem);//作为新目标被跟踪
}
}
/*for (auto &tem : possibleBlobs)
{//不正确的写法
int flag = false;//匹配标志
for (auto &exist : Blobs)
{
if (abs(tem.currentArea - exist.currentArea) <= scale && abs(tem.currentRate - exist.currentRate) <= scale)
{
exist.centerPos.push_back(tem.currentCenter);
flag = true;
break;
}
}
if (flag == false)Blobs.push_back(tem);
}*/
cout <<"追踪对象有:"<< Blobs.size() << endl;
for (int i = 0; i < Blobs.size(); i++)
{
int size = Blobs[i].centerPos.size();
cout << "第" << i << "个追踪对象内的centerPos点数:" << size << endl;
for (int k= 0;k < size; k++)
{
circle(cur, Blobs[i].centerPos[k],1,Scalar(255,0,0),1,8);
}
if (size == 1)
{
rectangle(cur, Blobs[i].currentRect, Scalar(255, 0, 0), 1, 8);
}
else
{
Point p1;
p1.x = Blobs[i].centerPos[size - 1].x - Blobs[i].currentRect.width*0.5;
p1.y= Blobs[i].centerPos[size - 1].y - Blobs[i].currentRect.height*0.5;
Rect rect(p1.x,p1.y,Blobs[i].currentRect.width,Blobs[i].currentRect.height);
rectangle(cur, rect, Scalar(255, 0, 0), 1, 8);
}
}
//drawContours(cur,contours,-1,Scalar(255,0,0),1,8);
imshow("df",df);
imshow("cur",cur);
//pre=cur;//加上这一句就是帧差法
if (waitKey(100) == 27)break;//或者当前的帧数是最后一帧,采用判断当前读取的帧数是否非空即可
}
cap.release();
destroyAllWindows();
system("pause");
return 0;
}
其中cpp文件里有初始化和预测函数,预测方法与鼠标移动预测类似,所以就不贴了。