探索人流量分析技术在疫情期间对校园设备管理系统的设计与实现(删减版)

1 简介

1.1 项目背景及作品创意

创意来源:

        作品的灵感来源于以下两个方面,一是新冠肺炎疫情的肆虐。新冠疫情期间,在机场、 火车站等公共场所下,人与人之间不好控制距离,容易引发交叉感染,一些重要大型活动的 举办也会因为人流量的变化不定而未能成功举行。到现在的 2021 年 3 月,中国的疫情得到 了有效控制,但还有许多国家仍在“抗疫”之中。我们想能否通过人流量统计分析预测客流 量,为此类存在安全隐患的公共场所进行合理导流、预警。二是对校园设备的管理。在教室 空闲的时间里,许多电灯、空调仍然开启,造成电力的浪费,于是我们思考能否通过人流量 检测实现教室电器的控制,这个作品利用单片机间接模拟设备展示效果。

产生背景:

        如今人流量检测逐渐成熟,有许多领域都使用了这项技术,随着计算机视觉技术的快速 发展,视频内容分析技术在监控领域迅速兴起,通过监控摄像头实现数据的收集,是学术界目 前关注的热点。如袁烨曾基于智能监控,对人流量计数及人群密度检测进行研究。目前在处 于新冠疫情期间,这项技术仍需拓展应用,在将来类似的公共卫生突发事件下,也可很好地 利用这项技术。

主要功能:

        1.通过对人流量数据的分级处理,实现对校园公共场所电器设备开关数量的分层管理。 如通过合理提高设备利用率,为校园提供可行的疫情防控方案,同时合理减少设备不必要的 待机时间,以达到有效利用电力资源、节能减排的目的。

        2.通过后台程序对电器设备开关的智能调控,减少公共区域电器设备管理人员流动和降 低风险,以及人工开关电器设备的工作量。 成果应用目标: 拓展人流量检测技术的应用方向,优化现有算法与人流量检测技术。

1.2 项目实施计划

项目初期:

1.列出本项目涉及的技术清单,查阅相关资料,了解各项技术目前的成熟程度及应用 方向。

2.学习基于 OpenCV 库的目标检测算法,编写人流量分析代码,并通过简单的样本调试。

项目中期:

1.根据准备阶段的前两点获得的资料,对项目流程进行轻微调整。精细化项目实施全流 程并列出任务清单、完成任务分配。

2.利用单片机的 LED 灯模拟基于人流量数据实现对电器设备的管理。

3.尝试分析并列出影响校园公共场所人流量高峰期的各项因素,并进行人工收集人流量 数据,搭建校园公共场所人流量变化模型。

项目后期:

1.对准备阶段和研究阶段的工作进行复盘,将任务清单和预期结果与实际结果进行一一对应地审核,分别标出完成度和任务结果的满意程度。对不满意的任务项进行调整以达 到更好的结果。

2.对综合技术和技术产品实现进行全流程模拟,确定各环节的流畅运行。

3.对准备阶段和研究阶段中遇到的失败困难和相对应的处理办法进行整理统计,反思造 成上述问题的原因。为处理办法打分,分析有无更高效的处理办法。

4.对研究方法进行复盘,审核研究方法的效果,分析有无更高效的研究方法。

2 总体设计

2.1 作品功能

2.1.1 功能概述

如今 21 世纪科技正飞速发达,人流量检测技术逐渐完善,开发方式各种各样,开发平 台各有优势。计算机视觉技术的发展,智能监控技术也被广泛应用于国民生活的各个方面。 我们利用计算机人流量检测技术,借助单片机模拟基于人流量数据的电器设备管理,单片机 灯亮的数量展示了通过识别区域的人数变化。

2.1.2 功能说明

我们的作品由两部分组成,一部分是人流量检测部分,另一部分是单片机实现部分。人 流量检测代码主要是借助了 Open CV 库里的 hog 特征提取和 svm 分类器。当检测画面中有行 人通过时,被系统识别的人会用一个矩形将其框出,其中心点会以一个绿色的点标明。此时 用户操纵鼠标,在视频画面上画出一条红色直线,红线上有一个直线迭代器,当绿色的点越 过直线迭代器时,总人数增加。 单片机实现部分。我们在单片机的代码部分设置了串口通信函数,并设置了 LED 灯的不 同亮起情况。单片机和 PC 机通过串口函数相连,当人流量检测程序检测到人数增加时,人 数增加的信息会通过串口传给单片机,从而实现了 LED 灯的亮起数目增加。当亮起的灯数为 八盏灯时,由于设备的限制就不再增加灯亮起的数目。

