【制作图片中的多边形mask】

这篇文章描述了一个Python程序,该程序利用OpenCV库实现鼠标交互功能,用户可以通过双击鼠标获取图片上的点坐标,按照顺时针顺序记录这些点以构建多边形。然后,程序创建一个mask,并在mask上填充多边形,最后通过与原图进行按位与操作生成目标mask图像。

鼠标获取图片上面的点

双击即可获取点的坐标,注意顺时针方向记录点的位置,不然会画出奇怪的多边形

import cv2
img = cv2.imread(r'xxxx.jpg')#读取图片
font_face,font_scale,thickness=cv2.FONT_HERSHEY_SIMPLEX,0.5,1
#鼠标交互
def mouseHandler(event,x,y,flags,param):
    points = (x,y)
    global imgCopy
    #鼠标左键双击事件
    if event == cv2.EVENT_LBUTTONDBLCLK:
    	#输出坐标
        print('('+str(x)+','+str(y)+')')
        #拷贝一张与原图像格式相同的新图像
        imgCopy = img.copy()
        cv2.imshow('win',imgCopy)
    #鼠标移动事件
    elif event == cv2.EVENT_MOUSEMOVE:
    	#显示原图片能使文本框消失
        cv2.imshow('win',img)

cv2.namedWindow('win')
#窗口与回调函数绑定
cv2.setMouseCallback('win',mouseHandler)
cv2.imshow('win',img)
cv2.waitKey()
# [67,476]  578 235  775 473 280 235
# 280 235  579 235 65 475  775 475

记录对应的点用于后面绘制多边形和生成mask

生成对应的mask图片

# -*- coding: utf-8 -*-

import cv2
import numpy as np

# 读取图像
img_name = r'xxx.jpg'
img = cv2.imread(img_name)

# 显示图像
# cv2.imshow('image', img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

# 定义多边形的顶点坐标
# 顺序是从左上到右下,顺时针方向
pts = np.array([[0,583], [0,566], [517,246], [1280,305], [1280,720]], np.int32)

# 创建一个空白的黑色图像
mask = np.zeros_like(img)

# 在空白图像上绘制多边形,填充为白色
cv2.fillPoly(mask, [pts], (255, 255, 255))

# 将原图像与mask图像进行按位与操作,得到最终的mask图像
# masked_img = cv2.bitwise_and(img, mask)

