Hendra Bunyamin

Forgiven sinner and Lecturer at Maranatha Christian University

Creating Mask Images from Shapely Polygons

This tutorial will show you how to create masks from Shapely polygons. Specifically, a Shapely polygon has a WKT format and we will convert this WKT format into a mask. OK, concretely, suppose we have installed Shapely library, Anaconda, and had an image like the one on left side of images below. In this tutorial, we will convert this image into a mask image like the one on the right side.

img4 img5

The image on right side is called a mask image. It contains only binary pixels (black and white). Particularly, this mask contains black pixels as buildings and white pixels otherwise.

Let’s get started with the code! We are going to show all these codes as codes in Jupyter notebook cells.

Firstly, we import the libraries

%reload_ext autoreload
%autoreload 2
%matplotlib inline

from skimage.draw import line, polygon, circle, ellipse
import matplotlib.pyplot as plt
from pathlib import Path
from PIL import Image
import numpy as np
import skimage.io

# To read files in a directory
from os import listdir
from os.path import isfile, join

# To load wkt; this is a specific method in shapely library 
from shapely.wkt import loads

Next, we construct paths to the images, the binary labels where the masks will be saved, and the labels where the json files are saved.

# We construct the path to the image 
images_path = Path('/home/hbunyamin/Datasets/asses-building-damage/train/images')

# We construct the path to label path where we want to put the mask image 
label_path = Path('/home/hbunyamin/Datasets/asses-building-damage/train/binaryLabels')

# We construct the path to the json file; the json file contains coordinates of polygons
json_path  = Path('/home/hbunyamin/Datasets/asses-building-damage/train/labels')

Then, we put all the image files in a list.

list_files = [f for f in listdir(images_path) if isfile(join(images_path, f))]

Finally, we process all the images and convert them into mask images.

counter = 0

for img_name in list_files:
    # split the file name
    prefix_file_name = img_name.split(".")
    
    # construct the path to the image
    temp_image_path = images_path / img_name    
    
    # construct the path to the json    
    temp_json_path = json_path / (prefix_file_name[0]+".json")
    
    # read the json
    json_dict = None 
    with open(temp_json_path, 'r') as read_file:
        json_dict = json.load(read_file)  
    
    # construct the list of xy of buildings
    props_xy_list = json_dict['features']['xy']     
    
    # construct list of polygons 
    polygon_geom_list = []
    for prop in props_xy_list:
        polygon_temp = loads(prop['wkt'])
        polygon_geom_list.append(polygon_temp)    
    
    # read the image which we want to draw the polygons
    the_image = skimage.io.imread( temp_image_path )    
    
    # Create the basic mask
    a_mask = np.ones(shape=the_image.shape[0:2], dtype="bool") # original
    
    # For each polygon, draw the polygon inside the mask
    for polygon_geom in polygon_geom_list:
        poly_coordinates = np.array(list(polygon_geom.exterior.coords))
        rr, cc = polygon(poly_coordinates[:,0], poly_coordinates[:,1], the_image.shape)
        a_mask[cc,rr] = False        
    
    # Convert numpy array of the mask into an image with the help of PIL
    mask_image = Image.fromarray(a_mask)
    
    # Save the image of the mask into the "binaryLabels" folder 
    mask_image.save( label_path / (prefix_file_name[0]+"_mask.png"), format="PNG" )
    
    # For debugging purposes
    if counter % 1000 == 0:
        print("Number of images have been processed:", counter)
    counter += 1

For conclusion, all the mask images are saved in label_path.


Written on March 28, 2020