项目展示:基于ReID的行人计数

简介

现实生活中有这样的场景,摄像头并不能覆盖一个区域的所有地方,现在想不借助其他手段,仅通过摄像头拍摄到的图像,计算一个区域中的行人数目。

首先不能使用人群计数的方法,因为有些有人的地方没有被摄像头覆盖。人群计数或者人头计数只能计算拍摄到的人数。

该项目使用的策略是:

  • 仅处理在入口处摄像头拍摄的画面
  • 因为摄像头是固定的,由人为划定入口位置,一条直线。
  • 行人检测用来检测行人位置。
  • 行人重识别用来判断入口内的行人是否被加入到数据库中,如果有则用来刷新信息,如果没有则添加,同时计数加1;同时判断入口外的行人,是否在数据库中,如果有,则从数据库中删除,计数减1。

项目使用当前sota的EANet作为ReID模型,使用YoloV3作为行人检测网络。

项目部分代码展示

YoloV3使用tensorflow所支持的pb模型,然后使用TF C++ API调用,EANet使用LibTorch调用pth文件。
Yolov3模型类定义

	class YoloV3 : public Model_Loader_Base {
	public:
		YoloV3() {};
		bool predict(vector<BBoxes>&);
		std::string output_node = "post_processing/result";
		Tensor t_in = Tensor(DT_FLOAT, TensorShape({ 1,544,544,3 }));
		void preProcessing(const Mat &ori);  
		void drawBBoxes(Mat&, vector<BBoxes>&);
		vector<std::pair<string, Tensor> > inputs_for_yolo;
	};

EANet模型类定义

class EANet {
	public:
		EANet(string path) { model_path = path; calDistacneModel = new CalCosDistance("calculateCosHead.pt");
		calDistacneModel->InitModel();
		};
		...
		...
		void preprocessing(const Mat &img);
		void cropPerson(const Mat &, const vector<TfModel::BBoxes> &bboxes, vector<Mat> &personContainer);
		at::Tensor predict();
		vector<int> countPersonNumber(vector<Mat> &query);
		void calFunction(Point p1, Point p2);
		bool calDirection( Point center, int);
		vector<float> lineFunction;
		vector<int> countPersonNumberByRegion(vector<Mat> &query, Point &p1, Point &p2, int direction, vector<TfModel::BBoxes> &bboxes);
		void drawInfo(Mat &frame, vector<int> &recorder, vector<TfModel::BBoxes> bboxes);
		vector<int> countPersonNumberByRegionV2(vector<Mat> &query, Point &p1, Point &p2, int direction, vector<TfModel::BBoxes> &bboxes);
		int delPersonByRegion(const Mat &query);
		void writeGalleryForEachFrame();
	}; 

我省略了一些变量,仅仅展示几个比较关键的函数功能。

  • 经过行人检测得到行人位置,如何得知行人是在入口内还是入口外?
    用户指定一个直线(非平行)作为入口,同时还需给出正方向的位置。
    void calFunction(Point p1, Point p2) 函数计算两个点代表的直线方程。 行人的位置用点表示(点可取在中点偏下位置)。该点带入方程,如果点在直线上方,计算得到正值,如果点在直线下方,计算得到负值。用户指定正值还是负值为正方向,正方向即点在门内。以此得知行人和门的关系。
    其中 bool calDirection( Point center, int)就是用来判断行人和门的关系。

  • 行人信息如何保存
    行人一旦进入入口,EANet会通过余弦相似度判断该人和数据库中其他人的相似度,从而得到该人是否本来就在门内。如果不是则计数加一,同时把该人这一帧的形象以及对应的特征向量(经过EANet)保存下来(内存中)。

  • 行人首次被保存的形象也许不够表达这个人的全面信息
    我们不仅仅保存行人出现的第一个形象,连续6帧保存他的形象。用一个长度为6的队列,保存距离当前帧前的6帧的形象。这6个形象的特征向量会按照比例加权,得到更加能代表行人的特征向量。

//如果 min_index不为-1,则说明当前query和gallery中有一个目标相同,且min_index指向距离最小的gallery。
		if (min_index != -1) {
			count.push_back(0);
			// 如果当前的query和某一个gallery的距离小于0.1,非常确信这是同一个人,则把query添加进gallery
			if (galleryVectorMap[min_index].size() < 6 && min_distance < 0.12)
			{
				galleryVectorMap[min_index].push_back(queryVector);
				galleryMap[min_index].push_back(query[i]);
			}
			else if(galleryVectorMap[min_index].size() == 6 && min_distance < 0.12){
				// 保持每一个人的gallery最多有6张。
				galleryVectorMap[min_index].push_back(queryVector);
				galleryVectorMap[min_index].pop_front();
				galleryMap[min_index].push_back(query[i]);
				galleryMap[min_index].pop_front();
			}
			else if(galleryVectorMap[min_index].size() > 6){
				std::cerr << "max size of gallery vector container is 6" << endl;
				exit(-1);
			}
		}
		//如果 min_index为-1,则说明当前query和gallery中的任何一个目标都不同,则把这个query加入gallery中。
		else if (min_index == -1)
		{
			int new_index = galleryMap.size();
			assert(new_index == galleryVectorMap.size());
			if (priority_index.empty()) {
				// 空
			}
			else {
				new_index = priority_index.front();
				priority_index.pop();
			}

			count.push_back(1);
			galleryMap[new_index].push_back(query[i]);
			galleryVectorMap[new_index].push_back(queryVector);
		}
	

结果展示

该视频场景非实际应用场景。黄色的线代表入口位置。绿色框代表在入口外。红色代表这个人已经被计数,在数据库中保存。蓝色代表目标第一次进门内。
在这里插入图片描述
随着目标渐变移动,他的形象会和之前的不一样,所以我们采用连续6帧保存目标形象来获得更加可靠的目标特征向量。
在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值