计算机视觉——P2PNet基于点估计的人群计数原理与C++模型推理

简介

人群计数是计算机视觉领域的一个核心任务,旨在估算静止图像或视频帧中的行人数量。在过去几十年中,研究人员在这个领域投入了大量的精力,并在提高现有主流基准数据集性能方面取得了显著进展。然而,训练卷积神经网络需要大规模且高质量的标记数据集,而标记像素级别的行人位置成本昂贵,令人望而却步。

此外,由于数据分布之间存在领域转移,即在标签丰富的数据领域(源领域)上训练的模型无法很好地泛化到另一个标签稀缺的数据领域(目标领域),这严重限制了现有方法的实际应用。

《Rethinking Counting and Localization in Crowds: A Purely Point-Based Framework》提出了一个全新的基于点的框架,可以同时用于人群计数和个体定位。与传统的基于定位的方法不同,该框架完全依赖于点级别的表示,避免了中间表示(如密度图或伪目标框)可能引入的误差,并提出了一种新的性能评价指标,称为密度归一化平均精度,以更全面、更准确地评估模型性能。

研究团队还提出了一个名为点对点网络(P2PNet)的示例模型,该模型直接预测一系列人头点的集合来定位图像中的人群个体,避免了冗余步骤,并实现了与真实人工标注一致的定位。通过深入分析,研究者发现了实现该方法的核心策略,即为预测的候选点分配最优的学习目标,并通过基于匈牙利算法的一对一匹配策略来实现。实验证明,P2PNet在人群计数基准上显著超越了现有的最先进方法,并取得了非常高的定位精度。
在这里插入图片描述

网络结构

在这里插入图片描述
P2PNet的网络结构并不复杂。它建立在VGG16的基础上,并引入了一个上采样路径来获取细粒度的深度特征图,类似于特征金字塔网络(FPN)。然后,它利用两个分支来同时预测一组点及其置信度分数。在我们的流程中,关键步骤是确保预测点和真实点之间的一对一匹配,这决定了这些预测点的学习目标。

预测

在这里插入图片描述
Point proposals的初始化有两种方式,一种是全部初始化在中心点,另一种是网格式分布。Feature Map上的一个pixel对应着原图上的一个patch(sxs),并在这上面初始化K个Point proposal。
在这里插入图片描述
这些point proposals的坐标加上回归头分支得到的偏置就可以得到预测点的坐标。

匹配与损失计算

在这里插入图片描述
预测点与真实点之间的匹配用的是匈牙利算法,代价矩阵的计算方式如上图,它是坐标偏差与置信度分数的一个综合的考量。
在这里插入图片描述
分类损失函数是交叉熵损失,回归损失函数是欧氏距离。

在这里插入图片描述
文章还提出了一种新的度量指标nAP。nAP是根据平均精度计算出来的,平均精度是精度-召回率(PR)曲线下的面积。具体来说,给定所有预测的头部点ˆP,我们首先将其置信度得分从高到低进行排序。然后,根据预定义的密度感知标准,依次确定所调查的点是TP或FP。密度感知标准如上左图所示。

实验结果

在这里插入图片描述
研究者考虑了从ShanghaiTech Part A到Trancos的实验,如上表所示。显然,所提出的方法比现有的适应方法提高了2.9%。
在这里插入图片描述
由双重鉴别器生成的不同级别(分别为像素、补丁像素、补丁、图像)级别分数的可视化。图中的正方形代表一个标量。注意白色方块代表1,黑色方块代表0。

实现代码

训练代码可以参考:https://github.com/TencentYoutuResearch/CrowdCounting-P2PNet

推理代码可以参考下面的代码:

#include <sstream>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>using namespace cv;
using namespace dnn;
using namespace std;struct CrowdPoint
{cv::Point pt;float prob;
};static void shift(int w, int h, int stride, vector<float> anchor_points, vector<float>& shifted_anchor_points)
{vector<float> x_, y_;for (int i = 0; i < w; i++){float x = (i + 0.5) * stride;x_.push_back(x);}for (int i = 0; i < h; i++){float y = (i + 0.5) * stride;y_.push_back(y);}vector<float> shift_x((size_t)w * h, 0), shift_y((size_t)w * h, 0);for (int i = 0; i < h; i++){for (int j = 0; j < w; j++){shift_x[i * w + j] = x_[j];}}for (int i = 0; i < h; i++){for (int j = 0; j < w; j++){shift_y[i * w + j] = y_[i];}}vector<float> shifts((size_t)w * h * 2, 0);for (int i = 0; i < w * h; i++){shifts[i * 2] = shift_x[i];shifts[i * 2 + 1] = shift_y[i];}shifted_anchor_points.resize((size_t)2 * w * h * anchor_points.size() / 2, 0);for (int i = 0; i < w * h; i++){for (int j = 0; j < anchor_points.size() / 2; j++){float x = anchor_points[j * 2] + shifts[i * 2];float y = anchor_points[j * 2 + 1] + shifts[i * 2 + 1];shifted_anchor_points[i * anchor_points.size() / 2 * 2 + j * 2] = x;shifted_anchor_points[i * anchor_points.size() / 2 * 2 + j * 2 + 1] = y;}}
}
static void generate_anchor_points(int stride, int row, int line, vector<float>& anchor_points)
{float row_step = (float)stride / row;float line_step = (float)stride / line;vector<float> x_, y_;for (int i = 1; i < line + 1; i++){float x = (i - 0.5) * line_step - stride / 2;x_.push_back(x);}for (int i = 1; i < row + 1; i++){float y = (i - 0.5) * row_step - stride / 2;y_.push_back(y);}vector<float> shift_x((size_t)row * line, 0), shift_y((size_t)row * line, 0);for (int i = 0; i < row; i++){for (int j = 0; j < line; j++){shift_x[i * line + j] = x_[j];}}for (int i = 0; i < row; i++){for (int j = 0; j < line; j++){shift_y[i * line + j] = y_[i];}}anchor_points.resize((size_t)row * line * 2, 0);for (int i = 0; i < row * line; i++){float x = shift_x[i];float y = shift_y[i];anchor_points[i * 2] = x;anchor_points[i * 2 + 1] = y;}
}
static void generate_anchor_points(int img_w, int img_h, vector<int> pyramid_levels, int row, int line, vector<float>& all_anchor_points)
{vector<pair<int, int> > image_shapes;vector<int> strides;for (int i = 0; i < pyramid_levels.size(); i++){int new_h = floor((img_h + pow(2, pyramid_levels[i]) - 1) / pow(2, pyramid_levels[i]));int new_w = floor((img_w + pow(2, pyramid_levels[i]) - 1) / pow(2, pyramid_levels[i]));image_shapes.push_back(make_pair(new_w, new_h));strides.push_back(pow(2, pyramid_levels[i]));}all_anchor_points.clear();for (int i = 0; i < pyramid_levels.size(); i++){vector<float> anchor_points;generate_anchor_points(pow(2, pyramid_levels[i]), row, line, anchor_points);vector<float> shifted_anchor_points;shift(image_shapes[i].first, image_shapes[i].second, strides[i], anchor_points, shifted_anchor_points);all_anchor_points.insert(all_anchor_points.end(), shifted_anchor_points.begin(), shifted_anchor_points.end());}
}class P2PNet
{
public:P2PNet(const float confThreshold = 0.5){this->confThreshold = confThreshold;this->net = readNet("SHTechA.onnx");}void detect(Mat& frame);
private:float confThreshold;Net net;Mat preprocess(Mat srcimgt);const float mean[3] = { 0.485, 0.456, 0.406 };const float std[3] = { 0.229, 0.224, 0.225 };vector<String> output_names = { "pred_logits", "pred_points" };
};Mat P2PNet::preprocess(Mat srcimg)
{int srch = srcimg.rows, srcw = srcimg.cols;int new_width = srcw / 128 * 128;int new_height = srch / 128 * 128;Mat dstimg;cvtColor(srcimg, dstimg, cv::COLOR_BGR2RGB);resize(dstimg, dstimg, Size(new_width, new_height), INTER_AREA);dstimg.convertTo(dstimg, CV_32F);int i = 0, j = 0;for (i = 0; i < dstimg.rows; i++){float* pdata = (float*)(dstimg.data + i * dstimg.step);for (j = 0; j < dstimg.cols; j++){pdata[0] = (pdata[0] / 255.0 - this->mean[0]) / this->std[0];pdata[1] = (pdata[1] / 255.0 - this->mean[1]) / this->std[1];pdata[2] = (pdata[2] / 255.0 - this->mean[2]) / this->std[2];pdata += 3;}}return dstimg;
}void P2PNet::detect(Mat& frame)
{const int width = frame.cols;const int height = frame.rows;Mat img = this->preprocess(frame);const int new_width = img.cols;const int new_height = img.rows;Mat blob = blobFromImage(img);this->net.setInput(blob);vector<Mat> outs;//this->net.forward(outs, this->net.getUnconnectedOutLayersNames());this->net.forward(outs, output_names);vector<int> pyramid_levels(1, 3);vector<float> all_anchor_points;generate_anchor_points(img.cols, img.rows, pyramid_levels, 2, 2, all_anchor_points);const int num_proposal = outs[0].cols;int i = 0;float* pscore = (float*)outs[0].data;float* pcoord = (float*)outs[1].data;vector<CrowdPoint> crowd_points;for (i = 0; i < num_proposal; i++){if (pscore[i] > this->confThreshold){float x = (pcoord[i] + all_anchor_points[i * 2]) / (float)new_width * (float)width;float y = (pcoord[i + 1] + all_anchor_points[i * 2 + 1]) / (float)new_height * (float)height;crowd_points.push_back({ Point(int(x), int(y)), pscore[i] });}pcoord += 2;}cout << "have " << crowd_points.size() << " people" << endl;for (i = 0; i < crowd_points.size(); i++){cv::circle(frame, crowd_points[i].pt, 2, cv::Scalar(0, 0, 255), -1, 8, 0);}
}int main()
{P2PNet net(0.3);string imgpath = "2.jpeg";Mat srcimg = imread(imgpath);net.detect(srcimg);static const string kWinName = "dst";namedWindow(kWinName, WINDOW_NORMAL);imshow(kWinName, srcimg);waitKey(0);destroyAllWindows();
}

