当前位置: 首页 > news >正文

Python在图片上画多边形:从简单轮廓到复杂区域标注

这是一篇为您定制的技术博客,风格延续了上一篇的实用教程风,重点讲解如何使用 Python 绘制多边形,适合发布在 CSDN、知乎、掘金等技术社区。


在上一篇《Python在图片上画圆形》中,我们掌握了基础的几何绘制。但在实际的计算机视觉任务中,比如图像分割目标检测框选不规则区域标注(如标注车牌、建筑物轮廓),仅仅画圆和矩形是不够的。

多边形(Polygon)才是描述不规则形状的王者。今天,我们就来深入探讨如何使用 Python 的 OpenCV 和 Pillow 库,在图片上绘制任意形状的多边形。


一、 为什么需要画多边形?

  • 数据标注:在制作 AI 数据集时,需要精确框出物体的边缘(Polygon Annotation)。
  • ROI 提取:指定图像中的感兴趣区域(Region of Interest),屏蔽掉无关背景。
  • 视觉特效:在图片上添加水印遮罩、马赛克块(本质也是多边形组合)。

二、 环境准备

确保你已经安装了必要的库:

pipinstallopencv-python pipinstallPillow pipinstallnumpy

准备一张测试图片test_image.jpg


三、 方法一:使用 OpenCV (cv2) —— 精准控制

OpenCV 是处理多边形的首选,因为它提供了非常底层的控制能力。核心函数是cv2.polylines()cv2.fillPoly()

1. 核心难点:点的格式

OpenCV 对多边形顶点的格式要求非常严格:必须是int32类型的 NumPy 数组,且形状为(-1, 1, 2)

2. 基础代码:画一个五边形

importcv2importnumpyasnp# 1. 读取图片image=cv2.imread('test_image.jpg')ifimageisNone:print("图片读取失败")exit()# 2. 定义多边形的顶点坐标 (x, y)# 假设我们要画一个五边形,手动定义5个点points=np.array([[100,300],# 点1[200,200],# 点2[300,300],# 点3[250,400],# 点4[150,400]# 点5],dtype=np.int32)# 3. 重塑数组形状 (这一步非常重要!)# 从 (5, 2) 变为 (5, 1, 2)points=points.reshape((-1,1,2))# 4. 绘制轮廓# 参数:图像, [顶点数组], 是否闭合, 颜色(BGR), 线条粗细cv2.polylines(image,[points],isClosed=True,color=(0,255,255),thickness=3)# 5. 显示与保存cv2.imshow('Polygon (Outline)',image)cv2.imwrite('result_polyline.jpg',image)cv2.waitKey(0)cv2.destroyAllWindows()

3. 参数详解

  • isClosed
    • True:多边形是闭合的(最后一个点会连回第一个点)。
    • False:多边形是开放的(折线)。
  • color:依然是 BGR 格式。(0, 255, 255)是黄色。
  • thickness
    • 正数(如3):线条宽度。
    • 负数(如-1):如果是用cv2.fillPoly或者配合特定参数,表示填充。注意:cv2.polylines不支持直接填充,填充请用下面的方法。

4. 进阶:填充实心多边形

如果你想画一个实心的红色多边形,使用cv2.fillPoly()

# 复制原图以免混淆filled_image=image.copy()# 使用 fillPoly# 注意:这里不需要 reshape 成 (-1, 1, 2),直接传 (N, 2) 也可以,但传标准格式更安全cv2.fillPoly(filled_image,[points],color=(0,0,255))# 红色 (BGR)cv2.imshow('Polygon (Filled)',filled_image)cv2.waitKey(0)

5. 高级技巧:半透明多边形(Alpha 混合)

在做标注时,我们常需要半透明的遮罩。OpenCV 没有直接的 “Alpha” 参数,需要手动混合:

