PaddleOCR-PP-OCRv4推理详解及部署实现(下)

目录

    • 前言
    • 1. 检测模型
      • 1.1 预处理
      • 1.2 后处理
      • 1.3 推理
    • 2. 方向分类器模型
      • 2.1 预处理
      • 2.2 后处理
      • 2.3 推理
    • 3. 识别模型
      • 3.1 预处理
      • 3.2 后处理
      • 3.3 推理
    • 4. PP-OCRv4部署
      • 4.1 源码下载
      • 4.2 环境配置
        • 4.2.1 配置CMakeLists.txt
        • 4.2.2 配置Makefile
      • 4.3 ONNX导出
      • 4.4 engine生成
        • 4.4.1 检测模型
        • 4.4.2 方向分类器模型
        • 4.4.3 识别模型
      • 4.5 源码修改
      • 4.6 运行
    • 5. 补充说明
    • 结语
    • 下载链接
    • 参考

前言

接着上篇文章 PaddleOCR-PP-OCRv4推理详解及部署实现(中) 来讲,在上篇文章中我们已经梳理完 PP-OCRv4 模型中三个模块的前后处理,这篇文章我们将在 C++ 上实现并利用 tensorRT 推理得到结果。若有问题欢迎各位看官批评指正😄

repo:https://github.com/Melody-Zhou/tensorRT_Pro-YOLOv8

1. 检测模型

1.1 预处理

上篇文章我们提到过检测模型的预处理主要做了如下操作:

  • 1. resize
  • 2. /255.0 减均值除标准差
  • 3. c,h,w->h,w,c
  • 4. h,w,c->b,c,h,w

那其实它就是做一个 resize 操作,再加上对每个像素值除以 255.0,减去均值除以标准差,这一系列的操作我们可以用一个 CUDA 核函数来完成,由于核函数中是对每个像素进行操作,因此非常容易实现除 255,减均值除标准差等操作,具体代码如下:

__global__ void resize_bilinear_and_normalize_kernel(uint8_t* src, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height, float sx, float sy, Norm norm, int edge
){int position = blockDim.x * blockIdx.x + threadIdx.x;if (position >= edge) return;int dx      = position % dst_width;int dy      = position / dst_width;float src_x = (dx + 0.5f) * sx - 0.5f;float src_y = (dy + 0.5f) * sy - 0.5f;float c0, c1, c2;int y_low = floorf(src_y);int x_low = floorf(src_x);int y_high = limit(y_low + 1, 0, src_height - 1);int x_high = limit(x_low + 1, 0, src_width - 1);y_low = limit(y_low, 0, src_height - 1);x_low = limit(x_low, 0, src_width - 1);int ly    = rint((src_y - y_low) * INTER_RESIZE_COEF_SCALE);int lx    = rint((src_x - x_low) * INTER_RESIZE_COEF_SCALE);int hy    = INTER_RESIZE_COEF_SCALE - ly;int hx    = INTER_RESIZE_COEF_SCALE - lx;int w1    = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx;float* pdst = dst + dy * dst_width + dx * 3;uint8_t* v1 = src + y_low * src_line_size + x_low * 3;uint8_t* v2 = src + y_low * src_line_size + x_high * 3;uint8_t* v3 = src + y_high * src_line_size + x_low * 3;uint8_t* v4 = src + y_high * src_line_size + x_high * 3;c0 = resize_cast(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0]);c1 = resize_cast(w1 * v1[1] + w2 * v2[1] + w3 * v3[1] + w4 * v4[1]);c2 = resize_cast(w1 * v1[2] + w2 * v2[2] + w3 * v3[2] + w4 * v4[2]);if(norm.channel_type == ChannelType::Invert){float t = c2;c2 = c0;  c0 = t;}if(norm.type == NormType::MeanStd){c0 = (c0 * norm.alpha - norm.mean[0]) / norm.std[0];c1 = (c1 * norm.alpha - norm.mean[1]) / norm.std[1];c2 = (c2 * norm.alpha - norm.mean[2]) / norm.std[2];}else if(norm.type == NormType::AlphaBeta){c0 = c0 * norm.alpha + norm.beta;c1 = c1 * norm.alpha + norm.beta;c2 = c2 * norm.alpha + norm.beta;}int area = dst_width * dst_height;float* pdst_c0 = dst + dy * dst_width + dx;float* pdst_c1 = pdst_c0 + area;float* pdst_c2 = pdst_c1 + area;*pdst_c0 = c0;*pdst_c1 = c1;*pdst_c2 = c2;
}

