热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

多目标跟踪计数opencv(C++实现)

今天小博就把多目标检测,追踪,计数的代码贴出来供大家把玩,如有有疑问,可发邮件到1039463596@qq.com,欢迎提出批评指正:我们先看一下追踪计数的效果吧1.算法目的:运动

今天小博就把多目标检测,追踪,计数的代码贴出来供大家把玩,如有有疑问,可发邮件到1039463596@qq.com,欢迎提出批评指正:
我们先看一下追踪计数的效果吧
《多目标跟踪计数opencv(C++实现)》
1. 算法目的:

运动目标跟踪算法的目的就是对视频中的图象序列进行分析,计算出目标在每帧图象上的位置。这里要根据区域分割过程给出的目标质心位置,计算出目标位移,并且根据质心位置的变化判断出目标的运动方向,以及运动目标是否在观察窗口,实现对客流量的统计。因为该跟踪是对多目标的追踪,需要找出运动目标在相邻帧上的对应区域。

系统具有固有噪声,目标周围背景的干扰可能会产生误差,但这些噪声在前面的过程已经去除,如有必要可做适当调整修正。
2. 算法难点:

(1)因为要跟踪的是多目标,需要找到相邻帧之间对应的运动目标区域不致跟踪混乱。

(2)如何判断运动目标区域是否是新的目标进入观测窗口

(3)运动目标是否离开了观测窗口以及离开的方向;即计数器何时加1、是否加1

(4)对跟踪过程中出现的一些偏差和问题,要进行必要的修正
3. 算法描述:

(1)跟踪首先要判断的是:帧与帧之间如何将运动目标对应起来。追踪过程中的追踪特

征是物体的质心(由运动区域分割过程中给出),这里判断对应目标可以:a.只利用质心间的最短距离做为特征; b.利用加权系数将最短距离,运动目标区域的长度,宽度以及长宽比和面积等综合起来作为特征。

(2)根据判断特征设置目标链,记录每个被跟踪目标的最新质心位置,为下步判断提供条件。另外将每个目标的质心位置存储起来,可以随时掌握目标的运动情况,为以后要输出目标的运动曲线做基础。

