Skip to content

Commit 62cf9a8

Browse files
committed
Add Vision safe search detection.
1 parent 51d11da commit 62cf9a8

File tree

6 files changed

+164
-1
lines changed

6 files changed

+164
-1
lines changed

docs/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,11 @@
149149

150150
vision-usage
151151
vision-client
152-
vision-image
153152
vision-entity
154153
vision-feature
155154
vision-face
155+
vision-image
156+
vision-safe-search
156157

157158
.. toctree::
158159
:maxdepth: 0

docs/vision-safe-search.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Vision Safe Search
2+
==================
3+
4+
Safe Search Annotation
5+
~~~~~~~~~~~~~~~~~~~~~~
6+
7+
.. automodule:: google.cloud.vision.safe
8+
:members:
9+
:undoc-members:
10+
:show-inheritance:

google/cloud/vision/image.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from google.cloud.vision.face import Face
2323
from google.cloud.vision.feature import Feature
2424
from google.cloud.vision.feature import FeatureTypes
25+
from google.cloud.vision.safe import SafeSearchAnnotation
2526

2627

2728
class Image(object):
@@ -162,6 +163,22 @@ def detect_logos(self, limit=10):
162163
feature = Feature(FeatureTypes.LOGO_DETECTION, limit)
163164
return self._detect_annotation(feature)
164165