2.2 软硬件运行平台

1. 电脑 win10,英特尔 i7 处理器等。若设备更好,实现效果会更佳。

2. C51 单片机

2.3 系统开发平台

1. 程序编写: Microsoft Visual Studio Enterprise 2019 Keil5(单片机开发平台) Win10 电脑自带的剪辑、截图工具

2. 开源平台、第三方库:

OpenCV(人流量检测代码) 博客:博客园、CSDN

2.4 作品特色

本作品代码简单,设计精巧有效,成本低,应用能力广泛,应用于高校等场所电器设 备的控制,节约电能,能够减少不必要的电能损失,为企业减少电费支出,降低成本,提高 经济效益,从而使有限的电力发挥更大的社会经济效益,提高电能利用率,更为有效地利用 好电力资源。同时节省相关工作人员的工作时间,在未来有很大的应用方向。如以下几个方 向的应用价值:

1、该智能管理系统可用于检测校园刷脸门禁系统附近的人流量,控制门禁系统设备的待 机状态,减少因长时间高负荷运作的电能浪费与性能损失,延长设备的使用寿命。

2、对于夜晚的校园操场,可通过附近的摄像头识别并分析人流量,对操场上的路灯开启 数量进行控制,也可把此系统装入空闲的校园教室监控上,对该教室的灯、空调、电扇灯装 置进行智能控制。

3、对于 24 小时自助银行、自助餐厅、自助超市等公共场所,管理员长期待岗未免浪费人 力。这时,可以通过实时人流量检测与统计分析,合理调节设备的使用数量,使设备达到最 高的利用价值,减少人的工作时间。

4、在无人机上植入智能摄像头,连接该控制系统,可适用于人物搜索、查找等,特别是 在自然灾害突发情况下,人物搜寻比较艰难,利用无人机进行搜寻工作能达到一个良好的效 果。

3 详细设计

3.1 HOG 行人检

HOG 的核心思想是所检测的局部物体外形能够被光强梯度或边缘方向的分布所描述。通 过将整幅图像分割成小的连接区域(称为 cells),每个 cell 生成一个方向梯度直方图或 者 cell 中 pixel 的边缘方向,这些直方图的组合可表示出(所检测目标的目标)描述子。 为改善准确率,局部直方图可以通过计算图像中一个较大区域(称为 block)的光强作为 measure 被对比标准化,然后用这个值(measure)归一化这个 block 中的所有 cells.这个归 一化过程完成了更好的照射/阴影不变性。与其他描述子相比,HOG 得到的描述子保持了几 何和光学转化不变性(除非物体方向改变)。因此 HOG 描述子尤其适合人的检测。

一、简要说明

 HOG的核心思想是所检测的局部物体外形能够被光强梯度或边缘方向的分布所描述。通过将整幅图像分割成小的连接区域(称为cells),每个cell生成一个方向梯度直方图或者cell中pixel的边缘方向,这些直方图的组合可表示出(所检测目标的目标)描述子。为改善准确率,局部直方图可以通过计算图像中一个较大区域(称为block)的光强作为measure被对比标准化,然后用这个值(measure)归一化这个block中的所有cells.这个归一化过程完成了更好的照射/阴影不变性。与其他描述子相比,HOG得到的描述子保持了几何和光学转化不变性(除非物体方向改变)。因此HOG描述子尤其适合人的检测。

 OpenCV实现了两种类型的基于HOG特征的行人检测,分别是SVM和Cascade,OpenCV自带的级联分类器的文件的位置在“XX\opencv\sources\data\hogcascades”(OpenCV4.x版本可用)。

  • HOGDescriptor
  1. 代码表示如下:

CV_WRAP HOGDescriptor(): winSize(64,128), blockSize(16,16),blockStride(8,8),

        cellSize(8,8), nbins(9), derivAperture(1), winSigma(-1),

        histogramNormType(HOGDescriptor::L2Hys), L2HysThreshold(0.2),                      gammaCorrection(true),

        free_coef(-1.f), nlevels(HOGDescriptor::DEFAULT_NLEVELS),                           signedGradient(false)

      {}

  1. 重要参数:

窗口大小 winSize(64,128), 块大小blockSize(16,16), 块滑动增量blockStride(8,8), 胞元大小cellSize(8,8), 梯度方向数nbins(9)。

