概述
在计算机视觉项目中,我们经常需要在图像上绘制文本,例如:
- 显示识别结果(如检测类别、置信度);
- 绘制图像说明或标注;
- 动态视频帧文字叠加;
- 制作视觉化报告。
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()