Note:代码 Copy 自 preprocess_kernel.cu#L49

预处理部分比较简单直接拿杜老师现成的代码就行,下面我们看后处理部分

1.2 后处理

上篇文章我们分析过检测模型的后处理主要做了如下操作:

  • boxes_from_bitmap:从给定的二值化图 _bitmap 中提取检测到的文本框并缩放
  • filter_tag_det_res:对检测到的文本框进行过滤
  • sorted_boxes:对过滤的文本框排序

我们先看如何从预测结果中提取检测到的文本框,部分代码如下所示:

static void boxes_from_bitmap(const Mat& pred, const Mat& bitmap, vector<vector<vector<int>>>& box_array, float box_thresh, float unclip_ratio, int min_size, int max_candidates
){int width  = bitmap.cols;int height = bitmap.rows;vector<vector<cv::Point>> contours;vector<cv::Vec4i> hierarchy;cv::findContours(bitmap, contours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);int num_contours = contours.size() >= max_candidates ? max_candidates : contours.size();Mat contour_image;cv::cvtColor(bitmap, contour_image, cv::COLOR_GRAY2BGR);// for(auto& contour : contours){//     vector<vector<cv::Point>> single_contour = {contour};//     cv::drawContours(contour_image, single_contour, -1, cv::Scalar(0, 0, 255), 2);// }// cv::imwrite("contour_image.jpg", contour_image);vector<vector<vector<int>>> boxes;for(auto& contour : contours){if(contour.size() <= min_size)continue;vector<vector<float>> array;float sside;auto box = cv::minAreaRect(contour);tie(array, sside) = get_mini_boxes(box);if(sside < min_size)continue;float score = box_score(pred, array);if(score < box_thresh)continue;auto points = box_unclip(array, unclip_ratio);// differenceif(points.size.height < 1.001 & points.size.width < 1.001)continue;vector<vector<float>> cliparray;tie(cliparray, sside) = get_mini_boxes(points);if(sside < min_size + 2)continue;int dest_width  = pred.cols;int dest_height = pred.rows;vector<vector<int>> intcliparray;intcliparray.reserve(4);float x_scale = float(dest_width)  / float(width);float y_scale = float(dest_height) / float(height);for(int i = 0; i < 4; ++i){int x = int(clamp(std::roundf(cliparray[i][0] * x_scale), 0.0f, float(dest_width)));int y = int(clamp(std::roundf(cliparray[i][1] * y_scale), 0.0f, float(dest_height)));intcliparray.push_back({x, y});}box_array.emplace_back(intcliparray);}
}

和我们上篇文章分析的一样,先通过 opencv 的 findContours 函数提取轮廓,接着从每个轮廓生成最小包围框,检查并过滤掉一些不符合条件的框,此外 score 也是通过计算框区域内的平均分数来评估,最后将符合条件的文本框的坐标映射回原图上即可,更多细节大家可以查看 app_ppocr/postprocess_det.cpp

Note:代码 Copy 自 postprocess_op.cpp#L246

检测模型的后处理代码比较繁琐,从二值化图中提取到文本框时使用的辅助函数比较多,其中对给定文本框做扩展处理的函数 unclip 需要使用到 PaddleOCR 官方提供的 clipper 库,大家感兴趣的可以看看:deploy/cpp_infer/src/clipper.cpp

1.3 推理

推理部分我们交给 tensorRT 就行,值得注意的是我们将检测模型的输入的宽高固定在 960x960,主要是因为 tensorRT 处理动态宽高比较麻烦,因此博主干脆将宽高固定

2. 方向分类器模型

2.1 预处理

上篇文章我们提到过方向分类器模型的预处理主要做了如下操作:

  • 1. resize
  • 2. /255.0,将像素值归一化到 [0,1]
  • 3. 减均值(0.5)除标准差(0.5),将像素值转换到 [-1,1]
  • 4. 填充

那它和检测模型的预处理差不多,区别在于 resize 尺寸并不固定,它是首先将高度 resize 到 48,再根据 ratio 缩放宽度,如果缩放后宽度大于 192 则只缩放到 192,这种情况就和检测模型的预处理一模一样,如果小于则正常缩放,此时剩余部分填充 0

因此我们可以将前面检测模型的核函数略微修改下即可,具体代码如下:

__global__ void resize_normalize_image_kernel(uint8_t* src, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height, float sx, float sy, int resized_w, Norm norm, int edge
){int position = blockDim.x * blockIdx.x + threadIdx.x;if (position >= edge) return;int dx = position % dst_width;int dy = position / dst_width;if(dx >= resized_w){int area = dst_width * dst_height;float* pdst_c0 = dst + dy * dst_width + dx;float* pdst_c1 = pdst_c0 + area;float* pdst_c2 = pdst_c1 + area;*pdst_c0 = 0.0f;*pdst_c1 = 0.0f;*pdst_c2 = 0.0f;return;}float src_x = (dx + 0.5f) * sx - 0.5f;float src_y = (dy + 0.5f) * sy - 0.5f;float c0, c1, c2;int y_low = floorf(src_y);int x_low = floorf(src_x);int y_high = limit(y_low + 1, 0, src_height - 1);int x_high = limit(x_low + 1, 0, src_width - 1);y_low = limit(y_low, 0, src_height - 1);x_low = limit(x_low, 0, src_width - 1);int ly = rint((src_y - y_low) * INTER_RESIZE_COEF_SCALE);int lx = rint((src_x - x_low) * INTER_RESIZE_COEF_SCALE);int hy = INTER_RESIZE_COEF_SCALE - ly;int hx = INTER_RESIZE_COEF_SCALE - lx;int w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx;float* pdst = dst + dy * dst_width + dx * 3;uint8_t* v1 = src + y_low * src_line_size + x_low * 3;uint8_t* v2 = src + y_low * src_line_size + x_high * 3;uint8_t* v3 = src + y_high * src_line_size + x_low * 3;uint8_t* v4 = src + y_high * src_line_size + x_high * 3;c0 = resize_cast(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0]);c1 = resize_cast(w1 * v1[1] + w2 * v2[1] + w3 * v3[1] + w4 * v4[1]);c2 = resize_cast(w1 * v1[2] + w2 * v2[2] + w3 * v3[2] + w4 * v4[2]);		if(norm.channel_type == ChannelType::Invert){float t = c2;c2 = c0;  c0 = t;}if(norm.type == NormType::MeanStd){c0 = (c0 * norm.alpha - norm.mean[0]) / norm.std[0];c1 = (c1 * norm.alpha - norm.mean[1]) / norm.std[1];c2 = (c2 * norm.alpha - norm.mean[2]) / norm.std[2];}else if(norm.type == NormType::AlphaBeta){c0 = c0 * norm.alpha + norm.beta;c1 = c1 * norm.alpha + norm.beta;c2 = c2 * norm.alpha + norm.beta;}int area = dst_width * dst_height;float* pdst_c0 = dst + dy * dst_width + dx;float* pdst_c1 = pdst_c0 + area;float* pdst_c2 = pdst_c1 + area;*pdst_c0 = c0;*pdst_c1 = c1;*pdst_c2 = c2;
}

其中我们新增 resized_w 参数传入,根据与 resized_w 比较将剩余部分的像素填充 0 即可

2.2 后处理

上篇文章我们分析过方向分类器模型的后处理主要是根据分类标签和得分判断是否需要旋转图像,处理比较简单

具体代码如下:

float* parray = output->cpu<float>(ibatch);
auto& job     = fetch_jobs[ibatch];
auto& image_based_cls = job.output;// get idx
int argmax_idx = parray[0] > parray[1] ? 0 : 1;
// get score
float max_value = std::max(parray[0], parray[1]);
if(argmax_idx == 1 && max_value > cls_thresh_){image_based_cls = 1;
}else{image_based_cls = 0;
}

这里的 parray 为模型预测结果,维度为 bx2,首先获取到预测概率最大的 index 和 score,根据 index 的类型和 score 大小判断是否需要旋转,对于需要旋转的图像返回 1,不需要的返回 0

2.3 推理

同样推理部分我们交给 tensorRT 就行,方向分类器的输入宽高是固定的(48x192),因此不需要做额外的处理

3. 识别模型

3.1 预处理

上篇文章我们提到过识别模型的预处理和方向分类器一模一样,只是 resize 的目标尺寸不同而已,因此代码可以直接沿用方向分类器的预处理代码,这边博主不再赘述

3.2 后处理

识别模型的后处理也比较简单,主要是将模型输出转换为可读的文本标签,具体代码如下:

float* parray = output->cpu<float>(ibatch);
auto& job     = fetch_jobs[ibatch];
auto& image_based_text = job.output;// batch, max_chars, vocab_size
// parry->1x80x6625
int argmax_idx;
int count       = 0;
int last_index  = 0;
float score     = 0.0f;
float max_value = 0.0f;for(int i = 0; i < max_chars; ++i){// get idxargmax_idx = int(argmax(&parray[i * vocab_size], &parray[(i + 1) * vocab_size]));// get scoremax_value  = float(*std::max_element(&parray[i * vocab_size], &parray[(i + 1) * vocab_size]));if(argmax_idx > 0 && (!(i > 0 && argmax_idx == last_index))){score   += max_value;count   += 1;image_based_text.text += label_list_[argmax_idx];}last_index = argmax_idx;
}
if(count != 0){score /= count;
}
image_based_text.score = score;

这里的 parray 为模型预测结果,维度为 bx80x6625,首先获取到预测概率最大的 index 和 score,根据 index 去字符集中取对应的字符,score 则是所有字符预测概率值的平均,最后将每个文本框的字符拼接成文本返回即可

3.3 推理

同样推理部分我们交给 tensorRT 就行,识别模型的高度是固定的为 48,宽度并不固定,需要根据输入文本框的长度进行 resize,不过博主这里为了方便处理统一固定到 640。注意对于不同的输入宽度,模型的预测结果大小也不尽相同,对于输入宽度为 640 的文本,模型预测的最大字符数为 80,而对于输入宽度为 320 的文本,模型预测的最大字符数则只有 40

至此,我们简单分析了 C++ 上 PP-OCRv4 各个模块的前后处理过程,下面我们将完整的走一遍流程

4. PP-OCRv4部署

博主新建了一个仓库 tensorRT_Pro-YOLOv8,该仓库基于 shouxieai/tensorRT_Pro,并进行了调整以支持 YOLOv8 的各项任务,目前已支持分类、检测、分割、姿态点估计任务。

下面我们就来具体看看如何利用 tensorRT_Pro-YOLOv8 这个 repo 完成 PP-OCRv4 的推理。

4.1 源码下载

tensorRT_Pro-YOLOv8 的代码可以直接从 GitHub 官网上下载,源码下载地址是 https://github.com/Melody-Zhou/tensorRT_Pro-YOLOv8,Linux 下代码克隆指令如下:

git clone https://github.com/Melody-Zhou/tensorRT_Pro-YOLOv8.git

