Skip to content

Instantly share code, notes, and snippets.

@innat
Last active November 10, 2023 08:25
Show Gist options
  • Save innat/80ccd3fc2453f90a7e574024e82333ed to your computer and use it in GitHub Desktop.
Save innat/80ccd3fc2453f90a7e574024e82333ed to your computer and use it in GitHub Desktop.
Utility function to create mask (png) from polygon (json).
# Optional
# I had the json (annotation) and image file in the same folder. That's why I did also
from pathlib import Path
import shutil
src_path = 'Mixed_Image_JSON_PATH'
trg_path = 'Separate_Path_For_Image'
for src_file in Path(src_path).glob('*.jpeg*'):
shutil.copy(src_file, trg_path)
# Case 2: Multiple Class Maks
# String to integer labels.
# Assuming we havae 5 classes like below (excluding background).
categories = {
'human' : 1,
'dog' : 2,
'cat' : 3,
'bus' : 4,
'road' : 5
}
labels = sorted(categories.keys())
# A function that will fill the polygon regions
def create_masks(raw_img_height, raw_img_width, points):
blank = np.zeros(shape=(raw_img_height, raw_img_width), dtype=np.uint8)
points = np.array(points, dtype=np.int32)
cv2.fillPoly(blank, [points], 255)
return np.array(blank, dtype=bool)
# A function that will assemble all the class mask
def create_multi_masks(raw_img_height, raw_img_width, shape_dicts):
channels = []
cls = [x['label'] for x in shape_dicts]
poly = [np.array(x['points'], dtype=np.int32) for x in shape_dicts]
label2poly = dict(zip(cls, poly))
background = np.zeros(shape=(raw_img_height, raw_img_width), dtype=np.int32)
# iterate through objects of interest
for i, label in enumerate(labels):
if label in cls:
cls_id = categories[label]
mask = create_masks(raw_img_height, raw_img_width, label2poly[label])
background[mask] = cls_id
channels.append(background)
Y = np.stack(channels, axis=2)
return Y
show = True
save = True
input_dir = 'json_annotation_file_directory'
json_mask_path = sorted(
[
os.path.join(input_dir, fname)
for fname in os.listdir(input_dir)
if fname.endswith(".json")
]
)
# CHECK
len(json_mask_path), json_mask_path[0]
# Loop over the json files
poly_to_mask_saving_path = 'mask' # I create a folder manually, and maned as 'mask'
for i, path in enumerate(json_mask_path):
shape_info, width, height = get_poly(path)
cls = [x['label'] for x in shape_info]
poly_to_mask = create_multi_masks(height, width, shape_info)
print(json.dumps(categories, indent=4, sort_keys=True))
print(path, cls)
print(poly_to_mask.dtype, poly_to_mask.shape, np.unique(poly_to_mask))
if show:
plt.imshow(poly_to_mask)
plt.show()
if save:
poly_name = path.split('\\')[1].split('.')[0]
print(f"{poly_to_mask_saving_path}/{poly_name}.png")
cv2.imwrite(f"{poly_to_mask_saving_path}/{poly_name}.png", poly_to_mask)
import cv2
import math
import json, os
import numpy as np
from tqdm import tqdm
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw
# It reads polygon containing json file.
# Below is the informaation structure inside a json file.
'''
{
...
...
"shapes": [
{
"label": "cat",
"points": [
[
19.0,
572.25
],
[
89.0,
638.5
],
...
...
"group_id": null,
"shape_type": "polygon",
"imageHeight": 1204,
"imageWidth": 1827
'''
# read json file and return some specific properties
def get_poly(annot_path):
with open(annot_path, encoding = 'utf-8') as handle:
data = json.load(handle)
return data['shapes'], data['imageWidth'], data['imageHeight']
# Case 1: Binary Maks
def create_binary_masks(raw_img_height, raw_img_width, shape_dicts):
# image must be grayscal
blank = np.zeros(shape=(raw_img_height, raw_img_width), dtype=np.float32)
# Iterate over the ['shapes'] properites of json file.
for shape in shape_dicts:
# shape['label'] may or may not contain 'background' label
# and here for making binary mask, we fill 255 value except the background
# note, shape['label'] may coontain multiple class.
if shape['label'] != 'background':
points = np.array(shape['points'], dtype=np.int32)
cv2.fillPoly(blank, [points], 255)
# normalize the mask, from value (0, 255) to (0, 1)
blank = blank / 255.0
# add channel axis
return np.expand_dims(blank, axis=2)
show = True
save = True
input_dir = 'mixed_150_train' # 'mixed_50_train' 'mixed_150_train' 'mixed_10_val'
json_mask_path = sorted(
[
os.path.join(input_dir, fname)
for fname in os.listdir(input_dir)
if fname.endswith(".json")
]
)
# check
len(json_mask_path), json_mask_path[0]
# file saving location
poly_to_mask_saving_path = 'binary_mask'
try:
os.mkdir(poly_to_mask_saving_path)
except FileExistsError:
print('Directory not created.')
# Iterate over the JSON file and save Mask as PNG
for i, path in enumerate(json_mask_path):
shape_info, width, height = get_poly(path)
bin_mask = create_binary_masks(height, width, shape_info)
print(bin_mask.shape)
if show:
plt.imshow(bin_mask)
plt.title(str(np.unique(bin_mask)))
plt.show()
if save:
poly_name = path.split('\\')[1].split('.')[0]
print(poly_name)
print(f"{poly_to_mask_saving_path}/{poly_name}.png")
cv2.imwrite(f"{poly_to_mask_saving_path}/{poly_name}.png", bin_mask)
@innat
Copy link
Author

innat commented Apr 4, 2022

From Mask to Polygon: Check: https://stackoverflow.com/a/60573495/9215780

@innat
Copy link
Author

innat commented May 26, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment