Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c9b6732

Browse filesBrowse files
committed
Add GAPIC support for image properties detection.
1 parent 309fa8d commit c9b6732
Copy full SHA for c9b6732

File tree

Expand file treeCollapse file tree

7 files changed

+225
-63
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+225
-63
lines changed
Open diff view settings
Collapse file

‎docs/vision-usage.rst‎

Copy file name to clipboardExpand all lines: docs/vision-usage.rst
+4-4Lines changed: 4 additions & 4 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -271,13 +271,13 @@ image and determine the dominant colors in the image.
271271
>>> client = vision.Client()
272272
>>> with open('./image.jpg', 'rb') as image_file:
273273
... image = client.image(content=image_file.read())
274-
>>> results = image.detect_properties()
275-
>>> colors = results[0].colors
274+
>>> properties = image.detect_properties()
275+
>>> colors = properties.colors
276276
>>> first_color = colors[0]
277277
>>> first_color.red
278-
244
278+
244.0
279279
>>> first_color.blue
280-
134
280+
134.0
281281
>>> first_color.score
282282
0.65519291
283283
>>> first_color.pixel_fraction
Collapse file

‎system_tests/vision.py‎

Copy file name to clipboardExpand all lines: system_tests/vision.py
+7-19Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -474,13 +474,13 @@ def tearDown(self):
474474
value.delete()
475475

476476
def _assert_color(self, color):
477-
self.assertIsInstance(color.red, int)
478-
self.assertIsInstance(color.green, int)
479-
self.assertIsInstance(color.blue, int)
477+
self.assertIsInstance(color.red, float)
478+
self.assertIsInstance(color.green, float)
479+
self.assertIsInstance(color.blue, float)
480+
self.assertIsInstance(color.alpha, float)
480481
self.assertNotEqual(color.red, 0.0)
481482
self.assertNotEqual(color.green, 0.0)
482483
self.assertNotEqual(color.blue, 0.0)
483-
self.assertIsInstance(color.alpha, float)
484484

485485
def _assert_properties(self, image_property):
486486
from google.cloud.vision.color import ImagePropertiesAnnotation
@@ -493,19 +493,13 @@ def _assert_properties(self, image_property):
493493
self.assertNotEqual(color_info.score, 0.0)
494494

495495
def test_detect_properties_content(self):
496-
self._pb_not_implemented_skip(
497-
'gRPC not implemented for image properties detection.')
498496
client = Config.CLIENT
499497
with open(FACE_FILE, 'rb') as image_file:
500498
image = client.image(content=image_file.read())
501499
properties = image.detect_properties()
502-
self.assertEqual(len(properties), 1)
503-
image_property = properties[0]
504-
self._assert_properties(image_property)
500+
self._assert_properties(properties)
505501

506502
def test_detect_properties_gcs(self):
507-
self._pb_not_implemented_skip(
508-
'gRPC not implemented for image properties detection.')
509503
client = Config.CLIENT
510504
bucket_name = Config.TEST_BUCKET.name
511505
blob_name = 'faces.jpg'
@@ -518,16 +512,10 @@ def test_detect_properties_gcs(self):
518512

519513
image = client.image(source_uri=source_uri)
520514
properties = image.detect_properties()
521-
self.assertEqual(len(properties), 1)
522-
image_property = properties[0]
523-
self._assert_properties(image_property)
515+
self._assert_properties(properties)
524516

525517
def test_detect_properties_filename(self):
526-
self._pb_not_implemented_skip(
527-
'gRPC not implemented for image properties detection.')
528518
client = Config.CLIENT
529519
image = client.image(filename=FACE_FILE)
530520
properties = image.detect_properties()
531-
self.assertEqual(len(properties), 1)
532-
image_property = properties[0]
533-
self._assert_properties(image_property)
521+
self._assert_properties(properties)
Collapse file

‎vision/google/cloud/vision/annotations.py‎