上面这些都是HOGDescriptor的成员变量,括号里的数值是它们的默认值,它们反应了HOG描述子的参数。这里做了几个示意图来表示它们的含义。

注明:nBins表示在一个胞元(cell)中统计梯度的方向数目,例如nBins=9时,在一个胞元内统计9个方向的梯度直方图,每个方向为180/9=20度。

  • HOG特征计算原理:

原理图

  1. Gramma归一化: 对图像颜色进行Gamma归一化处理,降低局部阴影及背景因素的影响。
  2. 计算梯度:通过差分计算出图像在水平方向上及垂直方向上的梯度,然后得到各个像素点的梯度的幅值及方向:

  1. 划分cell

将整个窗口划分成大小相同互不重叠的细胞单元cell(如8×8像素),计算出每个cell的梯度大小及方向.然后将每像素的梯度方向在0−180(无向:0-180,有向:0-360)平均分为9个bins,统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor. 

     采用9个bin的直方图来统计这8*8个像素的梯度信息。也就是将cell的梯度方向360度分成9个方向块,例如:如果这个像素的梯度方向是20-40度,直方图第2个bin的计数就加一,这样,对cell内每个像素用梯度方向在直方图中进行加权投影(映射到固定的角度范围),就可以得到这个cell的梯度方向直方图了,就是该cell对应的9维特征向量(因为有9个bin)。

  1. 组合成block,统计block直方图

将2×2个相邻的cell组成大小为16×16的像素块即block.依次将block大小的滑动窗口从左到右从上到下滑动,求其梯度方向直方图向量,一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor。

不同大小的cells与不同大小的block作用下的效果对比如下:

  1. 梯度直方图归一化
  2. 收集HOG特征

3.2 HOG+SVM 检测

在使用 HOG + SVM 进行行人检测时,采集 HOG 特征的主要思想是通过对一幅图像进行分 析, 局部目标的表象和形状可以被剃度或者边缘密度方向分布很好的好的描述. 我们对图 像的各个像素点采集土堆或者边缘的方向直方图, 根据直方图的信息就可以描述图片的特 征. 好在 OpenCV 中已经提供了计算 HOG 特征的方法, 根据采集到的 HOG 特征向量, 供 SVM 分类使用. SVM 简单来说就是一个分类器, 在行人检测中就可以转化为行人与非行人的两类 分类问题, 在OpenCV 中运用到的是基于网格法的 SVM.使用采集到的正样本(行人)和负样本 (非行人, 可以是汽车, 树木, 路灯等等)的HOG特征, 然后使用SVM分类器进行训练, 得到行人检测模型, 进行行人检测。

3.3 迭代器实现人数变化

我们利用 C++的迭代器实现人流量计数,标准库为每一种标准容器(包括 vector)定义 了一种迭代器类型。迭代器类型提供了比下标操作更一般化的方法:所有的标准库容器都定 义了相应的迭代器类型,而只有少数的容器支持下标操作。

1.容器的iterator类型

每种容器类型都定义了自己的迭代器类型,如vector:vector::iterator iter;这条语句定义了一个名为iter的变量,它的数据类型是由vector定义的iterator类型。每个标准库容器类型都定义了一个名为iterator的成员,这里的iterator与迭代器实际类型的含义相同。

2. Begin和end操作

每种容器都定义了一对命名为begin和end的函数,用于返回迭代器。如果容器中有元素的话,由begin返回的迭代器指向***个元素把iter初始化为由名为begin的vector操作返回的值。假设vector不空,初始化后,iter即指该元素为ivec[0]。

由end操作返回的迭代器指向vector的“末端元素的下一个”。通常称为超出末端迭代器(off-the-end iterator),表明它指向了一个不存在的元素。如果vector为空,begin返回的迭代器与end返回的迭代器相同。由end操作返回的迭代器并不指向vector中任何实际的元素,相反,它只是起一个哨兵(sentinel)的作用,表示我们已处理完vector中所有元素。

3.C++迭代器的自增和解引用运算

C++迭代器类型定义了一些操作来获取迭代器所指向的元素,并允许程序员将迭代器从一个元素移动到另一个元素。迭代器类型可使用解引用操作符(*操作符)来访问迭代器所指向r 元素解引用操作符返回迭代器当前所指向的元素。

对int对象来说,操作结果就是把int型值“加1”,而对迭代器对象则是把容器中的迭代器“向前移动一个位置”。因此,如果iter指向***个元素,则++iter指向第二个元素。由于end操作返回的C++迭代器不指向任何元素,因此不能对它进行解引用或自增操作。