# 创建一个和原图一样大的透明层overlay=image.copy()# 在透明层上画实心多边形cv2.fillPoly(overlay,[points],color=(0,0,255))# 红色# 混合原图和透明层 (原图权重0.7, 覆盖层权重0.3)alpha=0.3cv2.addWeighted(overlay,alpha,image,1-alpha,0,image)cv2.imshow('Transparent Polygon',image)cv2.waitKey(0)

四、 方法二:使用 Pillow (PIL) —— 简单直观

Pillow 的 API 设计更符合人类直觉,不需要复杂的数组重塑。

1. 基础代码示例

fromPILimportImage,ImageDraw# 1. 打开图片image=Image.open('test_image.jpg')draw=ImageDraw.Draw(image)# 2. 定义顶点 (直接是扁平列表 [x1, y1, x2, y2, ...] 或 元组列表)# Pillow 支持 [(x1, y1), (x2, y2), ...] 格式,更友好polygon_points=[(100,300),(200,200),(300,300),(250,400),(150,400)]# 3. 绘制# outline: 边框颜色 (RGB)# fill: 填充颜色 (RGB)# width: 边框宽度draw.polygon(polygon_points,outline=(255,0,0),fill=(0,255,0),width=3)# 4. 显示与保存image.show()image.save('result_pillow_poly.jpg')

2. Pillow 的优势

  • 坐标格式:支持简单的元组列表[(x, y), ...],不需要 Reshape。
  • 颜色格式:使用标准的 RGB,不用记 BGR。
  • 抗锯齿:Pillow 的绘制引擎自带抗锯齿效果,边缘看起来更平滑(OpenCV 需要手动开启抗锯齿,比较麻烦)。

五、 实战案例:不规则区域标注(模拟 LabelMe)

假设我们要在图片上标注出一个不规则的区域,并打上标签。

使用 OpenCV 实现完整流程

importcv2importnumpyasnp# 读取图片img=cv2.imread('test_image.jpg')# 1. 定义不规则区域的顶点 (模拟鼠标点击选取)pts=np.array([[50,50],[400,100],[300,300],[100,350]],np.int32)pts=pts.reshape((-1,1,2))# 2. 画轮廓 (黄色,粗线)cv2.polylines(img,[pts],True,(0,255,255),2)# 3. 计算区域中心点,用于写文字M=cv2.moments(pts)cX=int(M["m10"]/M["m00"])cY=int(M["m01"]/M["m00"])# 4. 写标签文字label="Object A"cv2.putText(img,label,(cX-30,cY),cv2.FONT_HERSHEY_SIMPLEX,0.6,(0,255,255),2)# 5. 显示结果cv2.imshow("Annotation",img)cv2.waitKey(0)cv2.destroyAllWindows()

六、 常见坑与 FAQ

Q1: 运行 OpenCV 代码报错(-2115:Assertion failed)怎么办?
A:90% 的情况是点数组的维度或数据类型不对

  • 检查是否执行了points.reshape((-1, 1, 2))
  • 检查数据类型是否为np.int32
  • 检查传入的是[points](列表包裹数组),而不是直接传points

Q2: 多边形画出来一部分在屏幕外怎么办?
A:OpenCV 和 Pillow 都会自动裁剪(Clip)到图像边界,不会报错,但超出部分看不见。如果需要处理坐标越界,需要自己写逻辑判断if 0 <= x < width

Q3: 如何画带虚线的多边形?
A: 这是一个进阶需求。

  • Pillow:不支持直接画虚线多边形,需要自己循环画线段。
  • OpenCV:可以通过计算线段交点或者使用cv2.line循环绘制每一条边来模拟虚线效果。

Q4: 如何生成多边形的掩码(Mask)?
A: 这是一个非常常用的操作(用于抠图)。

# 创建一张全黑的单通道图mask=np.zeros(img.shape[:2],dtype=np.uint8)# 在掩码上画白色的实心多边形cv2.fillPoly(mask,[points],255)# 使用 mask 提取原图区域result=cv2.bitwise_and(img,img,mask=mask)

