已知分割数据集图像的多边形边界,绘制其二值化掩膜

这篇博客介绍了如何利用Python和PIL库从图像的多边形边界创建二值化掩膜。首先,通过遍历像素并判断是否在多边形内来暴力实现,然后发现可以使用`ImageDraw`的`polygon`函数高效地裁剪多边形区域。通过循环遍历所有物体的多边形,生成了包含多个物体的掩膜。最终提供了一个完整的代码示例,从JSON文件读取多边形数据并生成掩膜图片。
摘要由CSDN通过智能技术生成

已知分割数据集图像的多边形边界,绘制其二值化掩膜

1:需求

对于一些分割网络,需要使用到二值化的掩膜。但是制作的数据集(非公开数据集)的图像的标注是图像里物体的多边形边界。
比如:
在这里插入图片描述
在这里插入图片描述
已有的是原图和标注过的边缘(json格式的文件),想要生成下面这种二值化的掩膜。
在这里插入图片描述
当然,实际的情况是一张图里不止一个物体,可能有几十个。

2:思路

刚开始我的想法是暴力,遍历每一个像素,之后判断该像素是否在某一个物体的边界内,如果是,就把像素变成白色,如果不是,就变成黑色。

但是这种做法十分复杂,一来要遍历每一个像素,而二来要判断该像素是否在物体边缘内(可能有好几十个物体),而且物体边缘是不规则的多边形。这样下来,光判断每像素是否在物体边界内就很麻烦了。

直到我想到了换一种思路:能否找到提取图片不规则多边形区域的方法。如果有,那么会很大的简化整个流程。

果然,网上有这个方法:使用Python/PIL裁剪多边形
代码如下:

import numpy
from PIL import Image, ImageDraw

# read image as RGB and add alpha (transparency)
im = Image.open("crop.jpg").convert("RGBA")

# convert to numpy (for convenience)
imArray = numpy.asarray(im)

# create mask
polygon = [(444,203),(623,243),(691,177),(581,26),(482,42)]   # 多边形的点坐标
maskIm = Image.new('L', (imArray.shape[1], imArray.shape[0]), 0)
ImageDraw.Draw(maskIm).polygon(polygon, outline=1, fill=1)
mask = numpy.array(maskIm)

# assemble new image (uint8: 0-255)
newImArray = numpy.empty(imArray.shape,dtype='uint8')

# colors (three first columns, RGB)
newImArray[:,:,:3] = imArray[:,:,:3]

# transparency (4th column)
newImArray[:,:,3] = mask*255

# back to Image from numpy
newIm = Image.fromarray(newImArray, "RGBA")
newIm.save("out.png")

把上面边界的坐标稍微修改一下,来做个测试:

polygon = [(500,100),(500,900),(800,900),(1300,500),(1000,100)]   # 多边形边界点坐标,(width,height)

在这里插入图片描述
可以看到,多边形内部被提取出来了,其余部分则全为白色。

其中生成掩膜的代码:

# create mask
polygon = [(500,100),(500,900),(800,900),(1300,500),(1000,100)]   # 多边形边界点坐标,(width,height)
maskIm = Image.new('L', (imArray.shape[1], imArray.shape[0]), 0)
ImageDraw.Draw(maskIm).polygon(polygon, outline=1, fill=1)   # outline为线条颜色,fill为填充颜色
mask = numpy.array(maskIm)              # 生成了掩膜,只有多边形区域内为1,其余(含边界)全为0

既然这样可以在一个二维的0矩阵上,把一个多边形区域内置1。那么,一张图片里有多少个物体,我们就用一个for循环,把所有的多边形区域内全部置1。这样不就行了。
于是,先将代码修改为:

import numpy
from PIL import Image, ImageDraw
import cv2

# read image as RGB and add alpha (transparency)
im = Image.open("car1.jpg").convert("RGBA")

# convert to numpy (for convenience)
imArray = numpy.asarray(im)

# create mask
polygon = [(500,100),(500,900),(800,900),(1300,500),(1000,100)]   # 多边形边界点坐标,(width,height)
maskIm = Image.new('L', (imArray.shape[1], imArray.shape[0]), 0)
ImageDraw.Draw(maskIm).polygon(polygon, outline=1, fill=255)   # outline为线条颜色,fill为填充颜色
mask = numpy.array(maskIm)              # 生成了掩膜,只有多边形区域内为1,其余(含边界)全为0

cv2.imwrite('./out2.jpg', mask)

在这里插入图片描述
可以看到,单独的一个掩膜已经绘制完成了。
那么我们再修改一下,使其绘制出所有的多边形。


import numpy
from PIL import Image, ImageDraw
import cv2



def draw_mask(img):
    # convert to numpy (for convenience)
    imArray = numpy.asarray(img)

    # create mask
    polygons = [[(50,10),(50,90),(80,90),(130,50),(100,10)],
                [(500,100),(500,900),(800,900),(1300,500),(1000,100)] ]# 多边形边界点坐标,(width,height)
    maskIm = Image.new('L', (imArray.shape[1], imArray.shape[0]), 0)

    for polygon in polygons:
        ImageDraw.Draw(maskIm).polygon(polygon, outline=1, fill=255)   # outline为线条颜色,fill为填充颜色
    mask = numpy.array(maskIm)              # 生成了掩膜,只有多边形区域内为1,其余(含边界)全为0

    return mask


if __name__ == '__main__':
    # read image as RGB and add alpha (transparency)
    im = Image.open("car1.jpg").convert("RGBA")

    mask = draw_mask(im)
    cv2.imwrite('out3.jpg', mask)

在这里插入图片描述
当然,实际的情况需要你自己读取json文件,提取出其中多边形的点坐标,存放到polygons中即可(polygons中的每一个元素是一个多边形的所有边界的的坐标)

3:完整代码

最后,还是放一个完整的代码,具体可能还要视json文件的格式来修改:

import cv2
import os
import numpy as np
import json
from PIL import Image, ImageDraw


def draw_mask(img_name):
    # 传入的是图片的名字
    img = Image.open('./test/images/' + img_name + '.jpg').convert("RGBA")
    imArray = np.asarray(img)

    json_dir = './test/json/' + img_name + '.json'
    with open(json_dir) as json_file:
        json_data = json.load(json_file)
    objects = json_data['shapes']   # 所有的实例
    polygons = []
    for object in objects:
        polygon = []
        for point in object['points']:
            tmp = (int(point[0]), int(point[1]))
            polygon.append(tmp)
        polygons.append(polygon)
    # polygons = [np.array(object['points'], dtype=int) for object in objects]    # 把所有的object的边界的坐标存到一个list里

    # polygons = np.array(polygons, dtype=int) # 这样改类型会报错,因为不是每一张图片里的实例数目都一样,导致array里各个元素维度不一样
    # print(polygons[0])
    maskIm = Image.new('L', (imArray.shape[1], imArray.shape[0]), 0)

    for polygon in polygons:
        ImageDraw.Draw(maskIm).polygon(polygon, outline=1, fill=255)   # outline为线条颜色,fill为填充颜色
    mask = np.array(maskIm)              # 生成了掩膜,只有多边形区域内为1,其余(含边界)全为0


    cv2.imwrite('./test/mask/' + img_name + '.jpg', mask)

if __name__ == '__main__':
    imgs_name = [i.split('.')[0] for i in os.listdir('./test/images') if i.split('.')[-1] == 'jpg']

    imgs_num = len(imgs_name)
    for index, img_name in enumerate(imgs_name):
        draw_mask(img_name)
        print('{} / {} is done '.format(index, imgs_num))
  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值