Skip to content

Commit

Permalink
Add GAPIC support for image properties detection.
Browse files Browse the repository at this point in the history
  • Loading branch information
daspecster committed Jan 19, 2017
1 parent 309fa8d commit 2aaec55
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 29 deletions.
17 changes: 7 additions & 10 deletions system_tests/vision.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,13 +474,16 @@ def tearDown(self):
value.delete()

def _assert_color(self, color):
self.assertIsInstance(color.red, int)
self.assertIsInstance(color.green, int)
self.assertIsInstance(color.blue, int)
self.assertIsInstance(color.red, (int, float))
self.assertIsInstance(color.green, (int, float))
self.assertIsInstance(color.blue, (int, float))
if not isinstance(color.alpha, float):
self.assertIsInstance(color.alpha.value, float)
else:
self.assertIsInstance(color.alpha, float)
self.assertNotEqual(color.red, 0.0)
self.assertNotEqual(color.green, 0.0)
self.assertNotEqual(color.blue, 0.0)
self.assertIsInstance(color.alpha, float)

def _assert_properties(self, image_property):
from google.cloud.vision.color import ImagePropertiesAnnotation
Expand All @@ -493,8 +496,6 @@ def _assert_properties(self, image_property):
self.assertNotEqual(color_info.score, 0.0)

def test_detect_properties_content(self):
self._pb_not_implemented_skip(
'gRPC not implemented for image properties detection.')
client = Config.CLIENT
with open(FACE_FILE, 'rb') as image_file:
image = client.image(content=image_file.read())
Expand All @@ -504,8 +505,6 @@ def test_detect_properties_content(self):
self._assert_properties(image_property)

def test_detect_properties_gcs(self):
self._pb_not_implemented_skip(
'gRPC not implemented for image properties detection.')
client = Config.CLIENT
bucket_name = Config.TEST_BUCKET.name
blob_name = 'faces.jpg'
Expand All @@ -523,8 +522,6 @@ def test_detect_properties_gcs(self):
self._assert_properties(image_property)

def test_detect_properties_filename(self):
self._pb_not_implemented_skip(
'gRPC not implemented for image properties detection.')
client = Config.CLIENT
image = client.image(filename=FACE_FILE)
properties = image.detect_properties()
Expand Down
20 changes: 18 additions & 2 deletions vision/google/cloud/vision/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,14 @@ def _process_image_annotations(image):
'labels': _make_entity_from_pb(image.label_annotations),
'landmarks': _make_entity_from_pb(image.landmark_annotations),
'logos': _make_entity_from_pb(image.logo_annotations),
'properties': _make_image_properties_from_pb(
image.image_properties_annotation),
'texts': _make_entity_from_pb(image.text_annotations),
}


def _make_entity_from_pb(annotations):
"""Create an entity from a gRPC response.
"""Create an entity from a protobuf response.
:type annotations:
:class:`~google.cloud.grpc.vision.v1.image_annotator_pb2.EntityAnnotation`
Expand All @@ -141,7 +143,7 @@ def _make_entity_from_pb(annotations):


def _make_faces_from_pb(faces):
"""Create face objects from a gRPC response.
"""Create face objects from a protobuf response.
:type faces:
:class:`~google.cloud.grpc.vision.v1.image_annotator_pb2.FaceAnnotation`
Expand All @@ -153,6 +155,20 @@ def _make_faces_from_pb(faces):
return [Face.from_pb(face) for face in faces]


def _make_image_properties_from_pb(image_properties):
"""Create ``ImageProperties`` object from a protobuf response.
:type image_properties: :class:`~google.cloud.grpc.vision.v1.\
image_annotator_pb2.ImagePropertiesAnnotation`
:param image_properties: Protobuf instance of
``ImagePropertiesAnnotation``.
:rtype: list
:returns: List of ``ImageProperties``.
"""
return ImagePropertiesAnnotation.from_pb(image_properties)


def _entity_from_response_type(feature_type, results):
"""Convert a JSON result to an entity type based on the feature.
Expand Down
83 changes: 66 additions & 17 deletions vision/google/cloud/vision/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,38 @@ def __init__(self, colors):
self._colors = colors

@classmethod
def from_api_repr(cls, response):
def from_api_repr(cls, image_properties):
"""Factory: construct ``ImagePropertiesAnnotation`` from a response.
:type response: dict
:param response: Dictionary response from Vision API with image
properties data.
:type image_properties: dict
:param image_properties: Dictionary response from Vision API with image
properties data.
:rtype: :class:`~google.cloud.vision.color.ImagePropertiesAnnotation`.
:returns: Populated instance of ``ImagePropertiesAnnotation``.
:rtype: list of
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`.
:returns: List of ``ImagePropertiesAnnotation``.
"""
raw_colors = response.get('dominantColors', {}).get('colors', ())
colors = [ColorInformation.from_api_repr(color)
for color in raw_colors]
return cls(colors)
colors = image_properties.get('dominantColors', {}).get('colors', ())
return cls([ColorInformation.from_api_repr(color)
for color in colors])

@classmethod
def from_pb(cls, image_properties):
"""Factory: construct ``ImagePropertiesAnnotation`` from a response.
:type image_properties: :class:`~google.cloud.grpc.vision.v1.\
image_annotator_pb2.ImageProperties`
:param image_properties: Protobuf response from Vision API with image
properties data.
:rtype: list of
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`
:returns: List of ``ImagePropertiesAnnotation``.
"""
colors = getattr(image_properties.dominant_colors, 'colors', ())
if len(colors) == 0:
return ()
return [cls([ColorInformation.from_pb(color) for color in colors])]

@property
def colors(self):
Expand Down Expand Up @@ -93,6 +111,22 @@ def from_api_repr(cls, response):

return cls(red, green, blue, alpha)

@classmethod
def from_pb(cls, color):
"""Factory: construct a ``Color`` from a protobuf response.
:type color: :class:`~google.type.color_pb2`
:param color: ``Color`` from API Response.
:rtype: :class:`~google.cloud.vision.color.Color`
:returns: Instance of :class:`~google.cloud.vision.color.Color`.
"""
red = getattr(color, 'red', 0.0)
green = getattr(color, 'green', 0.0)
blue = getattr(color, 'blue', 0.0)
alpha = getattr(color.alpha, 'value', 0.0)
return cls(red, green, blue, alpha)

@property
def red(self):
"""Red component of the color.
Expand Down Expand Up @@ -149,19 +183,34 @@ def __init__(self, color, score, pixel_fraction):
self._pixel_fraction = pixel_fraction

