Skip to content

Commit 557f56f

Browse files
author
Matt Sokoloff
committed
add missing tests
1 parent d6c8fe1 commit 557f56f

File tree

2 files changed

+277
-0
lines changed

2 files changed

+277
-0
lines changed
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
from types import SimpleNamespace
2+
3+
import pytest
4+
5+
from labelbox.data.annotation_types import ClassificationAnnotation, ObjectAnnotation
6+
from labelbox.data.annotation_types import Polygon, Point, Rectangle, Mask, MaskData, Line, Radio, Text, Checklist, ClassificationAnswer
7+
import numpy as np
8+
9+
class NameSpace(SimpleNamespace):
10+
11+
def __init__(self, predictions, ground_truths, expected):
12+
super(NameSpace, self).__init__(predictions=predictions,
13+
ground_truths=ground_truths,
14+
expected=expected)
15+
16+
17+
def get_radio(name, answer_name):
18+
return ClassificationAnnotation(
19+
name = name,
20+
value = Radio(answer = ClassificationAnswer(name = answer_name))
21+
)
22+
23+
def get_text(name, text_content):
24+
return ClassificationAnnotation(
25+
name = name,
26+
value = Text(answer = text_content)
27+
)
28+
29+
def get_checklist(name, answer_names):
30+
return ClassificationAnnotation(
31+
name = name,
32+
value = Radio(answer = [ClassificationAnswer(name = answer_name) for answer_name in answer_names])
33+
)
34+
35+
36+
def get_polygon(name, points, subclasses = None):
37+
return ObjectAnnotation(name = name,
38+
value = Polygon( points = [Point(x = x, y = y) for x,y in points]),
39+
classifications = [] if subclasses is None else subclasses
40+
)
41+
42+
def get_rectangle(name, start, end):
43+
return ObjectAnnotation(name = name,
44+
value = Rectangle( start = Point(x = start[0], y = start[1]), end = Point(x = end[0], y = end[1]))
45+
)
46+
47+
def get_mask(name, pixels, color = (1,1,1)):
48+
mask = np.zeros((32,32,3)).astype(np.uint8)
49+
for pixel in pixels:
50+
mask[pixel[0], pixel[1]] = color
51+
return ObjectAnnotation(name=name,
52+
value=Mask(mask = MaskData(arr = mask), color =color)
53+
)
54+
55+
def get_line(name, points):
56+
return ObjectAnnotation(name = name,
57+
value = Line( points = [Point(x = x, y = y) for x,y in points])
58+
)
59+
60+
def get_point(name, x, y):
61+
return ObjectAnnotation(name = name,
62+
value = Point(x = x, y = y)
63+
)
64+
65+
66+
def get_object_pairs(tool_fn, **kwargs):
67+
return [
68+
NameSpace(
69+
predictions=[
70+
tool_fn("cat", **kwargs)
71+
],
72+
ground_truths=[
73+
tool_fn("cat", **kwargs)
74+
],
75+
expected = [1,0,0,0]
76+
),
77+
NameSpace(
78+
predictions=[
79+
tool_fn("cat", **kwargs),
80+
tool_fn("cat", **kwargs)
81+
],
82+
ground_truths=[
83+
tool_fn("cat", **kwargs),
84+
tool_fn("cat", **kwargs)
85+
],
86+
expected = [2,0,0,0]
87+
),
88+
NameSpace(
89+
predictions=[
90+
tool_fn("cat", **kwargs),
91+
tool_fn("cat", **kwargs)
92+
],
93+
ground_truths=[
94+
tool_fn("cat", **kwargs)
95+
],
96+
expected = [1,1,0,0]
97+
),
98+
NameSpace(
99+
predictions=[
100+
tool_fn("cat", **kwargs)
101+
],
102+
ground_truths=[
103+
tool_fn("cat", **kwargs),
104+
tool_fn("cat", **kwargs)
105+
],
106+
expected = [1,0,0,1]
107+
),
108+
NameSpace(
109+
predictions=[],
110+
ground_truths=[],
111+
expected = []
112+
),
113+
NameSpace(
114+
predictions=[],
115+
ground_truths=[
116+
tool_fn("cat", **kwargs)
117+
],
118+
expected = [0,0,0,1]
119+
),
120+
NameSpace(
121+
predictions=[
122+
tool_fn("cat", **kwargs)
123+
],
124+
ground_truths=[],
125+
expected = [0,1,0,0]
126+
),
127+
NameSpace(
128+
predictions=[
129+
tool_fn("cat", **kwargs)
130+
],
131+
ground_truths=[
132+
tool_fn("dog", **kwargs)
133+
],
134+
expected = [0,1,0,1]
135+
),
136+
]
137+
138+
@pytest.fixture
139+
def polygon_pair():
140+
return get_object_pairs(get_polygon, points = [[0,0], [10,0], [10,10], [0,10]] )
141+
142+
143+
@pytest.fixture
144+
def rectangle_pair():
145+
return get_object_pairs(get_rectangle, start = [0,0], end = [10,10] )
146+
147+
@pytest.fixture
148+
def mask_pair():
149+
return get_object_pairs(get_mask, pixels = [[0,0]])
150+
151+
@pytest.fixture
152+
def line_pair():
153+
return get_object_pairs(get_line, points = [[0,0], [10,0], [10,10], [0,10]])
154+
155+
@pytest.fixture
156+
def point_pair():
157+
return get_object_pairs(get_point, x = 0, y = 0)
158+
159+
160+
"""
161+
def get_radio(name, answer_name):
162+
return ClassificationAnnotation(
163+
name = name,
164+
value = Radio(answer = ClassificationAnswer(name = answer_name))
165+
)
166+
167+
def get_text(name, text_content):
168+
return ClassificationAnnotation(
169+
name = name,
170+
value = Text(answer = text_content)
171+
)
172+
173+
def get_checklist(name, answer_names):
174+
return ClassificationAnnotation(
175+
name = name,
176+
value = Radio(answer = [ClassificationAnswer(name = answer_name) for answer_name in answer_names])
177+
)
178+
179+
@pytest.fixture
180+
def radio_pairs():
181+
return [
182+
NameSpace(
183+
predictions=[get_radio("is_animal", answer_name = "yes")],
184+
ground_truths=[get_radio("is_animal", answer_name = "yes")],
185+
expected = [1,0,0,0]
186+
),
187+
NameSpace(
188+
predictions=[get_radio("is_animal", answer_name = "yes")],
189+
ground_truths=[get_radio("is_animal", answer_name = "no")],
190+
expected = [1,0,0,0]
191+
),
192+
NameSpace(
193+
predictions=[
194+
get_radio("is_animal", answer_name = "yes")
195+
],
196+
ground_truths=[],
197+
expected = [0,1,0,0]
198+
),
199+
NameSpace(
200+
predictions=[],
201+
ground_truths=[get_radio("is_animal", answer_name = "yes")],
202+
expected = [0,0,0,1]
203+
),
204+
NameSpace(
205+
predictions=[],
206+
ground_truths=[],
207+
expected = [0,0,1,0]
208+
)
209+
]
210+
211+
212+
@pytest.fixture
213+
def radio_pairs():
214+
return [
215+
NameSpace(
216+
predictions=[get_text("animal_name", answer_name = "yes")],
217+
ground_truths=[get_text("animal_name", answer_name = "yes")],
218+
expected = [1,0,0,0]
219+
),
220+
NameSpace(
221+
predictions=[
222+
get_text("is_animal", answer_name = "yes")
223+
],
224+
ground_truths=[],
225+
expected = [0,1,0,0]
226+
),
227+
NameSpace(
228+
predictions=[],
229+
ground_truths=[get_text("is_animal", answer_name = "yes")],
230+
expected = [0,0,0,1]
231+
),
232+
NameSpace(
233+
predictions=[],
234+
ground_truths=[],
235+
expected = [0,0,1,0]
236+
)
237+
]
238+
239+
# TODO: Do we actually capture true negatives? We def should for classifications. and maybe for non-classifications too..
240+
# TODO: Change the values to be named. I can't even keep track of this shit
241+
242+
"""
243+
244+
# Current question.. how do we handle classification precision and recall...
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from labelbox.data.metrics.confusion_matrix.confusion_matrix import confusion_matrix_metric
2+
from labelbox.data.metrics.iou.iou import miou_metric
3+
from pytest_cases import parametrize, fixture_ref
4+
from unittest.mock import patch
5+
import math
6+
from pytest_cases import pytest_parametrize_plus, fixture_ref
7+
import numpy as np
8+
import base64
9+
10+
from labelbox.data.metrics.iou import data_row_miou, feature_miou_metric
11+
from labelbox.data.serialization import NDJsonConverter, LBV1Converter
12+
from labelbox.data.annotation_types import Label, ImageData, Mask
13+
14+
15+
16+
17+
@pytest_parametrize_plus("tool_examples",
18+
[
19+
fixture_ref('polygon_pair'),
20+
fixture_ref('rectangle_pair'),
21+
fixture_ref('mask_pair'),
22+
fixture_ref('line_pair'),
23+
fixture_ref('point_pair')]
24+
)
25+
def test_overlapping_pairs(tool_examples):
26+
for example in tool_examples:
27+
score = confusion_matrix_metric(example.predictions, example.ground_truths)
28+
if len(example.expected) == 0:
29+
assert len(score) == 0
30+
else:
31+
assert score[0].value == tuple(example.expected), f"{example.predictions},{example.ground_truths}"
32+
33+

0 commit comments

Comments
 (0)