也可手动点击下载,点击右上角的 Code 按键,将代码下载下来。至此整个项目就已经准备好了。也可以点击 here 下载博主准备好的源代码(注意代码下载于 2024/7/24 日,若有改动请参考最新

4.2 环境配置

需要使用的软件环境有 TensorRT、CUDA、cuDNN、OpenCV、Protobuf,所有软件环境的安装可以参考 Ubuntu20.04软件安装大全,这里不再赘述,需要各位看官自行配置好相关环境😄,外网访问较慢,这里提供下博主安装过程中的软件安装包下载链接 Baidu Drive【pwd:yolo】🚀🚀🚀

tensorRT_Pro-YOLOv8 提供 CMakeLists.txt 和 Makefile 两种方式编译,二者选一即可

4.2.1 配置CMakeLists.txt

主要修改五处

1. 修改第 13 行,修改 OpenCV 路径

set(OpenCV_DIR   "/usr/local/include/opencv4/")

2. 修改第 15 行,修改 CUDA 路径

set(CUDA_TOOLKIT_ROOT_DIR     "/usr/local/cuda-11.6")

3. 修改第 16 行,修改 cuDNN 路径

set(CUDNN_DIR    "/usr/local/cudnn8.4.0.27-cuda11.6")

4. 修改第 17 行,修改 tensorRT 路径

set(TENSORRT_DIR "/home/jarvis/lean/TensorRT-8.6.1.6")

5. 修改第 20 行,修改 protobuf 路径

set(PROTOBUF_DIR "/home/jarvis/protobuf")
4.2.2 配置Makefile

主要修改五处

1. 修改第 4 行,修改 protobuf 路径

lean_protobuf  := /home/jarvis/protobuf

2. 修改第 5 行,修改 tensorRT 路径

lean_tensor_rt := /home/jarvis/lean/TensorRT-8.6.1.6

3. 修改第 6 行,修改 cuDNN 路径

lean_cudnn     := /usr/local/cudnn8.4.0.27-cuda11.6

4. 修改第 7 行,修改 OpenCV 路径

lean_opencv    := /usr/local

5. 修改第 8 行,修改 CUDA 路径

lean_cuda      := /usr/local/cuda-11.6

4.3 ONNX导出

导出细节可以查看 PaddleOCR-PP-OCRv4推理详解及部署实现(上),这边不再赘述。记得将导出的 ONNX 模型放在 tensorRT_Pro-YOLOv8/workspace 文件夹下,大家也可以点击 here 下载博主导出好的 ONNX

4.4 engine生成

4.4.1 检测模型

在 workspace 下新建 ppocr_det_build.sh,其内容如下:

#! /usr/bin/bashTRTEXEC=/home/jarvis/lean/TensorRT-8.6.1.6/bin/trtexecexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/jarvis/lean/TensorRT-8.6.1.6/lib${TRTEXEC} \--onnx=ppocr_det.sim.onnx \--minShapes=images:1x3x960x960 \--optShapes=images:1x3x960x960 \--maxShapes=images:8x3x960x960 \--memPoolSize=workspace:2048 \--saveEngine=ppocr_det.sim.FP16.trtmodel \--fp16 \> ppocr_det.log 2>&1

其中需要修改 TRTEXEC 的路径为你自己的路径,终端执行如下指令:

cd tensorRT_Pro-YOLOv8/workspace
bash ppocr_det_build.sh

执行后等待一段时间会在当前文件夹生成 ppocr_det.sim.FP16.trtmodel 即检测模型引擎文件,注意终端看不到任何日志打印输出,这是因为博主将 tensorRT 输出的日志信息保存到了 ppocr_det.log 文件中,大家也可以删除保存直接在终端显示相关日志信息

4.4.2 方向分类器模型

在 workspace 下新建 ppocr_cls_build.sh,其内容如下:

#! /usr/bin/bashTRTEXEC=/home/jarvis/lean/TensorRT-8.6.1.6/bin/trtexecexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/jarvis/lean/TensorRT-8.6.1.6/lib${TRTEXEC} \--onnx=ppocr_cls.sim.onnx \--minShapes=images:1x3x48x192 \--optShapes=images:1x3x48x192 \--maxShapes=images:8x3x48x192 \--memPoolSize=workspace:2048 \--saveEngine=ppocr_cls.sim.FP16.trtmodel \--fp16 \> ppocr_cls.log 2>&1

其中需要修改 TRTEXEC 的路径为你自己的路径,终端执行如下指令:

cd tensorRT_Pro-YOLOv8/workspace
bash ppocr_cls_build.sh

执行后等待一段时间会在当前文件夹生成 ppocr_cls.sim.FP16.trtmodel 即方向分类器模型引擎文件

4.4.3 识别模型

在 workspace 下新建 ppocr_rec_build.sh,其内容如下:

#! /usr/bin/bashTRTEXEC=/home/jarvis/lean/TensorRT-8.6.1.6/bin/trtexecexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/jarvis/lean/TensorRT-8.6.1.6/lib${TRTEXEC} \--onnx=ppocr_rec.sim.onnx \--minShapes=images:1x3x48x640 \--optShapes=images:1x3x48x640 \--maxShapes=images:8x3x48x640 \--memPoolSize=workspace:2048 \--saveEngine=ppocr_rec.sim.FP16.trtmodel \--fp16 \> ppocr_rec.log 2>&1

其中需要修改 TRTEXEC 的路径为你自己的路径,终端执行如下指令:

cd tensorRT_Pro-YOLOv8/workspace
bash ppocr_rec_build.sh

执行后等待一段时间会在当前文件夹生成 ppocr_rec.sim.FP16.trtmodel 即识别模型引擎文件

4.5 源码修改

Note:如果想推理自己训练的模型还需要修改下源码,PP-OCRv4 模型的推理代码主要在 app_ppocr.cpp 文件中,源码修改较简单,主要有以下几点:

  • app_ppocr.cpp 118 行,修改自己想识别的图片路径
  • app_ppocr.cpp 120-122 行,修改自己生成的 engine 引擎文件名

4.6 运行

OK!现在源码修改好了,Makefile 编译文件也搞定了,engine 模型也准备好了,可以编译运行了,直接在终端执行如下指令即可:

make ppocr -j64

推理过程如下图所示:

在这里插入图片描述

推理成功后会在 workspace 文件夹下保存 result_ocr.jpg 即推理好的图片

模型推理效果如下图所示:

在这里插入图片描述

可以看到与 Paddle 和 ONNX 的推理还是有些差别的,部分文本未识别出来,例如左上角的 0、1 未识别出来,还有右下角的字符 被识别成了 7,那这可能是博主梳理的预处理或者后处理未与 python 版本完全对齐导致的

我们再多看看几张图的推理效果:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

可以看到这几张图的效果还行

OK,以上就是使用 tensorRT_Pro-YOLOv8 推理 PP-OCRv4 的大致流程,若有问题,欢迎各位看官批评指正。

5. 补充说明

  • 1. 前后处理代码主要参考自官方实现:https://github.com/PaddlePaddle/PaddleOCR/tree/main/deploy/cpp_infer

  • 2. 由于博主能力有限,在梳理模型前后处理过程中难免有所遗漏,特别是检测模型的后处理,某些细节可能并未完全实现,这可能是影响精度对齐的原因

  • 3. PP-OCRv4 整个推理框架的实现有借鉴 CUDA-BEVFusion,但并不多,因为代码还没有仔细调试看过,韩君老师之前有简单讲过 CUDA-BEVFusion 推理框架设计模式,大家感兴趣的可以看看:八. 实战:CUDA-BEVFusion部署分析-学习CUDA-BEVFusion推理框架设计模式

  • 4. 某些后处理是否可以考虑核函数来实现呢,特别是文本检测部分,如果不能将全部的后处理放在 GPU 上完成,那是不是需要考虑 host 和 device 之间 memcpy 的 overhead

  • 5. 高版本 TensorRT 对于 getMaxBatchSize 方法弃用了,因此它始终返回的是 1,我们也可以从日志中观察到这个警告,此时我们可以通过查询 profile 的维度来获取 maxBatchSize

    // ====== src/tensorRT/infer/trt_infer.cpp =====// ====== trt_infer.cpp  261行 =====
    // int max_batchsize = context->engine_->getMaxBatchSize();
    nvinfer1::Dims maxDims = context->engine_->getProfileDimensions(0, 0, nvinfer1::OptProfileSelector::kMAX);
    int max_batchsize = maxDims.d[0];// ====== trt_infer.cpp  422行 =====
    // return this->context_->engine_->getMaxBatchSize();
    nvinfer1::Dims maxDims = this->context_->engine_->getProfileDimensions(0, 0, nvinfer1::OptProfileSelector::kMAX);
    int max_batchsize = maxDims.d[0];
    return max_batchsize;
    
  • 6. 在可视化中博主遇到了 OpenCV 不支持中文绘制的问题,因此这边直接把 cuOSD 库拿过来用了,它支持 stb_truetype 和 pango-cairo 后端,允许通过 TFF 或者使用 font-family 读取字体,大家对 cuOSD 感兴趣的话可以看看:cuOSD(CUDA On-Screen Display Library)库的学习

  • 7. cuOSD 库 pango-cairo 后端需要包含 pango、glib、cairo 头文件,可以通过 dkpg -L 指令查找相关头文件,若没有找到需要自行安装

    # 1. 查找相关库
    dpkg -L libpango1.0-dev libglib2.0-dev libcairo2-dev# 2. 安装
    sudo apt-get update
    sudo apt-get install libpango1.0-dev libglib2.0-dev libcairo2-dev# 3. 头文件路径
    /usr/include/pango-1.0
    /usr/include/glib-2.0
    /usr/lib/x86_64-linux-gnu/glib-2.0/include
    /usr/include/cairo
    
  • 8. 方向分类器模块默认不开启,如果需要使用将 OcrParameter 中的 use_angle_cls 参数设置为 true 即可

  • 9. 如果使用 TRT::compile 接口生成 engine 模型会出现 HardSwish 算子解析问题,这是因为默认的 onnxparser 版本比较老不支持 HardSwish 算子,可以自己手动替换,具体可以参考:RT-DETR推理详解及部署实现。另外低版本也可以通过写插件来支持,刚好杜老师有写 HSwish 的插件,我们直接用就行,在使用之前需要将所有的 HardSwish 的 op_type 修改为 Plugin,并且需要新增 name 属性名字为 HSwish,这个我们之前讲过,大家感兴趣的可以看下:LayerNorm Plugin的使用与说明

结语

博主在这里通过分析 PP-OCRv4 模型各个模块的导出,前后处理分析以及部署对 OCR 相关的任务有进一步的了解,同时把一些学过的知识内容又回顾了一遍,总归还是有所收获的🤗。感谢各位看到最后,创作不易,读后有收获的看官请帮忙点个👍⭐️

最后大家如果觉得 tensorRT_Pro-YOLOv8 这个 repo 对你有帮助的话,不妨点个 ⭐️ 支持一波,这对博主来说非常重要,感谢各位🙏。

下载链接

  • 软件安装包下载链接【提取码:yolo】🚀🚀🚀
  • 源代码、权重下载链接【提取码:20tr】

参考

  • PaddleOCR-PP-OCRv4推理详解及部署实现(上)
  • PaddleOCR-PP-OCRv4推理详解及部署实现(中)
  • https://github.com/shouxieai/tensorRT_Pro
  • https://github.com/Melody-Zhou/tensorRT_Pro-YOLOv8
  • https://github.com/PaddlePaddle/PaddleOCR/blob/main/deploy/cpp_infer
  • cuOSD(CUDA On-Screen Display Library)库的学习
  • 八. 实战:CUDA-BEVFusion部署分析-学习CUDA-BEVFusion推理框架设计模式
  • RT-DETR推理详解及部署实现
  • LayerNorm Plugin的使用与说明

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

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

相关文章

linux进程——解析命令行参数——环境变量详解

前言&#xff1a;本节内容还是linux进程&#xff0c; 主要讲解里面的环境变量——我们首先要知道的就是环境变量其实就是操作系统维护的一组kv值&#xff0c; 环境变量是系统提供的一组 变量名变量值 形式的变量。不同的环境变量之间具有不同的用途&#xff0c; 并且具有全局属…

(雷达数据处理中的)跟踪算法(4) --- 基于数据集的目标跟踪实践

说明 本文作为跟踪系列的第4篇博文&#xff0c;在前面几篇博文[1][2][3]的基础上对所采集的实际数据(来自国防科技大学电子科学学院所主导发布的数据集[4])进行跟踪实践。读者在阅读本文前&#xff0c;建议先阅读本系列的前3篇博文。 Blog 20240724 博文第一次撰写 目录 说明…

【Linux】管道通信和 system V 通信

文章目录 一、进程通信原理&#xff08;让不同进程看到同一份资源&#xff09;二、管道通信2.1 管道原理及其特点2.1 匿名管道和命名管道 三、共享内存通信3.1 共享内存原理3.2 创建和关联共享内存3.3 去关联、ipc 指令和删除共享内存 四、消息队列和信号量&#xff08;了解&am…

【Unity2D 2022:UI】无法拖拽TextMeshPro组件

在预制体Card上挂载了四个Text Mesh Pro组件&#xff0c;分别命名为Name、HP、ATK、Description。 将预制体Card挂载脚本CardDisplay用来在预制体上显示属性&#xff0c;并创建TextMeshPro对象来接收TextMeshPro组件。 using TMPro; using UnityEngine; using UnityEngine.UI;…

HCIP之PPP协议(PAP认证,CHAP认证)、GRE、MGRE综合实验

实验过程 一、IP配置 [r1]interface Serial 4/0/0 [r1-Serial4/0/0]ip ad 15.1.1.1 24 [r1]interface GigabitEthernet 0/0/0 [r1-GigabitEthernet0/0/0]ip ad 192.168.1.1 24 r2]interface Serial 4/0/0 [r2-Serial4/0/0]ip ad 25.1.1.2 24 [r2]interface GigabitEthernet 0/…

