Python-OpenCV:画布、图形、文字、调色滑动条

目录

一、绘图前置基础:画布如何创建

[1. 画布本质](#1. 画布本质)

[2. 颜色规则](#2. 颜色规则)

二、基础线条绘制:直线

三、矩形绘制

四、圆形绘制

五、椭圆绘制

六、多边形绘制

七、画布写入文字

八、综合交互:RGB动态调色滑块

九、绘图通用参数对照表

十、绘图高频踩坑点

[1. 画布 shape 顺序:高度在前,宽度在后](#1. 画布 shape 顺序:高度在前,宽度在后)

[2. 颜色顺序是 BGR,不是 RGB](#2. 颜色顺序是 BGR,不是 RGB)

[3. 坐标系统:x 是水平方向,y 是垂直方向](#3. 坐标系统:x 是水平方向,y 是垂直方向)

[4. cv2.waitKey(0) 在循环中不能填 0](#4. cv2.waitKey(0) 在循环中不能填 0)

[5. 滑动条回调函数不能省略或删除](#5. 滑动条回调函数不能省略或删除)

[6. 多边形顶点数组需要外层再加 \[\]](#6. 多边形顶点数组需要外层再加 [])

[7. cv2.putText 原生不支持中文](#7. cv2.putText 原生不支持中文)

[8. 椭圆角度单位是角度制,不是弧度](#8. 椭圆角度单位是角度制,不是弧度)

[9. cv2.polylines 不会填充多边形](#9. cv2.polylines 不会填充多边形)

十一、本章小结


在上一章中,我们完成了 OpenCV 的基础入门,掌握了图片的读取与写入、缩放旋转、摄像头拍摄与录像等图像基础操作。这些技能让我们能够熟练地处理已有的图像数据。然而,图像处理不仅仅是"处理",更是"创造"。

本章,我们将脱离对本地照片的依赖,从一张空白的画布开始,系统地学习 OpenCV 强大的绘图 API。我们将从最基础的直线、矩形、圆形、椭圆、多边形绘制,到在画布上写入文字,最后通过一个综合案例------结合滑动条(Trackbar)实现一个可实时调整的 RGB 调色面板,将理论与实践紧密结合。

一、绘图前置基础:画布如何创建

1. 画布本质

画布就是numpy创建的像素矩阵,格式np.zeros(高度, 宽度, 通道数, 数据类型)

  • 通道=3:BGR彩色画布,蓝、绿、红三色通道
  • 通道=1:灰度黑白画布
  • np.uint8:像素取值范围0~255,图像标准数据类型

2. 颜色规则

OpenCV颜色书写格式为(B, G, R),三个数值范围0~255,数值越大对应颜色越亮。

二、基础线条绘制:直线

函数语法

cv2.line(画布, 起点(x,y), 终点(x,y), 颜色(BGR), 线宽, 线型)

下表详细列出了 cv2.line 函数的各个参数:

参数名 数据类型 默认值 作用说明 示例值
画布 (img) numpy.ndarray 要在其上绘制直线的图像矩阵,通常由 np.zeros()cv2.imread() 创建。 img(一个形状为 (800,800,3) 的数组)
起点 (pt1) tuple (int, int) 直线的起始点坐标,格式为 (x, y),其中 x 为水平方向(从左到右),y 为垂直方向(从上到下)。 (0, 0)
终点 (pt2) tuple (int, int) 直线的结束点坐标,格式同样为 (x, y) (200, 200)
颜色 (color) tuple (int, int, int) 线条的 BGR 颜色值,每个分量取值范围 0~255。例如 (255, 0, 255) 表示紫色(B=255, G=0, R=255)。 (255, 0, 255)
线宽 (thickness) int 1 线条的粗细(以像素为单位)。必须为正整数。值越大线条越粗。 5
线型 (lineType) int 8 控制线条的绘制算法和连接方式,影响线条的平滑度和抗锯齿效果。常用值: * 44-连通线型。使用 Bresenham 算法,只允许在水平、垂直或对角线方向(45度)绘制像素。线条可能出现锯齿,但绘制速度快。 * 88-连通线型(默认)。使用 Bresenham 算法,允许在水平、垂直以及对角线(45度)方向绘制像素,线条相对更平滑。 * cv2.LINE_AA抗锯齿线型。使用高斯滤波进行抗锯齿处理,线条边缘最平滑,但绘制速度稍慢。通常用于需要高质量显示的图形。 在代码中,cv2.LINE_AA 的值通常为 16。 4, 8, cv2.LINE_AA
shift (可选) int 0 坐标和半径值中的小数位数。例如,shift=1 表示坐标值可以精确到 0.5 像素(即坐标值乘以 2shift)。通常保持默认值 0。 0

关于线型参数的进一步说明:

  • 4-连通 (lineType=4):在绘制斜线时,只能沿严格的对角线方向(45度)移动,因此线条可能看起来有"阶梯"感,不够平滑。适用于对性能要求高、对视觉效果要求不高的场景。
  • 8-连通 (lineType=8):允许更灵活的像素连接方式,斜线绘制更平滑,是大多数情况下的推荐选择。
  • 抗锯齿 (lineType=cv2.LINE_AA):通过插值算法使线条边缘过渡平滑,消除锯齿。在需要高质量显示(如文字、精细图形)时使用。

在示例代码中,第一条线使用 lineType=4,第二条线使用 lineType=8,你可以运行代码观察两者视觉上的细微差别。

python 复制代码
# opencv直线.py
import cv2
import numpy as np
# 创建800×800黑色三通道画布
img = np.zeros(shape = (800,800,3), dtype = np.uint8)
# 第一条线:起点(0,0),终点(200,200),紫粉色,线宽5,4线型
cv2.line(img, (0,0), (200,200), (255, 0, 255),5,4)
# 第二条线:起点(0,200),终点(200,0),浅紫,线宽10,8线型
cv2.line(img, (0,200), (200,0), (255, 100, 255),10,8)
# 展示画布
cv2.imshow('line', img)
# 循环监听q退出
while 1:
    key = cv2.waitKey(10)
    if key == ord('q'):
        break
cv2.destroyAllWindows()

三、矩形绘制

函数语法

cv2.rectangle(画布, 左上角坐标, 右下角坐标, 颜色, 线宽)

线宽填-1代表填充内部,大于0仅绘制边框

python 复制代码
# opencv绘制矩形圆形.py
import cv2
import numpy as np
# 512×512空白画布
img = np.zeros((512,512,3), np.uint8)
# 绘制矩形:左上(100,100) 右下(300,300),紫色边框,线宽5
cv2.rectangle(img, (100,100), (300,300), (255, 0, 255),5,4)
# 绘制圆形:圆心(300,300),半径100,紫色,线宽5
cv2.circle(img, (300,300), 100, (255, 0, 255),5,4)
cv2.imshow('img', img)
while 1:
    key = cv2.waitKey(10)
    if key == ord('q'):
        break
cv2.destroyAllWindows()

四、圆形绘制

函数语法

cv2.circle(画布, 圆心(x,y), 半径, 颜色, 线宽)

线宽-1直接填充整圆。

五、椭圆绘制

函数语法

cv2.ellipse(画布, 中心点, (半长轴,半短轴), 旋转角度, 起始角, 结束角, 颜色, 线宽)

python 复制代码
# opencv绘制椭圆.py
import cv2
import numpy as np
img = np.zeros((512,512,3), np.uint8)
# 中心点(200,200),长半轴100,短半轴50,无旋转,完整360度椭圆
cv2.ellipse(img, (200,200), (100,50), 0, 0, 360, (255, 0, 255), 5, 4)
cv2.imshow('img', img)
while 1:
    key = cv2.waitKey(10)
    if key == ord('q'):
        break
cv2.destroyAllWindows()

六、多边形绘制

函数说明

cv2.polylines(画布, [顶点数组], 是否闭合, 颜色, 线宽)

顶点必须用np.int32类型,外层加[]包裹;fillPoly实现填充多边形

python 复制代码
# opencv绘制多边形.py
import cv2
import numpy as np
img = np.zeros((512,512,3), np.uint8)
# 定义四个顶点坐标
pts = np.array([(10,5),(200,70),(54,200),(500,483)], np.int32)
# 绘制闭合多边形,蓝色,线宽3,使用抗锯齿线型
cv2.polylines(img, [pts], True, (255,0,0), 3, cv2.LINE_AA)
# 取消注释可填充多边形
# cv2.fillPoly(img, [pts], (0,255,0))
cv2.imshow('img', img)
while 1:
    key = cv2.waitKey(10)
    if key == ord('q'):
        break
cv2.destroyAllWindows()

七、画布写入文字

函数语法

cv2.putText(画布, 文字内容, 文字左下角坐标, 字体, 字号, 颜色, 线宽, lineType, bottomLeftOrigin)

python 复制代码
# opencv绘制文字.py
import cv2
import numpy as np
img = np.zeros((520,520,3), np.uint8)
# 在坐标(10,500)写入文字,基础字体,字号1,紫色
cv2.putText(img, 'Hello World', (10, 500), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 2, cv2.LINE_AA,False)
cv2.imshow('img', img)
while 1:
    key = cv2.waitKey(10)
    if key == ord('q'):
        break
cv2.destroyAllWindows()

八、综合交互:RGB动态调色滑块

核心知识点

  1. createTrackbar 创建滑动条,绑定回调函数
  2. getTrackbarPos 获取滑块实时数值
  3. 实时修改画布全部像素BGR数值,实现变色效果
python 复制代码
# opencv调色块.py
import cv2
import numpy as np
# 空回调函数,滑动条变动时触发
def callback(val):
    pass
# 创建可缩放窗口
cv2.namedWindow('trackbar',cv2.WINDOW_NORMAL)
cv2.resizeWindow('trackbar',680,480)
# 创建R、G、B三条滑动条,取值范围0~255
cv2.createTrackbar('R','trackbar',0,255,callback)
cv2.createTrackbar('G','trackbar',0,255,callback)
cv2.createTrackbar('B','trackbar',0,255,callback)
# 创建480行680列三通道黑色画布
img = np.zeros(shape = (480,680,3) , dtype = np.uint8)
# 循环实时读取滑块数值,更新画布颜色
while 1:
    # 获取三个通道滑块值
    r = cv2.getTrackbarPos('R','trackbar')
    g = cv2.getTrackbarPos('G','trackbar')
    b = cv2.getTrackbarPos('B','trackbar')
    # 整张画布统一填充BGR颜色
    img[:] = [b,g,r]
    cv2.imshow('trackbar',img)
    key = cv2.waitKey(10) # 循环不能填0,画面实时刷新
    if key == ord('q'):
        break
cv2.destroyAllWindows()

九、绘图通用参数对照表

绘图函数 必传核心参数 填充规则
cv2.line 起点、终点、颜色、线宽 仅线条,无填充
cv2.rectangle 对角两点、颜色、线宽 thickness=-1 内部填充
cv2.circle 圆心、半径、颜色、线宽 thickness=-1 实心圆
cv2.ellipse 中心、轴长、角度、颜色 可绘制完整/部分圆弧
cv2.polylines 顶点数组、闭合开关 fillPoly专门用来实现内部填充

十、绘图高频踩坑点

OpenCV 绘图功能虽然强大,但新手在使用时常常会遇到一些看似简单却容易卡住的细节问题。本节汇总了从画布创建到图形绘制、交互操作中最常见的 9 个踩坑点,帮你快速避坑。

1. 画布 shape 顺序:高度在前,宽度在后

创建画布时,np.zeros() 的第一个参数是 (高度, 宽度, 通道数),而不是 (宽度, 高度, 通道数)。这与图像读取后得到的数组维度一致,但容易与日常的"宽×高"习惯混淆。

python 复制代码
# 正确:高度在前,宽度在后
img = np.zeros((480, 640, 3), dtype=np.uint8)  # 480行(高)× 640列(宽)× 3通道
错误:宽度在前,高度在后(会导致后续坐标计算混乱)
img_wrong = np.zeros((640, 480, 3), dtype=np.uint8)

2. 颜色顺序是 BGR,不是 RGB

OpenCV 默认使用 BGR 颜色通道顺序,这与大多数图像处理库(如 Matplotlib、PIL)的 RGB 顺序相反。直接使用 RGB 三元组会导致颜色显示异常。

python 复制代码
# 正确:BGR 顺序
color_bgr = (255, 0, 0)      # 蓝色(B=255, G=0, R=0)
color_green = (0, 255, 0)    # 绿色
color_red = (0, 0, 255)      # 红色
错误:RGB 顺序(在 OpenCV 中会显示为红、绿、蓝错乱)
color_wrong = (255, 0, 0)    # 在 RGB 中是红色,在 OpenCV 中显示为蓝色

3. 坐标系统:x 是水平方向,y 是垂直方向

OpenCV 使用笛卡尔坐标系,但原点在左上角。坐标表示为 (x, y),其中 x 是水平方向(从左到右),y 是垂直方向(从上到下)。这与数学中常见的 (行, 列)(y, x) 顺序不同。

python 复制代码
# 正确:(x, y) 顺序
cv2.circle(img, (100, 200), 30, (0, 255, 0), -1)  # 圆心在 x=100, y=200 处
错误:颠倒顺序(可能导致图形绘制在画布外)
cv2.circle(img, (200, 100), 30, (0, 255, 0), -1)  # 如果画布尺寸较小,可能超出边界

4. cv2.waitKey(0) 在循环中不能填 0

在实时交互程序(如滑动条调色板)中,cv2.waitKey() 的参数表示等待时间(毫秒)。如果填 0,程序会无限等待用户按键,导致画面无法实时刷新。

python 复制代码
# 正确:在循环中使用非零值,如 10 毫秒
while True:
    cv2.imshow('window', img)
    key = cv2.waitKey(10)  # 等待 10 毫秒,然后继续循环
    if key == ord('q'):
        break
错误:在循环中填 0,画面会卡住不动
while True:
    cv2.imshow('window', img)
    key = cv2.waitKey(0)   # 程序停在这里,直到用户按键
    if key == ord('q'):
        break

5. 滑动条回调函数不能省略或删除

cv2.createTrackbar() 的最后一个参数是回调函数,即使不需要执行任何操作,也必须传入一个空函数。如果传入 None 或省略,滑动条创建会失败。

python 复制代码
# 正确:定义空回调函数
def nothing(x):
    pass
cv2.createTrackbar('R', 'window', 0, 255, nothing)
错误:省略回调函数参数
cv2.createTrackbar('R', 'window', 0, 255)  # 会报错

6. 多边形顶点数组需要外层再加 \[\]

cv2.polylines()cv2.fillPoly() 要求顶点数组外层用方括号包裹,即使只有一个多边形也要写成 [pts] 形式。

python 复制代码
import numpy as np
正确:外层加 []
pts = np.array([(10, 20), (30, 40), (50, 60)], np.int32)
cv2.polylines(img, [pts], True, (255, 0, 0), 2)  # 注意 [pts]
cv2.fillPoly(img, [pts], (0, 255, 0))           # 同样需要 [pts]
错误:直接传入 pts
cv2.polylines(img, pts, True, (255, 0, 0), 2)   # 会报错

7. cv2.putText 原生不支持中文

OpenCV 的 cv2.putText 函数默认只支持 ASCII 字符集,直接写入中文字符会显示为乱码或方块。这是因为 OpenCV 底层使用的字体库不包含中文字形。

解决方案:

  • 使用 PIL (Pillow) 库配合 OpenCV:先用 PIL 绘制中文文字到图像,再转换为 OpenCV 格式
  • 使用支持中文的字体文件(如微软雅黑、宋体等)
python 复制代码
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
创建空白画布
img = np.zeros((200, 400, 3), dtype=np.uint8)
img.fill(255)  # 白色背景
转换为 PIL 图像
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img_pil)
加载中文字体(需要指定字体文件路径)
font = ImageFont.truetype("msyh.ttc", 20)  # 微软雅黑字体
绘制中文
draw.text((50, 50), "你好,OpenCV!", font=font, fill=(0, 0, 255))
转换回 OpenCV 格式
img_cv = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
cv2.imshow("Chinese Text", img_cv)
cv2.waitKey(0)
cv2.destroyAllWindows()

8. 椭圆角度单位是角度制,不是弧度

cv2.ellipse 函数的 angle 参数(旋转角度)使用的是角度制,而不是弧度制。很多初学者误以为和数学中的弧度制相同,导致椭圆方向错误。

关键点:

  • angle:椭圆绕中心点顺时针旋转的角度,单位是(0-360)
  • startAngleendAngle:椭圆弧的起始和结束角度,同样是为单位
  • 0 度指向水平向右(x 轴正方向)
python 复制代码
import cv2
import numpy as np
img = np.zeros((400, 400, 3), dtype=np.uint8)
绘制一个旋转45度的椭圆
angle=45 表示顺时针旋转45度,0度指向水平向右
cv2.ellipse(img, (200, 200), (100, 50), 45, 0, 360, (0, 255, 0), 2)
绘制参考线:水平向右的红色箭头
cv2.arrowedLine(img, (200, 200), (300, 200), (0, 0, 255), 2)
cv2.imshow("Ellipse Angle Demo", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

9. cv2.polylines 不会填充多边形

cv2.polylines 函数只绘制多边形的轮廓 ,不会填充内部。很多新手误以为通过设置 thickness 参数或闭合标志就能实现填充,实际上必须单独调用 cv2.fillPoly 函数。

正确用法:

  • cv2.polylines:绘制多边形轮廓(边框)
  • cv2.fillPoly:填充多边形内部
  • 可以同时使用两个函数实现带边框的填充多边形
python 复制代码
import cv2
import numpy as np
img = np.zeros((400, 400, 3), dtype=np.uint8)
定义三角形顶点
pts = np.array([(100, 100), (300, 100), (200, 300)], np.int32)
错误:以为 polylines 能填充
cv2.polylines(img, [pts], True, (0, 255, 0), -1)  # -1 不会填充!
正确:先填充内部,再绘制边框
cv2.fillPoly(img, [pts], (0, 255, 0))  # 绿色填充
cv2.polylines(img, [pts], True, (0, 0, 255), 3)  # 红色边框,线宽3
cv2.imshow("Polygon Fill Demo", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

总结: 掌握这 9 个高频踩坑点,能帮你节省大量调试时间。记住:画布尺寸是(高, 宽)、颜色用 BGR 顺序、坐标是 (x, y)、循环中 waitKey 不能填 0、滑动条回调不能省、多边形顶点要加 \[\]、中文显示需额外处理、椭圆角度用度不用弧度、多边形填充必须用 fillPoly

十一、本章小结

本章我们从一张空白画布出发,系统掌握了 OpenCV 绘图的核心 API:直线、矩形、圆形、椭圆、多边形,以及画布文字写入,最后通过 RGB 动态调色滑块把绘图与交互结合起来。回顾一下,画布本质是 np.zeros 创建的像素矩阵,尺寸顺序是(高, 宽, 通道数);颜色一律使用 BGR 顺序;坐标是 (x, y),原点在左上角。绘制时,thickness=-1 表示填充,椭圆角度用角度制,多边形顶点要加 []cv2.putText 原生不支持中文,循环里 waitKey 不能填 0,滑动条回调不能省略,填充多边形必须用 fillPoly。这些细节看似琐碎,却是写出稳定绘图程序的关键。

学会了在画布上绘制各种图形,下一步就是让画面真正"动"起来。下一章我们将进入 OpenCV 的视频处理与实时视觉应用,从摄像头视频流的读取与保存,到帧率控制、画面翻转与多窗口显示,一步步搭建属于你自己的实时视觉小工具。

相关推荐
Promising_GEO1 小时前
新电脑科研环境配置指南:Miniforge + PyCharm + GitHub 从安装到可用
ide·python·pycharm·github·地理
空堂与归1 小时前
机器学习如何入门?AI/ML/DL概念与建模流程全景
人工智能·机器学习
Patrick在香港1 小时前
Claude API 成本直降90%:Prompt Caching 提示词缓存 Python 实战
python·缓存·prompt
没落之王1 小时前
易学使者郑氏正脉郑冰推演马航事件
大数据·人工智能·算法·机器学习·可用性测试
郝学胜-神的一滴2 小时前
并查集深度入门:从玄学抽象到 QuickFind & QuickUnion 源码实战
数据结构·c++·python·程序人生·算法·软件开发
她说可以呀2 小时前
Spring AI 常用 Advisor
人工智能·python·spring
小玮看世界2 小时前
[Python] str() 和 join() 的区别与实战避坑指南
前端·javascript·python
卷无止境2 小时前
FastAPI 的 WebSocket 装了些什么
后端·python·fastapi
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章103-空车位识别
人工智能·opencv·计算机视觉