Skip to content

Commit 5cbbcb9

Browse files
authored
Merge pull request #2770 from daspecster/add-manual-detect-to-vision-2697
Add image.detect() for detecting multiple types.
2 parents f0950ca + 52aaf05 commit 5cbbcb9

File tree

6 files changed

+245
-64
lines changed

6 files changed

+245
-64
lines changed

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
:caption: Vision
154154

155155
vision-usage
156+
vision-annotations
156157
vision-client
157158
vision-color
158159
vision-entity

docs/vision-annotations.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Vision Annotations
2+
==================
3+
4+
.. automodule:: google.cloud.vision.annotations
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:

docs/vision-usage.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,35 @@ or pass in ``credentials`` and ``project`` explicitly.
3131
>>> client = vision.Client(project='my-project', credentials=creds)
3232
3333
34+
Manual Detection
35+
~~~~~~~~~~~~~~~~
36+
37+
You can call the detection method manually.
38+
39+
.. code-block:: python
40+
41+
>>> from google.cloud import vision
42+
>>> from google.cloud.vision.feature import Feature
43+
>>> from google.cloud.vision.feature import FeatureTypes
44+
>>> client = vision.Client()
45+
>>> image = client.image(source_uri='gs://my-test-bucket/image.jpg')
46+
>>> features = [Feature(FeatureTypes.FACE_DETECTION, 5),
47+
... Feature(FeatureTypes.LOGO_DETECTION, 3)]
48+
>>> annotations = image.detect(features)
49+
>>> len(annotations.faces)
50+
2
51+
>>> for face in annotations.faces:
52+
... print(face.joy_likelihood)
53+
0.94099093
54+
0.54453093
55+
>>> len(annotations.logos)
56+
2
57+
>>> for logo in annotations.logos:
58+
... print(logo.description)
59+
'google'
60+
'github'
61+
62+
3463
Face Detection
3564
~~~~~~~~~~~~~~
3665

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Copyright 2016 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Annotations management for Vision API responses."""
16+
17+
18+
from google.cloud.vision.color import ImagePropertiesAnnotation
19+
from google.cloud.vision.entity import EntityAnnotation
20+
from google.cloud.vision.face import Face
21+
from google.cloud.vision.safe import SafeSearchAnnotation
22+
23+
24+
FACE_ANNOTATIONS = 'faceAnnotations'
25+
IMAGE_PROPERTIES_ANNOTATION = 'imagePropertiesAnnotation'
26+
SAFE_SEARCH_ANNOTATION = 'safeSearchAnnotation'
27+
28+
_KEY_MAP = {
29+
FACE_ANNOTATIONS: 'faces',
30+
IMAGE_PROPERTIES_ANNOTATION: 'properties',
31+
'labelAnnotations': 'labels',
32+
'landmarkAnnotations': 'landmarks',
33+
'logoAnnotations': 'logos',
34+
SAFE_SEARCH_ANNOTATION: 'safe_searches',
35+
'textAnnotations': 'texts'
36+
}
37+
38+
39+
class Annotations(object):
40+
"""Helper class to bundle annotation responses.
41+
42+
:type faces: list
43+
:param faces: List of :class:`~google.cloud.vision.face.Face`.
44+
45+
:type properties: list
46+
:param properties:
47+
List of :class:`~google.cloud.vision.color.ImagePropertiesAnnotation`.
48+
49+
:type labels: list
50+
:param labels: List of
51+
:class:`~google.cloud.vision.entity.EntityAnnotation`.
52+
53+
:type landmarks: list
54+
:param landmarks: List of
55+
:class:`~google.cloud.vision.entity.EntityAnnotation.`
56+
57+
:type logos: list
58+
:param logos: List of
59+
:class:`~google.cloud.vision.entity.EntityAnnotation`.
60+
61+
:type safe_searches: list
62+
:param safe_searches:
63+
List of :class:`~google.cloud.vision.safe.SafeSearchAnnotation`
64+
65+
:type texts: list
66+
:param texts: List of
67+
:class:`~google.cloud.vision.entity.EntityAnnotation`.
68+
"""
69+
def __init__(self, faces=(), properties=(), labels=(), landmarks=(),
70+
logos=(), safe_searches=(), texts=()):
71+
self.faces = faces
72+
self.properties = properties
73+
self.labels = labels
74+
self.landmarks = landmarks
75+
self.logos = logos
76+
self.safe_searches = safe_searches
77+
self.texts = texts
78+
79+
@classmethod
80+
def from_api_repr(cls, response):
81+
"""Factory: construct an instance of ``Annotations`` from a response.
82+
83+
:type response: dict
84+
:param response: Vision API response object.
85+
86+
:rtype: :class:`~google.cloud.vision.annotations.Annotations`
87+
:returns: An instance of ``Annotations`` with detection types loaded.
88+
"""
89+
annotations = {}
90+
for feature_type, annotation in response.items():
91+
curr_feature = annotations.setdefault(_KEY_MAP[feature_type], [])
92+
curr_feature.extend(
93+
_entity_from_response_type(feature_type, annotation))
94+
return cls(**annotations)
95+
96+
97+
def _entity_from_response_type(feature_type, results):
98+
"""Convert a JSON result to an entity type based on the feature.
99+
100+
:rtype: list
101+
:returns: List containing any of
102+
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`,
103+
:class:`~google.cloud.vision.entity.EntityAnnotation`,
104+
:class:`~google.cloud.vision.face.Face`,
105+
:class:`~google.cloud.vision.safe.SafeSearchAnnotation`.
106+
"""
107+
detected_objects = []
108+
if feature_type == FACE_ANNOTATIONS:
109+
detected_objects.extend(
110+
Face.from_api_repr(face) for face in results)
111+
elif feature_type == IMAGE_PROPERTIES_ANNOTATION:
112+
detected_objects.append(
113+
ImagePropertiesAnnotation.from_api_repr(results))
114+
elif feature_type == SAFE_SEARCH_ANNOTATION:
115+
detected_objects.append(SafeSearchAnnotation.from_api_repr(results))
116+
else:
117+
for result in results:
118+
detected_objects.append(EntityAnnotation.from_api_repr(result))
119+
return detected_objects

vision/google/cloud/vision/image.py

Lines changed: 30 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,9 @@
1919

2020
from google.cloud._helpers import _to_bytes
2121
from google.cloud._helpers import _bytes_to_unicode
22-
from google.cloud.vision.entity import EntityAnnotation
23-
from google.cloud.vision.face import Face
22+
from google.cloud.vision.annotations import Annotations
2423
from google.cloud.vision.feature import Feature
2524
from google.cloud.vision.feature import FeatureTypes
26-
from google.cloud.vision.color import ImagePropertiesAnnotation
27-
from google.cloud.vision.safe import SafeSearchAnnotation
28-
29-
30-
_FACE_DETECTION = 'FACE_DETECTION'
31-
_IMAGE_PROPERTIES = 'IMAGE_PROPERTIES'
32-
_LABEL_DETECTION = 'LABEL_DETECTION'
33-
_LANDMARK_DETECTION = 'LANDMARK_DETECTION'
34-
_LOGO_DETECTION = 'LOGO_DETECTION'
35-
_SAFE_SEARCH_DETECTION = 'SAFE_SEARCH_DETECTION'
36-
_TEXT_DETECTION = 'TEXT_DETECTION'
37-
38-
_REVERSE_TYPES = {
39-
_FACE_DETECTION: 'faceAnnotations',
40-
_IMAGE_PROPERTIES: 'imagePropertiesAnnotation',
41-
_LABEL_DETECTION: 'labelAnnotations',
42-
_LANDMARK_DETECTION: 'landmarkAnnotations',
43-
_LOGO_DETECTION: 'logoAnnotations',
44-
_SAFE_SEARCH_DETECTION: 'safeSearchAnnotation',
45-
_TEXT_DETECTION: 'textAnnotations',
46-
}
4725

4826

4927
class Image(object):
@@ -105,7 +83,7 @@ def source(self):
10583
return self._source
10684

10785
def _detect_annotation(self, features):
108-
"""Generic method for detecting a single annotation.
86+
"""Generic method for detecting annotations.
10987
11088
:type features: list
11189
:param features: List of :class:`~google.cloud.vision.feature.Feature`
@@ -118,12 +96,21 @@ def _detect_annotation(self, features):
11896
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`,
11997
:class:`~google.cloud.vision.sage.SafeSearchAnnotation`,
12098
"""
121-
detected_objects = []
12299
results = self.client.annotate(self, features)
123-
for feature in features:
124-
detected_objects.extend(
125-
_entity_from_response_type(feature.feature_type, results))
126-
return detected_objects
100+
return Annotations.from_api_repr(results)
101+
102+
def detect(self, features):
103+
"""Detect multiple feature types.
104+
105+
:type features: list of :class:`~google.cloud.vision.feature.Feature`
106+
:param features: List of the ``Feature`` indication the type of
107+
annotation to perform.
108+
109+
:rtype: list
110+
:returns: List of
111+
:class:`~google.cloud.vision.entity.EntityAnnotation`.
112+
"""
113+
return self._detect_annotation(features)
127114

128115
def detect_faces(self, limit=10):
129116
"""Detect faces in image.
@@ -135,7 +122,8 @@ def detect_faces(self, limit=10):
135122
:returns: List of :class:`~google.cloud.vision.face.Face`.
136123
"""
137124
features = [Feature(FeatureTypes.FACE_DETECTION, limit)]
138-
return self._detect_annotation(features)
125+
annotations = self._detect_annotation(features)
126+
return annotations.faces
139127

140128
def detect_labels(self, limit=10):
141129
"""Detect labels that describe objects in an image.
@@ -147,7 +135,8 @@ def detect_labels(self, limit=10):
147135
:returns: List of :class:`~google.cloud.vision.entity.EntityAnnotation`
148136
"""
149137
features = [Feature(FeatureTypes.LABEL_DETECTION, limit)]
150-
return self._detect_annotation(features)
138+
annotations = self._detect_annotation(features)
139+
return annotations.labels
151140

152141
def detect_landmarks(self, limit=10):
153142
"""Detect landmarks in an image.
@@ -160,7 +149,8 @@ def detect_landmarks(self, limit=10):
160149
:class:`~google.cloud.vision.entity.EntityAnnotation`.
161150
"""
162151
features = [Feature(FeatureTypes.LANDMARK_DETECTION, limit)]
163-
return self._detect_annotation(features)
152+
annotations = self._detect_annotation(features)
153+
return annotations.landmarks
164154

165155
def detect_logos(self, limit=10):
166156
"""Detect logos in an image.
@@ -173,7 +163,8 @@ def detect_logos(self, limit=10):
173163
:class:`~google.cloud.vision.entity.EntityAnnotation`.
174164
"""
175165
features = [Feature(FeatureTypes.LOGO_DETECTION, limit)]
176-
return self._detect_annotation(features)
166+
annotations = self._detect_annotation(features)
167+
return annotations.logos
177168

178169
def detect_properties(self, limit=10):
179170
"""Detect the color properties of an image.
@@ -186,7 +177,8 @@ def detect_properties(self, limit=10):
186177
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`.
187178
"""
188179
features = [Feature(FeatureTypes.IMAGE_PROPERTIES, limit)]
189-
return self._detect_annotation(features)
180+
annotations = self._detect_annotation(features)
181+
return annotations.properties
190182

191183
def detect_safe_search(self, limit=10):
192184
"""Retreive safe search properties from an image.
@@ -199,7 +191,8 @@ def detect_safe_search(self, limit=10):
199191
:class:`~google.cloud.vision.sage.SafeSearchAnnotation`.
200192
"""
201193
features = [Feature(FeatureTypes.SAFE_SEARCH_DETECTION, limit)]
202-
return self._detect_annotation(features)
194+
annotations = self._detect_annotation(features)
195+
return annotations.safe_searches
203196

204197
def detect_text(self, limit=10):
205198
"""Detect text in an image.
@@ -212,27 +205,5 @@ def detect_text(self, limit=10):
212205
:class:`~google.cloud.vision.entity.EntityAnnotation`.
213206
"""
214207
features = [Feature(FeatureTypes.TEXT_DETECTION, limit)]
215-
return self._detect_annotation(features)
216-
217-
218-
def _entity_from_response_type(feature_type, results):
219-
"""Convert a JSON result to an entity type based on the feature."""
220-
feature_key = _REVERSE_TYPES[feature_type]
221-
annotations = results.get(feature_key, ())
222-
if not annotations:
223-
return []
224-
225-
detected_objects = []
226-
if feature_type == _FACE_DETECTION:
227-
detected_objects.extend(
228-
Face.from_api_repr(face) for face in annotations)
229-
elif feature_type == _IMAGE_PROPERTIES:
230-
detected_objects.append(
231-
ImagePropertiesAnnotation.from_api_repr(annotations))
232-
elif feature_type == _SAFE_SEARCH_DETECTION:
233-
detected_objects.append(
234-
SafeSearchAnnotation.from_api_repr(annotations))
235-
else:
236-
for result in annotations:
237-
detected_objects.append(EntityAnnotation.from_api_repr(result))
238-
return detected_objects
208+
annotations = self._detect_annotation(features)
209+
return annotations.texts

0 commit comments

Comments
 (0)