OpenCVfreetype实战为AI识别结果图自动添加中文标签Python/C双版本在计算机视觉项目中我们经常需要将模型识别结果可视化展示。当检测到物体后除了绘制边界框添加清晰可读的标签同样重要。然而OpenCV原生putText函数对中文支持不佳直接使用会导致乱码。本文将介绍如何利用freetype库解决这一问题并实现专业级的中文标签渲染效果。1. 环境准备与字体配置1.1 安装必要的库首先确保已安装OpenCV的contrib版本它包含了freetype模块# Python安装 pip install opencv-contrib-python # C编译 # 需要编译时添加-DOPENCV_ENABLE_NONFREEON和-DBUILD_opencv_freetypeON选项1.2 字体文件选择选择合适的中文字体文件如.ttf格式推荐使用系统自带字体或从合法渠道获取Windowssimhei.ttf黑体、msyh.ttf微软雅黑Linuxwqy-microhei.ttf文泉驿微米黑MacOSPingFang.ttc苹方注意商业项目使用字体时需确认版权许可2. 基础中文渲染实现2.1 Python实现import cv2 import numpy as np def init_freetype(font_pathsimhei.ttf): ft2 cv2.freetype.createFreeType2() ft2.loadFontData(font_path, 0) return ft2 def draw_chinese_text(img, text, pos, font_size, color, thickness1): ft2 init_freetype() text_size, baseline ft2.getTextSize(text, font_size, thickness) ft2.putText(img, text, pos, font_size, color, thickness, cv2.LINE_AA, True) return text_size2.2 C实现#include opencv2/opencv.hpp #include opencv2/freetype.hpp cv::Ptrcv::freetype::FreeType2 initFreetype(const std::string font_path simhei.ttf) { auto ft2 cv::freetype::createFreeType2(); ft2-loadFontData(font_path, 0); return ft2; } cv::Size drawChineseText(cv::Mat img, const std::string text, cv::Point pos, int font_size, const cv::Scalar color, int thickness 1) { auto ft2 initFreetype(); int baseline 0; cv::Size text_size ft2-getTextSize(text, font_size, thickness, baseline); ft2-putText(img, text, pos, font_size, color, thickness, cv::LINE_AA, true); return text_size; }3. 在AI识别结果上应用3.1 与检测框配合为YOLO等模型的检测结果添加标签时需要考虑标签位置、颜色对比度等问题def draw_detection_with_label(img, boxes, labels, scores, font_size20): for box, label, score in zip(boxes, labels, scores): # 绘制检测框 x1, y1, x2, y2 box cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 构造标签文本 text f{label}: {score:.2f} # 计算标签背景区域 text_size, _ draw_chinese_text(img, text, (0, 0), font_size, (0, 0, 0)) label_bg (x1, max(0, y1 - text_size.height - 5), x1 text_size.width, y1) # 绘制半透明背景 overlay img.copy() cv2.rectangle(overlay, (label_bg[0], label_bg[1]), (label_bg[2], label_bg[3]), (0, 255, 0), -1) cv2.addWeighted(overlay, 0.5, img, 0.5, 0, img) # 绘制文本 draw_chinese_text(img, text, (x1, y1 - 5), font_size, (255, 255, 255))3.2 多行文本处理当需要显示多行中文时需要手动处理换行和位置计算void drawMultilineChinese(cv::Mat img, const std::vectorstd::string lines, cv::Point origin, int font_size, const cv::Scalar color) { auto ft2 initFreetype(); int line_height font_size * 1.2; // 行间距 for (size_t i 0; i lines.size(); i) { cv::Point pos(origin.x, origin.y i * line_height); ft2-putText(img, lines[i], pos, font_size, color, -1, cv::LINE_AA, true); } }4. 高级样式与优化技巧4.1 自动调整字体颜色根据背景自动选择高对比度文字颜色def get_contrast_color(background): 根据背景色返回最佳文字颜色黑或白 luminance 0.299 * background[2] 0.587 * background[1] 0.114 * background[0] return (255, 255, 255) if luminance 128 else (0, 0, 0) def draw_adaptive_text(img, text, pos, font_size): # 获取文本区域平均颜色 text_size, _ draw_chinese_text(img, text, pos, font_size, (0, 0, 0)) roi img[pos.y:text_size.height, pos.x:text_size.width] avg_color np.mean(roi, axis(0, 1)) # 选择对比色并绘制 text_color get_contrast_color(avg_color) draw_chinese_text(img, text, pos, font_size, text_color)4.2 文本描边效果通过多次绘制实现描边效果增强文字可读性void drawTextWithOutline(cv::Mat img, const std::string text, cv::Point pos, int font_size, const cv::Scalar text_color, const cv::Scalar outline_color, int outline_width 2) { auto ft2 initFreetype(); // 先绘制轮廓多个偏移位置 for (int y -outline_width; y outline_width; y) { for (int x -outline_width; x outline_width; x) { if (x ! 0 || y ! 0) { ft2-putText(img, text, pos cv::Point(x, y), font_size, outline_color, -1, cv::LINE_AA, true); } } } // 再绘制中心文本 ft2-putText(img, text, pos, font_size, text_color, -1, cv::LINE_AA, true); }5. 性能优化与批量处理5.1 字体对象复用避免重复加载字体文件提高处理速度class ChineseTextRenderer: def __init__(self, font_pathsimhei.ttf): self.ft2 cv2.freetype.createFreeType2() self.ft2.loadFontData(font_path, 0) def draw_text(self, img, text, pos, font_size, color, thickness1): text_size, _ self.ft2.getTextSize(text, font_size, thickness) self.ft2.putText(img, text, pos, font_size, color, thickness, cv2.LINE_AA, True) return text_size # 使用示例 renderer ChineseTextRenderer() renderer.draw_text(img, 中文标签, (100, 100), 30, (0, 0, 255))5.2 多线程批量处理对于大量图片的批量标注任务可以使用多线程加速#include thread #include vector void processImagesParallel(const std::vectorstd::string image_paths, const std::vectorstd::string texts) { auto ft2 initFreetype(); std::vectorstd::thread threads; for (size_t i 0; i image_paths.size(); i) { threads.emplace_back([, i]() { cv::Mat img cv::imread(image_paths[i]); if (!img.empty()) { ft2-putText(img, texts[i], cv::Point(50, 50), 30, cv::Scalar(0, 0, 255), -1, cv::LINE_AA, true); cv::imwrite(output_ std::to_string(i) .jpg, img); } }); } for (auto t : threads) { t.join(); } }在实际项目中我发现字体渲染性能对整体处理速度影响较大。通过预加载字体对象和合理使用多线程可以将标注效率提升3-5倍。特别是在处理视频流时这些优化措施能显著降低延迟。