|
OpenCV Python 绘图完全指南:线、圆、矩形、椭圆、多边形、文字

OpenCV Python 绘图完全指南:线、圆、矩形、椭圆、多边形、文字

简介

在计算机视觉项目中,绘图是最基础也最不可或缺的技能之一。无论是标注目标检测结果、绘制感兴趣区域(ROI)、可视化特征点,还是在图像上叠加调试信息,你都离不开 OpenCV 提供的 2D 绘图函数。

很多初学者在学习 OpenCV 时,往往会遇到这样的困境:搜索到的资料零散分布在不同的文章中,每篇文章只介绍一个函数的用法,缺少全局视角,很难建立起完整的知识体系。本指南的目标就是解决这个问题——将 OpenCV Python 中所有常用的 2D 绘图函数整合在一起,用系统化的方式逐一讲解。

本文将从 cv.line 开始,依次覆盖 cv.rectanglecv.circlecv.ellipsecv.polylinescv.fillConvexPolycv.arrowedLinecv.drawMarkercv.putText,以及使用 PIL 绘制中文文字的完整方案。每个函数都会给出完整可运行的代码示例、参数详解和实际使用技巧。文末还有综合示例、参数速查表和常见问题解答,帮你一篇搞定 OpenCV 2D 绘图。

本指南使用的 OpenCV 版本为 4.1.1+,运行环境为 Jupyter Notebook。


环境准备

在开始绘图之前,我们需要先导入必要的库,并读取一张示例图片作为绘图的「画布」。

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取示例图片
img = cv2.imread('MakerOnsite-Logo.png')

# 使用 matplotlib 显示图片
plt.imshow(img)
plt.axis('off')  # 不显示坐标轴
plt.show()

提示:在 Jupyter Notebook 中使用 plt.imshow() 即可内联显示图像。需要注意的是,cv2.imread() 读取的图片是 BGR 格式,而 matplotlib 默认按 RGB 格式显示,所以颜色会有偏差。如果颜色显示异常,可以在显示前做转换:plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))。这一点在绘图时尤其重要,因为 OpenCV 的绘图函数接受的颜色值也是 BGR 顺序。

原图


cv.line 绘制线段

cv.line 是最基础的绘图函数,用于在图像上绘制一条从一个点到另一个点的直线段。它在标注方向、画网格线、绘制坐标轴等场景中经常用到。

函数语法

img = cv.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]])

参数说明

参数说明
img输入图像(会被直接修改,即原地操作)
pt1起始点坐标 (x, y)
pt2结束点坐标 (x, y)
color线段颜色,BGR 格式,如 (0, 0, 255) 表示红色
thickness(可选)线段厚度,默认为 1
lineType(可选)线段类型,详见下文 lineType 详解
shift(可选)坐标值的小数位移因子

代码示例

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('MakerOnsite-Logo.png')

# 在图像上绘制一条红色线段,从 (50, 50) 到 (450, 450),线宽为 5
img = cv2.line(img, (50, 50), (450, 450), (0, 0, 255), 5)

plt.imshow(img)
plt.axis('off')
plt.show()

绘制线段

注意:OpenCV 中所有绘图函数都是原地修改图像的,也就是说函数会直接修改传入的 img 对象,同时也会返回修改后的图像。所以 img = cv.line(img, ...) 中的赋值其实是多余的,但写上也没有问题,能让代码意图更清晰。


cv.rectangle 绘制矩形

矩形在目标检测中应用最广泛——每个检测框就是一个矩形。cv.rectangle 通过指定对角线的两个顶点来绘制矩形。

函数语法

img = cv.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]])

参数说明

参数说明
img输入图像
pt1矩形左上角坐标 (x, y)
pt2矩形右下角坐标 (x, y)
color线段颜色,BGR 格式
thickness(可选)线段厚度。thickness = -1 时,绘制填充矩形
lineType(可选)线段类型
shift(可选)坐标位移因子

代码示例

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('MakerOnsite-Logo.png')

