Skip to content

Commit

Permalink
Merge pull request #2320 from daspecster/vision-image-properties
Browse files Browse the repository at this point in the history
Vision image properties
  • Loading branch information
daspecster authored Sep 15, 2016
2 parents a92d69f + 5e0da90 commit 0f5d982
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@

vision-usage
vision-client
vision-color
vision-entity
vision-feature
vision-face
Expand Down
10 changes: 10 additions & 0 deletions docs/vision-color.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Vision Image Properties
=======================

Image Properties Annotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. automodule:: google.cloud.vision.color
:members:
:undoc-members:
:show-inheritance:
191 changes: 191 additions & 0 deletions google/cloud/vision/color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# Copyright 2016 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Image properties class representation derived from Vision API response."""


class ImagePropertiesAnnotation(object):
"""Representation of image properties
:type colors: list
:param colors: List of
:class:`~google.cloud.vision.color.ColorInformation`.
"""
def __init__(self, colors):
self._colors = colors

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

@property
def colors(self):
"""Colors in an image.
:rtype: list of :class:`~google.cloud.vision.color.ColorInformation`
:returns: Populated list of ``ColorInformation``.
"""
return self._colors


class Color(object):
"""Representation of RGBA color information.
:type red: int
:param red: The amount of red in the color as a value in the interval
[0, 255].
:type green: int
:param green: The amount of green in the color as a value in the interval
[0, 255].
:type blue: int
:param blue: The amount of blue in the color as a value in the interval
[0, 255].
:type alpha: float
:param alpha: The fraction of this color that should be applied to the
pixel.
"""
def __init__(self, red, green, blue, alpha):
self._red = red
self._green = green
self._blue = blue
self._alpha = alpha

@classmethod
def from_api_repr(cls, response):
"""Factory: construct a ``Color`` from a Vision API response.
:type response: dict
:param response: Color from API Response.
:rtype: :class:`~google.cloud.vision.color.Color`
:returns: Instance of :class:`~google.cloud.vision.color.Color`.
"""
red = response['red']
green = response['green']
blue = response['blue']
alpha = response.get('alpha')

return cls(red, green, blue, alpha)

@property
def red(self):
"""Red component of the color.
:rtype: int
:returns: Red RGB value.
"""
return self._red

@property
def green(self):
"""Green component of the color.
:rtype: int
:returns: Green RGB value.
"""
return self._green

@property
def blue(self):
"""Blue component of the color.
:rtype: int
:returns: Blue RGB value.
"""
return self._blue

@property
def alpha(self):
"""Alpha transparency level.
:rtype: float
:returns: Alpha transparency level.
"""
return self._alpha


class ColorInformation(object):
"""Representation of color information from API response.
:type color: :class:`~google.cloud.vision.color.Color`
:param color: RGB components of the color.
:type score: float
:param score: Image-specific score for this color. Value in range [0, 1].
:type pixel_fraction: float
:param pixel_fraction: Stores the fraction of pixels the color occupies in
the image. Value in range [0, 1].
"""
def __init__(self, color, score, pixel_fraction):
self._color = color
self._score = score
self._pixel_fraction = pixel_fraction

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

return cls(color, score, pixel_fraction)

@property
def color(self):
"""RGB components of the color.
:rtype: :class:`~google.vision.color.Color`
:returns: Instance of ``Color``.
"""
return self._color

@property
def score(self):
"""Image-specific score for this color. Value in range [0, 1].
:rtype: float
:returns: Image score for this color.
"""
return self._score