3.4 串口函数连接 PC 机与单片机

借助串口通信函数,单片机和人流量检测程序通过串口连接。将检测画面中的人数增 加信息传递给单片机。

一、开发步骤

1. 设置串口中断寄存器;

2. 求出给定波特率对应的T1定时器初始值(因为传统的C51是用T1定时器产生波特率的);

3. 写程序。

  • 串口模块

用USB口就可以代替串口,所以用一般的学习板进行开发,只要插上USB线就可以进行串口测试。

  • SBUF缓冲区

SBUF是一个寄存器,作为一个缓冲区,当单片机准备接收数据时,会先把数据放到SBUF中,然后再接收。发送也是,当单片机准备发送数据时,会先把数据放到SBUF中,再发送出去。

  • 串口相应寄存器

SM1/SM0/SM2/REN/TE8/RB8/TI/RI

  • 给定波特率求定时器

串口中断要有定时器T1参加,因为C51是用定时器1来产生波特率的。因此就需要给定时器T1 设置初值。首先,我们要选择一个合适的波特率,波特率关乎数据发送的快慢,一般我们选择9600b/s好了。一般我们会选择定时器的方式2(这里不是说上面的方式2,而是最大计数为256且自动重载定时器初值的方式)使用这种方式是因为它能自动重载定时器初值,不用在中断服务程序中人工重设,可以减少误差。

下面给出公式:

Fosc:为晶振频率

(256-X):最终要求的是X,256出现的原因是定时器的方式2最大技术是256,如果是方式1,则是65536-X最后把X化成16进制,高八位赋给 TH1,第八位赋给 TL1

4 系统安装及使用说明

运行环境:Windows 操作系统即可运行,另需搭建了单片机的开发板。

安装说明:

        1.keil5 的程序烧制到单片机。

        2.VS 代码中的检测视频更换地址。

        3.连接 PC 机与开发板,运行 VS 中的程序。

5 总结

作品利用计算机人流量检测技术,借助单片机模拟基于人流量数据的电器设备管理,展 现安全、环保、健康、方便等优势。

1. 电器设备安全。使用此系统,让电器设备减少不必要的使用时间,系统可以根据生 活节奏自动开启或关闭电路,避免不必要的浪费和电气老化引起的火灾。

2. 环保节能。有效控制设备电源,在不使用电器时会自动将其关闭,减少设备用电量。

3. 健康。设备通过识别计算人的数量,在流量到达一定程度时会启动不同的电器设备 数目,通过对通风排气系统的智能化管理,减少传染疾病的风险。此智能管理系统将会为其 带来有必要的帮助。同时提高了人们的生活质量,减少不必要的时间花费。

4. 安装使用方便,抗干扰性强。不需要复杂的布线,也能使用该智能管理系统,将会 增加的无线通信接口会更加方便使用。此智能设备通过红外或者协议信号控制方式,安全方 便不干扰。对于商场或者工厂等大型场地,可以实现对灯光的集中控制或者是定时控制。 由于团队经验有限以及硬件设备的限制,本次作品只采用处理保存好的视频的方式, 未能做到实时检测统计人流量状况。所以作品今后的改进方向是:增加人流量系统检测的实 时功能。如通过摄像头实时捕捉人流量变化情况,做到更精准、更动态的统计人流量数目, 便于更灵活的控制电器设备,以达到节能减排、防控疾病(通风排气设备)的目的。

素材目录

1. 人流量实验视频(饭堂门口)

2. 单片机 STC89C52 型号

名词定义

1.目标检测

目标检测,也叫目标提取,是一种基于目标几何和统计特征的图像分割。它将目标的分 割和识别合二为一,其准确性和实时性是整个系统的一项重要能力。尤其是在复杂场景中, 需要对多个目标进行实时处理时,目标自动提取和识别就显得特别重要。

2.OpenCV—HOG 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉 和图像处理中用来进行物体检测的特征描述子。HOG 特征通过计算和统计图像局部区域的梯 度方向直方图来构成特征。

3.OpenCV—SVM 支持向量机(Support Vector Machine, SVM)是一类按监督学习(supervised learning) 方式对数据进行二元分类的广义线性分类器(generalized linear classifier),其决策 边界是对学习样本求解的最大边距超平面(maximum-margin hyperplane)。SVM 使用铰链损 失函数(hinge loss)计算经验风险(empirical risk)并在求解系统中加入了正则化项以 优化结构风险(structural risk),是一个具有稀疏性和稳健性的分类器。SVM 可以通过 核方法(kernel method)进行非线性分类,是常见的核学习(kernel learning)方法之一。