# 显示最终的mask图像
cv2.imshow('mask', mask)
cv2.imwrite(img_name[:-4]+'_target.jpg',mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
<think>根据用户需求,用户想要在Unity中创建多边形遮罩的方法。结合引用内容,我们知道有两种实现方式:一种是类似于Unity内置Mask组件的实现方式,但效率更高;另一种是使用Canvas和UI元素来创建遮罩效果。 首先,我们需要明确多边形遮罩的基本原理:通过一个多边形的轮廓来限制子对象的可见区域。在Unity中,常见的实现方式有: 1. 使用内置的Mask组件(但只支持矩形) 2. 使用Image组件的FillMethod(但只支持简单形状) 3. 自定义Shader实现任意多边形遮罩 由于用户需要多边形(可能是任意多边形),我们通常采用自定义Shader的方式。 步骤: 1. 创建遮罩材质:编写一个Shader,使用模板测试(Stencil)来定义遮罩区域。 2. 创建遮罩游戏对象:使用一个多边形(比如Mesh)作为遮罩物体,并应用遮罩材质。 3. 被遮罩的对象:需要设置材质,使其只在遮罩区域内显示。 具体实现: ### 方法一:使用模板缓冲(Stencil Buffer) 这是一种高效的方式,通过设置模板缓冲的值来标记遮罩区域,然后被遮罩的物体只在模板值匹配的区域绘制。 #### 1. 遮罩Shader(用于遮罩物体) ```glsl Shader "Custom/PolygonMask" { SubShader { Tags { "Queue"="Transparent" } ColorMask 0 // 不写入颜色 ZWrite Off // 不写入深度 Stencil { Ref 1 Comp Always Pass Replace } Pass { // 不需要实际渲染颜色,所以用空片段着色器 CGPROGRAM #pragma vertex vert #pragma fragment frag struct appdata { float4 vertex : POSITION; }; struct v2f { float4 pos : SV_POSITION; }; v2f vert(appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); return o; } half4 frag(v2f i) : COLOR { return half4(0,0,0,0); } ENDCG } } } ``` #### 2. 被遮罩物体的Shader 在原有Shader基础上添加模板测试,只绘制在模板值为1的区域。 ```glsl Shader "Custom/MaskedObject" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "Queue"="Transparent" } // 模板测试设置:只当模板值为1时绘制 Stencil { Ref 1 Comp Equal } // ... 其他原有Shader代码 } } ``` #### 3. 在Unity中的使用 - 创建一个空物体,添加MeshFilter和MeshRenderer,并赋予一个多边形网格(可以是任意形状),然后使用上面的遮罩材质。 - 将被遮罩的物体放在遮罩物体的子层级(或者使用同一层级的渲染顺序),并确保被遮罩物体使用修改后的Shader(即添加了模板测试的Shader)。 ### 方法二:使用Unity的UI系统(针对UI元素) 如果遮罩用于UI,可以使用Unity的Mask组件,但它只支持矩形。对于多边形遮罩,我们可以使用Image组件的可填充类型(如Radial)来近似一些简单多边形,或者使用自定义的UI Shader。 #### 使用自定义UI Shader实现多边形遮罩 1. 创建一个Image,设置Source Image为一张透明图片(或者使用Sprite Editor编辑多边形轮廓)。 2. 为这个Image编写一个Shader,使用Alpha Test来裁剪像素。 但是,这种方法对于任意多边形不够灵活。因此,更通用的方法还是使用模板缓冲(方法一),并且可以同时用于3D物体和UI。 ### 性能对比 引用[^1]中提到,该实现效率优于Unity内置Mask。这是因为Unity内置Mask会生成一个遮罩用的Mesh,而使用模板缓冲的方法不需要额外的网格,只需要在GPU上进行模板测试,性能更好。 ### 教程步骤总结 1. 创建遮罩材质(使用模板缓冲写入的Shader)。 2. 创建一个多边形网格物体,应用遮罩材质。 3. 为需要被遮罩的物体材质添加模板测试(Comp Equal,Ref 1)。 4. 调整渲染顺序,确保遮罩物体先于被遮罩物体渲染。 注意:模板缓冲是全局的,因此需要确保遮罩物体的渲染顺序在被遮罩物体之前。 ### 示例工程 如果用户需要,可以提供一个简单的示例工程。但根据问题,我们只需提供方法。 §§相关问题§§ 1. 如何在Unity中实现圆形遮罩? 2. 模板缓冲(Stencil Buffer)在Unity中还有哪些应用? 3. 如何优化多边形遮罩的性能?</think>在Unity中实现多边形遮罩主要有两种高效方法:使用自定义Shader模板缓冲技术或优化后的Mesh生成方案。以下是具体实现步骤和原理: ### 方法一:模板缓冲Shader方案(推荐) 这是最高效的实现方式,利用GPU的模板缓冲功能: ```glsl // 遮罩Shader (Mask.shader) Shader "Custom/PolygonMask" { SubShader { Tags {"Queue" = "Transparent"} ColorMask 0 // 不写入颜色 ZWrite Off Stencil { Ref 1 Comp Always Pass Replace } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct appdata { float4 vertex : POSITION; }; struct v2f { float4 pos : SV_POSITION; }; v2f vert(appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); return o; } half4 frag() : COLOR { return 0; // 空输出 } ENDCG } } } // 被遮罩物体Shader (MaskedObject.shader) Shader "Custom/MaskedObject" { Properties { _MainTex("Texture", 2D) = "white" {} } SubShader { Tags {"Queue" = "Transparent"} Stencil { Ref 1 Comp Equal // 只渲染模板值为1的区域 } // ... 原有材质代码 } } ``` **实现步骤:** 1. 创建空物体 → 添加`MeshFilter`+`MeshRenderer` 2. 在MeshRenderer中使用`Mask.shader` 3. 编辑Mesh顶点形成任意多边形 4. 被遮罩物体使用`MaskedObject.shader` 5. **关键设置**:遮罩物体必须比被遮罩物体先渲染(调整Render Queue或物体顺序) > 优势:支持任意复杂多边形,GPU直接处理,性能优于Unity内置Mask[^1] ### 方法二:动态Mesh生成方案 适合需要运行时修改遮罩形状的场景: ```csharp // 多边形遮罩生成器 public class DynamicMask : MonoBehaviour { public Vector2[] polygonPoints; // 多边形顶点 private MeshFilter meshFilter; void Start() { meshFilter = GetComponent<MeshFilter>(); UpdateMaskMesh(); } void UpdateMaskMesh() { Mesh mesh = new Mesh(); // 顶点计算 Vector3[] vertices = new Vector3[polygonPoints.Length]; for(int i=0; i<polygonPoints.Length; i++){ vertices[i] = new Vector3(polygonPoints[i].x, polygonPoints[i].y, 0); } // 三角剖分(需实现三角化算法如Ear Clipping) int[] triangles = Triangulate(vertices); mesh.vertices = vertices; mesh.triangles = triangles; meshFilter.mesh = mesh; // 添加材质 GetComponent<Renderer>().material = maskMaterial; } // 三角剖分算法实现(伪代码) private int[] Triangulate(Vector3[] verts) { ... } } ``` **使用要点:** 1. 将此脚本挂载到空物体 2. 在Inspector中设置多边形顶点坐标 3. 被遮罩物体设为该物体的子对象 4. 添加`MeshCollider`可实现交互检测 ### 性能对比 | 方法 | 支持形状 | 性能 | 适用场景 | |------|----------|------|----------| | 模板Shader | 任意多边形 | ⭐⭐⭐⭐⭐ | 复杂静态遮罩 | | 动态Mesh | 任意多边形 | ⭐⭐⭐⭐ | 动态变形遮罩 | | Unity内置Mask | 仅矩形 | ⭐⭐ | 简单UI | ### 最佳实践建议 1. **移动端优化**: - 限制多边形顶点数量(≤50) - 使用MeshLOD分级 - 避免每帧更新遮罩形状 2. **高级技巧**: ```csharp // 实时更新遮罩示例 void Update() { if(needUpdate) { polygonPoints = GetNewShape(); // 获取新形状 UpdateMaskMesh(); } } ``` 3. **遮罩边缘柔化**: - 在Shader中添加边缘渐变计算 - 使用SDF(Signed Distance Field)技术 > 提示:凹多边形需要正确的三角剖分算法,推荐使用`UnityEngine.ProBuilder`或`Clipper`库处理复杂形状[^1]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值