sdk/python/jobs/automl-standalone-jobs/jsonl-conversion/coco_jsonl_converter.py (97 lines of code) (raw):
from base_jsonl_converter import JSONLConverter
import masktools
import pycocotools.mask as mask
import json
class COCOJSONLConverter(JSONLConverter):
"""
Class for converting COCO data for object detection and instance segmentation into jsonl files
...
Attributes
---------
base_url : str
the base for the image_url to be written into the jsonl file
coco_file : str
file containing coco annotations
compressed_rle : bool
flag indicating if coco segmentation annotations are stored in comprssed rle format
"""
def __init__(self, base_url, coco_file, compressed_rle=False):
super().__init__(base_url=base_url)
self.categories = {}
self.compressed_rle = compressed_rle
with open(coco_file) as f_in:
self.coco_data = json.load(f_in)
self.image_id_to_data_index = {}
for i in range(0, len(self.coco_data["images"])):
self.jsonl_data.append({})
self.jsonl_data[i]["image_url"] = ""
self.jsonl_data[i]["image_details"] = {}
self.jsonl_data[i]["label"] = []
for i in range(0, len(self.coco_data["categories"])):
self.categories[self.coco_data["categories"][i]["id"]] = self.coco_data[
"categories"
][i]["name"]
def convert(self):
"""
Generate jsonl data for object detection or instance segmentation
return: list of lines for jsonl
rtype: List <class 'dict'>
"""
for i in range(0, len(self.coco_data["images"])):
self._populate_image_url(i, self.coco_data["images"][i])
self._populate_image_details(i, self.coco_data["images"][i])
for i in range(0, len(self.coco_data["annotations"])):
self._populate_label(self.coco_data["annotations"][i])
return self.jsonl_data
def _populate_image_url(self, index, coco_image):
"""
populates image url for jsonl entry
Parameters:
index (int): image entry index
coco_image (dict): image entry from coco data file
"""
image_url = coco_image["file_name"]
self.jsonl_data[index]["image_url"] = (
self.base_url + image_url[image_url.rfind("/") + 1 :]
)
self.image_id_to_data_index[coco_image["id"]] = index
def _populate_image_details(self, index, coco_image):
"""
populates image details for jsonl entry
Parameters:
index (int): image entry index
coco_image (dict): image entry from coco data file
return: list of lines for jsonl
"""
file_name = coco_image["file_name"]
self.jsonl_data[index]["image_details"]["format"] = file_name[
file_name.rfind(".") + 1 :
]
self.jsonl_data[index]["image_details"]["width"] = coco_image["width"]
self.jsonl_data[index]["image_details"]["height"] = coco_image["height"]
def _populate_label(self, annotation):
"""
populates label entry for object detection or instance segmentation
Parameters:
annotation (dict): annotation entry from coco data file
"""
index = self.image_id_to_data_index[annotation["image_id"]]
image_details = self.jsonl_data[index]["image_details"]
label = {"label": self.categories[annotation["category_id"]]}
# check if object detection or instance segmentation
if (
"segmentation" not in annotation.keys()
or len(annotation["segmentation"]) == 0
):
self._populate_bbox_in_label(label, annotation, image_details)
else:
self.__populate_segmentation_in_label(label, annotation, image_details)
self._populate_isCrowd(label, annotation)
self.jsonl_data[index]["label"].append(label)
def _populate_bbox_in_label(self, label, annotation, image_details):
"""
populates bounding box in label entry for object detection
Parameters:
label (dict): label to populate for jsonl entry
annotation (dict): annotation entry from coco data file
"""
# if bbox comes as normalized, skip normalization.
if max(annotation["bbox"]) < 1.5:
width = 1
height = 1
else:
width = image_details["width"]
height = image_details["height"]
label["topX"] = annotation["bbox"][0] / width
label["topY"] = annotation["bbox"][1] / height
label["bottomX"] = (annotation["bbox"][0] + annotation["bbox"][2]) / width
label["bottomY"] = (annotation["bbox"][1] + annotation["bbox"][3]) / height
def __populate_segmentation_in_label(self, label, annotation, image_details):
"""
populates polygon segmentation in label entry for instance segmentation
Parameters:
label (dict): label to populate for jsonl entry
annotation (dict): annotation entry from coco data file
image_details (dict): image details from coco data file
"""
# if bbox comes as normalized, skip normalization.
if max(annotation["bbox"]) < 1.5:
width = 1
height = 1
else:
width = image_details["width"]
height = image_details["height"]
polygons = []
if (
type(annotation["segmentation"]) is dict
): # segmentations are in uncompressed rle format
rle = annotation["segmentation"]
if self.compressed_rle:
compressed_rle = rle
else:
compressed_rle = mask.frPyObjects(rle, rle["size"][0], rle["size"][1])
polygons = masktools.convert_mask_to_polygon(compressed_rle)
else: # segmentation is list of vertices
for segmentation in annotation["segmentation"]:
polygon = []
# loop through vertices:
for id, vertex in enumerate(segmentation):
if (id % 2) == 0:
# x-coordinates (even index)
x = vertex / width
polygon.append(x)
else:
y = vertex / height
polygon.append(y)
polygons.append(polygon)
label["polygon"] = polygons
def _populate_isCrowd(self, label, annotation):
"""
populates iscrowd in label entry for object detection and instance segmentation
Parameters:
label (dict): label to populate for json entry
annotation (dict): annotation entry from coco data file
"""
if "iscrowd" in annotation.keys():
label["isCrowd"] = annotation["iscrowd"]