实现结果:
在这里插入图片描述
在这里插入图片描述
工程源码下载:https://download.csdn.net/download/matt45m/88936724?spm=1001.2014.3001.5503

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/273302.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

转移表回调函数实现

回调函数实现 计算器的模拟&#xff08;函数指针数组的使用&#xff09;&#xff08;回调函数&#xff09; 简化 冗余 老的代码的问题就是 冗余 写死 不能完成不同的任务 函数调用的时候只需要知道地址就可以 calc计算器 这里也称之为转移表 #define _CRT_SECURE_NO_WAR…

基于鹦鹉优化算法(Parrot optimizer,PO)的无人机三维路径规划(提供MATLAB代码)

一、无人机路径规划模型介绍 无人机三维路径规划是指在三维空间中为无人机规划一条合理的飞行路径&#xff0c;使其能够安全、高效地完成任务。路径规划是无人机自主飞行的关键技术之一&#xff0c;它可以通过算法和模型来确定无人机的航迹&#xff0c;以避开障碍物、优化飞行…

Cisco Packet Tracer 模拟器实现一些交换机的基本配置

1. 内容 应用Cisco Packet Tracer 5.3搭建网络 应用Cisco Packet Tracer 5.3配置网络 通过不同的命令实现交换机的基本配置&#xff0c;包括交换机的各种配置模式、交换机的基本配置、交换机的端口配置。 2. 过程 2.1 打开软件 安装模拟器后打开如下&#xff1a; 图1 安装并…

Intel® Extension for PyTorch*详细安装教程

最近在研究Intel的pytorch的加速拓展Intel Extension for PyTorch*,但是发现官网的文档全是英文的&#xff0c;不太好找安装教程。所以特此分享Intel Extension for PyTorch*的详细安装教程。 文章目录 一、安装所需系统要求1.1 硬件需求1.2 软件需求 二、准备2.1 安装驱动程序…

基于冠豪猪优化算法(Crested Porcupine Optimizer,CPO)的无人机三维路径规划(MATLAB)

一、无人机路径规划模型介绍 无人机三维路径规划是指在三维空间中为无人机规划一条合理的飞行路径&#xff0c;使其能够安全、高效地完成任务。路径规划是无人机自主飞行的关键技术之一&#xff0c;它可以通过算法和模型来确定无人机的航迹&#xff0c;以避开障碍物、优化飞行…

基于pytorch的视觉变换器-Vision Transformer(ViT)的介绍与应用

近年来&#xff0c;计算机视觉领域因变换器模型的出现而发生了革命性变化。最初为自然语言处理任务设计的变换器&#xff0c;在捕捉视觉数据的空间依赖性方面也显示出了惊人的能力。视觉变换器&#xff08;Vision Transformer&#xff0c;简称ViT&#xff09;就是这种变革的一个…

后量子时代,未来密码该何去何从?

古有飞鸽&#xff0c;现有网络&#xff0c;在知识经济为基础的信息化社会中&#xff0c;保障网络信息安全无疑成为成为国与国之间无形的较量。小到个人通讯&#xff0c;大到机要信息传输&#xff0c;信息安全对于国家安全和经济活动正常运转至关重要。密码学作为保障网络与信息…

iOS17.4获取UDID安装mobileconfig描述文件失败 提示“安全延迟进行中”问题 | 失窃设备保护

