Skip to content

Commit a720bff

Browse files
committed
Streamline annotations and filtering responses.
1 parent b8d6b06 commit a720bff

File tree

3 files changed

+64
-74
lines changed

3 files changed

+64
-74
lines changed

docs/vision-annotations.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
Vision Annotations
22
==================
33

4-
Image Annotations
5-
~~~~~~~~~~~~~~~~~
6-
74
.. automodule:: google.cloud.vision.annotations
85
:members:
96
:undoc-members:

vision/google/cloud/vision/annotations.py

Lines changed: 33 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,14 @@
1515
"""Annotations management for Vision API responses."""
1616

1717

18+
from google.cloud.vision.color import ImagePropertiesAnnotation
1819
from google.cloud.vision.entity import EntityAnnotation
1920
from google.cloud.vision.face import Face
20-
from google.cloud.vision.feature import FeatureTypes
21-
from google.cloud.vision.color import ImagePropertiesAnnotation
2221
from google.cloud.vision.safe import SafeSearchAnnotation
2322

2423

25-
_REVERSE_TYPES = {
26-
FeatureTypes.FACE_DETECTION: 'faceAnnotations',
27-
FeatureTypes.IMAGE_PROPERTIES: 'imagePropertiesAnnotation',
28-
FeatureTypes.LABEL_DETECTION: 'labelAnnotations',
29-
FeatureTypes.LANDMARK_DETECTION: 'landmarkAnnotations',
30-
FeatureTypes.LOGO_DETECTION: 'logoAnnotations',
31-
FeatureTypes.SAFE_SEARCH_DETECTION: 'safeSearchAnnotation',
32-
FeatureTypes.TEXT_DETECTION: 'textAnnotations',
33-
}
34-
35-
3624
class Annotations(object):
37-
"""Annotation class for managing responses.
25+
"""Helper class to bundle annotation responses.
3826
3927
:type faces: list
4028
:param faces: List of :class:`~google.cloud.vision.face.Face`.
@@ -65,13 +53,13 @@ class Annotations(object):
6553
"""
6654
def __init__(self, faces=None, properties=None, labels=None,
6755
landmarks=None, logos=None, safe_searches=None, texts=None):
68-
self.faces = faces or []
69-
self.properties = properties or []
70-
self.labels = labels or []
71-
self.landmarks = landmarks or []
72-
self.logos = logos or []
73-
self.safe_searches = safe_searches or []
74-
self.texts = texts or []
56+
self.faces = faces or ()
57+
self.properties = properties or ()
58+
self.labels = labels or ()
59+
self.landmarks = landmarks or ()
60+
self.logos = logos or ()
61+
self.safe_searches = safe_searches or ()
62+
self.texts = texts or ()
7563

7664
@classmethod
7765
def from_api_repr(cls, response):
@@ -84,45 +72,41 @@ def from_api_repr(cls, response):
8472
:returns: An instance of ``Annotations`` with detection types loaded.
8573
"""
8674
annotations = {}
87-
88-
for feature_type in response.keys():
89-
annotations[feature_type] = []
75+
key_map = {
76+
'faceAnnotations': 'faces',
77+
'imagePropertiesAnnotation': 'properties',
78+
'labelAnnotations': 'labels',
79+
'landmarkAnnotations': 'landmarks',
80+
'logoAnnotations': 'logos',
81+
'safeSearchAnnotation': 'safe_searches',
82+
'textAnnotations': 'texts'
83+
}
9084

