Skip to content

Commit

Permalink
Merge pull request #2770 from daspecster/add-manual-detect-to-vision-…
Browse files Browse the repository at this point in the history
…2697

Add image.detect() for detecting multiple types.
  • Loading branch information
daspecster authored Nov 30, 2016
2 parents f0950ca + 52aaf05 commit 5cbbcb9
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 64 deletions.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
:caption: Vision

vision-usage
vision-annotations
vision-client
vision-color
vision-entity
Expand Down
7 changes: 7 additions & 0 deletions docs/vision-annotations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Vision Annotations
==================

.. automodule:: google.cloud.vision.annotations
:members:
:undoc-members:
:show-inheritance:
29 changes: 29 additions & 0 deletions docs/vision-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,35 @@ or pass in ``credentials`` and ``project`` explicitly.
>>> client = vision.Client(project='my-project', credentials=creds)
Manual Detection
~~~~~~~~~~~~~~~~

You can call the detection method manually.

.. code-block:: python
>>> from google.cloud import vision
>>> from google.cloud.vision.feature import Feature
>>> from google.cloud.vision.feature import FeatureTypes
>>> client = vision.Client()
>>> image = client.image(source_uri='gs://my-test-bucket/image.jpg')
>>> features = [Feature(FeatureTypes.FACE_DETECTION, 5),
... Feature(FeatureTypes.LOGO_DETECTION, 3)]
>>> annotations = image.detect(features)
>>> len(annotations.faces)
2
>>> for face in annotations.faces:
... print(face.joy_likelihood)
0.94099093
0.54453093
>>> len(annotations.logos)
2
>>> for logo in annotations.logos:
... print(logo.description)
'google'
'github'
Face Detection
~~~~~~~~~~~~~~

Expand Down
119 changes: 119 additions & 0 deletions vision/google/cloud/vision/annotations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright 2016 Google Inc.
#
# 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.

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


from google.cloud.vision.color import ImagePropertiesAnnotation
from google.cloud.vision.entity import EntityAnnotation
from google.cloud.vision.face import Face
from google.cloud.vision.safe import SafeSearchAnnotation


FACE_ANNOTATIONS = 'faceAnnotations'
IMAGE_PROPERTIES_ANNOTATION = 'imagePropertiesAnnotation'
SAFE_SEARCH_ANNOTATION = 'safeSearchAnnotation'

_KEY_MAP = {
FACE_ANNOTATIONS: 'faces',
IMAGE_PROPERTIES_ANNOTATION: 'properties',
'labelAnnotations': 'labels',
'landmarkAnnotations': 'landmarks',
'logoAnnotations': 'logos',
SAFE_SEARCH_ANNOTATION: 'safe_searches',
'textAnnotations': 'texts'
}


class Annotations(object):
"""Helper class to bundle annotation responses.
:type faces: list
:param faces: List of :class:`~google.cloud.vision.face.Face`.
:type properties: list
:param properties:
List of :class:`~google.cloud.vision.color.ImagePropertiesAnnotation`.
:type labels: list
:param labels: List of
:class:`~google.cloud.vision.entity.EntityAnnotation`.
:type landmarks: list
:param landmarks: List of
:class:`~google.cloud.vision.entity.EntityAnnotation.`
:type logos: list
:param logos: List of
:class:`~google.cloud.vision.entity.EntityAnnotation`.
:type safe_searches: list
:param safe_searches:
List of :class:`~google.cloud.vision.safe.SafeSearchAnnotation`
:type texts: list
:param texts: List of
:class:`~google.cloud.vision.entity.EntityAnnotation`.
"""
def __init__(self, faces=(), properties=(), labels=(), landmarks=(),
logos=(), safe_searches=(), texts=()):
self.faces = faces
self.properties = properties
self.labels = labels
self.landmarks = landmarks
self.logos = logos
self.safe_searches = safe_searches
self.texts = texts

@classmethod
def from_api_repr(cls, response):
"""Factory: construct an instance of ``Annotations`` from a response.
:type response: dict
:param response: Vision API response object.
:rtype: :class:`~google.cloud.vision.annotations.Annotations`
:returns: An instance of ``Annotations`` with detection types loaded.
"""
annotations = {}
for feature_type, annotation in response.items():
curr_feature = annotations.setdefault(_KEY_MAP[feature_type], [])
curr_feature.extend(
_entity_from_response_type(feature_type, annotation))
return cls(**annotations)


def _entity_from_response_type(feature_type, results):
"""Convert a JSON result to an entity type based on the feature.
:rtype: list
:returns: List containing any of
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`,
:class:`~google.cloud.vision.entity.EntityAnnotation`,
:class:`~google.cloud.vision.face.Face`,
:class:`~google.cloud.vision.safe.SafeSearchAnnotation`.
"""
detected_objects = []
if feature_type == FACE_ANNOTATIONS:
detected_objects.extend(
Face.from_api_repr(face) for face in results)
elif feature_type == IMAGE_PROPERTIES_ANNOTATION:
detected_objects.append(
ImagePropertiesAnnotation.from_api_repr(results))
elif feature_type == SAFE_SEARCH_ANNOTATION:
detected_objects.append(SafeSearchAnnotation.from_api_repr(results))
else:
for result in results:
detected_objects.append(EntityAnnotation.from_api_repr(result))
return detected_objects
89 changes: 30 additions & 59 deletions vision/google/cloud/vision/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,9 @@

from google.cloud._helpers import _to_bytes
from google.cloud._helpers import _bytes_to_unicode
from google.cloud.vision.entity import EntityAnnotation
from google.cloud.vision.face import Face
from google.cloud.vision.annotations import Annotations
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


