VIA annotation adapter. To use the VIA annotation tool, refer to the Homepage.
Current supported annotations:
- circle
- ellipse
- point
- polygon
- polyline
- rectangle
Current supported annotation tool versions:
The adapter has the following parameters:
--path
: the path to the base folder containing the annotations (e.g.: data/image_object_detection/my_collection)--version
: The version format to read or write the anotations. If not set, parse the annotation-format on read, use version 2 on write annotation--categories_file_name
: The path to the categories file if not set, default to categories.txt--annotations_file_name
: The name of annotations file. If not set, default to via_region_data.json--category_label_key
: The key, the category label key is stored in the annotation file, default to category.
Define the test data to use in tests. We declare 2 annotations, one with all supported region shapes set and one with no region set at all. The we define the result formats in CSV and JSON for VIA version 1 and 2.
import unittest
from unittest.mock import patch, mock_open
from fastcore.test import *
CATEGORIES_TEST_DATA = "foo\nbar"
TEST_FILEPATH = "testpath"
ANNOTATION_TEST_DATA = {
"foo.jpg12": Annotation(annotation_id="foo.jpg12", file_path=join(TEST_FILEPATH, "foo.jpg"), regions=[
Region(shape=RegionShape.CIRCLE, points_x=[20], points_y=[21], radius_x=5, radius_y=5, labels=['foo']),
Region(shape=RegionShape.ELLIPSE, points_x=[30], points_y=[31], radius_x=10, radius_y=15, rotation=0.6, labels=['bar']),
Region(shape=RegionShape.POINT, points_x=[40], points_y=[41], labels=['foo']),
Region(shape=RegionShape.POLYGON, points_x=[50, 60, 70, 80], points_y=[51, 61, 71, 81], labels=['bar']),
Region(shape=RegionShape.POLYLINE, points_x=[52, 62, 72, 82], points_y=[53, 63, 73, 83], labels=['foo']),
Region(shape=RegionShape.RECTANGLE, points_x=[90, 100], points_y=[91, 111], labels=['bar']),
]),
"bar.jpg34": Annotation(annotation_id="bar.jpg34", file_path=join(TEST_FILEPATH, "bar.jpg"))
}
# The CSV test data for VIA version 1
DATA_CSV_V1 = """#filename,file_size,file_attributes,region_count,region_id,region_shape_attributes,region_attributes
foo.jpg,12,"{}",6,0,"{""name"":""circle"",""cx"":20,""cy"":21,""r"":5}","{""category"":""foo""}"
foo.jpg,12,"{}",6,1,"{""name"":""ellipse"",""cx"":30,""cy"":31,""rx"":10,""ry"":15}","{""category"":""bar""}"
foo.jpg,12,"{}",6,2,"{""name"":""point"",""cx"":40,""cy"":41}","{""category"":""foo""}"
foo.jpg,12,"{}",6,3,"{""name"":""polygon"",""all_points_x"":[50, 60, 70, 80],""all_points_y"":[51, 61, 71, 81]}","{""category"":""bar""}"
foo.jpg,12,"{}",6,4,"{""name"":""polyline"",""all_points_x"":[52, 62, 72, 82],""all_points_y"":[53, 63, 73, 83]}","{""category"":""foo""}"
foo.jpg,12,"{}",6,5,"{""name"":""rect"",""x"":90,""y"":91,""width"":10,""height"":20}","{""category"":""bar""}"
bar.jpg,34,"{}",0,0,"{}","{}"
"""
# The CSV test data for VIA version 2
DATA_CSV_V2 = """filename,file_size,file_attributes,region_count,region_id,region_shape_attributes,region_attributes
foo.jpg,12,"{}",6,0,"{""name"":""circle"",""cx"":20,""cy"":21,""r"":5}","{""category"":""foo""}"
foo.jpg,12,"{}",6,1,"{""name"":""ellipse"",""cx"":30,""cy"":31,""rx"":10,""ry"":15,""theta"":0.6}","{""category"":""bar""}"
foo.jpg,12,"{}",6,2,"{""name"":""point"",""cx"":40,""cy"":41}","{""category"":""foo""}"
foo.jpg,12,"{}",6,3,"{""name"":""polygon"",""all_points_x"":[50, 60, 70, 80],""all_points_y"":[51, 61, 71, 81]}","{""category"":""bar""}"
foo.jpg,12,"{}",6,4,"{""name"":""polyline"",""all_points_x"":[52, 62, 72, 82],""all_points_y"":[53, 63, 73, 83]}","{""category"":""foo""}"
foo.jpg,12,"{}",6,5,"{""name"":""rect"",""x"":90,""y"":91,""width"":10,""height"":20}","{""category"":""bar""}"
bar.jpg,34,"{}",0,0,"{}","{}"
"""
# The JSON test data for VIA version 1
DATA_JSON_V1 = """{
"foo.jpg12":{"fileref":"","size":12,"filename":"foo.jpg","base64_img_data":"","file_attributes":{},"regions":{
"0":{"shape_attributes":{"name":"circle","cx":20,"cy":21,"r":5},"region_attributes":{"category":"foo"}},
"1":{"shape_attributes":{"name":"ellipse","cx":30,"cy":31,"rx":10,"ry":15},"region_attributes":{"category":"bar"}},
"2":{"shape_attributes":{"name":"point","cx":40,"cy":41},"region_attributes":{"category":"foo"}},
"3":{"shape_attributes":{"name":"polygon","all_points_x":[50, 60, 70, 80],"all_points_y":[51, 61, 71, 81]},"region_attributes":{"category":"bar"}},
"4":{"shape_attributes":{"name":"polyline","all_points_x":[52, 62, 72, 82],"all_points_y":[53, 63, 73, 83]},"region_attributes":{"category":"foo"}},
"5":{"shape_attributes":{"name":"rect","x":90,"y":91,"width":10,"height":20},"region_attributes":{"category":"bar"}}}},
"bar.jpg34":{"fileref":"","size":34,"filename":"bar.jpg","base64_img_data":"","file_attributes":{},"regions":{}}}"""
# The JSON test data for VIA version 2
DATA_JSON_V2 = """{
"foo.jpg12":{"fileref":"","size":12,"filename":"foo.jpg","regions":[
{"shape_attributes":{"name":"circle","cx":20,"cy":21,"r":5},"region_attributes":{"category":"foo"}},
{"shape_attributes":{"name":"ellipse","cx":30,"cy":31,"rx":10,"ry":15,"theta":0.6},"region_attributes":{"category":"bar"}},
{"shape_attributes":{"name":"point","cx":40,"cy":41},"region_attributes":{"category":"foo"}},
{"shape_attributes":{"name":"polygon","all_points_x":[50, 60, 70, 80],"all_points_y":[51, 61, 71, 81]},"region_attributes":{"category":"bar"}},
{"shape_attributes":{"name":"polyline","all_points_x":[52, 62, 72, 82],"all_points_y":[53, 63, 73, 83]},"region_attributes":{"category":"foo"}},
{"shape_attributes":{"name":"rect","x":90,"y":91,"width":10,"height":20},"region_attributes":{"category":"bar"}}],"file_attributes":{}},
"bar.jpg34":{"fileref":"","size":34,"filename":"bar.jpg","regions":{},"file_attributes":{}}}"""
adapter = VIAAnnotationAdapter(TEST_FILEPATH)
with patch('__main__.isfile', return_value=True):
with patch('__main__.open'.format(__name__), new=mock_open(read_data=DATA_JSON_V2)) as _file:
actual = adapter.read_annotations()
_file.assert_called_once_with(join(TEST_FILEPATH, 'via_region_data.json'))
test_eq(actual, ANNOTATION_TEST_DATA)
actual