Skip to content

Commit 0d9c4b7

Browse files
committed
Handle bad VOC exports
1 parent e5f7dc2 commit 0d9c4b7

File tree

3 files changed

+36
-14
lines changed

3 files changed

+36
-14
lines changed

labelbox/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"The Labelbox python package."
22

3-
__version__ = '0.0.3'
3+
__version__ = '0.0.4'

labelbox/exporters/voc_exporter.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@ def from_json(labeled_data, annotations_output_dir, images_output_dir,
4444

4545
for data in label_data:
4646
try:
47-
_write_label(data, label_format, images_output_dir, annotations_output_dir)
47+
write_label(
48+
data['ID'],
49+
data['Labeled Data'],
50+
data['Label'],
51+
label_format,
52+
images_output_dir,
53+
annotations_output_dir)
4854

4955
except requests.exceptions.MissingSchema as exc:
5056
logging.exception(exc)
@@ -54,14 +60,23 @@ def from_json(labeled_data, annotations_output_dir, images_output_dir,
5460
continue
5561

5662

57-
def _write_label(
58-
data, label_format, images_output_dir, annotations_output_dir):
59-
"Writes a Pascal VOC formatted image and label pair to disk."
63+
def write_label(label_id, image_url, labels, label_format, images_output_dir, annotations_output_dir):
64+
"""Writes a single Pascal VOC formatted image and label pair to disk.
65+
66+
Args:
67+
label_id (str): ID for the instance to write
68+
image_url (str): URL to download image file from
69+
labels (str): Labelbox formatted labels to use for generating annotation
70+
label_format (str): Format of the labeled data. Valid options are: "WKT" and "XY", default is "WKT".
71+
annotations_output_dir (str): File path of directory to write Pascal VOC
72+
annotation files.
73+
images_output_dir (str): File path of directory to write images.
74+
"""
6075
# Download image and save it
61-
response = requests.get(data['Labeled Data'], stream=True)
76+
response = requests.get(image_url, stream=True)
6277
response.raw.decode_content = True
6378
image = Image.open(response.raw)
64-
image_name = ('{img_id}.{ext}'.format(img_id=data['ID'], ext=image.format.lower()))
79+
image_name = ('{img_id}.{ext}'.format(img_id=label_id, ext=image.format.lower()))
6580
image_fqn = os.path.join(images_output_dir, image_name)
6681
image.save(image_fqn, format=image.format)
6782

@@ -70,27 +85,27 @@ def _write_label(
7085
xml_writer = PascalWriter(image_fqn, width, height)
7186

7287
# remove classification labels (Skip, etc...)
73-
if not callable(getattr(data['Label'], 'keys', None)):
88+
if not callable(getattr(labels, 'keys', None)):
7489
# skip if no categories (e.g. "Skip")
7590
return
7691

7792
# convert label to Pascal VOC format
78-
for category_name, wkt_data in data['Label'].items():
93+
for category_name, paths in labels.items():
7994
if label_format == 'WKT':
8095
xml_writer = _add_pascal_object_from_wkt(
81-
xml_writer, img_height=height, wkt_data=wkt_data,
96+
xml_writer, img_height=height, wkt_data=paths,
8297
label=category_name)
8398
elif label_format == 'XY':
8499
xml_writer = _add_pascal_object_from_xy(
85-
xml_writer, img_height=height, polygons=wkt_data,
100+
xml_writer, img_height=height, polygons=paths,
86101
label=category_name)
87102
else:
88103
exc = UnknownFormatError(label_format=label_format)
89104
logging.exception(exc.message)
90105
raise exc
91106

92107
# write Pascal VOC xml annotation for image
93-
xml_writer.save(os.path.join(annotations_output_dir, '{}.xml'.format(data['ID'])))
108+
xml_writer.save(os.path.join(annotations_output_dir, '{}.xml'.format(label_id)))
94109

95110

96111
def _add_pascal_object_from_wkt(xml_writer, img_height, wkt_data, label):
@@ -112,10 +127,16 @@ def _add_pascal_object_from_wkt(xml_writer, img_height, wkt_data, label):
112127

113128

114129
def _add_pascal_object_from_xy(xml_writer, img_height, polygons, label):
130+
if not isinstance(polygons, list):
131+
# polygons is not [{'geometry': [xy]}] nor [[xy]]
132+
return xml_writer
115133
for polygon in polygons:
116134
if 'geometry' in polygon: # V3
117135
polygon = polygon['geometry']
118-
assert isinstance(polygon, list) # V2 and V3
136+
if not isinstance(polygon, list) \
137+
or not all(map(lambda p: 'x' in p and 'y' in p, polygon)):
138+
# couldn't make a list of points, give up
139+
return xml_writer
119140

120141
xy_coords = []
121142
for point in polygon:

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ classifiers =
2525

2626
[options]
2727
zip_safe = False
28-
packages = find_namespace:
28+
packages =
29+
labelbox
2930
include_package_data = True
3031
install_requires =
3132
jinja2

0 commit comments

Comments
 (0)