Copy file name to clipboardExpand all lines: vision/google/cloud/vision/annotations.py
+25-9Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Annotations management for Vision API responses."""
1616

17+
import six
1718

1819
from google.cloud.vision.color import ImagePropertiesAnnotation
1920
from google.cloud.vision.entity import EntityAnnotation
@@ -86,11 +87,11 @@ def from_api_repr(cls, response):
8687
:rtype: :class:`~google.cloud.vision.annotations.Annotations`
8788
:returns: An instance of ``Annotations`` with detection types loaded.
8889
"""
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))
90+
annotations = {
91+
_KEY_MAP[feature_type]: _entity_from_response_type(
92+
feature_type, annotation)
93+
for feature_type, annotation in six.iteritems(response)
94+
}
9495
return cls(**annotations)
9596

9697
@classmethod
@@ -123,12 +124,14 @@ def _process_image_annotations(image):
123124
'labels': _make_entity_from_pb(image.label_annotations),
124125
'landmarks': _make_entity_from_pb(image.landmark_annotations),
125126
'logos': _make_entity_from_pb(image.logo_annotations),
127+
'properties': _make_image_properties_from_pb(
128+
image.image_properties_annotation),
126129
'texts': _make_entity_from_pb(image.text_annotations),
127130
}
128131

129132

130133
def _make_entity_from_pb(annotations):
131-
"""Create an entity from a gRPC response.
134+
"""Create an entity from a protobuf response.
132135
133136
:type annotations:
134137
:class:`~google.cloud.grpc.vision.v1.image_annotator_pb2.EntityAnnotation`
@@ -141,7 +144,7 @@ def _make_entity_from_pb(annotations):
141144

142145

143146
def _make_faces_from_pb(faces):
144-
"""Create face objects from a gRPC response.
147+
"""Create face objects from a protobuf response.
145148
146149
:type faces:
147150
:class:`~google.cloud.grpc.vision.v1.image_annotator_pb2.FaceAnnotation`
@@ -153,6 +156,20 @@ def _make_faces_from_pb(faces):
153156
return [Face.from_pb(face) for face in faces]
154157

155158