9185
for feature_type, annotation in response.items():
92-
annotations[feature_type].extend(
86+
curr_feature = annotations.setdefault(key_map[feature_type], [])
87+
curr_feature.extend(
9388
_entity_from_response_type(feature_type, annotation))
94-
95-
faces = annotations.get(
96-
_REVERSE_TYPES[FeatureTypes.FACE_DETECTION], [])
97-
properties = annotations.get(
98-
_REVERSE_TYPES[FeatureTypes.IMAGE_PROPERTIES], [])
99-
labels = annotations.get(
100-
_REVERSE_TYPES[FeatureTypes.LABEL_DETECTION], [])
101-
landmarks = annotations.get(
102-
_REVERSE_TYPES[FeatureTypes.LANDMARK_DETECTION], [])
103-
logos = annotations.get(
104-
_REVERSE_TYPES[FeatureTypes.LOGO_DETECTION], [])
105-
safe_searches = annotations.get(
106-
_REVERSE_TYPES[FeatureTypes.SAFE_SEARCH_DETECTION], [])
107-
texts = annotations.get(
108-
_REVERSE_TYPES[FeatureTypes.TEXT_DETECTION], [])
109-
110-
return cls(faces=faces, properties=properties, labels=labels,
111-
landmarks=landmarks, logos=logos,
112-
safe_searches=safe_searches, texts=texts)
89+
return cls(**annotations)
11390

11491

11592
def _entity_from_response_type(feature_type, results):
116-
"""Convert a JSON result to an entity type based on the feature."""
117-
93+
"""Convert a JSON result to an entity type based on the feature.
94+
95+
:rtype: list
96+
:returns: List containing any of
97+
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`,
98+
:class:`~google.cloud.vision.entity.EntityAnnotation`,
99+
:class:`~google.cloud.vision.face.Face`,
100+
:class:`~google.cloud.vision.safe.SafeSearchAnnotation`.
101+
"""
118102
detected_objects = []
119-
if feature_type == _REVERSE_TYPES[FeatureTypes.FACE_DETECTION]:
103+
if feature_type == 'faceAnnotations':
120104
detected_objects.extend(
121105
Face.from_api_repr(face) for face in results)
122-
elif feature_type == _REVERSE_TYPES[FeatureTypes.IMAGE_PROPERTIES]:
106+
elif feature_type == 'imagePropertiesAnnotation':
123107
detected_objects.append(
124108
ImagePropertiesAnnotation.from_api_repr(results))
125-
elif feature_type == _REVERSE_TYPES[FeatureTypes.SAFE_SEARCH_DETECTION]:
109+
elif feature_type == 'safeSearchAnnotation':
126110
detected_objects.append(SafeSearchAnnotation.from_api_repr(results))
127111
else:
128112
for result in results:

vision/unit_tests/test_client.py

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,19 @@ def test_image_with_client(self):
8282
self.assertIsInstance(image, Image)
8383

8484
def test_multiple_detection_from_content(self):
85+
import copy
8586
from google.cloud.vision.feature import Feature
8687
from google.cloud.vision.feature import FeatureTypes
8788
from unit_tests._fixtures import LABEL_DETECTION_RESPONSE
8889
from unit_tests._fixtures import LOGO_DETECTION_RESPONSE
89-
RETURNED = LABEL_DETECTION_RESPONSE
90-
LOGOS = LOGO_DETECTION_RESPONSE['responses'][0]['logoAnnotations']
91-
RETURNED['responses'][0]['logoAnnotations'] = LOGOS
90+
91+
returned = copy.deepcopy(LABEL_DETECTION_RESPONSE)
92+
logos = copy.deepcopy(LOGO_DETECTION_RESPONSE['responses'][0])
93+
returned['responses'][0]['logoAnnotations'] = logos['logoAnnotations']
9294

9395
credentials = _Credentials()
9496
client = self._make_one(project=PROJECT, credentials=credentials)
95-
client._connection = _Connection(RETURNED)
97+
client._connection = _Connection(returned)
9698

9799
limit = 2
98100
label_feature = Feature(FeatureTypes.LABEL_DETECTION, limit)
@@ -103,19 +105,26 @@ def test_multiple_detection_from_content(self):
103105

104106
self.assertEqual(len(items.logos), 2)
105107
self.assertEqual(len(items.labels), 3)
106-
self.assertEqual(items.logos[0].description, 'Brand1')
107-
self.assertEqual(items.logos[0].score, 0.63192177)
108-
self.assertEqual(items.logos[1].description, 'Brand2')
109-
self.assertEqual(items.logos[1].score, 0.5492993)
110-
111-
self.assertEqual(items.labels[0].description, 'automobile')
112-
self.assertEqual(items.labels[0].score, 0.9776855)
113-
self.assertEqual(items.labels[1].description, 'vehicle')
114-
self.assertEqual(items.labels[1].score, 0.947987)
115-
self.assertEqual(items.labels[2].description, 'truck')
116-
self.assertEqual(items.labels[2].score, 0.88429511)
117-
118-
image_request = client._connection._requested[0]['data']['requests'][0]
108+
first_logo = items.logos[0]
109+
second_logo = items.logos[1]
110+
self.assertEqual(first_logo.description, 'Brand1')
111+
self.assertEqual(first_logo.score, 0.63192177)
112+
self.assertEqual(second_logo.description, 'Brand2')
113+
self.assertEqual(second_logo.score, 0.5492993)
114+
115+
first_label = items.labels[0]
116+
second_label = items.labels[1]
117+
third_label = items.labels[2]
118+
self.assertEqual(first_label.description, 'automobile')
119+
self.assertEqual(first_label.score, 0.9776855)
120+
self.assertEqual(second_label.description, 'vehicle')
121+
self.assertEqual(second_label.score, 0.947987)
122+
self.assertEqual(third_label.description, 'truck')
123+
self.assertEqual(third_label.score, 0.88429511)
124+
125+
requested = client._connection._requested
126+
requests = requested[0]['data']['requests']
127+
image_request = requests[0]
119128
label_request = image_request['features'][0]
120129
logo_request = image_request['features'][1]
121130

@@ -171,7 +180,7 @@ def test_face_detection_from_content_no_results(self):
171180

172181
image = client.image(content=IMAGE_CONTENT)
173182
faces = image.detect_faces(limit=5)
174-
self.assertEqual(faces, [])
183+
self.assertEqual(faces, ())
175184
self.assertEqual(len(faces), 0)
176185
image_request = client._connection._requested[0]['data']['requests'][0]
177186

@@ -211,7 +220,7 @@ def test_label_detection_no_results(self):
211220

212221
image = client.image(content=IMAGE_CONTENT)
213222
labels = image.detect_labels()
214-
self.assertEqual(labels, [])
223+
self.assertEqual(labels, ())
215224
self.assertEqual(len(labels), 0)
216225

217226
def test_landmark_detection_from_source(self):
@@ -264,7 +273,7 @@ def test_landmark_detection_no_results(self):
264273

265274
image = client.image(content=IMAGE_CONTENT)
266275
landmarks = image.detect_landmarks()
267-
self.assertEqual(landmarks, [])
276+
self.assertEqual(landmarks, ())
268277
self.assertEqual(len(landmarks), 0)
269278

270279
def test_logo_detection_from_source(self):
@@ -353,7 +362,7 @@ def test_safe_search_no_results(self):
353362

354363
image = client.image(content=IMAGE_CONTENT)
355364
safe_search = image.detect_safe_search()
356-
self.assertEqual(safe_search, [])
365+
self.assertEqual(safe_search, ())
357366
self.assertEqual(len(safe_search), 0)
358367

359368
def test_image_properties_detection_from_source(self):
@@ -389,7 +398,7 @@ def test_image_properties_no_results(self):
389398

390399
image = client.image(content=IMAGE_CONTENT)
391400
image_properties = image.detect_properties()
392-
self.assertEqual(image_properties, [])
401+
self.assertEqual(image_properties, ())
393402
self.assertEqual(len(image_properties), 0)
394403

395404

0 commit comments

Comments
 (0)