绘图功能
OpenCV 提供了丰富的绘图功能,可以在图像上绘制各种几何图形和文字。这些功能在可视化结果、标注图像、创建交互界面等场景中非常有用。
绘图基础
在 OpenCV 中,绘图操作直接在图像数组上进行,会修改原始图像。如果需要保留原始图像,应该先创建副本:
import cv2
import numpy as np
# 创建空白图像(黑色背景)
image = np.zeros((400, 600, 3), dtype=np.uint8)
# 或者复制已有图像
original = cv2.imread('image.jpg')
image = original.copy()
所有绘图函数都有一些共同的参数:
img:目标图像,绘图操作会直接修改这个图像。
color:颜色,对于彩色图像是 BGR 格式的元组,如 (255, 0, 0) 表示蓝色。对于灰度图像是单个数值。
thickness:线条粗细,默认为 1。设为 -1 表示填充图形内部。
lineType:线条类型,影响线条的平滑程度:
cv2.LINE_8:8 连通线(默认)cv2.LINE_4:4 连通线cv2.LINE_AA:抗锯齿线,线条更平滑
绘制直线
使用 cv2.line() 函数绘制直线:
import cv2
import numpy as np
image = np.zeros((400, 600, 3), dtype=np.uint8)
# 绘制蓝色直线
cv2.line(image, (0, 0), (600, 400), (255, 0, 0), 2)
# 绘制绿色抗锯齿直线
cv2.line(image, (0, 400), (600, 0), (0, 255, 0), 2, cv2.LINE_AA)
# 绘制粗红色直线
cv2.line(image, (300, 0), (300, 400), (0, 0, 255), 5)
cv2.imshow('Lines', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
参数说明:
pt1:起点坐标(x, y)pt2:终点坐标(x, y)color:线条颜色thickness:线条粗细
绘制矩形
使用 cv2.rectangle() 函数绘制矩形:
import cv2
import numpy as np
image = np.zeros((400, 600, 3), dtype=np.uint8)
# 绘制空心矩形(蓝色边框)
cv2.rectangle(image, (50, 50), (200, 150), (255, 0, 0), 2)
# 绘制填充矩形(绿色填充)
cv2.rectangle(image, (250, 50), (400, 150), (0, 255, 0), -1)
# 绘制红色边框矩形
cv2.rectangle(image, (450, 50), (550, 150), (0, 0, 255), 3)
cv2.imshow('Rectangles', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
参数说明:
pt1:矩形左上角坐标pt2:矩形右下角坐标thickness:边框粗细,-1 表示填充
矩形绘制常用于目标检测结果的可视化,标注检测到的物体位置。
绘制圆形
使用 cv2.circle() 函数绘制圆形:
import cv2
import numpy as np
image = np.zeros((400, 600, 3), dtype=np.uint8)
# 绘制空心圆
cv2.circle(image, (150, 200), 80, (255, 0, 0), 2)
# 绘制填充圆
cv2.circle(image, (300, 200), 80, (0, 255, 0), -1)
# 绘制抗锯齿圆
cv2.circle(image, (450, 200), 80, (0, 0, 255), 2, cv2.LINE_AA)
cv2.imshow('Circles', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
参数说明:
center:圆心坐标(x, y)radius:圆的半径thickness:边框粗细,-1 表示填充
圆形常用于标注特征点、人脸检测结果等场景。
绘制椭圆
使用 cv2.ellipse() 函数绘制椭圆:
import cv2
import numpy as np
image = np.zeros((400, 600, 3), dtype=np.uint8)
# 绘制完整椭圆
cv2.ellipse(image, (150, 200), (100, 50), 0, 0, 360, (255, 0, 0), 2)
# 绘制旋转椭圆(旋转角度 45 度)
cv2.ellipse(image, (300, 200), (100, 50), 45, 0, 360, (0, 255, 0), 2)
# 绘制椭圆弧(从 0 度到 180 度)
cv2.ellipse(image, (450, 200), (100, 50), 0, 0, 180, (0, 0, 255), 2)
# 绘制填充椭圆
cv2.ellipse(image, (300, 350), (80, 40), 0, 0, 360, (255, 255, 0), -1)
cv2.imshow('Ellipses', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
参数说明:
center:椭圆中心坐标axes:椭圆半轴长度(长轴, 短轴)angle:椭圆旋转角度(度)startAngle:起始角度endAngle:结束角度
椭圆绘制在人脸检测、姿态估计等任务中常用于标注检测结果。
绘制多边形
使用 cv2.polylines() 函数绘制多边形:
import cv2
import numpy as np
image = np.zeros((400, 600, 3), dtype=np.uint8)
# 定义多边形顶点
pts = np.array([[100, 50], [200, 150], [150, 300], [50, 300], [0, 150]], np.int32)
# 重塑为 (n, 1, 2) 形状
pts = pts.reshape((-1, 1, 2))
# 绘制空心多边形
cv2.polylines(image, [pts], True, (255, 0, 0), 2)
# 绘制填充多边形
pts2 = np.array([[350, 50], [500, 100], [450, 300], [300, 250]], np.int32)
pts2 = pts2.reshape((-1, 1, 2))
cv2.fillPoly(image, [pts2], (0, 255, 0))
cv2.imshow('Polygons', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
参数说明:
pts:多边形顶点数组列表isClosed:是否闭合多边形thickness:线条粗细
多边形绘制常用于图像分割结果的标注,以及标注不规则形状的目标区域。
绘制文字
使用 cv2.putText() 函数在图像上绘制文字:
import cv2
import numpy as np
image = np.zeros((400, 600, 3), dtype=np.uint8)
# 绘制基本文字
cv2.putText(image, 'Hello OpenCV', (50, 100),
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
# 不同字体
fonts = [
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
]
font_names = ['SIMPLEX', 'PLAIN', 'DUPLEX', 'COMPLEX', 'TRIPLEX', 'COMPLEX_SMALL']
for i, (font, name) in enumerate(zip(fonts, font_names)):
cv2.putText(image, name, (50, 150 + i * 40), font, 0.8, (255, 255, 255), 1)
cv2.imshow('Text', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
参数说明:
text:要绘制的文字字符串org:文字左下角坐标(x, y)fontFace:字体类型fontScale:字体缩放比例thickness:线条粗细
获取文字尺寸
在绘制文字前,可以使用 cv2.getTextSize() 获取文字的尺寸,用于精确布局:
import cv2
import numpy as np
image = np.zeros((200, 600, 3), dtype=np.uint8)
text = 'Center Text'
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1.5
thickness = 2
# 获取文字尺寸
(text_width, text_height), baseline = cv2.getTextSize(text, font, font_scale, thickness)
# 计算居中位置
image_height, image_width = image.shape[:2]
x = (image_width - text_width) // 2
y = (image_height + text_height) // 2
# 绘制居中文字
cv2.putText(image, text, (x, y), font, font_scale, (255, 255, 255), thickness)
cv2.imshow('Centered Text', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
绘制中文文字
OpenCV 的 putText() 函数不支持中文,需要使用 PIL 库来绘制中文:
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
# 创建图像
image = np.zeros((200, 600, 3), dtype=np.uint8)
# 转换为 PIL 图像
pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(pil_image)
# 加载字体(需要指定字体文件路径)
try:
font = ImageFont.truetype('simhei.ttf', 30)
except:
font = ImageFont.load_default()
# 绘制中文
draw.text((50, 80), '中文文字示例', font=font, fill=(255, 255, 255))
# 转换回 OpenCV 格式
image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
cv2.imshow('Chinese Text', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
综合示例
目标检测可视化
import cv2
def draw_detection_result(image, boxes, labels, scores, colors=None):
"""
绘制目标检测结果
参数:
image: 输入图像
boxes: 边界框列表 [(x1, y1, x2, y2), ...]
labels: 标签列表
scores: 置信度列表
colors: 每个类别的颜色
"""
if colors is None:
colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255)]
for i, (box, label, score) in enumerate(zip(boxes, labels, scores)):
x1, y1, x2, y2 = box
color = colors[i % len(colors)]
# 绘制边界框
cv2.rectangle(image, (x1, y1), (x2, y2), color, 2)
# 准备标签文字
text = f'{label}: {score:.2f}'
# 获取文字尺寸
(text_width, text_height), _ = cv2.getTextSize(
text, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 1)
# 绘制标签背景
cv2.rectangle(image, (x1, y1 - text_height - 10),
(x1 + text_width, y1), color, -1)
# 绘制标签文字
cv2.putText(image, text, (x1, y1 - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
return image
# 使用示例
image = cv2.imread('street.jpg')
boxes = [(100, 100, 200, 300), (300, 150, 450, 350)]
labels = ['person', 'car']
scores = [0.95, 0.87]
result = draw_detection_result(image, boxes, labels, scores)
cv2.imshow('Detection Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
绘制统计图表
import cv2
import numpy as np
def draw_bar_chart(data, labels, title, width=600, height=400):
"""
绘制简单的柱状图
参数:
data: 数据列表
labels: 标签列表
title: 图表标题
width: 图像宽度
height: 图像高度
"""
image = np.ones((height, width, 3), dtype=np.uint8) * 255
margin = 60
chart_width = width - 2 * margin
chart_height = height - 2 * margin
# 绘制坐标轴
cv2.line(image, (margin, margin), (margin, height - margin), (0, 0, 0), 2)
cv2.line(image, (margin, height - margin), (width - margin, height - margin), (0, 0, 0), 2)
# 计算柱子参数
n = len(data)
bar_width = chart_width // (n * 2)
max_data = max(data)
# 绘制柱子
colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255)]
for i, (value, label) in enumerate(zip(data, labels)):
x = margin + bar_width + i * bar_width * 2
bar_height = int(value / max_data * chart_height)
y = height - margin - bar_height
color = colors[i % len(colors)]
cv2.rectangle(image, (x, y), (x + bar_width, height - margin), color, -1)
# 绘制数值
cv2.putText(image, str(value), (x + bar_width // 4, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
# 绘制标签
cv2.putText(image, label, (x, height - margin + 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
# 绘制标题
cv2.putText(image, title, (width // 2 - len(title) * 5, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 0), 2)
return image
# 使用示例
data = [85, 92, 78, 95, 88]
labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
chart = draw_bar_chart(data, labels, 'Weekly Sales')
cv2.imshow('Bar Chart', chart)
cv2.waitKey(0)
cv2.destroyAllWindows()
图像标注工具
import cv2
import numpy as np
class ImageAnnotator:
def __init__(self, image_path):
self.image = cv2.imread(image_path)
self.clone = self.image.copy()
self.points = []
self.drawing = False
def mouse_callback(self, event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
self.points.append((x, y))
elif event == cv2.EVENT_MOUSEMOVE:
if len(self.points) > 0:
self.image = self.clone.copy()
cv2.polylines(self.image, [np.array(self.points + [(x, y)])],
False, (0, 255, 0), 2)
elif event == cv2.EVENT_RBUTTONDOWN:
if len(self.points) >= 3:
self.image = self.clone.copy()
cv2.fillPoly(self.image, [np.array(self.points)], (0, 255, 0))
self.points = []
def run(self):
cv2.namedWindow('Annotator')
cv2.setMouseCallback('Annotator', self.mouse_callback)
while True:
cv2.imshow('Annotator', self.image)
key = cv2.waitKey(1) & 0xFF
if key == ord('r'):
self.image = self.clone.copy()
self.points = []
elif key == ord('s'):
cv2.imwrite('annotated.jpg', self.image)
elif key == 27:
break
cv2.destroyAllWindows()
annotator = ImageAnnotator('image.jpg')
annotator.run()
小结
本章介绍了 OpenCV 的绘图功能,包括直线、矩形、圆形、椭圆、多边形和文字的绘制。关键要点:
绘图修改原图:所有绘图操作都会直接修改目标图像,如需保留原图请先复制。
坐标系统:OpenCV 使用图像坐标系,原点在左上角,x 向右,y 向下。
颜色格式:OpenCV 使用 BGR 格式,注意与其他库的 RGB 格式区分。
抗锯齿:使用 cv2.LINE_AA 可以获得更平滑的线条效果。
中文支持:OpenCV 原生不支持中文,需要借助 PIL 库实现。
下一章将学习图像处理基础,包括颜色空间转换、几何变换和图像滤波等内容。