159+
def _make_image_properties_from_pb(image_properties):
160+
"""Create ``ImageProperties`` object from a protobuf response.
161+
162+
:type image_properties: :class:`~google.cloud.grpc.vision.v1.\
163+
image_annotator_pb2.ImagePropertiesAnnotation`
164+
:param image_properties: Protobuf instance of
165+
``ImagePropertiesAnnotation``.
166+
167+
:rtype: list or ``None``
168+
:returns: List of ``ImageProperties`` or ``None``.
169+
"""
170+
return ImagePropertiesAnnotation.from_pb(image_properties)
171+
172+
156173
def _entity_from_response_type(feature_type, results):
157174
"""Convert a JSON result to an entity type based on the feature.
158175
@@ -168,8 +185,7 @@ def _entity_from_response_type(feature_type, results):
168185
detected_objects.extend(
169186
Face.from_api_repr(face) for face in results)
170187
elif feature_type == _IMAGE_PROPERTIES_ANNOTATION:
171-
detected_objects.append(
172-
ImagePropertiesAnnotation.from_api_repr(results))
188+
return ImagePropertiesAnnotation.from_api_repr(results)
173189
elif feature_type == _SAFE_SEARCH_ANNOTATION:
174190
detected_objects.append(SafeSearchAnnotation.from_api_repr(results))
175191
else:
Collapse file

‎vision/google/cloud/vision/color.py‎

Copy file name to clipboardExpand all lines: vision/google/cloud/vision/color.py
+70-26Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,37 @@ def __init__(self, colors):
2626
self._colors = colors
2727

2828
@classmethod
29-
def from_api_repr(cls, response):
29+
def from_api_repr(cls, image_properties):
3030
"""Factory: construct ``ImagePropertiesAnnotation`` from a response.
3131
32-
:type response: dict
33-
:param response: Dictionary response from Vision API with image
34-
properties data.
32+
:type image_properties: dict
33+
:param image_properties: Dictionary response from Vision API with image
34+
properties data.
35+
36+
:rtype: list of
37+
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`.
38+
:returns: List of ``ImagePropertiesAnnotation``.
39+
"""
40+
colors = image_properties.get('dominantColors', {}).get('colors', ())
41+
return cls([ColorInformation.from_api_repr(color)
42+
for color in colors])
43+
44+
@classmethod
45+
def from_pb(cls, image_properties):
46+
"""Factory: construct ``ImagePropertiesAnnotation`` from a response.
47+
48+
:type image_properties: :class:`~google.cloud.grpc.vision.v1.\
49+
image_annotator_pb2.ImageProperties`
50+
:param image_properties: Protobuf response from Vision API with image
51+
properties data.
3552
36-
:rtype: :class:`~google.cloud.vision.color.ImagePropertiesAnnotation`.
37-
:returns: Populated instance of ``ImagePropertiesAnnotation``.
53+
:rtype: list of
54+
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`
55+
:returns: List of ``ImagePropertiesAnnotation``.
3856
"""
39-
raw_colors = response.get('dominantColors', {}).get('colors', ())
40-
colors = [ColorInformation.from_api_repr(color)
41-
for color in raw_colors]
42-
return cls(colors)
57+
colors = getattr(image_properties.dominant_colors, 'colors', ())
58+
if len(colors) > 0:
59+
return cls([ColorInformation.from_pb(color) for color in colors])
4360

4461
@property
4562
def colors(self):
@@ -54,17 +71,17 @@ def colors(self):
5471
class Color(object):
5572
"""Representation of RGBA color information.
5673
57-
:type red: int
74+
:type red: float
5875
:param red: The amount of red in the color as a value in the interval
59-
[0, 255].
76+
[0.0, 255.0].
6077
61-
:type green: int
78+
:type green: float
6279
:param green: The amount of green in the color as a value in the interval
63-
[0, 255].
80+
[0.0, 255.0].
6481
65-
:type blue: int
82+
:type blue: float
6683
:param blue: The amount of blue in the color as a value in the interval
67-
[0, 255].
84+
[0.0, 255.0].
6885
6986
:type alpha: float
7087
:param alpha: The fraction of this color that should be applied to the
@@ -86,13 +103,25 @@ def from_api_repr(cls, response):
86103
:rtype: :class:`~google.cloud.vision.color.Color`
87104
:returns: Instance of :class:`~google.cloud.vision.color.Color`.
88105
"""
89-
red = response.get('red', 0)
90-
green = response.get('green', 0)
91-
blue = response.get('blue', 0)
106+
red = float(response.get('red', 0.0))
107+
green = float(response.get('green', 0.0))
108+
blue = float(response.get('blue', 0.0))
92109
alpha = response.get('alpha', 0.0)
93110

94111
return cls(red, green, blue, alpha)
95112

113+
@classmethod
114+
def from_pb(cls, color):
115+
"""Factory: construct a ``Color`` from a protobuf response.
116+
117+
:type color: :module: `google.type.color_pb2`
118+
:param color: ``Color`` from API Response.
119+
120+
:rtype: :class:`~google.cloud.vision.color.Color`
121+
:returns: Instance of :class:`~google.cloud.vision.color.Color`.
122+
"""
123+
return cls(color.red, color.green, color.blue, color.alpha.value)
124+
96125
@property
97126
def red(self):
98127
"""Red component of the color.
@@ -149,19 +178,34 @@ def __init__(self, color, score, pixel_fraction):
149178
self._pixel_fraction = pixel_fraction
150179