iOS17.4这两天已经正式发布&#xff0c; 在iOS 17.4版本中新增了一个名为"失窃设备保护"的功能&#xff0c;并提供了一个"需要安全延迟"的选项。 iOS17.4获取UDID安装mobileconfig描述文件失败 提示“安全延迟进行中”问题 | 失窃设备保护 当用户选择启用…

WPF(1)的MVVM的数据驱动学习示例

MVVM Model:数据模型、View 界面、ViewModel 业务逻辑处理 项目结构 界面数据绑定 <Window x:Class"WpfApp1.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/x…

[Spark SQL]Spark SQL读取Kudu,写入Hive

SparkUnit Function&#xff1a;用于获取Spark Session package com.example.unitlimport org.apache.spark.sql.SparkSessionobject SparkUnit {def getLocal(appName: String): SparkSession {SparkSession.builder().appName(appName).master("local[*]").getO…

springcloud-alibaba Sentinel入门

Releases alibaba/Sentinel GitHubSentinel下载官方 在cmd 里面运行 启动命令 java -jar sentinel-dashboard-1.8.6.jar 启动成功前提 java环境 &#xff0c;已经注册到服务注册中心&#xff0c;8080端口没有被占用 启动后访问地址为 qhttp://localhost:8080http://lo…

AI入门笔记(四)

深度学习是人工智能的一种实现方法。本文我将学习到的关于深度学习的代表卷积神经网络的数学结构分享给大家。 深度学习是重叠了很多层的隐藏层&#xff08;中间层&#xff09;的神经网络。我们以一个例题为例。 建立一个卷积神经网络&#xff0c;用来识别通过 66 像素的图像读…

系统并发性能指标与测试工具介绍

目录 一、性能指标介绍 1.1 并发用户数 1.2 TPS(每秒事务数) 1.3 QPS&#xff08;每秒查询率&#xff09; 1.4 TPS与QPS的区别与关系 1.4.1 区别 1.4.2 关系 1.5 响应时间&#xff08;RT&#xff09; 二、指标评估 2.1 背景 2.2 获取性能指标 2.3 性能指标计算/统计…

Python 创建PPT

本篇为如何使用Python来创建ppt文件。 创建PPT 安装必要的库 命令如下&#xff1a; pip install python-pptx 安装过程&#xff1a; 创建ppt文件 在当前目录下创建一个test的ppt文件。其中包含两页&#xff0c;分别使用了不同的布局。 第一页设置了标题和内容。第二页只设…

C++变参模板

从c11开始&#xff0c;模板可以接受一组数量可变的参数&#xff0c;这种技术称为变参模板。 变参模板 下面一个例子&#xff0c;通过变参模板打印一组数量和类型都不确定的参数。 #include <iostream> #include <string>void print(void) {std::cout<<&quo…

计算机网络 —— 运输层

运输层 5.1 运输层概述 运输层的主要任务是&#xff0c;如何为运行在不同主机上的应用进程提供直接的通信服务。运输层协议又称为端到端协议。 根据应用需求的不同&#xff0c;因特网的运输层为应用层提供了两种不同的运输协议&#xff0c;即面向连接的TCP和无连接的UDP 5.2…

Chrome中如何导出和导入书签

导出书签 如下图所示&#xff1a; 右上角三点->书签和清单->书签管理器->右上角三点->导出书签 然后你选择保存地址即可。打开后如下&#xff1a; 导入书签 如下图所示&#xff1a; 右上角三点->书签和清单->导入书签和设置->选择以前导出的书签&…

0103n阶行列式-行列式-线性代数

文章目录 一 n阶行列式二 三阶行列式三 特殊行列式结语 一 n阶行列式 ∣ a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋯ ⋯ ⋯ ⋯ a n 1 a n 2 ⋯ a n n ∣ \begin{vmatrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&a_{22}&\cdots&a_{2n}\\\cdots&\cdots…

【大厂AI课学习笔记NO.68】开源和开源发展情况

开源即源代码公开&#xff0c;任何人能获取源代码&#xff0c;查看、修改、分发他们认为合适的代码。 依托同行评审和社区生成&#xff0c;旨在以分散、协作的方式开发。 我们曾经很详细的讨论过开源协议的问题&#xff0c;详细可以参考我的文章&#xff1a; https://giszz.…

政安晨:【深度学习处理实践】(五)—— 初识RNN-循环神经网络

RNN&#xff08;循环神经网络&#xff09;是一种在深度学习中常用的神经网络结构&#xff0c;用于处理序列数据。与传统的前馈神经网络不同&#xff0c;RNN通过引入循环连接在网络中保留了历史信息。 RNN中的每个神经元都有一个隐藏状态&#xff0c;它会根据当前输入和前一个时…