Implementation:Tencent Ncnn P2PNet Example
| Knowledge Sources | |
|---|---|
| Domains | Vision, Crowd Counting |
| Last Updated | 2026-02-09 19:00 GMT |
Overview
Concrete tool for point-based crowd counting using P2PNet with ncnn, detecting individual person positions in dense crowd scenes.
Description
This example demonstrates P2PNet crowd counting, a point-based detection approach that locates individual people as points rather than bounding boxes. The implementation generates shifted anchor points across a feature pyramid (level 3 with 2x2 anchor grid), then runs the P2PNet model which outputs per-point confidence scores and regression offsets. The input image is resized to dimensions divisible by 128 and preprocessed with ImageNet-style normalization. Anchor points are passed as a separate input tensor alongside the image. Detected points with confidence above 0.5 are drawn as red circles on the output image.
Usage
Use this example for crowd density estimation in scenes with many people where individual bounding boxes would be impractical. It is particularly suitable for surveillance, event management, and urban planning applications where a count or spatial distribution of people is needed.
Code Reference
Source Location
- Repository: Tencent_Ncnn
- File: examples/p2pnet.cpp
- Lines: 1-231
Signature
static void shift(int w, int h, int stride, std::vector<float> anchor_points,
std::vector<float>& shifted_anchor_points);
static void generate_anchor_points(int stride, int row, int line,
std::vector<float>& anchor_points);
static void generate_anchor_points(int img_w, int img_h, std::vector<int> pyramid_levels,
int row, int line, std::vector<float>& all_anchor_points);
static int detect_crowd(const cv::Mat& bgr, std::vector<CrowdPoint>& crowd_points);
static void draw_result(const cv::Mat& bgr, const std::vector<CrowdPoint>& crowd_points);
int main(int argc, char** argv);
Import
#include "net.h"
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| image_path | const char* | Yes | Path to input image (passed as command-line argument) |
| bgr | const cv::Mat& | Yes | BGR image, resized to dimensions divisible by 128 |
| anchor | ncnn::Mat | Yes (generated) | Anchor points tensor generated from pyramid level 3 with 2x2 grid |
Outputs
| Name | Type | Description |
|---|---|---|
| crowd_points | std::vector<CrowdPoint> | Detected person locations, each with a cv::Point and confidence prob |
| pred_scores | ncnn::Mat | Per-point confidence scores (2 columns: background and foreground) |
| pred_points | ncnn::Mat | Per-point (x, y) coordinate predictions |
| Visual output | cv::imshow window | Image with red circles drawn at detected person locations (threshold > 0.5) |
Model Files
| File | Description |
|---|---|
| p2pnet.param | P2PNet network parameter file |
| p2pnet.bin | P2PNet network weight file |
Preprocessing
- Resize: Dimensions rounded down to nearest multiple of 128 (
width / 128 * 128) - Color conversion: BGR to RGB via
ncnn::Mat::PIXEL_BGR2RGB - Mean values: [123.675, 116.28, 103.53]
- Norm values: [0.01712475, 0.0175, 0.01742919]
- Anchor generation: Pyramid level 3 with 2x2 anchor grid, shifted across the feature map
Usage Examples
Running the Example
./p2pnet crowd_image.jpg
Key Code Pattern
int new_width = width / 128 * 128;
int new_height = height / 128 * 128;
ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR2RGB,
width, height, new_width, new_height);
std::vector<int> pyramid_levels(1, 3);
std::vector<float> all_anchor_points;
generate_anchor_points(in.w, in.h, pyramid_levels, 2, 2, all_anchor_points);
ncnn::Mat anchor_points = ncnn::Mat(2, all_anchor_points.size() / 2, all_anchor_points.data());
ncnn::Extractor ex = net.create_extractor();
ex.input("input", in);
ex.input("anchor", anchor_points);
ncnn::Mat score, points;
ex.extract("pred_scores", score);
ex.extract("pred_points", points);