基于 HTML+ECharts 实现智慧交通数据可视化大屏(含源码)

构建智慧交通数据可视化大屏&#xff1a;基于 HTML 和 ECharts 的实现 随着城市化进程的加快&#xff0c;智慧交通系统已成为提升城市管理效率和居民生活质量的关键。通过数据可视化&#xff0c;交通管理部门可以实时监控交通流量、事故发生率、道路状况等关键指标&#xff0c;…

LabVIEW多种测试仪器集成控制系统

在现代工业生产与科研领域&#xff0c;对测试设备的需求日益增长。传统的手动操作测试不仅效率低下&#xff0c;而且易出错。本项目通过集成控制系统&#xff0c;实现了自动化控制&#xff0c;降低操作复杂度和错误率&#xff0c;提高生产和研究效率。 系统组成与硬件选择 系…

MSSQL注入前置知识

简述 Microsoft SQL server也叫SQL server / MSSQL&#xff0c;由微软推出的关系型数据库&#xff0c;默认端口1433 常见搭配C# / .net IISmssql mssql的数据库文件 数据文件&#xff08;.mdf&#xff09;&#xff1a;主要的数据文件&#xff0c;包含数据表中的数据和对象信息…

Mongodb入门介绍

文章目录 1、Mongodb&#xff1a;NoSQL数据库&#xff0c;分布式的文档型数据库2、适合场景&#xff1a;3、不适合场景&#xff1a;4、概念5、总结 1、Mongodb&#xff1a;NoSQL数据库&#xff0c;分布式的文档型数据库 2、适合场景&#xff1a; 1、web网站数据存储&#xff…