七、 总结

特性OpenCV (cv2.polylines)Pillow (ImageDraw.polygon)
易用性⭐⭐ (需处理数组维度)⭐⭐⭐⭐⭐ (非常简单)
性能⭐⭐⭐⭐⭐ (极快)⭐⭐⭐ (够用)
功能支持掩码、抗锯齿(较复杂)、填充支持抗锯齿、简单填充
颜色BGR (易踩坑)RGB (直观)
推荐场景视频处理、实时标注、生成Mask简单图片编辑、生成静态图

画多边形是图像处理从“初级”迈向“中级”的标志。当你能熟练地在图片上绘制任意多边形并生成掩码时,你就已经掌握了图像分割和复杂目标检测的基础工具。

下一篇我们将讲解如何结合鼠标事件,实时交互地在图片上画多边形(像 LabelMe 一样),敬请期待!


如果这篇文章帮你解决了问题,欢迎点赞、关注支持!

http://www.jsqmd.com/news/668172/

相关文章:

  • **发散创新:用Python实现因果推理在推荐系统中的落地应用**在当今数据
  • 【AI面试八股文 Vol.1.1 | 专题4:Conditional Edge】Conditional Edge:动态路由分支逻辑实现
  • SolidWorks参数化设计避坑指南:为什么你的VBA宏跑一次就报错?
  • 家庭网络总断网?可能是你家的路由器接错了!用环路检测功能快速排查
  • Unity Magica Cloth:从入门到精通,打造次世代角色动态服饰
  • 别再只用MD5了!聊聊PBKDF2如何用‘盐’和‘慢炖’保护你的用户密码
  • OpenClaw怎么搭建?2026年4月云端大模型Coding Plan配置指南
  • 如何快速掌握CREST:药物设计中分子构象采样的完整指南
  • NVIDIA Profile Inspector 终极指南:解锁隐藏设置,轻松优化游戏性能
  • 2026年降AI后重新检测还是偏高怎么处理:多轮降AI完整攻略
  • Orwell Dev-C++ 和 Embarcadero Dev-C++ 哪个更好
  • (build/soong/scripts/manifest_check.py --enforce-uses-libraries --enforce-uses-libraries-status
  • 从E·M·福斯特的《英国人性格的笔记》看技术文档写作:如何避免“未发育的心”与“自满的陷阱”
  • 【AI面试临阵磨枪】什么是 Tokenization?子词分词(Subword)的优缺点?
  • 保姆级教程:在CentOS 7上为Zabbix 6.0配置LAMP环境(Apache+MySQL 8.0+PHP 7.4)
  • 别只发GET请求了!ESP32的HTTPClient库POST数据到服务器,保姆级配置流程(含模拟测试)
  • Android Camera HAL层开发指南:深入理解camera3_profiles_rkxxxx.xml的metadata解析机制
  • 在setting菜单里显示的有些字符 不正常,
  • Orwell Dev-C++和Embarcadero Dev-C++哪个更轻量
  • 2026年降AI工具免费版和付费版区别:哪些场景下付费版才值得买
  • 2025届必备的六大AI科研工具横评
  • 从C1815到2N5401:搞懂NPN/PNP在Arduino和STM32控制电路中的选型与接线
  • 001、Git是什么?为什么是开发者的必备技能?
  • 3分钟集成滑块验证组件:为你的Web应用构建智能安全防线
  • Android Studio Layout Inspector 保姆级使用指南:从进程选取到设计图对比,一个功能都不落
  • 2026山东成人高考机构排行榜:Top5深度测评,帮你避开选机构的“坑” - 商业科技观察
  • 2026年降AI工具处理英文论文效果横评:Turnitin达标率对比
  • EPLAN结构标识符高级技巧:如何用表格批量编辑提升效率(附实战案例)
  • 002、Git安装与环境配置全攻略(Windows/macOS/Linux)
  • Python剪映API终极指南:5分钟掌握视频自动化批量处理技巧