参考资料:

1. https://blog.csdn.net/weixin_42532354/article/details/85373393?utm_source=app&app_versio n=4.5.4 基于 OpenCV 的简单人流量统计

2 https://blog.csdn.net/weixin_43002829/article/details/83111562?utm_source=app&app_versi on=4.5.4 VS2015 串口通信编程

3 https://blog.csdn.net/qq_42604176/article/details/105695859?utm_source=app&app_version =4.5.4 HOG+SVM 实现行人检

附件代码:

#include <opencv2/objdetect.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
#include <iomanip>

using namespace cv;
using namespace std;
void on_MouseHandle(int event, int x, int y, int flags, void* param);
void DrawLine(Mat& img, Rect box);

Rect g_box;
bool g_DrawingBox = false;
Point p1, p2;
int Thecount = 0;
int precolor = 0;
int all = 0;
int presentnumber = 0;


class Detector
{
	//enum Mode { Default, Daimler } m;
	enum { Default, Daimler };//定义枚举类型
	int m;
	HOGDescriptor hog, hog_d;
public:
	Detector(int a) : m(a), hog(), hog_d(Size(48, 96), Size(16, 16), Size(8, 8), Size(8, 8), 9)//构造函数,初始化对象时自动调用,m,hog,hog_d是数据成员,后跟一个放在圆括号中的初始化形式
	{
		hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
		hog_d.setSVMDetector(HOGDescriptor::getDaimlerPeopleDetector());
	}
	void toggleMode() { m = (m == Default ? Daimler : Default); }
	string modeName() const { return (m == Default ? "Default" : "Daimler"); }
	vector<Rect> detect(InputArray img)
	{
		// Run the detector with default parameters. to get a higher hit-rate
		// (and more false alarms, respectively), decrease the hitThreshold and
		// groupThreshold (set groupThreshold to 0 to turn off the grouping completely).
		vector<Rect> found;
		if (m == Default)
			hog.detectMultiScale(img, found, 0, Size(8, 8), Size(32, 32), 1.05, 2, false);
		else if (m == Daimler)
			hog_d.detectMultiScale(img, found, 0.5, Size(8, 8), Size(32, 32), 1.05, 2, true);
		return found;
	}
	void adjustRect(Rect& r) const
	{
		// The HOG detector returns slightly larger rectangles than the real objects,
		// so we slightly shrink the rectangles to get a nicer output.
		r.x += cvRound(r.width * 0.1);
		r.width = cvRound(r.width * 0.8);
		r.y += cvRound(r.height * 0.07);
		r.height = cvRound(r.height * 0.8);
	}
};
//修改参数区域
static const string keys = "{ help h   |   | print help message }"
"{ camera c | 0 | capture video from camera (device index starting from 0) }"
"{ video v  | C:/Users/zhy/Videos/4.avi| use video as input }";