@classmethod
def from_api_repr(cls, response):
"""Factory: construct ``ColorInformation`` for a color found.
def from_api_repr(cls, color_information):
"""Factory: construct ``ColorInformation`` for a color.
:type response: dict
:param response: Color data with extra meta information.
:type color_information: dict
:param color_information: Color data with extra meta information.
:rtype: :class:`~google.cloud.vision.color.ColorInformation`
:returns: Instance of ``ColorInformation``.
"""
color = Color.from_api_repr(response.get('color'))
score = response.get('score')
pixel_fraction = response.get('pixelFraction')
color = Color.from_api_repr(color_information.get('color'))
score = color_information.get('score')
pixel_fraction = color_information.get('pixelFraction')
return cls(color, score, pixel_fraction)

@classmethod
def from_pb(cls, color_information):
"""Factory: construct ``ColorInformation`` for a color.
:type color_information: :class:`~google.cloud.grpc.vision.v1.\
image_annotator_pb2.ColorInfo`
:param color_information: Color data with extra meta information.
:rtype: :class:`~google.cloud.vision.color.ColorInformation`
:returns: Instance of ``ColorInformation``.
"""
color = Color.from_pb(color_information.color)
score = color_information.score
pixel_fraction = color_information.pixel_fraction
return cls(color, score, pixel_fraction)

@property
Expand Down
61 changes: 61 additions & 0 deletions vision/unit_tests/test_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@ def test_rgb_color_data(self):
self.assertEqual(colors.blue, 255)
self.assertEqual(colors.alpha, 0.5)

def test_pb_rgb_color_data(self):
from google.protobuf.wrappers_pb2 import FloatValue
from google.type.color_pb2 import Color

alpha = FloatValue(value=1.0)
color_pb = Color(red=1.0, green=2.0, blue=3.0, alpha=alpha)
color_class = self._get_target_class()
color = color_class.from_pb(color_pb)
self.assertEqual(color.red, 1.0)
self.assertEqual(color.green, 2.0)
self.assertEqual(color.blue, 3.0)
self.assertEqual(color.alpha, 1.0)

def test_pb_rgb_color_no_alpha_data(self):
from google.protobuf.wrappers_pb2 import FloatValue
from google.type.color_pb2 import Color

alpha = FloatValue()
color_pb = Color(red=1.0, green=2.0, blue=3.0, alpha=alpha)
color_class = self._get_target_class()
color = color_class.from_pb(color_pb)
self.assertEqual(color.red, 1.0)
self.assertEqual(color.green, 2.0)
self.assertEqual(color.blue, 3.0)
self.assertEqual(color.alpha, 0.0)

def test_missing_rgb_values(self):
colors = {}
color_class = self._get_target_class()
Expand All @@ -45,3 +71,38 @@ def test_missing_rgb_values(self):
self.assertEqual(colors.green, 0)
self.assertEqual(colors.blue, 0)
self.assertEqual(colors.alpha, 0.0)


class TestImagePropertiesAnnotation(unittest.TestCase):
@staticmethod
def _get_target_class():
from google.cloud.vision.color import ImagePropertiesAnnotation
return ImagePropertiesAnnotation

def test_color_annotation_from_pb(self):
from google.cloud.grpc.vision.v1 import image_annotator_pb2
from google.protobuf.wrappers_pb2 import FloatValue
from google.type.color_pb2 import Color

alpha = FloatValue(value=1.0)
color_pb = Color(red=1.0, green=2.0, blue=3.0, alpha=alpha)
color_info_pb = image_annotator_pb2.ColorInfo(color=color_pb,
score=1.0,
pixel_fraction=1.0)
dominant_colors = image_annotator_pb2.DominantColorsAnnotation(
colors=[color_info_pb])

image_properties_pb = image_annotator_pb2.ImageProperties(
dominant_colors=dominant_colors)

color_info = self._get_target_class()
image_properties_result = color_info.from_pb(image_properties_pb)

self.assertEqual(len(image_properties_result), 1)
image_properties = image_properties_result[0]
self.assertEqual(image_properties.colors[0].pixel_fraction, 1.0)
self.assertEqual(image_properties.colors[0].score, 1.0)
self.assertEqual(image_properties.colors[0].color.red, 1.0)
self.assertEqual(image_properties.colors[0].color.green, 2.0)
self.assertEqual(image_properties.colors[0].color.blue, 3.0)
self.assertEqual(image_properties.colors[0].color.alpha, 1.0)

0 comments on commit 2aaec55

Please sign in to comment.