概述
在计算机视觉与图像处理中,多边形(Polygon)绘制 是常见的图形操作之一。无论是标注目标区域、生成掩码(mask)、可视化检测结果,还是图像增强中添加人工结构,绘制多边形都是基础功能。
OpenCV 作为功能强大的图像处理库,提供了多种绘制函数,其中包括:
cv2.polylines():绘制多边形边界(可闭合或不闭合);cv2.fillPoly():填充多边形区域;cv2.fillConvexPoly():填充凸多边形;cv2.line():用于手动连接点绘制多边形。
基本原理
OpenCV 中的绘制函数一般基于 像素坐标系,其基本原理如下:
-
点坐标表示
多边形由一系列顶点点集
[(x1, y1), (x2, y2), ..., (xn, yn)]组成。OpenCV 要求坐标点为 NumPy 数组格式:
pythonpts = np.array([[x1, y1], [x2, y2], ..., [xn, yn]], np.int32) -
坐标形状要求
对于
cv2.polylines()或cv2.fillPoly(),需要传入一个列表包裹的数组:pythonpts = [pts] # 转换为形如 [array([...])] 的格式 -
颜色与厚度
- 颜色使用
(B, G, R)格式; - 厚度参数
thickness表示线宽; - 若为填充多边形,厚度参数可忽略。
- 颜色使用
绘制函数详解
cv2.polylines()
用于绘制多边形边界,可选择是否闭合。
python
cv2.polylines(img, pts, isClosed, color, thickness)
- 参数说明 :
img:目标图像;pts:多边形顶点数组;isClosed:布尔值,True 表示闭合多边形;color:边界颜色;thickness:线条厚度。
示例:
python
cv2.polylines(img, [pts], True, (0, 255, 0), 2)
cv2.fillPoly()
填充一个或多个多边形区域。
python
cv2.fillPoly(img, pts, color)
pts为多边形数组列表;- 通常用于生成掩码图、区域高亮等操作。
cv2.fillConvexPoly()
针对凸多边形的高效填充函数,比 fillPoly 速度更快。
cv2.fillConvexPoly(img, pts, color)
实例
多边形绘制与填充
OpenCV 绘制多边形、填充区域并显示结果。
python
import cv2
import numpy as np
# 1. 创建一张空白图像(黑底)
img = np.zeros((480, 640, 3), dtype=np.uint8)
# 2. 定义多边形的顶点
pts = np.array([[100, 150], [200, 50], [300, 150], [250, 300], [150, 300]], np.int32)
pts = pts.reshape((-1, 1, 2)) # 转换为符合 OpenCV 要求的形状
# 3. 绘制多边形边界
cv2.polylines(img, [pts], isClosed=True, color=(0, 255, 0), thickness=3)
# 4. 填充多边形(在另一张图上)
img_fill = img.copy()
cv2.fillPoly(img_fill, [pts], color=(255, 0, 0))
# 5. 同时绘制多个多边形
poly2 = np.array([[400, 100], [500, 50], [550, 150], [450, 200]], np.int32)
cv2.fillPoly(img_fill, [poly2], color=(0, 0, 255))
# 6. 显示结果
cv2.imshow('Polygon Outline', img)
cv2.imshow('Polygon Filled', img_fill)
cv2.waitKey(0)
cv2.destroyAllWindows()
动态绘制(鼠标交互)
使用 cv2.setMouseCallback() 注册鼠标事件,实时绘制多边形顶点,实现类似标注工具的功能。
python
import cv2
import numpy as np
# ==========================
# 全局变量定义
# ==========================
drawing = False # 是否正在绘制
points = [] # 存储多边形顶点
img = np.zeros((600, 800, 3), np.uint8) # 黑色画布
# ==========================
# 鼠标事件回调函数
# ==========================
def draw_polygon(event, x, y, flags, param):
global drawing, points, img
# 左键单击:添加一个顶点
if event == cv2.EVENT_LBUTTONDOWN:
points.append((x, y))
# 绘制一个小圆点表示该顶点
cv2.circle(img, (x, y), 4, (0, 255, 255), -1)
# 如果已有两个以上点,则实时绘制线段连接前后点
if len(points) > 1:
cv2.line(img, points[-2], points[-1], (0, 255, 0), 2)
# 右键单击:闭合多边形并填充
elif event == cv2.EVENT_RBUTTONDOWN:
if len(points) >= 3:
# 生成多边形点数组
pts = np.array(points, np.int32).reshape((-1, 1, 2))
# 绘制闭合边界
cv2.polylines(img, [pts], isClosed=True, color=(0, 255, 0), thickness=2)
# 填充多边形(半透明)
overlay = img.copy()
cv2.fillPoly(overlay, [pts], (255, 0, 0))
alpha = 0.4 # 透明度
cv2.addWeighted(overlay, alpha, img, 1 - alpha, 0, img)
# 绘制完成后清空点集,准备下一次绘制
points.clear()
# ==========================
# 主程序逻辑
# ==========================
def main():
global img
cv2.namedWindow('Draw Polygon')
cv2.setMouseCallback('Draw Polygon', draw_polygon)
print("🖱️ 操作说明:")
print(" - 左键单击:添加多边形顶点")
print(" - 右键单击:闭合并填充多边形")
print(" - 按 ESC 退出")
while True:
cv2.imshow('Draw Polygon', img)
key = cv2.waitKey(1) & 0xFF
if key == 27: # ESC 退出
break
cv2.destroyAllWindows()
# ==========================
# 程序入口
# ==========================
if __name__ == "__main__":
main()
多边形掩码(ROI 提取)
多边形区域常用于生成掩码图,从原图中提取感兴趣区域(ROI):
python
import cv2
import numpy as np
# ==========================
# 1. 读取原始图像
# ==========================
# 你可以替换成自己的图片路径
img = cv2.imread('example.jpg')
if img is None:
# 若未找到图片,则生成一张示例图
img = np.zeros((400, 600, 3), dtype=np.uint8)
cv2.putText(img, "Sample Image", (100, 200), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (200, 200, 200), 3)
h, w = img.shape[:2]
# ==========================
# 2. 定义多边形顶点
# ==========================
# 示例:定义一个四边形区域(可换成任意点)
pts = np.array([[100, 100], [500, 80], [550, 300], [120, 350]], np.int32)
pts = pts.reshape((-1, 1, 2))
# ==========================
# 3. 创建掩码(mask)
# ==========================
mask = np.zeros((h, w), dtype=np.uint8) # 单通道黑图
cv2.fillPoly(mask, [pts], 255) # 在 mask 上填充白色多边形区域
# ==========================
# 4. 提取多边形区域(ROI)
# ==========================
# 方法1:使用 bitwise_and 提取对应区域
roi = cv2.bitwise_and(img, img, mask=mask)
# 方法2(可选):只保留多边形区域并将背景设为白色
white_bg = np.ones_like(img, dtype=np.uint8) * 255
result = np.where(mask[:, :, None] == 255, img, white_bg)
# ==========================
# 5. 可视化显示
# ==========================
# 绘制多边形边界在原图上
img_poly = img.copy()
cv2.polylines(img_poly, [pts], True, (0, 255, 0), 2)
cv2.imshow("Original Image", img_poly)
cv2.imshow("Mask", mask)
cv2.imshow("ROI Extracted", roi)
cv2.imshow("ROI with White BG", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
这种方法常见于目标检测后可视化或图像裁剪任务。
鼠标动态选取多边形 ROI
python
import cv2
import numpy as np
drawing = False
points = []
img = cv2.imread('example.jpg')
if img is None:
img = np.zeros((480, 640, 3), np.uint8)
cv2.putText(img, "Click to Select ROI", (100, 240),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (200, 200, 200), 2)
clone = img.copy()
def mouse_callback(event, x, y, flags, param):
global points, img
if event == cv2.EVENT_LBUTTONDOWN:
points.append((x, y))
cv2.circle(img, (x, y), 4, (0, 255, 255), -1)
if len(points) > 1:
cv2.line(img, points[-2], points[-1], (0, 255, 0), 2)
elif event == cv2.EVENT_RBUTTONDOWN and len(points) >= 3:
pts = np.array(points, np.int32).reshape((-1, 1, 2))
mask = np.zeros(img.shape[:2], np.uint8)
cv2.fillPoly(mask, [pts], 255)
roi = cv2.bitwise_and(clone, clone, mask=mask)
cv2.imshow("Extracted ROI", roi)
points.clear()
cv2.namedWindow("Polygon ROI Selector")
cv2.setMouseCallback("Polygon ROI Selector", mouse_callback)
print("🖱️ 操作指南:")
print(" - 左键:添加多边形顶点")
print(" - 右键:闭合多边形并提取 ROI")
print(" - ESC:退出")
while True:
cv2.imshow("Polygon ROI Selector", img)
key = cv2.waitKey(1) & 0xFF
if key == 27:
break
cv2.destroyAllWindows()
结合透明度绘制
在图像可视化中,半透明填充多边形能更好地与背景融合:
python
import cv2
import numpy as np
# ==========================
# 1. 创建基础图像
# ==========================
# 创建一张白色背景的画布
img = np.ones((480, 640, 3), dtype=np.uint8) * 255
# ==========================
# 2. 定义多边形顶点
# ==========================
polygon1 = np.array([[100, 150], [250, 100], [300, 250], [150, 300]], np.int32)
polygon2 = np.array([[200, 200], [400, 180], [450, 300], [250, 350]], np.int32)
# ==========================
# 3. 创建一个覆盖层(overlay)
# ==========================
overlay = img.copy()
# 在 overlay 上绘制多边形(填充色)
cv2.fillPoly(overlay, [polygon1], color=(0, 0, 255)) # 蓝色多边形
cv2.fillPoly(overlay, [polygon2], color=(0, 255, 0)) # 绿色多边形
# ==========================
# 4. 透明叠加(addWeighted 实现半透明效果)
# ==========================
alpha = 0.4 # 透明度:0~1,值越大越显眼
cv2.addWeighted(overlay, alpha, img, 1 - alpha, 0, img)
# ==========================
# 5. 绘制边框与文字标注
# ==========================
cv2.polylines(img, [polygon1], True, (0, 0, 200), 2, cv2.LINE_AA)
cv2.polylines(img, [polygon2], True, (0, 150, 0), 2, cv2.LINE_AA)
cv2.putText(img, "Transparent Polygon Example", (100, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1, (80, 80, 80), 2)
# ==========================
# 6. 显示结果
# ==========================
cv2.imshow("Transparent Polygon Overlay", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
多边形边界平滑与抗锯齿
OpenCV 默认绘制可能出现锯齿,可使用抗锯齿模式(适用于线段):
python
import cv2
import numpy as np
# ==========================
# 1. 创建画布
# ==========================
h, w = 480, 640
img_no_aa = np.ones((h, w, 3), np.uint8) * 255 # 无抗锯齿图像
img_aa = img_no_aa.copy() # 抗锯齿图像
# ==========================
# 2. 定义多边形顶点
# ==========================
polygon = np.array([[100, 100], [250, 80], [350, 200], [300, 350], [150, 300]], np.int32)
polygon = polygon.reshape((-1, 1, 2))
# ==========================
# 3. 绘制无抗锯齿的多边形边界
# ==========================
cv2.polylines(img_no_aa, [polygon], isClosed=True, color=(0, 0, 255), thickness=3, lineType=cv2.LINE_8)
# ==========================
# 4. 绘制抗锯齿多边形边界
# ==========================
cv2.polylines(img_aa, [polygon], isClosed=True, color=(0, 0, 255), thickness=3, lineType=cv2.LINE_AA)
# ==========================
# 5. 填充多边形并叠加边界(增强视觉对比)
# ==========================
overlay_no_aa = img_no_aa.copy()
overlay_aa = img_aa.copy()
cv2.fillPoly(overlay_no_aa, [polygon], (255, 200, 200))
cv2.fillPoly(overlay_aa, [polygon], (255, 200, 200))
cv2.addWeighted(overlay_no_aa, 0.5, img_no_aa, 0.5, 0, img_no_aa)
cv2.addWeighted(overlay_aa, 0.5, img_aa, 0.5, 0, img_aa)
# ==========================
# 6. 显示结果对比
# ==========================
cv2.putText(img_no_aa, "Without Anti-Aliasing", (80, 50),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (60, 60, 60), 2)
cv2.putText(img_aa, "With Anti-Aliasing (cv2.LINE_AA)", (50, 50),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (60, 60, 60), 2)
combined = np.hstack((img_no_aa, img_aa)) # 左右拼接对比
cv2.imshow("Polygon Edge Smoothing Comparison", combined)
cv2.waitKey(0)
cv2.destroyAllWindows()
目标区域高亮
python
import cv2
import numpy as np
# 读取图像
img = cv2.imread('scene.jpg')
# 定义目标区域(四边形)
polygon = np.array([[120, 220], [350, 200], [400, 400], [150, 420]], np.int32)
polygon = polygon.reshape((-1, 1, 2))
# 绘制半透明高亮区
overlay = img.copy()
cv2.fillPoly(overlay, [polygon], (0, 255, 0))
cv2.addWeighted(overlay, 0.4, img, 0.6, 0, img)
# 绘制边框
cv2.polylines(img, [polygon], True, (0, 200, 0), 2, cv2.LINE_AA)
cv2.imshow('Highlighted ROI', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
常见问题与优化建议
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 多边形不显示 | 坐标未转为 int32 或格式错误 | 使用 np.array(..., np.int32).reshape((-1,1,2)) |
| 线条断裂或重叠 | 顶点顺序不连续或未闭合 | 检查点顺序并设置 isClosed=True |
| 填充失败 | 点坐标格式不匹配 | 确保传入 [pts] 而非 pts |
| 显示颜色错误 | 使用了 RGB 而非 BGR | 调整颜色通道顺序 |
| 绘制效率低 | 填充多个多边形时循环调用 | 将所有多边形合并传入一次性绘制 |