-
2018-11-15 10:17:16
最近有空研究了一下基于Opencv的视频人数统计。总结了一下,视频人数统计系统的工作流程主要包括以下几个部分:
1.视频捕获
从视频源(摄像头或视频文件)获取到视频图像数据。
2.目标提取(背景建模、前景分析)
由视频图像分割出视频图像中的运动区域。
常见的方法:高斯背景建模,帧差法,三帧差法。
3.目标识别(模式识别、特征点分析)
根据目标特征分析得到具体目标。比如人脸识别、头肩部识别等。
OpenCV里可以使用Haar特征 、级联分类器来进行目标检测。但分类器的训练需要从事先准备好的成百或上千张正面或反面样本中训练得到,整个过程会比较复杂,这种方法应该会达到很好的检测效果 。OpenCV也自带了一些已经训练好的包括人眼、人脸和人体的分类器(位于OpenCV安装目录\data\haarcascades目录下,分类器是XML类型的文件),可以直接使用这些分类器来进行图像检测,但由于使用的环境不同,这些分类器不一定是最优的,检测效果不一定会很好,这些现成的分类器只是提供了一个基本的参考,在实际使用时,还需要根据实际环境对分类器中的配置参数做一些调整和优化才能使检测结果比较准确。当然,此种方式的开销也会大,而且只有OpenCV2.0版本以上才支持,因此在嵌入式领域,这并不是一个很好的方式。
4.目标跟踪
检测目标在每一帧的空间位置。从而得到目标的运动估计。
基本方法:
直方图特征匹配:根据目标区域的颜色直方图特征进行目标匹配。分别对运动目标的前后两帧计算颜色分布的直方图,如果两者的颜色直方图最接近,则认为目标匹配成功。
运动目标连续性匹配:利用相邻两帧图像中待匹配的目标在时间和空间上的相关性。由于视频中相邻两帧间时间间隔很短,可以认为在相邻两帧间目标是做匀速运动。这样,可以根据当前目标的运动速度和方向来预计目标在下一帧的位置,如果下一帧的所有运动目标中存在与待匹配目标的预期位置最接近的,则认为目标匹配成功。OpenCV里可以使用CamShift算法直接对彩色图像进行跟踪。它的基本原理也是通过直方图特征来进行目标匹配。测试了一下,在对我使用的视频进行跟踪时,效果不是很理想,会出现误跟踪的情况,没有达到预期的效果,而且CamShift算法是直接对彩色图像进行跟踪,开销会比较大,这里并不是说CamShift算法不好,其实视频图像处理本身就跟具体的外界环境有关,每一种图像算法一般都是针对特定的环境,并不是通用的,在某些环境下,比如目标物体的颜色跟环境差异比较明显,CamShift算法可能会达到比较好的效果。
5.轨迹分析
根据目标的运动轨迹计算出目标的运动方向和位移等,判断目标是进入还是离开指定区域,从而对目标进行数目统计。
编程实现
以下是采用运动目标连续性匹配算法实现的视频人数统计系统截图。开发环境是Opencv2.3.0+VS2005。
具体算法参考了论文《一种基于人头特征的人数统计方法研究》 作者:顾德军 伍铁军,论文介绍的很详细。这里就不再鳌述了。测试视频文件从优酷(地址:http://v.youku.com/v_show/id_XMTY2MDY1OTk2.html)上找到,从视频文件可以看出拍摄视频摄像头的是斜向下安装,此种方式得到的二值图像的人头特征不是很明显,因而不太好利用人头特征来进行目标识别。这里只是简单利用了运动目标面积大小来做了匹配,当多人之间互相有重叠或包含其它物体时,就不能很好的区分开来。
一种改进的方法是摄像头采用头顶式的安装,这样经过二值化处理的人头特征应该会比较明显一些,而且人头之间不会有重叠的现象,准确率会更大一些。但由于没有该环境下的视频数据,因次没办法来验证识别率到底如何。如果有对这方面感兴趣的朋友,可以一起来交流学习。
参考文献:
1.《一种基于人头特征的人数统计方法研究》 作者:顾德军 伍铁军
2.《基于视频的客流技术系统的研究》 作者:田京雷 毕胜
3.《智能视频中基于机器学习的自动人数统计》 作者:贾彗星 章毓晋
---------------------
作者:firehood
来源:CSDN
原文:https://blog.csdn.net/firehood_/article/details/8279014
版权声明:本文为博主原创文章,转载请附上博文链接!更多相关内容 -
Opencv人数统计 yolo kcf人头跟踪 人数统计 KCF目标跟踪 YOLO目标跟踪
2021-08-25 21:30:46该功能使用的darknet框架,训练的yolo-tiny人头模型,之后使用opencv4.5.1版本调用的yolo-tiny模型,跟踪使用KCF跟踪算法,整体上实现了三个功能: 1、区域内的人头统计; 2、区域内的绊线检测功能; 3、区域...该功能使用的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; } } } //找到了blob if (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);//转移矩阵A cv::setIdentity(pTracker->kf->measurementMatrix); //测量矩阵H cv::setIdentity(pTracker->kf->processNoiseCov, Scalar::all(1e-5)); //系统噪声方差矩阵Q cv::setIdentity(pTracker->kf->measurementNoiseCov, Scalar::all(1e-1));//测量噪声方差矩阵R cv::setIdentity(pTracker->kf->errorCovPost, Scalar::all(1)); //后验错误估计协方差矩阵P pTracker->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 points auto 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 union float 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 人数统计-电子政务文档类资源
2020-11-12 17:30:21本人毕设,见笑了,使用了基于HOG特征的SVM支持向量机,只能检查整个人体,不能检测人体的一部分,程序注释丰富。 -
Face counts.zip_opencv 人数统计_toldvk1_人数_人脸识别并统计人数_图片 人数
2022-07-14 05:44:18基于VS2017+opencv,实现图片中的人数自动统计 -
opencv跟踪检测及人数统计
2015-06-30 13:46:57opencv实现的跟踪检测及人数统计,对进行相关研究的人员有一定的参考价值。 -
opencv 人数统计
2011-08-11 20:01:39本人毕设,见笑了,使用了基于HOG特征的SVM支持向量机,只能检查整个人体,不能检测人体的一部分,程序注释丰富。 -
基于Linux和OpenCV的教室人数检测统计系统研究.pdf
2021-09-06 12:39:30基于Linux和OpenCV的教室人数检测统计系统研究.pdf -
opencv实现视频里人数统计
2017-08-01 11:38:52基于图像的人数统计属于模式识别问题,可应用于安防领域。传统的方法包括:1)视频捕获;2)目标提取(背景建模、前景分析)——常见方法有高斯背景建模、帧差法、三帧差法等;3)目标识别(模式识别、特征点分析)...小编有个群193369905,相关毕设也可找群主,里面分享的均是机器视觉的资料。基于图像的人数统计属于模式识别问题,可应用于安防领域。
先放个最近的人数教室人头统计图
前两张效果图为caffe训练完成看到最近好多留言大多数都是小白学生,一直在留言,所以我又继续更新了一下
传统的方法包括:1)视频捕获;2)目标提取(背景建模、前景分析)——常见方法有高斯背景建模、帧差法、三帧差法等;3)目标识别(模式识别、特征点分析),如人脸识别,头肩部识别等,OpenCV里可以使用Hear特征、级联分类器来进行特征检测;4)目标跟踪——基本方法有直方图特征匹配和运动目标连续性匹配,opencv里可以使用CamShift算法直接对彩色图像进行分析;5)轨迹分析——根据目标的运动轨迹计算目标目标运动方
-
opencv 人头统计
2013-12-24 14:29:41人头统计 优化的代码 采用 OPENCV 霍夫 变换技术 计算轮廓,训练模型,非常好 修过 代码简洁 -
视频人数统计(opencv)
2016-07-21 11:09:04步骤: 1.视频图像灰度化img 2,选取第一帧图像first_img,视频每帧和第一帧相减,...5,对边界区域进行统计,满足条件的进行计数代码实 现:using namespace std; using namespace cv; int main(){ Mat img, src,步骤:
1.视频图像灰度化img
2,选取第一帧图像first_img,视频每帧和第一帧相减,得到src
3,对src图片进行 阈值,滤波处理
4,查找处理好图片的边界findContours;
5,对边界区域进行统计,满足条件的进行计数代码实 现:
using namespace std; using namespace cv; int main(){ Mat img, src, frame, frame_gray, first_frame, threshold_src, gass_src, dilate_src; VideoCapture cap("m.avi"); cap >> first_frame; cvtColor(first_frame, first_frame, CV_BGR2GRAY); int num=0; int zz = 1; while (1){ cap >> frame; if (frame.empty()) break; vector<Vec4i> hierarchy; vector<vector<Point> > contour; cvtColor(frame, frame_gray, CV_BGR2GRAY); absdiff(frame_gray,first_frame,src); threshold(src, threshold_src,50,255,THRESH_BINARY); GaussianBlur(threshold_src, gass_src,Size(5,5),1.5); //blur(threshold_src, gass_src, Size(5, 5)); // medianBlur(threshold_src, gass_src,1); dilate(gass_src, dilate_src,Mat()); findContours(dilate_src,contour, CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE); //drawContours(frame, contour, -1, Scalar(0, 0, 255),2); for (int i = 0; i < contour.size();i++){ Rect bndRect = boundingRect(contour.at(i)); Point p1,p2; p1.x = bndRect.x; p1.y = bndRect.y; p2.x = bndRect.x + bndRect.width; p2.y = bndRect.y + bndRect.height; if (bndRect.area()>3000){ rectangle(frame, p1, p2, Scalar(0, 0, 255)); num++; } } string font = "Current number:"; putText(frame,font+to_string(num),Point(100,50),FONT_HERSHEY_COMPLEX_SMALL,1,Scalar(0,0,255)); // cout << "人数统计:" << num<<endl; num = 0; imshow("dilate_src", dilate_src); imshow("frame", frame); imshow("first_frame", first_frame); waitKey(20); } return 0; }
效果显示:
-
目标跟踪(6)OpenCV 人员计数器
2022-03-08 14:28:46最后,我们将检查将 OpenCV 的人数统计应用到实际视频中的结果。 1.了解对象检测与对象跟踪 在继续本教程的其余部分之前,您必须了解对象检测和对象跟踪之间的根本区别。 当我们应用对象检测时,我们是在确定一个... -
人流量统计(opencv,caffe,dlib)
2019-06-21 18:04:05直接接到一个小项目开发,是做公园的人流量统计,涉及到很多方面的知识,小编在这里记录一下流程,涉及行人识别,多目标追踪,匹配等知识。 参考国外博文,这是一个opencv学习的很好的网站。小编就是从这位大佬博客... -
Java基于百度AI+JavaCV+OpenCV 实现摄像头人数动态统计
2018-11-09 10:08:15本文主要是以摄像头画面进行人流量统计。并对返回图像进行展示。需要额外了解JavaCV OpenCV swing awt等 也许JavaCV OpenCV 不需要也可以实现效果。但是小帅丶就先用这样的方式实现了。别... -
OpenCV图像人脸识别人数统计
2020-04-08 09:01:33Python+OpenCV图像人脸识别人数统计 如需远程调试,可加QQ905733049由专业技术人员远程协助!运行代码如下: #!/usr/bin/python # -*- coding: utf-8 -*- from tkinter import messagebox from tkinter import * ... -
Opencv实战(一) 视频人数统计(C++ & Opencv)前后背景分离方法
2017-06-24 19:57:34在博客《视频人数统计(opencv)》中,作者使用的Absdiff帧差法降低背景影响,进而通过二值化,边缘化,滤波器,形态学变化,查找轮廓,轮廓面积控制,绘制轮廓等一系列方法完成了对样例图片的处理,并实现了人数... -
OpenCV 视频人数统计研究 (转别人的)
2014-01-02 11:14:39最近有空研究了一下基于Opencv的视频人数统计。总结了一下,视频人数统计系统的工作流程主要包括以下几个部分: 1.视频捕获 从视频源(摄像头或视频文件)获取到视频图像数据。 2.目标提取(背景建模、前景分析... -
树莓派4B+口罩人脸+人流量计数+web页面 —— 环境配置(包含opencv4、tensorflow、pytorch的安装)
2020-05-07 11:53:50opencv3 如果项目不需要opencv4的话,如果opencv3可以满足需求的话,那执行下面的命令即可: sudo apt-get install python3-opencv 3.2. opencv4 如果项目需要用到opencv4的话,那就很麻烦了。。。 # 安装依赖 sudo... -
OpenCV视频识别检测人数跟踪统计
2020-05-14 10:44:32Python+OpenCV视频识别检测人数跟踪统计 如需远程调试,可加QQ905733049由专业技术人员远程协助!运行代码如下: import numpy as np import cv2 import time import datetime cap = cv2.VideoCapture("vtest.... -
基于opencv的视频人数统计入门
2018-04-19 07:04:09最近接手了一个基于opencv进行视频人数统计的项目,其中还涉及到进出门等等问题。 我现在主要想通过opencv调用相机(网络相机,安装在过道顶上),比较精确地判断出视频中有几个人。 求问做过相关项目的大神们有没有... -
Raspberry Pi 4B树莓派 #项目笔记# | Python OpenCV 图像识别人数统计
2020-05-11 09:03:24[5] Python-OpenCv实现出入口计数并显示 [6] opencv实现视频里人数统计 [7] 树莓派(Raspberry Pi)中PiCamera+OpenCV的使用 [8]OpenCV操作不了树莓派原装摄像头(picam)解决方法 [9]树莓派(5):CSI摄像头 vs ... -
实时人数统计:使用IP摄像机实时统计人数
2021-01-30 02:22:30实时人数统计 使用OpenCV中的实时视频流/ IP摄像机实时计数的人数。 这是对的改进/修改。 请参阅附加。 此外,还增加了对IP摄像机的支持。 现场演示 主要目的是将项目用作可扩展的业务视角。 用例:实时计算商店... -
【Project 1】树莓派(raspberry pi)人脸识别计数+MJPG-streamer远程监控(上)
2020-02-22 16:21:52然后给树莓派安装在Python3环境下的OpenCV3.x.x的版本,接着从GitHub获取安装MJPG-streamer开源项目作为视频传输到web端的工具,并调用其提供的OpenCV接口来实现人脸识别加人数统计的功能,最后利用阿里云服务器和... -
基于Linux和OpenCV的教室人数检测统计系统研究
2021-05-18 01:43:59张开生+谢代胜摘要:为了让学生能远程了解自习室人数情况,从学生头部特征出发,分析现有的教室识别人数技术的特点和不足,以嵌入式Linux和OpenCV为软硬件搭建系统。系统采用垂直俯视拍摄...教室人数统计;椭圆拟合... -
Python+Opencv识别视频统计人数
2020-01-02 14:46:43Python+Opencv识别视频统计人数 如需远程调试,可加QQ905733049由专业技术人员远程协助! 运行代码如下: #!/usr/bin/python # -*- coding: utf-8 -*- import cv2 import numpy as np face_cascade=cv2.... -
Python基于Opencv来快速实现人脸识别过程详解(完整版)
2020-09-19 02:31:09主要介绍了Python基于Opencv来快速实现人脸识别过程详解(完整版)随着人工智能的日益火热,计算机视觉领域发展迅速,今天就为大家带来最基础的人脸识别基础,从一个个函数开始走进这个奥妙的世界,需要的朋友可以... -
基于OpenCV和Haar特征分类器的图像人数检测.
2012-07-27 15:57:24基于OpenCV和Haar特征分类器的图像人数检测 研究一种基于OpenCV和Haar特征检测固定区域图像中人数的方法。通过选取大量含有人的Haar特征的样本图片,利用OpenCV 训练出分类器,并通过实验深入...