Implementation:Tencent Ncnn PPOCRv5 Example
| Knowledge Sources | |
|---|---|
| Domains | Vision, OCR |
| Last Updated | 2026-02-09 19:00 GMT |
Overview
Concrete tool for optical character recognition inference using PaddleOCR v5 (text detection + recognition) with ncnn.
Description
This example implements a complete two-stage OCR pipeline using PaddleOCR v5 models converted from PaddlePaddle via PNNX. The PPOCRv5 class encapsulates two ncnn networks: a detection model and a recognition model.
Detection stage: The detection model (PP_OCRv5_mobile_det) takes an image resized to a maximum of 960 pixels on the longer side with padding to a multiple of 32 pixels. It applies ImageNet-style mean/std normalization and produces a probability map. The output is binarized with a threshold of 0.3, then contours are extracted using OpenCV. Each contour is scored against the probability map and filtered by a box threshold of 0.6. Minimum rotated rectangles are computed, with text orientation classification (horizontal vs. vertical) based on aspect ratio and angle heuristics. Boxes are enlarged by a factor of 1.95 to capture full text regions.
Recognition stage: For each detected text region, get_rotate_crop_image applies an affine warp to produce a normalized 48-pixel-height text strip (handling both horizontal and vertical text orientations). The recognition model (PP_OCRv5_mobile_rec) processes each strip with mean=127.5, scale=1/127.5 normalization and produces a sequence of character probabilities. CTC decoding (greedy, with duplicate merging) maps output indices to characters via a dictionary of 18,385 entries defined in ppocrv5_dict.h.
Both mobile and server model variants are supported (server variants require fp16 to be disabled).
Usage
Use this example to detect and recognize text in images containing printed or handwritten text in multiple languages. Supports both horizontal and vertical text orientations.
Code Reference
Source Location
- Repository: Tencent_Ncnn
- File: examples/ppocrv5.cpp
- Lines: 1-540
- Auxiliary file: examples/ppocrv5_dict.h (18,385-entry character dictionary)
Signature
class PPOCRv5 {
public:
void init();
void detect(const cv::Mat& bgr, std::vector<Object>& objects);
void recognize(const cv::Mat& bgr, Object& object);
};
static int detect_ppocrv5(const cv::Mat& bgr, std::vector<Object>& objects);
static int draw_objects(const cv::Mat& bgr, const std::vector<Object>& objects);
int main(int argc, char** argv);
Import
#include "layer.h"
#include "net.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "ppocrv5_dict.h"
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| imagepath | const char* (argv[1]) | Yes | Path to input image containing text |
Outputs
| Name | Type | Description |
|---|---|---|
| objects | std::vector<Object> | Detected text regions with rotated bounding box (RotatedRect), orientation (0=horizontal, 1=vertical), confidence score, and recognized character sequence |
| visualization | cv::Mat | Image with drawn rotated bounding boxes and recognized text displayed via cv::imshow |
Usage Examples
Running the Example
./ppocrv5 document.jpg
Key Code Pattern
PPOCRv5 ppocrv5;
ppocrv5.init(); // loads det + rec models
// Stage 1: Text detection
ppocrv5.detect(bgr, objects);
// Stage 2: Text recognition per region
for (size_t i = 0; i < objects.size(); i++)
{
ppocrv5.recognize(bgr, objects[i]);
}
// Inside detect():
ncnn::Extractor ex = ppocrv5_det.create_extractor();
ex.input("in0", in_pad);
ncnn::Mat out;
ex.extract("out0", out);
// ... binarize, find contours, extract rotated rects
// Inside recognize():
cv::Mat roi = get_rotate_crop_image(bgr, object);
ncnn::Mat in = ncnn::Mat::from_pixels(roi.data, ncnn::Mat::PIXEL_BGR, roi.cols, roi.rows);
const float mean_vals[3] = {127.5, 127.5, 127.5};
const float norm_vals[3] = {1.0 / 127.5, 1.0 / 127.5, 1.0 / 127.5};
in.substract_mean_normalize(mean_vals, norm_vals);
ncnn::Extractor ex = ppocrv5_rec.create_extractor();
ex.input("in0", in);
ncnn::Mat out;
ex.extract("out0", out);
// ... CTC decode with character_dict lookup