Implementation:Tencent Ncnn SCRFD Example
| Knowledge Sources | |
|---|---|
| Domains | Vision, Face_Detection |
| Last Updated | 2026-02-09 19:00 GMT |
Overview
Concrete tool for face detection inference using SCRFD (Sample and Computation Redistribution for Face Detection) with ncnn.
Description
This example implements the SCRFD face detector, an efficient anchor-based face detection architecture from InsightFace. It loads the scrfd_500m-opt2 model (a lightweight 500K-parameter variant). The input image is resized so the longer side fits within 640 pixels while preserving aspect ratio, converted from BGR to RGB, and padded to a multiple of 32 pixels with zero-padding. Normalization uses mean=127.5 and scale=1/128 per channel.
The model produces predictions at three feature pyramid strides (8, 16, 32). For each stride, anchors are generated with a single ratio (1.0) and two scales (1.0, 2.0), with base sizes of 16, 64, and 256 respectively. Unlike RetinaFace which uses exponential delta decoding, SCRFD uses a distance-to-bbox approach: raw outputs are multiplied by the feat_stride to get pixel distances, then converted to absolute coordinates via distance2bbox() (cx-dx, cy-dy, cx+dw, cy+dh). Proposals from all three strides are merged, sorted by confidence using quicksort with OMP parallel sections, and filtered via NMS with a threshold of 0.45. The probability threshold is 0.3. Detection coordinates are adjusted for letterbox padding and rescaled to the original image dimensions. Results are drawn as green bounding boxes with confidence percentages.
Usage
Use this example for efficient face detection. SCRFD provides a good balance between speed and accuracy, particularly suitable for mobile and edge deployment.
Code Reference
Source Location
- Repository: Tencent_Ncnn
- File: examples/scrfd.cpp
- Lines: 1-425
Signature
static ncnn::Mat generate_anchors(int base_size, const ncnn::Mat& ratios, const ncnn::Mat& scales);
static void generate_proposals(const ncnn::Mat& anchors, int feat_stride, const ncnn::Mat& score_blob, const ncnn::Mat& bbox_blob, float prob_threshold, std::vector<FaceObject>& faceobjects);
static int detect_scrfd(const cv::Mat& bgr, std::vector<FaceObject>& faceobjects);
static void draw_faceobjects(const cv::Mat& bgr, const std::vector<FaceObject>& faceobjects);
int main(int argc, char** argv);
Import
#include "net.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| imagepath | const char* (argv[1]) | Yes | Path to input image |
Outputs
| Name | Type | Description |
|---|---|---|
| faceobjects | std::vector<FaceObject> | Detected faces with bounding box (Rect_<float>) and confidence (float) |
| visualization | cv::Mat | Image with green bounding boxes and confidence labels displayed via cv::imshow |
Usage Examples
Running the Example
./scrfd image.jpg
Key Code Pattern
ncnn::Net scrfd;
scrfd.opt.use_vulkan_compute = true;
scrfd.load_param("scrfd_500m-opt2.param");
scrfd.load_model("scrfd_500m-opt2.bin");
ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR2RGB, width, height, w, h);
// Pad to multiple of 32
int wpad = (w + 31) / 32 * 32 - w;
int hpad = (h + 31) / 32 * 32 - h;
ncnn::Mat in_pad;
ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 0.f);
const float mean_vals[3] = {127.5f, 127.5f, 127.5f};
const float norm_vals[3] = {1 / 128.f, 1 / 128.f, 1 / 128.f};
in_pad.substract_mean_normalize(mean_vals, norm_vals);
ncnn::Extractor ex = scrfd.create_extractor();
ex.input("input.1", in_pad);
// Decode from 3 stride levels
std::vector<FaceObject> faceproposals;
// stride 8: extract("412", score), extract("415", bbox)
// stride 16: extract("474", score), extract("477", bbox)
// stride 32: extract("536", score), extract("539", bbox)
qsort_descent_inplace(faceproposals);
nms_sorted_bboxes(faceproposals, picked, 0.45f);
// Adjust for padding offset and scale