# 绘制一个灰色边框矩形,从 (50, 50) 到 (450, 450),线宽为 5
img = cv2.rectangle(img, (50, 50), (450, 450), (100, 100, 100), 5)

plt.imshow(img)
plt.axis('off')
plt.show()

绘制矩形

实用技巧:如果你需要绘制一个纯色填充的矩形(比如给图片加半透明遮罩),把 thickness 设为 -1 即可。这在标注 mask 区域、制作热力图叠加层时非常有用。


cv.circle 绘制圆形

画圆、画点、标记关键点是 OpenCV 绘图中最常见的操作之一。cv.circle 可以绘制不同大小的圆,从单个像素点到大圆环都能胜任。

函数语法

img = cv.circle(img, center, radius, color[, thickness[, lineType[, shift]]])

参数说明

参数说明
img输入图像
center圆心坐标 (x, y)
radius圆的半径
color颜色,BGR 格式
thickness(可选)轮廓厚度。thickness = -1 时绘制实心填充圆
lineType(可选)圆边界类型
shift(可选)坐标位移因子

代码示例

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('MakerOnsite-Logo.png')

# 绘制一个圆,圆心 (50, 50),半径 20,橙色边框,线宽 5
img = cv2.circle(img, (50, 50), 20, (0, 101, 255), 5)

plt.imshow(img)
plt.axis('off')
plt.show()

绘制圆形

画点技巧:如果你只是想标记一个点(比如关键点检测的结果),可以设置 radius=1radius=2,配合 thickness=-1,就能得到一个小实心点。这在标注人脸关键点、角点检测结果时非常常见。


cv.ellipse 绘制椭圆

椭圆比圆多了一个维度——它有两个轴(长轴和短轴),还可以旋转。这使得椭圆在标注倾斜目标、绘制旋转检测框时非常实用。

函数语法

OpenCV 提供了两种椭圆绘制方式:

# 方式一:指定中心点、轴长、旋转角度和弧度范围
img = cv.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType[, shift]]])

# 方式二:指定旋转矩形 box
img = cv.ellipse(img, box, color[, thickness[, lineType]])

参数说明

参数说明
img输入图像
center椭圆中心点坐标 (x, y)
axes椭圆的两个轴长度 (halfWidth, halfHeight),注意是半轴长
angle椭圆旋转角度(顺时针,单位为度)
startAngle椭圆弧的起始角度(单位为度)
endAngle椭圆弧的终止角度(单位为度)
color颜色,BGR 格式
thickness(可选)轮廓厚度,-1 为填充
lineType(可选)边界类型
shift(可选)坐标位移因子

代码示例

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('MakerOnsite-Logo.png')

# 绘制椭圆:中心 (250, 100),半轴 (20, 50),旋转 35 度,完整弧(0 到 360)
img = cv2.ellipse(img, (250, 100), (20, 50), 35, 0, 360, (0, 200, 200), 10)

plt.imshow(img)
plt.axis('off')
plt.show()

绘制椭圆

画弧线技巧:通过调整 startAngleendAngle,可以只绘制椭圆的一部分弧。比如 startAngle=0, endAngle=180 就只画半个椭圆。这在绘制仪表盘、进度环等可视化效果时很有用。


cv.polylines 绘制多边形

当你需要绘制不规则图形时(比如标注分割区域、绘制任意形状的 ROI),cv.polylines 就是你的选择。它接受一组顶点坐标,并按顺序用线段连接它们。

函数语法

img = cv.polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]])

参数说明

参数说明
img输入图像
pts多边形顶点坐标数组,必须是 int32 类型的 numpy 数组
isClosed是否闭合多边形。True 时最后一个顶点会自动连回第一个顶点
color颜色,BGR 格式
thickness(可选)线段厚度
lineType(可选)线段类型
shift(可选)坐标位移因子

代码示例

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('MakerOnsite-Logo.png')

# 定义多边形的顶点坐标
points = np.array([[50, 50], [50, 400], [400, 400], [450, 150], [350, 50]])