_FACE_DETECTION = 'FACE_DETECTION'
_IMAGE_PROPERTIES = 'IMAGE_PROPERTIES'
_LABEL_DETECTION = 'LABEL_DETECTION'
_LANDMARK_DETECTION = 'LANDMARK_DETECTION'
_LOGO_DETECTION = 'LOGO_DETECTION'
_SAFE_SEARCH_DETECTION = 'SAFE_SEARCH_DETECTION'
_TEXT_DETECTION = 'TEXT_DETECTION'

_REVERSE_TYPES = {
_FACE_DETECTION: 'faceAnnotations',
_IMAGE_PROPERTIES: 'imagePropertiesAnnotation',
_LABEL_DETECTION: 'labelAnnotations',
_LANDMARK_DETECTION: 'landmarkAnnotations',
_LOGO_DETECTION: 'logoAnnotations',
_SAFE_SEARCH_DETECTION: 'safeSearchAnnotation',
_TEXT_DETECTION: 'textAnnotations',
}


class Image(object):
Expand Down Expand Up @@ -105,7 +83,7 @@ def source(self):
return self._source

def _detect_annotation(self, features):
"""Generic method for detecting a single annotation.
"""Generic method for detecting annotations.
:type features: list
:param features: List of :class:`~google.cloud.vision.feature.Feature`
Expand All @@ -118,12 +96,21 @@ def _detect_annotation(self, features):
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`,
:class:`~google.cloud.vision.sage.SafeSearchAnnotation`,
"""
detected_objects = []
results = self.client.annotate(self, features)
for feature in features:
detected_objects.extend(
_entity_from_response_type(feature.feature_type, results))
return detected_objects
return Annotations.from_api_repr(results)

def detect(self, features):
"""Detect multiple feature types.
:type features: list of :class:`~google.cloud.vision.feature.Feature`
:param features: List of the ``Feature`` indication the type of
annotation to perform.
:rtype: list
:returns: List of
:class:`~google.cloud.vision.entity.EntityAnnotation`.
"""
return self._detect_annotation(features)

def detect_faces(self, limit=10):
"""Detect faces in image.
Expand All @@ -135,7 +122,8 @@ def detect_faces(self, limit=10):
:returns: List of :class:`~google.cloud.vision.face.Face`.
"""
features = [Feature(FeatureTypes.FACE_DETECTION, limit)]
return self._detect_annotation(features)
annotations = self._detect_annotation(features)
return annotations.faces

def detect_labels(self, limit=10):
"""Detect labels that describe objects in an image.
Expand All @@ -147,7 +135,8 @@ def detect_labels(self, limit=10):
:returns: List of :class:`~google.cloud.vision.entity.EntityAnnotation`
"""
features = [Feature(FeatureTypes.LABEL_DETECTION, limit)]
return self._detect_annotation(features)
annotations = self._detect_annotation(features)
return annotations.labels

def detect_landmarks(self, limit=10):
"""Detect landmarks in an image.
Expand All @@ -160,7 +149,8 @@ def detect_landmarks(self, limit=10):
:class:`~google.cloud.vision.entity.EntityAnnotation`.
"""
features = [Feature(FeatureTypes.LANDMARK_DETECTION, limit)]
return self._detect_annotation(features)
annotations = self._detect_annotation(features)
return annotations.landmarks

def detect_logos(self, limit=10):
"""Detect logos in an image.
Expand All @@ -173,7 +163,8 @@ def detect_logos(self, limit=10):
:class:`~google.cloud.vision.entity.EntityAnnotation`.
"""
features = [Feature(FeatureTypes.LOGO_DETECTION, limit)]
return self._detect_annotation(features)
annotations = self._detect_annotation(features)
return annotations.logos

def detect_properties(self, limit=10):
"""Detect the color properties of an image.
Expand All @@ -186,7 +177,8 @@ def detect_properties(self, limit=10):
:class:`~google.cloud.vision.color.ImagePropertiesAnnotation`.
"""
features = [Feature(FeatureTypes.IMAGE_PROPERTIES, limit)]
return self._detect_annotation(features)
annotations = self._detect_annotation(features)
return annotations.properties

def detect_safe_search(self, limit=10):
"""Retreive safe search properties from an image.
Expand All @@ -199,7 +191,8 @@ def detect_safe_search(self, limit=10):
:class:`~google.cloud.vision.sage.SafeSearchAnnotation`.
"""
features = [Feature(FeatureTypes.SAFE_SEARCH_DETECTION, limit)]
return self._detect_annotation(features)
annotations = self._detect_annotation(features)
return annotations.safe_searches

def detect_text(self, limit=10):
"""Detect text in an image.
Expand All @@ -212,27 +205,5 @@ def detect_text(self, limit=10):
:class:`~google.cloud.vision.entity.EntityAnnotation`.
"""
features = [Feature(FeatureTypes.TEXT_DETECTION, limit)]
return self._detect_annotation(features)


def _entity_from_response_type(feature_type, results):
"""Convert a JSON result to an entity type based on the feature."""
feature_key = _REVERSE_TYPES[feature_type]
annotations = results.get(feature_key, ())
if not annotations:
return []

detected_objects = []
if feature_type == _FACE_DETECTION:
detected_objects.extend(
Face.from_api_repr(face) for face in annotations)
elif feature_type == _IMAGE_PROPERTIES:
detected_objects.append(
ImagePropertiesAnnotation.from_api_repr(annotations))
elif feature_type == _SAFE_SEARCH_DETECTION:
detected_objects.append(
SafeSearchAnnotation.from_api_repr(annotations))
else:
for result in annotations:
detected_objects.append(EntityAnnotation.from_api_repr(result))
return detected_objects
annotations = self._detect_annotation(features)
return annotations.texts
Loading

0 comments on commit 5cbbcb9

Please sign in to comment.