Skip to content

Commit

Permalink
Add Vision image properties detection.
Browse files Browse the repository at this point in the history
  • Loading branch information
daspecster committed Sep 15, 2016
1 parent e221437 commit a08bd4e
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 16 deletions.
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 colors: list
:param colors: List of
:class:`~google.cloud.vision.color.ColorInformation`.
: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, 1].
:type green: int
:param green: The amount of green in the color as a value in the interval
[0, 1].
:type blue: int
:param blue: The amount of blue in the color as a value in the interval
[0, 1].
: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 color.
:rtype: int
:returns: Red RGB value.
"""
return self._red

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

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

@property
def alpha(self):
"""Alpha 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: 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
32 changes: 16 additions & 16 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 @@ -125,22 +126,6 @@ def detect_faces(self, limit=10):

return faces

def detect_safe_search(self, limit=10):
"""Retreive safe search properties from an image.
:type limit: int
:param limit: The number of faces to try and detect.
:rtype: list
:returns: List of
:class:`~google.cloud.vision.sage.SafeSearchAnnotation`.
"""
safe_detection_feature = Feature(FeatureTypes.SAFE_SEARCH_DETECTION,
limit)
result = self.client.annotate(self, [safe_detection_feature])
safe_search_response = result['safeSearchAnnotation']
return SafeSearchAnnotation.from_api_repr(safe_search_response)

def detect_labels(self, limit=10):
"""Detect labels that describe objects in an image.
Expand Down Expand Up @@ -179,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 a08bd4e

Please sign in to comment.