# 绘制多边形(闭合),红色,线宽 5
# 注意:顶点数组需要是 int32 类型
img = cv2.polylines(img, np.int32([points]), 1, (100, 100, 255), 5)

plt.imshow(img)
plt.axis('off')
plt.show()

绘制多边形

重要提醒pts 参数要求的数据类型是 int32,而通过 np.array() 直接创建的数组默认可能是 int64。如果不做转换,虽然某些版本不会报错,但为了兼容性,建议始终使用 np.int32()astype(np.int32) 进行显式转换。另外注意数组的维度——pts 应该是一个形状为 (N, 1, 2) 的三维数组,或者用 np.int32([points]) 来包装。


cv.fillConvexPoly 填充凸多边形

如果你不仅需要画多边形轮廓,还需要用颜色填充它,那么 cv.fillConvexPoly 就是答案。注意,这个函数只支持凸多边形——如果传入的是凹多边形,填充结果会不正确。对于凹多边形,应使用 cv.fillPoly(本文不做展开,可参考 OpenCV 官方文档)。

与 cv.polylines 的区别

对比项cv.polylinescv.fillConvexPoly
功能绘制多边形轮廓填充凸多边形内部
是否填充否(只画边)是(填充整个区域)
是否支持凹多边形否(仅限凸多边形)
是否需要 isClosed需要不需要(自动闭合)
thickness 参数

函数语法

img = cv.fillConvexPoly(img, points, color[, lineType[, shift]])

参数说明

参数说明
img输入图像
points多边形顶点坐标数组
color填充颜色,BGR 格式
lineType(可选)边界类型
shift(可选)坐标位移因子

代码示例

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('MakerOnsite-Logo.png')

# 定义凸多边形顶点
points = np.array([[100, 50], [150, 400], [400, 400], [450, 150], [350, 50]])

# 填充凸多边形
img = cv2.fillConvexPoly(img, points, (100, 100, 100))

plt.imshow(img)
plt.axis('off')
plt.show()

填充多边形


cv.arrowedLine 绘制箭头线段

箭头线段在标注方向、绘制向量场、标注光流等场景中非常实用。它本质上是一条带箭头的线段,箭头的大小可以通过 tipLength 参数控制。

函数语法

img = cv.arrowedLine(img, pt1, pt2, color[, thickness[, line_type[, shift[, tipLength]]]])

参数说明

参数说明
img输入图像
pt1起始点坐标 (x, y)
pt2结束点坐标 (x, y)(箭头指向这里)
color颜色,BGR 格式
thickness(可选)线段厚度
line_type(可选)线段类型
shift(可选)坐标位移因子
tipLength(可选)箭头尖端长度与线段总长度的比值,默认约为 0.1

代码示例

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('MakerOnsite-Logo.png')

# 绘制红色箭头线段,从 (50, 50) 到 (100, 100)
# tipLength=0.3 表示箭头部分占线段总长的 30%
img = cv2.arrowedLine(img, (50, 50), (100, 100), (0, 0, 255), 5, 8, 0, 0.3)

plt.imshow(img)
plt.axis('off')
plt.show()

绘制箭头线段

实用建议tipLength 的默认值比较小,在短线段上可能几乎看不到箭头。如果你的线段较短(比如长度只有几十个像素),建议把 tipLength 设大一些(0.3 ~ 0.5),箭头会更明显。


cv.drawMarker 绘制标记

cv.drawMarker 用于在图像的指定位置绘制各种预定义形状的标记。它比用 cv.circle 画点更灵活,支持十字形、星形、菱形等多种标记类型,非常适合标注特征点、角点、关键点等。

函数语法

img = cv.drawMarker(img, position, color[, markerType[, markerSize[, thickness[, line_type]]]])

参数说明

参数说明
img输入图像
position标记位置坐标 (x, y)
color颜色,BGR 格式
markerType(可选)标记类型,见下表
markerSize(可选)标记大小
thickness(可选)线宽
line_type(可选)线段类型