int main(int argc, char** argv)
{
	CommandLineParser parser(argc, argv, keys);		//keys:描述可接受的命令行参数的字符串
	parser.about("This sample demonstrates the use ot the HoG descriptor.");//设置相关信息。相关信息会在 printMessage 被调用时显示。
	if (parser.has("help"))
	{
		parser.printMessage();
		return 0;
	}
	int camera = parser.get<int>("camera");
	string file = parser.get<string>("video");
	if (!parser.check())//检查解析错误。当错误发生时返回true。错误可能是转换错误、丢失参数等。
	{
		parser.printErrors();
		return 1;
	}

	VideoCapture cap;
	if (file.empty())
		cap.open(camera);
	else
		cap.open(file.c_str());
	if (!cap.isOpened())
	{
		cout << "Can not open video stream: '" << (file.empty() ? "<camera>" : file) << "'" << endl;
		return 2;
	}

	cout << "Press 'q' or <ESC> to quit." << endl;
	cout << "Press <space> to toggle between Default and Daimler detector" << endl;
	//Default and Daimler detector
	Detector detector(1);		//初始化使用Daimler detector
	//全局变量的初始化
	g_box = Rect(-1, -1, 0, 0);
	p1 = Point(0, 0);
	p2 = Point(0, 0);

	Mat frame;
	
	cap.read(frame);
	//namedWindow("Video");
	imshow("People detector", frame);
	//因手机像素太高,未来便于观察,把图像缩小为以前的0.6倍
	resize(frame, frame, Size(round(frame.cols * 0.6), round(frame.rows * 0.6)));
	//鼠标回调函数
	setMouseCallback("People detector", on_MouseHandle, (void*)&frame);

	//Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	//Ptr<BackgroundSubtractor> pMOG2 = createBackgroundSubtractorMOG2();

	bool drawline = false;

	for (;;)
	{
		cap >> frame;
		if (frame.empty())
		{
			cout << "Finished reading: empty frame" << endl;
			break;
		}
		if (g_DrawingBox)
		{
			drawline = true;
		}
		if (drawline)
		{
			DrawLine(frame, g_box);
		}

		int64 t = getTickCount();
		vector<Rect> found = detector.detect(frame);
		t = getTickCount() - t;

		// show the window
		{
			ostringstream buf;
			buf << "Mode: " << detector.modeName() << " ||| "
				<< "FPS: " << fixed << setprecision(1) << (getTickFrequency() / (double)t);
			putText(frame, buf.str(), Point(10, 30), FONT_HERSHEY_PLAIN, 2.0, Scalar(0, 0, 255), 2, LINE_AA);
		}
		Point center;
		presentnumber = 0;
		for (vector<Rect>::iterator i = found.begin(); i != found.end(); ++i)
		{
			Rect& r = *i;
			detector.adjustRect(r);
			rectangle(frame, r.tl(), r.br(), cv::Scalar(0, 255, 0), 2);
			//presentnumber++;
			center.x = round((r.tl().x + r.br().x) / 2);
			center.y = round((r.tl().y + r.br().y) / 2);
			circle(frame, center, 5, Scalar(0, 255, 0), 5, 8, 0);
		}
		LineIterator it(frame, p1, p2, 8);
		all = 0;
		for (int j = 0; j < it.count; ++j, ++it)
		{
			if (precolor == 0 && (int)(*it)[1] == 255)
			{
				Thecount++;
				//precolor = 0;
			}
			all = all + (int)(*it)[1];
			if ((int)(*it)[1] == 255)
			{
				precolor = 255;
				break;
			}
		}
		if (all == 0)
		{
			precolor = 0;
		}
		stringstream ss;
		string str, s;
		string str1 = "The Count: ", s1 = "present number: ";
		ss << Thecount;
		ss >> str;
		str = str1 + str;
		//把记好的数打印到图像的(20,20)坐标点上
		putText(frame, str, Point(45, 45), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255));
		ss << presentnumber;
		ss >> s;
		s = s1 + s;
		//把记好的数打印到图像的(20,20)坐标点上
		putText(frame, s, Point(60, 60), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255));

		imshow("People detector", frame);

		// interact with user
		const char key = (char)waitKey(30);
		if (key == 27 || key == 'q') // ESC
		{
			cout << "Exit requested" << endl;
			break;
		}
		else if (key == ' ')
		{
			detector.toggleMode();
		}
	}
	return 0;
}



//鼠标事件的回调函数
void on_MouseHandle(int event, int x, int y, int flags, void* param)
{
	Mat& image = *(Mat*)param;
	switch (event)
	{
	case EVENT_MOUSEMOVE:
	{
		if (g_DrawingBox)
		{
			g_box.width = x - g_box.x;
			g_box.height = y - g_box.y;
		}
	}
	break;
	case EVENT_LBUTTONDOWN:
	{
		g_DrawingBox = true;
		g_box = Rect(x, y, 0, 0);
	}
	break;
	case EVENT_LBUTTONUP:
	{
		g_DrawingBox = false;
		if (g_box.width < 0)
		{
			g_box.x += g_box.width;
			g_box.width *= -1;
		}
		if (g_box.height < 0)
		{
			g_box.y += g_box.height;
			g_box.height *= -1;
		}
		DrawLine(image, g_box);
	}
	break;
	default:
		break;
	}
}
//划线函数
void DrawLine(Mat& img, Rect box)
{

	p1 = Point(box.x, box.y);
	p2 = Point(box.x + box.width, box.y + box.height);
	line(img, p1, p2, Scalar(0, 0, 255), 3, 8);
	//rectangle(img, box.tl(), box.br(), Scalar(0, 0, 255));
}

猜你喜欢

转载自blog.csdn.net/qq_46351409/article/details/128857440