OpenCV(十八):绘制文本

概述

在计算机视觉项目中,我们经常需要在图像上绘制文本,例如:

  • 显示识别结果(如检测类别、置信度);
  • 绘制图像说明或标注;
  • 动态视频帧文字叠加;
  • 制作视觉化报告。

OpenCV 提供了 cv2.putText() 函数,可快速在图像上绘制文本信息,支持多种字体、缩放、颜色、抗锯齿等属性。

cv2.putText() 函数

函数原型如下:

python 复制代码
cv2.putText(img, text, org, fontFace, fontScale, color, thickness=None, lineType=None, bottomLeftOrigin=None)
参数 含义
img 输入图像(支持彩色或灰度)
text 需要显示的字符串
org 文本左下角坐标 (x, y)
fontFace 字体类型(如 cv2.FONT_HERSHEY_SIMPLEX
fontScale 字体缩放因子(控制大小)
color 文本颜色 (B, G, R)
thickness 线条粗细
lineType 线型(推荐 cv2.LINE_AA 抗锯齿)
bottomLeftOrigin 若为 True,则文字从下向上绘制(一般为 False)

常用字体类型

常量名 字体样式 特点
cv2.FONT_HERSHEY_SIMPLEX 无衬线字体 最常用、清晰
cv2.FONT_HERSHEY_PLAIN 简单小字体 小尺寸显示
cv2.FONT_HERSHEY_DUPLEX 双线条字体 更平滑
cv2.FONT_HERSHEY_COMPLEX 复杂字体 较大、曲线更自然
cv2.FONT_HERSHEY_TRIPLEX 三线条字体 更醒目
cv2.FONT_HERSHEY_COMPLEX_SMALL 复杂小字体 小尺寸文字
cv2.FONT_HERSHEY_SCRIPT_SIMPLEX 手写体 花体风格
cv2.FONT_HERSHEY_SCRIPT_COMPLEX 手写复杂体 更艺术化
cv2.FONT_ITALIC 倾斜样式 与其他字体组合使用

例如:

python 复制代码
font = cv2.FONT_HERSHEY_SIMPLEX | cv2.FONT_ITALIC

示例

文本基本绘制

python 复制代码
import cv2
import numpy as np

# 创建白色画布
img = np.ones((500, 900, 3), dtype=np.uint8) * 255

# 绘制不同字体的文本
cv2.putText(img, "FONT_HERSHEY_SIMPLEX", (30, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
cv2.putText(img, "FONT_HERSHEY_PLAIN", (30, 110), cv2.FONT_HERSHEY_PLAIN, 2, (0, 128, 255), 2, cv2.LINE_AA)
cv2.putText(img, "FONT_HERSHEY_DUPLEX", (30, 160), cv2.FONT_HERSHEY_DUPLEX, 1.2, (0, 200, 0), 2, cv2.LINE_AA)
cv2.putText(img, "FONT_HERSHEY_COMPLEX", (30, 210), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 0), 2, cv2.LINE_AA)
cv2.putText(img, "FONT_HERSHEY_SCRIPT_COMPLEX", (30, 260), cv2.FONT_HERSHEY_SCRIPT_COMPLEX, 1.2, (255, 0, 255), 2, cv2.LINE_AA)
cv2.putText(img, "ITALIC STYLE", (30, 310), cv2.FONT_HERSHEY_SIMPLEX | cv2.FONT_ITALIC, 1.3, (128, 0, 255), 2, cv2.LINE_AA)

# 显示
cv2.imshow("Text Drawing Example", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

文本大小与对齐技巧

python 复制代码
import cv2
import numpy as np

# 创建白色画布
img = np.ones((600, 900, 3), dtype=np.uint8) * 255

# 通用字体设置
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1
thickness = 2
line_type = cv2.LINE_AA

# 函数:绘制带边框文字(方便观察对齐位置)
def draw_text(img, text, pos, align="left", color=(0, 0, 0)):
    """绘制文字并根据 align 对齐方式调整坐标"""
    (text_w, text_h), baseline = cv2.getTextSize(text, font, font_scale, thickness)

    x, y = pos
    if align == "center":
        x -= text_w // 2
    elif align == "right":
        x -= text_w

    # 绘制文字矩形框
    cv2.rectangle(img, (x, y - text_h - baseline), (x + text_w, y + baseline), (220, 220, 220), 1)
    # 绘制文字
    cv2.putText(img, text, (x, y), font, font_scale, color, thickness, line_type)


# ======================
# 1️⃣ 水平居中对齐示例
# ======================
(h, w) = img.shape[:2]
text1 = "Center Alignment Example"
(text_w, text_h), _ = cv2.getTextSize(text1, font, font_scale, thickness)

center_x = w // 2
center_y = 150

draw_text(img, text1, (center_x, center_y), align="center", color=(0, 0, 255))

# 绘制参考中心线
cv2.line(img, (center_x, 0), (center_x, h), (0, 200, 0), 1)

# ======================
# 2️⃣ 右对齐文本
# ======================
text2 = "Right Aligned Text"
right_x = w - 50
draw_text(img, text2, (right_x, 250), align="right", color=(255, 0, 0))

# 绘制右对齐参考线
cv2.line(img, (right_x, 0), (right_x, h), (100, 100, 100), 1)

# ======================
# 3️⃣ 左对齐文本
# ======================
text3 = "Left Aligned Text"
left_x = 50
draw_text(img, text3, (left_x, 350), align="left", color=(0, 128, 0))

cv2.line(img, (left_x, 0), (left_x, h), (100, 100, 100), 1)

# ======================
# 4️⃣ 多行自动换行(自适应宽度)
# ======================
def draw_multiline_text(img, text, pos, max_width=400, line_space=10, color=(0, 0, 0)):
    """在给定宽度范围内自动换行显示文本"""
    words = text.split(" ")
    x, y = pos
    line = ""
    for word in words:
        temp_line = line + word + " "
        (tw, th), _ = cv2.getTextSize(temp_line, font, font_scale, thickness)
        if tw > max_width:
            cv2.putText(img, line, (x, y), font, font_scale, color, thickness, cv2.LINE_AA)
            y += th + line_space
            line = word + " "
        else:
            line = temp_line
    cv2.putText(img, line, (x, y), font, font_scale, color, thickness, cv2.LINE_AA)


text4 = "This is a multi-line text demonstration using OpenCV automatic line wrapping."
draw_multiline_text(img, text4, (50, 450), max_width=500, color=(128, 0, 128))

# ======================
# 5️⃣ 显示结果
# ======================
cv2.putText(img, "Text Alignment Demo", (260, 40),
            cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0, 0), 2, cv2.LINE_AA)

cv2.imshow("Text Size & Alignment Example", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

中文文本绘制

python 复制代码
from PIL import ImageFont, ImageDraw, Image
import cv2
import numpy as np

# 创建 OpenCV 图像
img = np.ones((400, 800, 3), np.uint8) * 255

# 转换为 PIL 图像
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

# 选择中文字体(需存在)
font = ImageFont.truetype("simhei.ttf", 40)  # 黑体
draw = ImageDraw.Draw(img_pil)

# 绘制中文
draw.text((50, 100), "OpenCV 绘制中文文本示例", font=font, fill=(255, 0, 0, 0))
draw.text((50, 180), "支持字体、大小、颜色、透明度", font=font, fill=(0, 128, 0, 0))

# 转回 OpenCV 格式
img = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)

cv2.imshow("中文文本示例", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

文本透明叠加(Overlay)

python 复制代码
import cv2
import numpy as np

# 创建背景图像(可换成自己的图片)
img = cv2.imread(cv2.samples.findFile("Lenna.png"))  # 若无可改为自定义背景
if img is None:
    img = np.full((480, 640, 3), (60, 120, 200), np.uint8)  # 纯色背景

h, w = img.shape[:2]

# ========= 参数设置 =========
text = "OpenCV Transparent Text Overlay"
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1.2
thickness = 2
text_color = (255, 255, 255)  # 白色
bg_color = (0, 0, 0)          # 黑色背景
alpha_text = 0.9               # 文本不透明度
alpha_bg = 0.4                 # 背景透明度

# ========= 计算文字大小 =========
(text_w, text_h), baseline = cv2.getTextSize(text, font, font_scale, thickness)
text_x = (w - text_w) // 2
text_y = (h + text_h) // 2

# ========= 创建 Overlay 层 =========
overlay = img.copy()

# 计算背景矩形范围
pad = 20
rect_x1 = text_x - pad
rect_y1 = text_y - text_h - pad
rect_x2 = text_x + text_w + pad
rect_y2 = text_y + pad

# 限制边界(防止越界)
rect_x1 = max(rect_x1, 0)
rect_y1 = max(rect_y1, 0)
rect_x2 = min(rect_x2, w)
rect_y2 = min(rect_y2, h)

# ========= 绘制半透明矩形背景 =========
cv2.rectangle(overlay, (rect_x1, rect_y1), (rect_x2, rect_y2), bg_color, -1)

# ========= 将 overlay 与原图混合 =========
cv2.addWeighted(overlay, alpha_bg, img, 1 - alpha_bg, 0, img)

# ========= 绘制文本(再单独叠加文字)=========
text_overlay = img.copy()
cv2.putText(text_overlay, text, (text_x, text_y),
            font, font_scale, text_color, thickness, cv2.LINE_AA)

# 文本部分透明混合
cv2.addWeighted(text_overlay, alpha_text, img, 1 - alpha_text, 0, img)

# ========= 显示结果 =========
cv2.imshow("Transparent Text Overlay", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

动态文本绘制(视频帧实时显示)

python 复制代码
# 从摄像头或视频文件读取帧;
# 每帧实时绘制文本(时间、帧率、计数)
# 半透明背景叠加(防止文字难以看清)
# 支持淡入淡出(透明度动画)
# 自动居中或固定位置显示

import cv2
import numpy as np
import time

# 打开摄像头(或替换为视频路径)
cap = cv2.VideoCapture(0)

# 字体设置
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1
thickness = 2
text_color = (255, 255, 255)
bg_color = (0, 0, 0)

# 控制参数
alpha_bg = 0.5      # 背景透明度
alpha_text = 0.9    # 文本透明度
frame_count = 0
start_time = time.time()

while True:
    ret, frame = cap.read()
    if not ret:
        break

    frame_count += 1
    h, w = frame.shape[:2]

    # ==== 动态内容 ====
    elapsed = time.time() - start_time
    fps = frame_count / elapsed
    text1 = f"Elapsed: {elapsed:6.2f}s"
    text2 = f"FPS: {fps:5.2f}"
    text3 = time.strftime("Time: %H:%M:%S", time.localtime())

    # ==== 绘制函数 ====
    def draw_overlay_text(img, text, pos, color, bg_color, alpha_bg, alpha_text):
        """绘制半透明背景+文字"""
        (text_w, text_h), baseline = cv2.getTextSize(text, font, font_scale, thickness)
        x, y = pos
        pad = 8
        overlay = img.copy()
        rect_x1, rect_y1 = x - pad, y - text_h - pad
        rect_x2, rect_y2 = x + text_w + pad, y + pad

        # 绘制半透明矩形背景
        cv2.rectangle(overlay, (rect_x1, rect_y1), (rect_x2, rect_y2), bg_color, -1)
        cv2.addWeighted(overlay, alpha_bg, img, 1 - alpha_bg, 0, img)

        # 再叠加文字层
        text_overlay = img.copy()
        cv2.putText(text_overlay, text, (x, y), font, font_scale, color, thickness, cv2.LINE_AA)
        cv2.addWeighted(text_overlay, alpha_text, img, 1 - alpha_text, 0, img)

    # ==== 在不同位置绘制动态文本 ====
    draw_overlay_text(frame, text1, (20, 50), text_color, bg_color, alpha_bg, alpha_text)
    draw_overlay_text(frame, text2, (20, 100), text_color, bg_color, alpha_bg, alpha_text)
    draw_overlay_text(frame, text3, (20, 150), (255, 200, 0), bg_color, alpha_bg, alpha_text)

    # ==== 可选:动态淡入效果 ====
    alpha_dynamic = 0.5 + 0.5 * np.sin(time.time() * 2.0)
    overlay = frame.copy()
    cv2.putText(overlay, "Recording...", (w - 280, h - 40),
                cv2.FONT_HERSHEY_DUPLEX, 1.0, (0, 0, 255), 2, cv2.LINE_AA)
    cv2.addWeighted(overlay, alpha_dynamic, frame, 1 - alpha_dynamic, 0, frame)

    # ==== 显示结果 ====
    cv2.imshow("Dynamic Text Overlay", frame)

    # 按 ESC 退出
    key = cv2.waitKey(1)
    if key == 27:
        break

cap.release()
cv2.destroyAllWindows()

排版与背景框

python 复制代码
# 多行排版(标题、副标题、信息行)
# 自动背景框尺寸计算
# 居中、左对齐排版示例
# 半透明背景支持
# 自动换行与边距控制

import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import textwrap

# =======================
# 创建背景图
# =======================
img_h, img_w = 600, 900
img = np.full((img_h, img_w, 3), (30, 60, 120), np.uint8)

# =======================
# 文本内容
# =======================
title = "OpenCV 中文排版与背景示例"
subtitle = "多行排版、半透明背景、自动换行"
content = (
    "OpenCV 默认不支持中文显示,所以需要借助 Pillow 绘制中文。"
    "通过结合 getbbox()、rectangle() 与 addWeighted(),"
    "可以实现整齐排版、自动背景框、居中与左对齐等效果。"
)
bottom_text = "按 ESC 键退出"

# =======================
# 样式参数
# =======================
font_path = "C:/Windows/Fonts/simhei.ttf"  # Windows 黑体,可根据实际修改
font_size_title = 32
font_size_subtitle = 24
font_size_content = 20
font_size_bottom = 18

margin = 20
line_space = 8
x_start = 50
y_start = 80
alpha_bg = 0.5  # 背景透明度

# =======================
# 辅助函数:获取文本尺寸
# =======================
def get_text_size(font, text):
    """
    返回文本宽度和高度
    """
    bbox = font.getbbox(text)
    width = bbox[2] - bbox[0]
    height = bbox[3] - bbox[1]
    return width, height

# =======================
# 辅助函数:绘制文本块
# =======================
def draw_text_block(img, x, y, text_lines, font_path, font_size,
                    text_color=(255,255,255), bg_color=(0,0,0),
                    alpha_bg=0.5, line_space=8):
    """
    在 img 上绘制带半透明背景的文本块
    text_lines: list[str]
    返回更新后的 img 和下一段文本的 y 坐标
    """
    # 转 PIL
    img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img_pil)
    pil_font = ImageFont.truetype(font_path, font_size)

    # 计算背景框尺寸
    line_sizes = [get_text_size(pil_font, line) for line in text_lines]
    max_w = max([w for w,h in line_sizes])
    total_h = sum([h for w,h in line_sizes]) + line_space*(len(line_sizes)-1)

    x1, y1 = x - margin, y - margin
    x2, y2 = x + max_w + margin, y + total_h + margin

    # 防止越界
    x1 = max(0, x1)
    y1 = max(0, y1)
    x2 = min(img.shape[1], x2)
    y2 = min(img.shape[0], y2)

    # 绘制半透明背景
    overlay = img.copy()
    cv2.rectangle(overlay, (x1, y1), (x2, y2), bg_color, -1)
    cv2.addWeighted(overlay, alpha_bg, img, 1-alpha_bg, 0, img)

    # 绘制文字
    y_offset = y
    for line, (lw, lh) in zip(text_lines, line_sizes):
        draw.text((x, y_offset), line, font=pil_font, fill=text_color)
        y_offset += lh + line_space

    # 转回 OpenCV 图像
    img = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
    return img, y_offset + margin

# =======================
# 绘制标题、副标题
# =======================
y_current = y_start
img, y_current = draw_text_block(img, x_start, y_current, [title],
                                 font_path, font_size_title, text_color=(255,255,255),
                                 bg_color=(0,0,0), alpha_bg=alpha_bg, line_space=line_space)
img, y_current = draw_text_block(img, x_start, y_current, [subtitle],
                                 font_path, font_size_subtitle, text_color=(255,200,100),
                                 bg_color=(0,0,0), alpha_bg=alpha_bg, line_space=line_space)

# =======================
# 绘制正文,多行自动换行
# =======================
approx_char_per_line = 28
wrapped_lines = textwrap.wrap(content, width=approx_char_per_line)
img, _ = draw_text_block(img, x_start, y_current, wrapped_lines,
                         font_path, font_size_content, text_color=(255,255,255),
                         bg_color=(0,0,0), alpha_bg=alpha_bg, line_space=line_space)

# =======================
# 底部提示文字
# =======================
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img_pil)
pil_font_bottom = ImageFont.truetype(font_path, font_size_bottom)
text_w, text_h = get_text_size(pil_font_bottom, bottom_text)
x_bottom = max(0, img_w - text_w - margin)
y_bottom = max(text_h + margin, img_h - 30)
draw.text((x_bottom, y_bottom), bottom_text, font=pil_font_bottom, fill=(180,220,255))
img = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)

# =======================
# 显示结果
# =======================
cv2.imshow("中文排版与背景示例", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
相关推荐
rengang668 小时前
105-Spring AI Alibaba Module RAG 使用示例
java·人工智能·spring·rag·spring ai·ai应用编程
说私域8 小时前
开源AI智能客服、AI智能名片与S2B2C商城小程序在营销运营中的应用与重要性研究
人工智能·小程序·开源
美团技术团队8 小时前
LongCat-Flash-Omni正式发布并开源:开启全模态实时交互时代
人工智能
rengang668 小时前
09-神经网络的结构:描述神经网络的层次化组成和设计
人工智能·深度学习·神经网络
rengang668 小时前
07-神经元模型:介绍神经网络中神经元的结构和功能
人工智能·深度学习·神经网络
说私域8 小时前
开源AI智能名片链动2+1模式S2B2C商城小程序商业化路径优化研究
人工智能·小程序·开源
倦王9 小时前
PyTorch图片数据载入方法
人工智能·pytorch·python
无敌最俊朗@9 小时前
视频时间戳PTS和DTS的区别
人工智能·音视频