支持的标记类型

常量说明
cv2.MARKER_CROSS十字形 (+)
cv2.MARKER_TILTED_CROSS倾斜十字形 (x)
cv2.MARKER_STAR星形 (*,十字 + 倾斜十字的组合)
cv2.MARKER_DIAMOND菱形
cv2.MARKER_SQUARE正方形
cv2.MARKER_TRIANGLE_UP上三角形
cv2.MARKER_TRIANGLE_DOWN下三角形

代码示例

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('MakerOnsite-Logo.png')

# 在图像四个角分别绘制不同类型的标记
# 星形标记
img = cv2.drawMarker(img, (50, 50), (0, 255, 255),
                     markerType=cv2.MARKER_STAR, markerSize=20, thickness=3)
# 菱形标记
img = cv2.drawMarker(img, (50, 450), (0, 255, 255),
                     markerType=cv2.MARKER_DIAMOND, markerSize=20, thickness=3)
# 十字标记
img = cv2.drawMarker(img, (450, 450), (0, 255, 255),
                     markerType=cv2.MARKER_CROSS, markerSize=20, thickness=3)
# 倾斜十字标记 (X 形)
img = cv2.drawMarker(img, (450, 50), (0, 255, 255),
                     markerType=cv2.MARKER_TILTED_CROSS, markerSize=20, thickness=3)

plt.imshow(img)
plt.axis('off')
plt.show()

绘制标记


cv.putText 绘制文字

在图像上添加文字标注是调试和展示结果时的常见需求。cv.putText 可以方便地添加英文字符,但不支持中文。绘制中文需要使用 PIL 库作为替代方案。

绘制英文文字

函数语法

img = cv.putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])

参数说明

参数说明
img输入图像
text要绘制的英文字符串
org文字在图像中的坐标,对应文字左下角的位置
fontFace字体类型,如 cv2.FONT_HERSHEY_SIMPLEX
fontScale字体缩放因子,乘以字体基础大小
color文字颜色,BGR 格式
thickness(可选)文字笔画粗细
lineType(可选)线段类型
bottomLeftOrigin(可选)若为 True,坐标原点在左下角;否则在左上角

可用字体类型

  • cv2.FONT_HERSHEY_SIMPLEX — 常规无衬线字体
  • cv2.FONT_HERSHEY_PLAIN — 小号无衬线字体
  • cv2.FONT_HERSHEY_DUPLEX — 常规无衬线字体(更粗)
  • cv2.FONT_HERSHEY_COMPLEX — 带衬线字体
  • cv2.FONT_HERSHEY_TRIPLEX — 带衬线字体(更粗)
  • cv2.FONT_HERSHEY_SCRIPT_SIMPLEX — 手写体
  • cv2.FONT_HERSHEY_SCRIPT_COMPLEX — 手写体(更粗)
  • 以上字体加 cv2.FONT_ITALIC 可获得斜体效果

代码示例

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('MakerOnsite-Logo.png')

# 在图像上添加英文文字
img = cv2.putText(img, "Hello Maker!", (50, 80),
                  cv2.FONT_HERSHEY_SIMPLEX, 2, (200, 100, 90), 5)

plt.imshow(img)
plt.axis('off')
plt.show()

绘制英文文字

绘制中文文字(使用 PIL)

cv.putText 只支持 ASCII 字符,无法绘制中文。解决方案是借助 PIL(Pillow)库。核心思路是:将 OpenCV 的 numpy 数组格式图像转换为 PIL 的 Image 对象,用 PIL 绘制文字,再转换回 numpy 数组。

代码示例

import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont

img = cv2.imread('MakerOnsite-Logo.png')

