该功能使用的darknet框架,训练的yolo-tiny人头模型,之后使用opencv4.5.1版本调用的yolo-tiny模型,跟踪使用KCF跟踪算法,整体上实现了三个功能:

1、区域内的人头统计;

2、区域内的绊线检测功能;

3、区域内目标跟踪,统计人头数量的功能。

一、网址:https://github.com/AlexeyAB/darknet

二、参考训练参考我的另两篇博客:

Darknet下的Yolo v3_dragon_perfect的博客-CSDN博客

深度学习 客流统计 人流计数_dragon_perfect的博客-CSDN博客_人流统计算法

跟踪部分的代码如下:

#pragma once#include "HeadDetect.h"
#include "opencv2/opencv.hpp"
#include "KCFLib/kcftracker.hpp"using namespace cv;
using namespace std;#define MAX_TRACK_COUNT         100//目标的最大个数
#define MAX_TRAJECTORY_COUNT    200//目标轨迹的最大个数
#define MAX_MISS_FRAME          10//最大丢失帧数
#define MAX_MATCH_FRAME         3//最大匹配帧数
#define MAX_TRACK_DIST          40000//最大跟踪距离struct ZTracker
{int nID;int nMissTimes;bool bMatchID;bool bTrack;bool bStatOK;bool bInitDirection;int nInitDirection;cv::KalmanFilter *kf;cv::Point predPoint;cv::Point curCentre;cv::Rect curRect;int nTrajCount;cv::Point centres[MAX_TRAJECTORY_COUNT];cv::Rect blobs[MAX_TRAJECTORY_COUNT];
};class ZHeadTrack
{
public:ZHeadTrack();~ZHeadTrack();int m_InCount;int m_OutCount;int m_RegionCount;ZTracker *m_pTrackers;KCFTracker m_pKCF[MAX_TRACK_COUNT];void trackInitial(cv::Point arrowStart, cv::Point arrowEnd, cv::Point lineStart, cv::Point lineEnd, vector<Point> regionPonits,int width,int height);void trackProcess(HDBox_Container *pHDBox_Container, HDRect *rect,bool bMask);void kcfProcess(HDBox_Container *pHDBox_Container, HDRect *rect, cv::Mat image);void trackInOutStatistics();void trackRegionStatistics();int pointInLineSide(cv::Point point, cv::Point lineStart, cv::Point lineEnd);float CalculateIou(cv::Rect& det, cv::Rect& track);public:int m_trackID;int m_nArrowStart;int  m_nArrowEnd;cv::Point m_lineStart;cv::Point m_lineEnd;cv::Mat m_detectRegion;HDBox_Container m_boxContainer;HDTracker_Region m_trackeRegion;
};
#include "HeadTrack.h"ZHeadTrack::ZHeadTrack()
{m_trackID = 0;m_InCount = 0;m_OutCount = 0;m_RegionCount = 0;m_nArrowStart = 0;m_nArrowEnd = 0;m_pTrackers = new ZTracker[MAX_TRACK_COUNT];memset(m_pTrackers, 0, sizeof(ZTracker));
}ZHeadTrack::~ZHeadTrack()
{delete m_pTrackers;
}void ZHeadTrack::trackInitial(cv::Point arrowStart, cv::Point arrowEnd, cv::Point lineStart, cv::Point lineEnd, vector<Point> regionPonits, int width, int height)
{m_trackID = 0;m_InCount = 0;m_OutCount = 0;m_nArrowStart = pointInLineSide(arrowStart, lineStart, lineEnd);m_nArrowEnd = pointInLineSide(arrowEnd, lineStart, lineEnd);m_lineStart = lineStart;m_lineEnd = lineEnd;vector<vector<Point>> vpts;vpts.push_back(regionPonits);cv::Mat detectRegion = cv::Mat::zeros(height, width, CV_8UC3);cv::fillPoly(detectRegion, vpts, cv::Scalar(255, 255, 255));cv::cvtColor(detectRegion, m_detectRegion, COLOR_BGR2GRAY);//m_detectRegion = detectRegion.clone();//cv::imshow("detectRegion", m_detectRegion);//cv::waitKey(0);
}void ZHeadTrack::trackProcess(HDBox_Container *pHDBox_Container, HDRect *rect, bool bMask)
{int i = 0, j = 0;//把太小的rect删除m_boxContainer.headCount = 0;int width = m_detectRegion.cols;uchar *pDetectData = m_detectRegion.data;int step1 = m_detectRegion.step1();//cv::imshow("m_detectRegion", m_detectRegion);//cv::waitKey(10);for (i = 0;i< pHDBox_Container->headCount;i++){HDRect *pHDRect = &pHDBox_Container->candidates[i];int nx = (pHDRect->right + pHDRect->left) / 2;int ny = (pHDRect->top + pHDRect->bottom) / 2;int rectW = pHDRect->right - pHDRect->left;int rectH = pHDRect->bottom - pHDRect->top;if (bMask){if (rectW > 60 && rectH > 60 && (*(pDetectData + ny*width + nx) == 255)){m_boxContainer.candidates[m_boxContainer.headCount++] = pHDBox_Container->candidates[i];}}else{if (rectW > 40 && rectH > 40 && nx > rect->left && nx < rect->right && ny > rect->top && ny < rect->bottom){m_boxContainer.candidates[m_boxContainer.headCount++] = pHDBox_Container->candidates[i];}}}bool bMatch[HD_MAX_HEADS] = { false };for (i = 0;i< m_boxContainer.headCount;i++){bMatch[i] = false;}for (i = 0; i < MAX_TRACK_COUNT; i++){ZTracker *pTracker = &m_pTrackers[i];if (pTracker->bTrack){bool bMinst = false;int nMatchID = -1;int maxDist = MAX_TRACK_DIST;for (j = 0; j < m_boxContainer.headCount; j++){if (!bMatch[j]){HDRect *pHDRect = &m_boxContainer.candidates[j];cv::Rect curRect;curRect.x = pHDRect->left;curRect.y = pHDRect->top;curRect.width = pHDRect->right - pHDRect->left;curRect.height = pHDRect->bottom - pHDRect->top;int nx = (pHDRect->left + pHDRect->right) / 2;int ny = (pHDRect->top + pHDRect->bottom) / 2;int dist = (pTracker->predPoint.x - nx)*(pTracker->predPoint.x - nx) + (pTracker->predPoint.y - ny)*(pTracker->predPoint.y - ny);if (dist < maxDist){maxDist = dist;pTracker->curRect = curRect;//后面更新用pTracker->curCentre.x = nx;pTracker->curCentre.y = ny;nMatchID = j;bMinst = true;}}}//找到了blobif (bMinst){bMatch[nMatchID] = true;HDRect *pHDRect = &m_boxContainer.candidates[nMatchID];cv::Rect curRect;curRect.x = pHDRect->left;curRect.y = pHDRect->top;curRect.width = pHDRect->right - pHDRect->left;curRect.height = pHDRect->bottom - pHDRect->top;int nx = (pHDRect->left + pHDRect->right) / 2;int ny = (pHDRect->top + pHDRect->bottom) / 2;pTracker->bMatchID = true;pTracker->nMissTimes = 0;pTracker->curCentre.x = nx;pTracker->curCentre.y = ny;pTracker->curRect = curRect;//更新预测值Mat measurement = Mat::zeros(2, 1, CV_32F);measurement.at<float>(0) = (float)nx;measurement.at<float>(1) = (float)ny;pTracker->kf->correct(measurement);Mat prediction = pTracker->kf->predict();pTracker->predPoint = Point(prediction.at<float>(0), prediction.at<float>(1)); //预测值(x',y')cv::Point centre = pTracker->centres[pTracker->nTrajCount - 1];if ((centre.x - nx)*(centre.x - nx) + (centre.y - ny)*(centre.y - ny) > 30){pTracker->centres[pTracker->nTrajCount].x = nx;pTracker->centres[pTracker->nTrajCount].y = ny;pTracker->blobs[pTracker->nTrajCount] = curRect;pTracker->nTrajCount++;if (pTracker->nTrajCount >= MAX_TRAJECTORY_COUNT - 1){pTracker->nTrajCount = MAX_TRAJECTORY_COUNT - 1;for (int k = 1; k < pTracker->nTrajCount; k++){pTracker->centres[k - 1] = pTracker->centres[k];pTracker->blobs[k - 1] = pTracker->blobs[k];}}}}  else//没找到blob{pTracker->nMissTimes++;//Mat prediction = pTracker->kf->predict();//pTracker->predPoint = Point(prediction.at<float>(0), prediction.at<float>(1)); //预测值(x',y')//更新预测值Mat measurement = Mat::zeros(2, 1, CV_32F);measurement.at<float>(0) = (float)pTracker->curCentre.x;measurement.at<float>(1) = (float)pTracker->curCentre.y;pTracker->kf->correct(measurement);Mat prediction = pTracker->kf->predict();pTracker->predPoint = Point(prediction.at<float>(0), prediction.at<float>(1)); //预测值(x',y')if (pTracker->nMissTimes > MAX_MISS_FRAME){pTracker->bTrack = false;delete pTracker->kf;}}}}//没有匹配上的,需要重新创建目标for (i = 0; i < m_boxContainer.headCount; i++){HDRect *pHDRect = &m_boxContainer.candidates[i];cv::Rect curRect;curRect.x = pHDRect->left;curRect.y = pHDRect->top;curRect.width = pHDRect->right - pHDRect->left;curRect.height = pHDRect->bottom - pHDRect->top;int nx = (pHDRect->left + pHDRect->right) / 2;int ny = (pHDRect->top + pHDRect->bottom) / 2;if (!bMatch[i]){for (j = 0; j < MAX_TRACK_COUNT; j++){ZTracker *pTracker = &m_pTrackers[j];if (!pTracker->bTrack){pTracker->bTrack = true;pTracker->bMatchID = true;pTracker->bStatOK = false;pTracker->bInitDirection = false;pTracker->nID = ++m_trackID;pTracker->curCentre.x = nx;pTracker->curCentre.y = ny;pTracker->curRect = curRect;pTracker->nMissTimes = 0;pTracker->predPoint.x = nx;pTracker->predPoint.y = ny;pTracker->kf = new cv::KalmanFilter(4, 2, 0);pTracker->kf->transitionMatrix = (Mat_<float>(4, 4) << 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1);//转移矩阵Acv::setIdentity(pTracker->kf->measurementMatrix);                     //测量矩阵Hcv::setIdentity(pTracker->kf->processNoiseCov, Scalar::all(1e-5));    //系统噪声方差矩阵Qcv::setIdentity(pTracker->kf->measurementNoiseCov, Scalar::all(1e-1));//测量噪声方差矩阵Rcv::setIdentity(pTracker->kf->errorCovPost, Scalar::all(1));          //后验错误估计协方差矩阵PpTracker->kf->statePost = (Mat_<float>(4, 1) << nx, ny, 0, 0);Mat prediction = pTracker->kf->predict();pTracker->predPoint = Point(prediction.at<float>(0), prediction.at<float>(1)); //预测值(x',y')pTracker->centres[0].x = nx;pTracker->centres[0].y = ny;pTracker->blobs[0] = curRect;pTracker->nTrajCount = 1;break;}}}}
}float ZHeadTrack::CalculateIou(cv::Rect& det, cv::Rect& track)
{// get min/max pointsauto xx1 = std::max(det.tl().x, track.tl().x);auto yy1 = std::max(det.tl().y, track.tl().y);auto xx2 = std::min(det.br().x, track.br().x);auto yy2 = std::min(det.br().y, track.br().y);auto w = std::max(0, xx2 - xx1);auto h = std::max(0, yy2 - yy1);// calculate area of intersection and unionfloat det_area = det.area();float trk_area = track.area();auto intersection_area = w * h;float union_area = det_area + trk_area - intersection_area;auto iou = intersection_area / union_area;return iou;
}void ZHeadTrack::kcfProcess(HDBox_Container *pHDBox_Container, HDRect *rect,cv::Mat image)
{int i = 0, j = 0;//HDBox_Container container;m_boxContainer.headCount = 0;for (i = 0; i < pHDBox_Container->headCount; i++){HDRect *pHDRect = &pHDBox_Container->candidates[i];int nx = (pHDRect->right + pHDRect->left) / 2;int ny = (pHDRect->top + pHDRect->bottom) / 2;int rectW = pHDRect->right - pHDRect->left;int rectH = pHDRect->bottom - pHDRect->top;if (rectW > 20 && rectH > 20 && nx > rect->left && nx < rect->right && ny > rect->top && ny < rect->bottom){m_boxContainer.candidates[m_boxContainer.headCount++] = pHDBox_Container->candidates[i];}}bool bMatch[HD_MAX_HEADS] = { false };for (i = 0; i < m_boxContainer.headCount; i++){bMatch[i] = false;}for (i = 0; i < MAX_TRACK_COUNT; i++){KCFTracker *pKCF = &m_pKCF[i];if (pKCF->bTrack){cv::Rect tRect = pKCF->update(image);pKCF->nTrackTimes++;pKCF->curRect = tRect;int nx = tRect.x + tRect.width / 2;int ny = tRect.y + tRect.height / 2;int dist = 10000000;float dfiou = 0.4;cv::Rect roiRect;int nFlag = 0;bool bOK = false;int nlimit = 0.6*std::sqrt(tRect.width*tRect.width + tRect.height*tRect.height);for (j = 0; j < m_boxContainer.headCount; j++){HDRect *pHDRect = &m_boxContainer.candidates[j];int x0 = (pHDRect->left + pHDRect->right) / 2;int y0 = (pHDRect->top + pHDRect->bottom) / 2;int tempDist = std::sqrt((nx - x0)*(nx - x0) + (ny - y0)*(ny - y0));cv::Rect trackR;trackR.x = pHDRect->left;trackR.y = pHDRect->top;trackR.width = pHDRect->right - pHDRect->left;trackR.height = pHDRect->bottom - pHDRect->top;float fiou = CalculateIou(tRect, trackR);if (fiou > dfiou){dfiou = fiou;nFlag = j;bOK = true;roiRect = trackR;}}if (bOK){bMatch[nFlag] = true;pKCF->nMissFrame = 0;pKCF->init(roiRect, image);pKCF->curRect = roiRect;pKCF->nMatchTimes++;if (!pKCF->bValid){if (/*pKCF->nMatchTimes > MAX_MATCH_FRAME && */pKCF->nTrackTimes == pKCF->nMatchTimes){pKCF->bValid = true;pKCF->nID = ++m_trackID;}else{pKCF->bTrack = false;pKCF->bValid = false;}}}else{pKCF->nMissFrame++;if (pKCF->nMissFrame > MAX_MISS_FRAME){pKCF->bTrack = false;pKCF->bValid = false;}}}}for (j = 0; j < m_boxContainer.headCount; j++){if (!bMatch[j]){HDRect *pHDRect = &m_boxContainer.candidates[j];for (i = 0; i < MAX_TRACK_COUNT; i++){KCFTracker *pKCF = &m_pKCF[i];if (!pKCF->bTrack){pKCF->nID = -1;// ++m_trackID;pKCF->nMissFrame = 0;pKCF->nTrackTimes = 0;pKCF->nMatchTimes = 0;pKCF->bValid = false;cv::Rect roiRect;roiRect.x = pHDRect->left;roiRect.y = pHDRect->top;roiRect.width = pHDRect->right - pHDRect->left;roiRect.height = pHDRect->bottom - pHDRect->top;pKCF->bTrack = true;pKCF->init(roiRect, image);break;}}}}
}int ZHeadTrack::pointInLineSide(cv::Point point, cv::Point lineStart, cv::Point lineEnd)
{int x0 = 0, x1 = 0;int y0 = 0, y1 = 0;int v0 = 0, v1 = 0;bool bFlag = false;bool bFlagX = false;bool bFlagY = false;x0 = lineStart.x;x1 = lineEnd.x;y0 = lineStart.y;y1 = lineEnd.y;先保证点在线段内if (x0 > x1){bFlagX = point.x > x1 && point.x < x0;}else{bFlagX = point.x <x1 && point.x >x0;}if (y0 > y1){bFlagY = point.y > y1 && point.y < y0;}else{bFlagY = point.y <y1 && point.y >y0;}bFlag = bFlagX || bFlagY;if (!bFlag){return 0;}v0 = (point.x - x0)*(y1 - y0) - (point.y - y0)*(x1 - x0);v1 = x1 - x0;if (x1 - x0 == 0){if (v0 < 0){return -1;}else{return 1;}}else{if (v0*v1 < 0){return -1;}else{return 1;}}return 0;
}void ZHeadTrack::trackInOutStatistics()
{int i = 0, j = 0;for (i = 0; i < MAX_TRACK_COUNT; i++){ZTracker *pTracker = &m_pTrackers[i];if (pTracker->bTrack && pTracker->nTrajCount > 20 && !pTracker->bStatOK){连续确认跟踪起始点的方向if (!pTracker->bInitDirection){int count0 = 0;for (j = 0; j < 10; j++){int flag = pointInLineSide(pTracker->centres[j], m_lineStart, m_lineEnd);count0 += flag;}if (count0 > 0){pTracker->nInitDirection = 1;}else{pTracker->nInitDirection = -1;}}跟踪过线需要连续确认int count1 = 0;for (j = pTracker->nTrajCount - 10; j < pTracker->nTrajCount - 1; j++){int flag = pointInLineSide(pTracker->centres[j], m_lineStart, m_lineEnd);if (flag != 0 && pTracker->nInitDirection != flag){count1++;}}if (count1 > 6){if (pTracker->nInitDirection == m_nArrowStart){m_InCount++;}else{m_OutCount++;}pTracker->bStatOK = true;}}}
}void ZHeadTrack::trackRegionStatistics()
{int i = 0, j = 0;m_trackeRegion.currentCount = 0;for (i = 0; i < MAX_TRACK_COUNT; i++){ZTracker *pTracker = &m_pTrackers[i];if (pTracker->bTrack && pTracker->nTrajCount > 5 && !pTracker->bStatOK){m_trackeRegion.rect[m_trackeRegion.currentCount].id = pTracker->nID;m_trackeRegion.rect[m_trackeRegion.currentCount].left = pTracker->curRect.x;m_trackeRegion.rect[m_trackeRegion.currentCount].top = pTracker->curRect.y;m_trackeRegion.rect[m_trackeRegion.currentCount].right = pTracker->curRect.x + pTracker->curRect.width;m_trackeRegion.rect[m_trackeRegion.currentCount].bottom = pTracker->curRect.y + pTracker->curRect.height;m_trackeRegion.currentCount++;m_RegionCount++;pTracker->bStatOK = true;}}
}

