You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何生成自相交多边形相交区域为1的二值掩码?

自相交多边形转二值掩码(相交区域填充)的解决方法

问题背景

需要将自相交多边形转换为相交区域为1的二值掩码,但使用PIL的ImageDraw.polygon或polygon2mask工具生成的结果不符合预期:生成的掩码按非零环绕规则填充,而预期是相交区域(交叉次数为奇数的区域)被标记为1。

核心原因

PIL的ImageDraw.polygon默认使用非零环绕填充规则,而我们需要的是奇偶填充规则(Even-Odd Rule)——该规则会填充多边形交叉次数为奇数的区域,正好匹配自相交多边形相交区域的标记需求。

可行解决方案

方案1:使用OpenCV实现奇偶规则填充

OpenCV的fillPoly函数支持指定填充规则,直接设置cv2.FILLRULE_EVEN_ODD即可实现预期效果。

代码示例:

import numpy as np
import cv2
from PIL import Image

def polygons_to_mask_even_odd(poly_str, shape):
    # 解析多边形点
    def parse_point(point_str):
        x, y = map(float, point_str.split(","))
        return (int(x), int(y))
    
    points = [parse_point(p) for p in poly_str.split(";")]
    # 转换为OpenCV需要的格式:(num_polygons, num_points, 2)
    pts = np.array([points], dtype=np.int32)
    
    # 创建空掩码(OpenCV的shape是(高,宽),对应输入的(宽,高)反转)
    mask = np.zeros(shape[::-1], dtype=np.uint8)
    # 使用奇偶规则填充
    cv2.fillPoly(mask, pts, color=1, lineType=cv2.LINE_AA, fillRule=cv2.FILLRULE_EVEN_ODD)
    
    return mask

# 测试代码
poly = "30,20;30,60;60,60;60,30;20,30;20,40;50,40;50,50;40,50;40,20"
mask = polygons_to_mask_even_odd(poly, (80, 80))

# 显示结果
Image.fromarray(mask * 255).show()

方案2:使用Shapely处理自相交多边形

Shapely库可以正确解析自相交多边形的内部区域(自动处理为多个简单多边形的并集),再通过栅格化生成掩码。

先安装依赖:

pip install shapely rasterio

代码示例:

import numpy as np
from shapely.geometry import Polygon
from rasterio.features import rasterize
from PIL import Image

def polygons_to_mask_shapely(poly_str, shape):
    # 解析点
    def parse_point(point_str):
        x, y = map(float, point_str.split(","))
        return (x, y)
    
    points = [parse_point(p) for p in poly_str.split(";")]
    # 创建自相交多边形,Shapely会自动处理为有效几何对象
    poly = Polygon(points)
    # 修复无效多边形(自相交情况)
    if not poly.is_valid:
        poly = poly.buffer(0)
    
    # 生成掩码:设置坐标原点为左上角,像素大小1x1
    transform = rasterio.transform.from_origin(0, shape[1], 1, 1)
    mask = rasterize([poly], out_shape=shape[::-1], transform=transform, fill=0, default_value=1)
    
    return mask

# 测试代码
poly = "30,20;30,60;60,60;60,30;20,30;20,40;50,40;50,50;40,50;40,20"
mask = polygons_to_mask_shapely(poly, (80, 80))

# 显示结果
Image.fromarray(mask * 255).show()

原代码小问题修正

原代码存在两处语法/逻辑错误:

  1. points = [ points_to_float(x) for x in poly.split(";"))] 多了一个右括号,应改为points = [ points_to_float(x) for x in poly.split(";")]
  2. resize函数未定义,可替换为PIL的Image.resize方法,例如img = Image.fromarray(np.uint8(img)).resize((300,300))

内容的提问来源于stack exchange,提问作者Boris G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 22:27:23