# 判断是否为 OpenCV 图片类型(numpy.ndarray)
if isinstance(img, np.ndarray):
    # 将 OpenCV 图像(BGR)转换为 PIL Image(RGB)
    img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    # 创建绘图对象
    draw = ImageDraw.Draw(img)

    # 加载字体文件(需要确保字体文件存在)
    # simsun.ttc 是宋体字体文件,可以替换为你系统中的任意中文字体
    fontText = ImageFont.truetype("simsun.ttc", 66, encoding="utf-8")

    # 在图像上绘制中文文字
    draw.text((100, 40), '创客出手!', (255, 100, 200), font=fontText)

    # 转换回 OpenCV 可处理的 numpy 数组类型
    img = np.asarray(img)

plt.imshow(img)
plt.axis('off')
plt.show()

绘制中文文字

注意事项

  1. 需要确保中文字体文件(如 simsun.ttc)存在于工作目录中。Linux 系统常用 /usr/share/fonts/ 下的字体,macOS 可以使用 /System/Library/Fonts/ 下的字体。
  2. 在 PIL 中颜色是 RGB 格式,与 OpenCV 的 BGR 格式不同。转换时需要注意颜色通道的顺序。
  3. 绘制完成后转换回 numpy.ndarray,就可以继续使用 OpenCV 的其他函数或进行深度学习推理。

完整综合示例

下面是一个综合示例,将所有绘图函数组合使用在一张图像上,帮你直观感受各函数的效果:

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 创建一张 500x500 的黑色画布
img = np.zeros((500, 500, 3), dtype=np.uint8)

# 1. 绘制线段:绿色对角线
img = cv2.line(img, (50, 50), (450, 450), (0, 255, 0), 3)

# 2. 绘制矩形:蓝色边框
img = cv2.rectangle(img, (100, 100), (400, 400), (255, 0, 0), 2)

# 3. 绘制实心圆:红色圆点
img = cv2.circle(img, (250, 250), 50, (0, 0, 255), -1)

# 4. 绘制椭圆:黄色椭圆
img = cv2.ellipse(img, (250, 250), (120, 60), 45, 0, 360, (0, 255, 255), 2)

# 5. 绘制多边形:白色三角形
triangle_pts = np.array([[250, 50], [100, 350], [400, 350]])
img = cv2.polylines(img, np.int32([triangle_pts]), True, (255, 255, 255), 2)

# 6. 填充凸多边形:半透明紫色五边形(先在副本上画再叠加)
overlay = img.copy()
poly_pts = np.array([[350, 50], [450, 100], [430, 200], [370, 200], [320, 100]])
cv2.fillConvexPoly(overlay, poly_pts, (200, 0, 200))
img = cv2.addWeighted(overlay, 0.5, img, 0.5, 0)

# 7. 绘制箭头线段:橙色箭头
img = cv2.arrowedLine(img, (50, 450), (200, 300), (0, 165, 255), 2, tipLength=0.15)

# 8. 绘制标记:在多个位置添加不同类型的标记
img = cv2.drawMarker(img, (50, 50), (255, 255, 0),
                     markerType=cv2.MARKER_STAR, markerSize=20, thickness=2)
img = cv2.drawMarker(img, (450, 450), (255, 255, 0),
                     markerType=cv2.MARKER_CROSS, markerSize=20, thickness=2)

# 9. 绘制英文文字
img = cv2.putText(img, "OpenCV Drawing", (120, 490),
                  cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)

# 显示结果
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.title("OpenCV Drawing Functions Combined")
plt.show()

这个综合示例几乎涵盖了所有常用绘图函数,你可以直接运行这段代码来观察效果,然后根据需求修改参数。


参数详解表

下表汇总了所有绘图函数的核心参数,方便查阅对比:

函数关键参数特殊说明
cv.linept1, pt2, color, thickness最基本的线段绘制
cv.rectanglept1(左上), pt2(右下), color, thicknessthickness=-1 填充矩形
cv.circlecenter, radius, color, thicknessthickness=-1 填充圆
cv.ellipsecenter, axes, angle, startAngle, endAngleaxes 是半轴长,角度单位为度
cv.polylinespts, isClosed, color, thicknesspts 需为 int32 数组
cv.fillConvexPolypoints, color仅限凸多边形
cv.arrowedLinept1, pt2, color, thickness, tipLengthtipLength 控制箭头大小
cv.drawMarkerposition, color, markerType, markerSize多种标记类型可选
cv.putTexttext, org, fontFace, fontScale, color仅支持英文,中文需 PIL