效果展示:

跟踪的原理:

用yolo-tiny检测出来的结果,送到KCF跟踪器中,连续匹配上两次,则确认为一个目标,之后采用tracking-by-detection的方式进行人头跟踪。

人头跟踪的demo(内涵KCF跟踪代码部分):

链接:百度网盘 请输入提取码
提取码:dr2a
有问题加Q:187100248

Opencv人数统计 yolo kcf人头跟踪 人数统计 KCF目标跟踪 YOLO目标跟踪相关推荐

  1. oracle统计一个班男生女生,一条sql统计一个班级的男女人数

    有一个 学生记录的表student,形式如下:  name  class sex  1 a 男 2 b 女 3 c 男 4 a 男 5 a 女 6 a 男 --  现要统计每个班级的男女人数,结果如下 ...

  2. 清华计算机考研报考人数,2020部分院校考研报考人数统计

    下面是在2020考研初试过后公布的79所院校报名人数,看看有没有你想要报考的院校吧~ 1.南昌大学 据统计,报考2020年南昌大学硕士生人数22131人,比往年增长39.3%. 2.广东工业大学 本年 ...

  3. wow服务器人数最新统计,魔兽世界怀旧服服务器人数统计 魔兽世界怀旧服人数比例查询...

    魔兽世界怀旧服服务器人数统计是游戏每个服务器玩家数量,大家想知道排队人数喔,那么魔兽世界怀旧服服务器人数统计有多少.魔兽世界怀旧服人数比例查询呢,跑跑车游戏网为大家带来介绍. *魔兽世界怀旧服服务器人 ...

  4. 部门平均工资mysql_按部门统计各工资级别的人数、平均工资。

    部门名称 0--1000 1000--2000 2000--3000 3000--5000 5000+ 平均工资 技术部 0 0 3 10 5 4000 业务部 1 1 6 4 8 5000 客户服务 ...

  5. 用函数统计各分数段人数c语言,如何使用Excel函数统计各或分数段的人数(五种方法).doc...

    Excel教程:妙用Excel五个函数统计学生期末考试分数段 考试结束,老师们都要对学生的考试成绩进行分析.各分数段人数的统计是其中一项必做的工作.在Excel中,怎样快速准确地统计分数段人数呢?以下 ...

  6. 用函数统计各分数段人数c语言,如何使用Excel函数统计各分数段的人数(五种方法)...

    如何使用Excel函数统计各分数段的人数(五种方法),对老师很有用哦. Excel教程:妙用Excel五个函数统计学生期末考试分数段 考试结束,老师们都要对学生的考试成绩进行分析.各分数段人数的统计是 ...

  7. 统计在线人数、每日访问人数和总人数

    统计在线人数.每日访问人数和总人数 这个程序难一点,特点在于,因为他是分别的传送每个图片,所以不像CGI产生的图片那样大.而且图片可以重复的利用所以较率高速度快.只是没有JavaScript就不行了. ...

  8. 2021年河南省高考数据统计:河南省参加考试人数占报名人数的83.7%,本土156所高校(1所211大学)

    依据河南日报信息,2021年河南省高考报名人数125万人(含参加高职单招已录取20.4万人),占全国报名人数的11.6%,实际参加考试104.6万人(含专升本省统考18.74万人.对口招生省统考6.9 ...

  9. 计算机函数if80到90分怎么弄,excel统计80~90分的人数,如何操作?

    excel统计80~90分的人数,如何操作?以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! excel统计80~90分的 ...

最新文章

  1. sklearn.preprocessing下的数据标准化(scale、MinMaxScaler)
  2. Java游戏聊斋聂小倩_《聊斋倩女幽魂》聂小倩教你赢得战斗
  3. 一个25岁董事长给程序员的18条忠告
  4. Ubuntu10.10 Server+Nginx+Django+Postgresql安装步骤
  5. 将View兑换Bitmap
  6. 【图像融合】基于matlab GUI SIFT+小波变换图像拼接融合系统【含Matlab源码 815期】
  7. retrofit2 上传图片
  8. odoo报表内部和外部布局
  9. MyEclipse10破解,亲测实用
  10. uclinux上任天堂游戏模拟器移植
  11. 恢复grub 修复分区表
  12. git报错unsafe repository xxx is owned by someone else
  13. 【数学】 海 盗 埋 宝
  14. Mac用homebrew安装unrar
  15. RabitMQ系列之 Spring RabbitMQ流量削锋实现案例之抢红包
  16. Android --- 5G网络,jetpack库
  17. Python为什么这么火,它能做什么?——Python编程语言应用综述
  18. apa引用要在文中吗_SCI论文:常见的两种参考文献引用方式
  19. 抓取报表ALV GRID上的数据 转于elliott
  20. nyoj166-小珂的数学题

热门文章

  1. HTML中的单选按钮实现男女性别选择
  2. CT 三维重建的后处理方法
  3. 知乎热榜“人脸安全”,这些博主怎么看
  4. Java中过大整数储存问题(bushi
  5. windows2008服务器sa密码修改,关于SQL Server 2008忘记sa密码修改sa密码的方法图解
  6. MMA-清除所有变量的方法
  7. Python函数设计与使用
  8. A component required a bean of type ‘com.lw.mapper.StudentMapper‘ that could not be found.
  9. SpringBoot3.x原生镜像-Native Image尝鲜
  10. 10M/s,保姆级教程,制作自己的网盘不限速工具!