151180
@classmethod
152-
def from_api_repr(cls, response):
153-
"""Factory: construct ``ColorInformation`` for a color found.
181+
def from_api_repr(cls, color_information):
182+
"""Factory: construct ``ColorInformation`` for a color.
154183
155-
:type response: dict
156-
:param response: Color data with extra meta information.
184+
:type color_information: dict
185+
:param color_information: Color data with extra meta information.
157186
158187
:rtype: :class:`~google.cloud.vision.color.ColorInformation`
159188
:returns: Instance of ``ColorInformation``.
160189
"""
161-
color = Color.from_api_repr(response.get('color'))
162-
score = response.get('score')
163-
pixel_fraction = response.get('pixelFraction')
190+
color = Color.from_api_repr(color_information.get('color', {}))
191+
score = color_information.get('score')
192+
pixel_fraction = color_information.get('pixelFraction')
193+
return cls(color, score, pixel_fraction)
164194

195+
@classmethod
196+
def from_pb(cls, color_information):
197+
"""Factory: construct ``ColorInformation`` for a color.
198+
199+
:type color_information: :class:`~google.cloud.grpc.vision.v1.\
200+
image_annotator_pb2.ColorInfo`
201+
:param color_information: Color data with extra meta information.
202+
203+
:rtype: :class:`~google.cloud.vision.color.ColorInformation`
204+
:returns: Instance of ``ColorInformation``.
205+
"""
206+
color = Color.from_pb(color_information.color)
207+
score = color_information.score
208+
pixel_fraction = color_information.pixel_fraction
165209
return cls(color, score, pixel_fraction)
166210

167211
@property
Collapse file

‎vision/unit_tests/test_annotations.py‎

Copy file name to clipboardExpand all lines: vision/unit_tests/test_annotations.py
+32-1Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def test_from_pb(self):
7777
self.assertEqual(annotations.landmarks, [])
7878
self.assertEqual(annotations.texts, [])
7979
self.assertEqual(annotations.safe_searches, ())
80-
self.assertEqual(annotations.properties, ())
80+
self.assertIsNone(annotations.properties)
8181

8282

8383
class Test__make_entity_from_pb(unittest.TestCase):
@@ -122,6 +122,37 @@ def test_it(self):
122122
self.assertIsInstance(faces[0], Face)
123123

124124

125+
class Test__make_image_properties_from_pb(unittest.TestCase):
126+
def _call_fut(self, annotations):
127+
from google.cloud.vision.annotations import (
128+
_make_image_properties_from_pb)
129+
return _make_image_properties_from_pb(annotations)
130+
131+
def test_it(self):
132+
from google.cloud.grpc.vision.v1 import image_annotator_pb2
133+
from google.protobuf.wrappers_pb2 import FloatValue
134+
from google.type.color_pb2 import Color
135+
136+
alpha = FloatValue(value=1.0)
137+
color_pb = Color(red=1.0, green=2.0, blue=3.0, alpha=alpha)
138+
color_info_pb = image_annotator_pb2.ColorInfo(color=color_pb,
139+
score=1.0,
140+
pixel_fraction=1.0)
141+
dominant_colors = image_annotator_pb2.DominantColorsAnnotation(
142+
colors=[color_info_pb])
143+
144+
image_properties_pb = image_annotator_pb2.ImageProperties(
145+
dominant_colors=dominant_colors)
146+
147+
image_properties = self._call_fut(image_properties_pb)
148+
self.assertEqual(image_properties.colors[0].pixel_fraction, 1.0)
149+
self.assertEqual(image_properties.colors[0].score, 1.0)
150+
self.assertEqual(image_properties.colors[0].color.red, 1.0)
151+
self.assertEqual(image_properties.colors[0].color.green, 2.0)
152+
self.assertEqual(image_properties.colors[0].color.blue, 3.0)
153+
self.assertEqual(image_properties.colors[0].color.alpha, 1.0)
154+
155+
125156
class Test__process_image_annotations(unittest.TestCase):
126157
def _call_fut(self, image):
127158
from google.cloud.vision.annotations import _process_image_annotations
Collapse file

‎vision/unit_tests/test_client.py‎

Copy file name to clipboardExpand all lines: vision/unit_tests/test_client.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ def test_image_properties_detection_from_source(self):
455455
client._connection = _Connection(RETURNED)
456456

457457
image = client.image(source_uri=IMAGE_SOURCE)
458-
image_properties = image.detect_properties()[0]
458+
image_properties = image.detect_properties()
459459
self.assertIsInstance(image_properties, ImagePropertiesAnnotation)
460460
image_request = client._connection._requested[0]['data']['requests'][0]
461461
self.assertEqual(IMAGE_SOURCE,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.