(3)每进入观察窗口一个新的运动目标,就将它的最新质心位置加入该目标链。如何判断该运动目标是新的:设置门限值ymin,ymax(当yminymax或yymax并且它的标志位为未被跟踪,则肯定是新目标(在新目标区域出现的目标有可能是要离开观测窗口的目标,不过它们的标志位肯定为被跟踪)。这样判断出来的新目标方向是有出、入之分的。

(4)与目标链(MB[h][l])相对应的还有位置数组(WZ[m][n]),用来存放目标链中相应目标的质心位置。最初进行处理时,目标链是空的,在当前帧中若有新目标,则将其横、纵坐标加入到目标链中,并存储在对应的位置数组中,置标记为被跟踪。

(5)处理新的当前帧时,首先将目标链MB中每个元素(代表前一帧中所有运动目标)依次与当前帧中记录的每个运动目标进行距离计算,求出其中最小距离d(假设是MB[i]与当前帧中运动目标t的质心距离)则判断d与门限值λ的大小,若d<λ,则说明运动目标t就是前一祯MB[i]对应的运动目标区域,则将t的质心位置更新代替MB[i]中的横纵坐标(MB[i]中记录目标i的最新质心位置),并将其质心位置加入到WZ[i]中去,记录该目标运动质心的记录,将该运动目标标记为被跟踪;如d>λ则说明该最小值不足以证明它们是对应目标,可能MB[i]代表的目标已经不在跟踪窗口,则做如下处理:

检查对应MB[i]代表的目标质心位置的最新记录,如果MB[i]1>ymax,可认为目标离开观测窗口,并且方向是进入,则计数器加1;如果MB[i][1]

(6)设置数组CC[m][n],存储离开观测窗口并且方向是进入的运动目标质心位置记录,象(5)中的MB[i]若是进入就将对应位置记录WZ[i]存放在数组CC中,同时清除MB[i]中的特征值和WZ[i]中的质心记录。这里可以另外设置一个空闲位置数组KWZ[i],记录被清空的WZ中的位置,每次有新目标到来,须分配数组空间就先检查是否有被清空的记录。

(7)直至目标链中的目标检查完毕,则查看当前祯中的各运动目标,如有未被标记的,且在新目标区域中则将其加入到目标链中,标记设为已被跟踪。

(8)接收新的当前祯,继续如此处理。

处理过程中可能出现的几个问题为:

(1) 如果当前祯中一个运动目标t已经被标记,但是当处理到目标链中下一个目标是最小距离的仍是t,说明目标链中有两个目标都与t的距离最小,这样就需要调整。

(2) 如果门限值λ设置不当,则可能产生误判。

(3) 如果目标链中所有目标检查完毕,但是在当前祯中有未被标记并且不在新目标区域,

即不是新目标,又不是被跟踪目标的相应目标,则有差错出现,可能是噪声。

(4) 是否将离开观测窗口并且运动方向是出去的目标质心位置存储在数组CC中。
4.算法实现

#include
#include
//#include
using namespace std;
using namespace cv;
#define drawCross( img, center, color, d )\
line(img, Point(center.x - d, center.y - d), Point(center.x + d, center.y + d), color, 2, CV_AA, 0);\
line(img, Point(center.x + d, center.y - d), Point(center.x - d, center.y + d), color, 2, CV_AA, 0 )\
vector mousev,kalmanv;

cv::KalmanFilter KF;
cv::Mat_ measurement(2,1);
Mat_ state(4, 1); // (x, y, Vx, Vy)
int incr=0;

void initKalman(float x, float y)
{
// Instantate Kalman Filter with
// 4 dynamic parameters and 2 measurement parameters,
// where my measurement is: 2D location of object,
// and dynamic is: 2D location and 2D velocity.
KF.init(4, 2, 0);
measurement = Mat_::zeros(2,1);
measurement.at(0, 0) = x;
measurement.at(0, 0) = y;

KF.statePre.setTo(0);
KF.statePre.at(0, 0) = x;
KF.statePre.at(1, 0) = y;
KF.statePost.setTo(0);
KF.statePost.at(0, 0) = x;
KF.statePost.at(1, 0) = y;
setIdentity(KF.transitionMatrix);
setIdentity(KF.measurementMatrix);
setIdentity(KF.processNoiseCov, Scalar::all(.005)); //adjust this for faster convergence - but higher noise
setIdentity(KF.measurementNoiseCov, Scalar::all(1e-1));
setIdentity(KF.errorCovPost, Scalar::all(.1));
}
Point kalmanPredict()
{
Mat prediction = KF.predict();
Point predictPt(prediction.at(0),prediction.at(1));
KF.statePre.copyTo(KF.statePost);
KF.errorCovPre.copyTo(KF.errorCovPost);
return predictPt;
}
Point kalmanCorrect(float x, float y)
{
measurement(0) = x;
measurement(1) = y;
Mat estimated = KF.correct(measurement);
Point statePt(estimated.at(0),estimated.at(1));
return statePt;
}
int main()
{
Mat frame, thresh_frame;
vector channels;
VideoCapture capture;
vector hierarchy;
vector > contours;
// cv::Mat frame;
cv::Mat back;
cv::Mat fore;
cv::BackgroundSubtractorMOG2 bg;
//bg.nmixtures = 3;//nmixtures
//bg.bShadowDetection = false;
int incr=0;
int track=0;
capture.open("4.avi");
if(!capture.isOpened())
cerr <<"Problem opening video source" <
mousev.clear();
kalmanv.clear();
initKalman(0, 0);
while((char)waitKey(1) != 'q' && capture.grab())
{
Point s, p;
capture.retrieve(frame);
bg.operator ()(frame,fore);
bg.getBackgroundImage(back);
erode(fore,fore,Mat());
erode(fore,fore,Mat());
dilate(fore,fore,Mat());
dilate(fore,fore,Mat());
dilate(fore,fore,Mat());
dilate(fore,fore,Mat());
dilate(fore,fore,Mat());
dilate(fore,fore,Mat());
dilate(fore,fore,Mat());
cv::normalize(fore, fore, 0, 1., cv::NORM_MINMAX);
cv::threshold(fore, fore, .5, 1., CV_THRESH_BINARY);

split(frame, channels);
add(channels[0], channels[1], channels[1]);
subtract(channels[2], channels[1], channels[2]);
threshold(channels[2], thresh_frame, 50, 255, CV_THRESH_BINARY);
medianBlur(thresh_frame, thresh_frame, 5);
// imshow("Red", channels[1]);
findContours(fore, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
vector > contours_poly( contours.size() );
vector boundRect( contours.size() );
Mat drawing = Mat::zeros(thresh_frame.size(), CV_8UC1);
for(size_t i = 0; i {
// cout < if(contourArea(contours[i]) > 500)
drawContours(drawing, contours, i, Scalar::all(255), CV_FILLED, 8, vector(), 0, Point());
}
thresh_frame = drawing;
findContours(fore, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
drawing = Mat::zeros(thresh_frame.size(), CV_8UC1);
for(size_t i = 0; i {
// cout < if(contourArea(contours[i]) > 3000)
drawContours(drawing, contours, i, Scalar::all(255), CV_FILLED, 8, vector(), 0, Point());
}
thresh_frame = drawing;
// Get the moments
vector mu(contours.size() );
for( size_t i = 0; i {
mu[i] = moments( contours[i], false ); }
// Get the mass centers:
vector mc( contours.size() );
for( size_t i = 0; i {
mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 );

/*
for(size_t i = 0; i {
// drawCross(frame, mc[i], Scalar(255, 0, 0), 5);
//measurement(0) = mc[i].x;
//measurement(1) = mc[i].y;

// line(frame, p, s, Scalar(255,255,0), 1);
// if (measurement(1) <= 130 && measurement(1) >= 120) {
// incr++;
// cout <<"Conter " < // }
}*/
}

for( size_t i = 0; i { approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat(contours_poly[i]) );
}
p = kalmanPredict();
// cout <<"kalman prediction: " < mousev.push_back(p);
for( size_t i = 0; i {
if(contourArea(contours[i]) > 1000){
rectangle( frame, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 2, 8, 0 );
Point center = Point(boundRect[i].x + (boundRect[i].width /2), boundRect[i].y + (boundRect[i].height/2));
cv::circle(frame,center, 8, Scalar(0, 0, 255), -1, 1,0);

s = kalmanCorrect(center.x, center.y);
drawCross(frame, s, Scalar(255, 255, 255), 5);
if (s.y <= 130 && p.y > 130 && s.x > 15) {
incr++;
cout <<"Counter " < }

for (int i = mousev.size()-20; i line(frame, mousev[i], mousev[i+1], Scalar(0,255,0), 1);
}
}
}

imshow("Video", frame);
imshow("Red", channels[2]);
imshow("Binary", thresh_frame);
}
return 0;
}

运动目标检测与卡尔曼滤波追踪结合的追踪方法实现了这一过程。。。`


推荐阅读
  • 本文介绍了在PostgreSQL中批量导入数据时的优化方法。包括使用unlogged表、删除重建索引、删除重建外键、禁用触发器、使用COPY方法、批量插入等。同时还提到了一些参数优化的注意事项,如设置effective_cache_size、shared_buffer等,并强调了在导入大量数据后使用analyze命令重新收集统计信息的重要性。 ... [详细]
  • 如何使用Python从工程图图像中提取底部的方法?
    本文介绍了使用Python从工程图图像中提取底部的方法。首先将输入图片转换为灰度图像,并进行高斯模糊和阈值处理。然后通过填充潜在的轮廓以及使用轮廓逼近和矩形核进行过滤,去除非矩形轮廓。最后通过查找轮廓并使用轮廓近似、宽高比和轮廓区域进行过滤,隔离所需的底部轮廓,并使用Numpy切片提取底部模板部分。 ... [详细]
  • PatchODAX8: ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • tcpdump 4.5.1 crash 深入分析
    tcpdump 4.5.1 crash 深入分析 ... [详细]
  • 颜色迁移(reinhard VS welsh)
    不要谈什么天分,运气,你需要的是一个截稿日,以及一个不交稿就能打爆你狗头的人,然后你就会被自己的才华吓到。------ ... [详细]
  • 前言:拿到一个案例,去分析:它该是做分类还是做回归,哪部分该做分类,哪部分该做回归,哪部分该做优化,它们的目标值分别是什么。再挑影响因素,哪些和分类有关的影响因素,哪些和回归有关的 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
author-avatar
薇薇llfnc
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有