鸿蒙 Navigation VS Router 对比

当前HarmonyOS支持两套路由机制&#xff08;Navigation和Router&#xff09;&#xff0c;Navigation作为后续长期演进及推荐的路由选择方案&#xff0c;其与Router比较的优势如下&#xff1a; 易用性层面&#xff1a; Navigation天然具备标题、内容、回退按钮的功能联动&…

Springboot循环依赖的解决方式

Springboot循环依赖的解决方式 起因原因解决方案配置文件解决使用工具类获取bean还有一种我设想的方案 起因 今天重构代码时&#xff0c;发现之前的代码结构完全混乱&#xff0c;没有按照MVC分层思想去编写&#xff0c;很多业务逻辑写在了controller中&#xff0c;导致引用的很…

Java | Leetcode Java题解之第278题第一个错误的版本

题目&#xff1a; 题解&#xff1a; public class Solution extends VersionControl {public int firstBadVersion(int n) {int left 1, right n;while (left < right) { // 循环直至区间左右端点相同int mid left (right - left) / 2; // 防止计算时溢出if (isBadVers…

哪个邮箱最安全最好用啊

企业邮箱安全至关重要&#xff0c;需保护隐私、防财务损失、维护通信安全、避免纠纷&#xff0c;并维持业务连续性。哪个企业邮箱最安全好用呢&#xff1f;Zoho企业邮箱&#xff0c;采用加密技术、反垃圾邮件和病毒保护&#xff0c;支持多因素认证&#xff0c;确保数据安全合规…

