OpenCV(十七):绘制多边形

概述

在计算机视觉与图像处理中,多边形(Polygon)绘制 是常见的图形操作之一。无论是标注目标区域、生成掩码(mask)、可视化检测结果,还是图像增强中添加人工结构,绘制多边形都是基础功能。

OpenCV 作为功能强大的图像处理库,提供了多种绘制函数,其中包括:

  • cv2.polylines():绘制多边形边界(可闭合或不闭合);
  • cv2.fillPoly():填充多边形区域;
  • cv2.fillConvexPoly():填充凸多边形
  • cv2.line():用于手动连接点绘制多边形。

基本原理

OpenCV 中的绘制函数一般基于 像素坐标系,其基本原理如下:

  1. 点坐标表示

    多边形由一系列顶点点集 [(x1, y1), (x2, y2), ..., (xn, yn)] 组成。

    OpenCV 要求坐标点为 NumPy 数组格式:

    python 复制代码
    pts = np.array([[x1, y1], [x2, y2], ..., [xn, yn]], np.int32)
  2. 坐标形状要求

    对于 cv2.polylines()cv2.fillPoly(),需要传入一个列表包裹的数组:

    python 复制代码
    pts = [pts]  # 转换为形如 [array([...])] 的格式
  3. 颜色与厚度

    • 颜色使用 (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 调整颜色通道顺序
绘制效率低 填充多个多边形时循环调用 将所有多边形合并传入一次性绘制
相关推荐
却道天凉_好个秋8 小时前
OpenCV(十八):绘制文本
人工智能·opencv·计算机视觉
格林威11 小时前
AOI在FPC制造领域的检测应用
人工智能·数码相机·计算机视觉·目标跟踪·视觉检测·制造
CoookeCola14 小时前
MovieNet (paper) :推动电影理解研究的综合数据集与基准
数据库·论文阅读·人工智能·计算机视觉·视觉检测·database
CoovallyAIHub14 小时前
视觉语言模型(VLM)深度解析:如何用它来处理文档?
深度学习·算法·计算机视觉
CoovallyAIHub14 小时前
估值百亿独角兽创始人硕士论文曝光!宇树科技王兴兴的“性价比”思维10年前就已注定
深度学习·算法·计算机视觉
DogDaoDao15 小时前
OpenCV音视频编解码器详解
人工智能·opencv·音视频·视频编解码·h264·h265·音视频编解码
sponge'18 小时前
opencv学习笔记8:haar特征、决策树、adaboost初步认识
笔记·opencv·学习
leafff12321 小时前
新手入坑 Stable Diffusion:模型、LoRA、硬件一篇讲透
人工智能·计算机视觉·stable diffusion
格林威1 天前
AOI在产品质量检测制造领域的应用
人工智能·数码相机·计算机网络·计算机视觉·目标跟踪·视觉检测·制造