166+
def detect_safe_search(self, limit=10):
167+
"""Retreive safe search properties from an image.
168+
169+
:type limit: int
170+
:param limit: The number of faces to try and detect.
171+
172+
:rtype: list
173+
:returns: List of
174+
:class:`~google.cloud.vision.sage.SafeSearchAnnotation`.
175+
"""
176+
safe_detection_feature = Feature(FeatureTypes.SAFE_SEARCH_DETECTION,
177+
limit)
178+
result = self.client.annotate(self, [safe_detection_feature])
179+
safe_search_response = result['safeSearchAnnotation']
180+
return SafeSearchAnnotation.from_api_repr(safe_search_response)
181+
165182
def detect_text(self, limit=10):
166183
"""Detect text in an image.
167184

google/cloud/vision/safe.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
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+
"""Safe search class for information returned from annotating an image."""
16+
17+
18+
from google.cloud.vision.likelihood import Likelihood
19+
20+
21+
class SafeSearchAnnotation(object):
22+
"""Representation of a SafeSearchAnnotation.
23+
24+
:type adult_likelihood: :class:`~google.cloud.vision.likelihood.Likelihood`
25+
:param adult_likelihood: Likelihood that image contains adult material.
26+
27+
:type spoof_likelihood: :class:`~google.cloud.vision.likelihood.Likelihood`
28+
:param spoof_likelihood: Likelihood that image is a spoof.
29+
30+
:type medical_likelihood:
31+
:class:`~google.cloud.vision.likelihood.Likelihood`
32+
:param medical_likelihood: Likelihood that image contains medical material.
33+
34+
:type violence_likelihood:
35+
:class:`~google.cloud.vision.likelihood.Likelihood`
36+
:param violence_likelihood: Likelihood that image contains violence.
37+
"""
38+
39+
def __init__(self, adult_likelihood, spoof_likelihood, medical_likelihood,
40+
violence_likelihood):
41+
self._adult_likelihood = adult_likelihood
42+
self._spoof_likelihood = spoof_likelihood
43+
self._medical_likeliehood = medical_likelihood
44+
self._violence_likelihood = violence_likelihood
45+
46+
@classmethod
47+
def from_api_repr(cls, response):
48+
"""Factory: construct SafeSearchAnnotation from Vision API response.
49+
50+
:type response: dict
51+
:param response: Dictionary response from Vision API with safe search
52+
data.
53+
54+
:rtype: :class:`~google.cloud.vision.safe.SafeSearchAnnotation`
55+
:returns: Instance of ``SafeSearchAnnotation``.
56+
"""
57+
adult_likelihood = getattr(Likelihood, response['adult'])
58+
spoof_likelihood = getattr(Likelihood, response['spoof'])
59+
medical_likelihood = getattr(Likelihood, response['medical'])
60+
violence_likelihood = getattr(Likelihood, response['violence'])
61+
62+
return cls(adult_likelihood, spoof_likelihood, medical_likelihood,
63+
violence_likelihood)
64+
65+
@property
66+
def adult(self):
67+
"""Represents the adult contents likelihood for the image.
68+
69+
:rtype: :class:`~google.cloud.vision.likelihood.Likelihood`
70+
:returns: ``Likelihood`` of the image containing adult content.
71+
"""
72+
return self._adult_likelihood
73+
74+
@property
75+
def spoof(self):
76+
"""The likelihood that an obvious modification was made to the image.
77+
78+
:rtype: :class:`~google.cloud.vision.likelihood.Likelihood`
79+
:returns: The ``Likelihood`` that an obvious modification was made to
80+
the image's canonical version to make it appear funny or
81+
offensive.
82+
"""
83+
return self._spoof_likelihood
84+
85+
@property
86+
def medical(self):
87+
"""Likelihood this is a medical image.
88+
89+
:rtype: :class:`~google.cloud.vision.likelihood.Likelihood`
90+
:returns: The ``Likelihood`` that the image is medical in origin.
91+
"""
92+
return self._medical_likeliehood
93+
94+
@property
95+
def violence(self):
96+
"""Likeliehood that this image contains violence.
97+
98+
:rtype: :class:`~google.cloud.vision.likelihood.Likelihood`
99+
:returns: The ``Likelihood`` that the image contains violence.
100+
"""
101+
return self._violence_likelihood

unit_tests/vision/_fixtures.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,20 @@
15701570
}
15711571

15721572

1573+
SAFE_SEARCH_DETECTION_RESPONSE = {
1574+
'responses': [
1575+
{
1576+
'safeSearchAnnotation': {
1577+
'adult': 'VERY_UNLIKELY',
1578+
'spoof': 'UNLIKELY',
1579+
'medical': 'POSSIBLE',
1580+
'violence': 'VERY_UNLIKELY'
1581+
}
1582+
}
1583+
]
1584+
}
1585+
1586+
15731587
TEXT_DETECTION_RESPONSE = {
15741588
'responses': [
15751589
{

unit_tests/vision/test_client.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,26 @@ def test_text_detection_from_source(self):
228228
self.assertEqual('Google', text[1].description)
229229
self.assertEqual(694, text[0].bounds.vertices[0].y_coordinate)
230230

231+
def test_safe_search_detection_from_source(self):
232+
from google.cloud.vision.safe import SafeSearchAnnotation
233+
from unit_tests.vision._fixtures import SAFE_SEARCH_DETECTION_RESPONSE
234+
235+
RETURNED = SAFE_SEARCH_DETECTION_RESPONSE
236+
credentials = _Credentials()
237+
client = self._makeOne(project=self.PROJECT, credentials=credentials)
238+
client.connection = _Connection(RETURNED)
239+
240+
image = client.image(source_uri=_IMAGE_SOURCE)
241+
safe_search = image.detect_safe_search()
242+
self.assertTrue(isinstance(safe_search, SafeSearchAnnotation))
243+
image_request = client.connection._requested[0]['data']['requests'][0]
244+
self.assertEqual(_IMAGE_SOURCE,
245+
image_request['image']['source']['gcs_image_uri'])
246+
self.assertEqual('VERY_UNLIKELY', safe_search.adult)
247+
self.assertEqual('UNLIKELY', safe_search.spoof)
248+
self.assertEqual('POSSIBLE', safe_search.medical)
249+
self.assertEqual('VERY_UNLIKELY', safe_search.violence)
250+
231251

232252
class TestVisionRequest(unittest.TestCase):
233253
def _getTargetClass(self):

0 commit comments

Comments
 (0)