@property
def pixel_fraction(self):
"""Stores the fraction of pixels the color occupies in the image.
:rtype: float
:returns: Pixel fraction value in range [0, 1].
"""
return self._pixel_fraction
1 change: 0 additions & 1 deletion google/cloud/vision/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ def from_api_repr(cls, response):
"""
bounds = Bounds.from_api_repr(response.get('boundingPoly'))
description = response['description']

locale = response.get('locale', None)
locations = [LocationInformation.from_api_repr(location)
for location in response.get('locations', [])]
Expand Down
1 change: 1 addition & 0 deletions google/cloud/vision/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class FeatureTypes(object):
LABEL_DETECTION = 'LABEL_DETECTION'
TEXT_DETECTION = 'TEXT_DETECTION'
SAFE_SEARCH_DETECTION = 'SAFE_SEARCH_DETECTION'
IMAGE_PROPERTIES = 'IMAGE_PROPERTIES'


class Feature(object):
Expand Down
16 changes: 16 additions & 0 deletions google/cloud/vision/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from google.cloud.vision.face import Face
from google.cloud.vision.feature import Feature
from google.cloud.vision.feature import FeatureTypes
from google.cloud.vision.color import ImagePropertiesAnnotation
from google.cloud.vision.safe import SafeSearchAnnotation


Expand Down Expand Up @@ -163,6 +164,21 @@ def detect_logos(self, limit=10):
feature = Feature(FeatureTypes.LOGO_DETECTION, limit)
return self._detect_annotation(feature)

def detect_properties(self, limit=10):
"""Detect the color properties of an image.
:type limit: int
:param limit: The maximum number of image properties to find.
:rtype: list
:returns: List of
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`.
"""
feature = Feature(FeatureTypes.IMAGE_PROPERTIES, limit)
result = self.client.annotate(self, [feature])
response = result['imagePropertiesAnnotation']
return ImagePropertiesAnnotation.from_api_repr(response)

def detect_safe_search(self, limit=10):
"""Retreive safe search properties from an image.
Expand Down
104 changes: 104 additions & 0 deletions unit_tests/vision/_fixtures.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,107 @@
IMAGE_PROPERTIES_RESPONSE = {
'responses': [
{
'imagePropertiesAnnotation': {
'dominantColors': {
'colors': [
{
'color': {
'red': 253,
'green': 203,
'blue': 65,
'alpha': 0.0
},
'score': 0.42258179,
'pixelFraction': 0.025376344
},
{
'color': {
'red': 216,
'green': 69,
'blue': 56
},
'score': 0.34945792,
'pixelFraction': 0.026093191
},
{
'color': {
'red': 79,
'green': 142,
'blue': 245
},
'score': 0.050921876,
'pixelFraction': 0.014193549
},
{
'color': {
'red': 249,
'green': 246,
'blue': 246
},
'score': 0.0059412993,
'pixelFraction': 0.86896056
},
{
'color': {
'red': 222,
'green': 119,
'blue': 51
},
'score': 0.0043299114,
'pixelFraction': 0.00021505376
},
{
'color': {
'red': 226,
'green': 138,
'blue': 130
},
'score': 0.0038594988,
'pixelFraction': 0.00086021505
},
{
'color': {
'red': 165,
'green': 194,
'blue': 243
},
'score': 0.0029492097,
'pixelFraction': 0.0015053763
},
{
'color': {
'red': 231,
'green': 169,
'blue': 164
},
'score': 0.0017002203,
'pixelFraction': 0.00043010752
},
{
'color': {
'red': 137,
'green': 98,
'blue': 142
},
'score': 0.0013974205,
'pixelFraction': 0.00071684585
},
{
'color': {
'red': 239,
'green': 179,
'blue': 56
},
'score': 0.050473157,
'pixelFraction': 0.0022222223
}
]
}
}
}
]
}

LABEL_DETECTION_RESPONSE = {
'responses': [
{
Expand Down
24 changes: 24 additions & 0 deletions unit_tests/vision/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,30 @@ def test_safe_search_detection_from_source(self):
self.assertEqual('POSSIBLE', safe_search.medical)
self.assertEqual('VERY_UNLIKELY', safe_search.violence)

def test_image_properties_detection_from_source(self):
from google.cloud.vision.color import ImagePropertiesAnnotation
from unit_tests.vision._fixtures import IMAGE_PROPERTIES_RESPONSE

RETURNED = IMAGE_PROPERTIES_RESPONSE
credentials = _Credentials()
client = self._makeOne(project=self.PROJECT, credentials=credentials)
client.connection = _Connection(RETURNED)

image = client.image(source_uri=_IMAGE_SOURCE)
image_properties = image.detect_properties()
self.assertTrue(isinstance(image_properties,
ImagePropertiesAnnotation))
image_request = client.connection._requested[0]['data']['requests'][0]
self.assertEqual(_IMAGE_SOURCE,
image_request['image']['source']['gcs_image_uri'])
self.assertEqual(0.42258179, image_properties.colors[0].score)
self.assertEqual(0.025376344,
image_properties.colors[0].pixel_fraction)
self.assertEqual(253, image_properties.colors[0].color.red)
self.assertEqual(203, image_properties.colors[0].color.green)
self.assertEqual(65, image_properties.colors[0].color.blue)
self.assertEqual(0.0, image_properties.colors[0].color.alpha)


class TestVisionRequest(unittest.TestCase):
def _getTargetClass(self):
Expand Down

0 comments on commit 0f5d982

Please sign in to comment.