常见问题与技巧

1. 颜色格式:BGR 而非 RGB

这是 OpenCV 新手最常踩的坑。OpenCV 使用 BGR(蓝-绿-红)顺序,而不是 RGB。

# 红色 —— 注意是第三个通道为 255
red = (0, 0, 255)

# 绿色
green = (0, 255, 0)

# 蓝色
blue = (255, 0, 0)

如果你发现画出来的颜色和预期不一致,首先检查是否搞混了 BGR 和 RGB。

2. thickness 参数的含义

  • thickness > 0:绘制轮廓线,值为线的像素宽度
  • thickness = -1:绘制填充图形(适用于 rectanglecircleellipse

对于 cv.polylinescv.linethickness = -1 不会报错但也不会有填充效果。需要填充多边形请使用 cv.fillConvexPolycv.fillPoly

3. lineType 的三种选择

常量说明推荐场景
cv2.LINE_44 连接线段速度最快,但锯齿明显
cv2.LINE_88 连接线段(默认)平衡速度与质量
cv2.LINE_AA抗锯齿线段质量最好,适合需要平滑曲线的场景

如果你对绘图质量有较高要求(比如画圆或椭圆),建议使用 cv2.LINE_AA 抗锯齿,效果会好很多:

img = cv2.circle(img, (250, 250), 100, (0, 255, 0), 2, cv2.LINE_AA)

4. shift 参数的用途

shift 参数用于亚像素级精度绘图。当 shift = n 时,你传入的坐标值会被除以 2^n。例如 shift=2 时,坐标 (200, 200) 实际表示 (50, 50)。这在需要高精度绘图(如亚像素级特征点标注)时非常有用。

5. 绘图函数都修改原图

OpenCV 的所有绘图函数都是原地操作(in-place),直接修改传入的 numpy 数组。如果你需要在原图的副本上绘图而不影响原图,请先复制:

img_copy = img.copy()
cv2.rectangle(img_copy, (50, 50), (200, 200), (0, 255, 0), 2)
# img 保持不变,img_copy 被修改

6. 在 Jupyter Notebook 中显示图像

Jupyter 中显示 OpenCV 图像时,建议封装一个辅助函数来处理 BGR 到 RGB 的转换:

def show_img(img):
    """在 Jupyter Notebook 中正确显示 OpenCV 图像"""
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.axis('off')
    plt.show()

总结

本文系统地介绍了 OpenCV Python 中的 2D 绘图函数,从最基础的 cv.line 到功能丰富的 cv.ellipsecv.polylines,再到文字绘制和中文支持方案。回顾一下我们学到的核心内容:

  1. 基础图形cv.line(线段)、cv.rectangle(矩形)、cv.circle(圆/点)是最常用的三个绘图函数
  2. 高级图形cv.ellipse(椭圆/弧线)、cv.polylines(多边形轮廓)、cv.fillConvexPoly(凸多边形填充)
  3. 标记与箭头cv.drawMarker(多种标记类型)、cv.arrowedLine(带箭头线段)
  4. 文字绘制cv.putText 绘制英文,PIL + ImageDraw 绘制中文
  5. 关键参数:颜色用 BGR 格式,thickness=-1 表示填充,lineType 可选抗锯齿

这些绘图函数在目标检测可视化、图像标注、调试信息叠加、UI 原型设计等场景中都有广泛的应用。掌握了它们,你就能在图像处理流程的任意环节自由地添加视觉元素。

建议你把综合示例的代码保存下来,作为日后绘图的模板。遇到具体需求时,直接修改对应参数即可。

更多详细信息可参考 OpenCV 官方绘图模块文档