【大师与bug里特】M_Studio《王国之梦》学习笔记

1️⃣ Object & object(✅) 之辨 《7.泛型事件框架〈余2min左右时〉》 不然inspector窗口的最后一行&#xff08;告诉我们订阅者是SceneLoadManager它身上挂了☝️ObjectEventListener用来监听这个事件 有多少个事件注册到这里来了都能够看到&#xff09;还是不会出现 加上…

开源邮箱套件介绍系列1:SOGo

项目网站&#xff1a;SOGo | Free Open Source Webmail 提示&#xff1a;如下内容大部分来自官方网站&#xff0c;通过AI智能翻译而来。 1. SOGo功能概述 SOGo提供了多种访问日历和消息数据的方式。您的用户可以使用网页浏览器、Microsoft Outlook、Mozilla Thunderbird、Ap…

create-vue源码学习之 gradient-string 渐变色打印

效果 在使用 create-vue 脚手架时&#xff0c;想实现如下的打印效果。 探究过程 翻到源码里看到这一行 没错&#xff0c;绿色部分就是告诉我们如何生成的。可以看到引入了 gradient-string 包 于是乎&#xff0c;我来试试 pnpm i gradient-string pnpm i --save-dev …

【IT人生系列二】第一次离职你下定了什么决心

本文承接【IT人生系列一】你的第一份工作激起了多少浪花 转眼间&#xff0c;博主已经在java这趟列车上漂流了18个月之久&#xff0c;再美的风景也会厌倦&#xff0c;我也不是那个初到上海充满干劲的少年&#xff0c;理想与现实的落差让我越发迷茫&#xff0c;我无数次想过放弃…

怎么在PPT插入视频?3个做PPT常用的使用技巧分享!

PPT技巧在日常办公中扮演着重要角色&#xff0c;ppt是一个开放的视觉呈现工具和载体&#xff0c;它支持在页面中插入各种内容媒介&#xff0c;包括文本、图片、视频、音频、矢量素材等&#xff0c;特别是当涉及到PPT插入视频时&#xff0c;它的作用就显得尤为突出。 不过说到p…

ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间

前言 在做数据库设计时&#xff0c;为了方便进行数据追踪&#xff0c;通常会有几个字段是每个表都有的&#xff0c;比如创建时间、创建人、更新时间、更新人、备注等&#xff0c;在存储这些时间时&#xff0c;要么存储 WEB 服务器的时间&#xff0c;要么存储数据库服务器的时间…

计算机三级嵌入式笔记(二)——嵌入式处理器

目录 考点1 嵌入式处理器的结构类型 考点2 嵌入式处理器简介 考点3 ARM处理器概述 考点4 处理器和处理器核 考点5 ARM 处理器的分类 考点6 经典 ARM 处理器 考点7 ARM Cortex 嵌入式处理器 考点8 ARM Cortex实时嵌入式处理器 考点9 ARM Cortex 应用处理器 考点10 AR…