如何使用OpenCV为文本添加自定义颜色轮廓/阴影并保证帧率
解决方案结论
不需要重新开发字体库。现有两次调用putText绘制阴影/轮廓的方案性能差,核心原因是重复执行了文本光栅化这个最耗时的步骤——多数人误以为性能损耗来自多绘制了一层像素,实际上像素填充的开销占比不到20%,80%的耗时都集中在putText内部解析字体、生成矢量轮廓、光栅化为位图的流程。只要把光栅化、绘制两个步骤拆分,就能降低一半以上的计算开销,完全满足高FPS下大量文本绘制的需求,不需要从零实现字体渲染逻辑。
- 方案1:单通道Mask双层绘制(适配OpenCV原生接口,无额外依赖)
只调用一次putText将文本光栅化到独立的单通道alpha掩码上,再用这个掩码分别完成阴影层、正文层的像素填充,把两次光栅化压缩为一次:
这个方案比两次直接调用// 步骤1:仅执行一次光栅化,生成文本的alpha掩码 cv::Mat text_mask(frame.rows, frame.cols, CV_8UC1, cv::Scalar(0)); cv::putText(text_mask, draw_text, text_pos, cv::FONT_HERSHEY_SIMPLEX, font_scale, cv::Scalar(255), font_thickness); // 步骤2:绘制阴影层:将掩码偏移1-2像素,用指定阴影颜色填充对应区域 cv::Rect shadow_roi(text_pos.x + shadow_offset_x, text_pos.y + shadow_offset_y, text_mask.cols, text_mask.rows); frame(shadow_roi).setTo(shadow_color, text_mask); // 步骤3:绘制正文层:用原掩码在原位置填充正文颜色 cv::Rect text_roi(text_pos.x, text_pos.y, text_mask.cols, text_mask.rows); frame(text_roi).setTo(text_color, text_mask);putText性能高50%左右,如果单帧内存在重复内容、重复字号的文本,还可以把生成的text_mask做缓存,同参数文本直接复用掩码,性能还能进一步提升。 - 方案2:描边轮廓效果快速实现
如果需要文本轮廓效果,不需要多次偏移绘制文本。对方案1中生成的text_mask做对应尺寸核的形态学膨胀,得到比原文本粗一圈的轮廓掩码,先把膨胀后的掩码用轮廓颜色填充到帧上,再填充原掩码的正文即可,描边粗细可以直接通过膨胀核大小调整,性能远高于多次偏移绘制文本的方案。 - 方案3:FreeType自定义分层渲染(解决阴影颜色不可控问题)
你之前使用FreeType时无法单独修改阴影颜色,问题出在选用了内置阴影效果的TTF字体——这类字体把阴影和文字主体打包在同一个字形路径里,自然只能统一修改颜色,不是FreeType本身的能力限制。正确的实现方式是放弃自带效果的字体,自己控制渲染层级:- 提前将常用字号的字形渲染为alpha位图做全局缓存,避免每次绘制重复做光栅化计算
- 绘制时先按设定的偏移量渲染阴影层字形,填充指定的黑/白阴影色
- 回到原位置渲染正文层字形,填充正文颜色
由于自带字形缓存机制,这个方案在绘制大量重复文本时,性能比OpenCV原生putText高2-3倍,同时支持任意TTF字体,阴影、描边的颜色、透明度、偏移量、粗细都可以自由调整。
性能提示:如果单帧文本量过千,尽量避免逐段文本单独申请掩码内存、单独调用绘制接口,可以提前统计所有文本的绘制区域,合并内存操作,进一步降低函数调用和内存拷贝的开销。
内容的提问来源于stack exchange,提问作者Velandrie
相关产品推荐
相关产品推荐

