diff --git a/docs/index.rst b/docs/index.rst index a0d69c71a585..bf608fb622ae 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -150,6 +150,7 @@ vision-usage vision-client vision-image + vision-entity vision-feature vision-face diff --git a/docs/vision-entity.rst b/docs/vision-entity.rst new file mode 100644 index 000000000000..7c5145f54d7d --- /dev/null +++ b/docs/vision-entity.rst @@ -0,0 +1,10 @@ +Vision Entity +============= + +Entity +~~~~~~ + +.. automodule:: google.cloud.vision.entity + :members: + :undoc-members: + :show-inheritance: diff --git a/google/cloud/vision/entity.py b/google/cloud/vision/entity.py new file mode 100644 index 000000000000..3cd0c8538963 --- /dev/null +++ b/google/cloud/vision/entity.py @@ -0,0 +1,93 @@ +# 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. + +"""Entity class for holding information returned from annotating an image.""" + + +from google.cloud.vision.geometry import Bounds + + +class EntityAnnotation(object): + """Representation of an entity returned from the Vision API. + + :type bounds: dict + :param bounds: Dictionary of bounary information of detected entity. + + :type description: str + :param description: Description of entity detected in an image. + + :type mid: str + :param mid: Opaque entity ID. + + :type score: float + :param score: Overall score of the result. Range [0, 1]. + """ + def __init__(self, bounds, description, mid, score): + self._bounds = bounds + self._description = description + self._mid = mid + self._score = score + + @classmethod + def from_api_repr(cls, response): + """Factory: construct entity from Vision API response. + + :type response: dict + :param response: Dictionary response from Vision API with entity data. + + :rtype: :class:`~google.cloud.vision.entiy.EntityAnnotation` + :returns: Instance of ``EntityAnnotation``. + """ + bounds = Bounds.from_api_repr(response['boundingPoly']) + description = response['description'] + mid = response['mid'] + score = response['score'] + + return cls(bounds, description, mid, score) + + @property + def bounds(self): + """Bounding polygon of detected image feature. + + :rtype: :class:`~google.cloud.vision.geometry.Bounds` + :returns: Instance of ``Bounds`` with populated vertices. + """ + return self._bounds + + @property + def description(self): + """Description of feature detected in image. + + :rtype: str + :returns: String description of feature detected in image. + """ + return self._description + + @property + def mid(self): + """MID of feature detected in image. + + :rtype: str + :returns: String MID of feature detected in image. + """ + return self._mid + + @property + def score(self): + """Overall score of the result. Range [0, 1]. + + :rtype: float + :returns: Overall score of the result. Range [0, 1]. + """ + return self._score diff --git a/google/cloud/vision/feature.py b/google/cloud/vision/feature.py index 3c5ac6d1b24d..6716b3021921 100644 --- a/google/cloud/vision/feature.py +++ b/google/cloud/vision/feature.py @@ -51,7 +51,12 @@ def __init__(self, feature_type, max_results=1): self._max_results = int(max_results) def as_dict(self): - """Generate dictionary for Feature request format.""" + """Generate dictionary for Feature request format. + + :rtype: dict + :returns: Dictionary representation of a + :class:`~google.cloud.vision.feature.FeatureType`. + """ return { 'type': self.feature_type, 'maxResults': self.max_results @@ -59,10 +64,19 @@ def as_dict(self): @property def feature_type(self): - """"Feature type string.""" + """"Feature type string. + + :rtype: :class:`~google.cloud.vision.feature.FeatureTypes` + :returns: Instance of + :class:`~google.cloud.vision.feature.FeatureTypes` + """ return self._feature_type @property def max_results(self): - """Maximum number of results for feature type.""" + """Maximum number of results for feature type. + + :rtype: int + :returns: Maxium results to be returned. + """ return self._max_results diff --git a/google/cloud/vision/geometry.py b/google/cloud/vision/geometry.py index eeb1795a015f..443538ccc715 100644 --- a/google/cloud/vision/geometry.py +++ b/google/cloud/vision/geometry.py @@ -16,7 +16,11 @@ class BoundsBase(object): - """Base class for handling bounds with vertices.""" + """Base class for handling bounds with vertices. + + :type vertices: list of :class:`~google.cloud.vision.geometry.Vertex` + :param vertices: List of vertcies describing points on an image. + """ def __init__(self, vertices): self._vertices = vertices @@ -40,17 +44,34 @@ def from_api_repr(cls, response_vertices): def vertices(self): """List of vertices. - :rtype: list + :rtype: list of :class:`~google.cloud.vision.geometry.Vertex` :returns: List of populated vertices. """ return self._vertices +class Bounds(BoundsBase): + """A polygon boundry of the detected feature.""" + + +class FDBounds(BoundsBase): + """The bounding polygon of just the skin portion of the face.""" + + class Position(object): """A 3D position in the image. See: https://cloud.google.com/vision/reference/rest/v1/images/annotate#Position + + :type x_coordinate: float + :param x_coordinate: X position coordinate. + + :type y_coordinate: float + :param y_coordinate: Y position coordinate. + + :type z_coordinate: float + :param z_coordinate: Z position coordinate. """ def __init__(self, x_coordinate, y_coordinate, z_coordinate): self._x_coordinate = x_coordinate @@ -102,6 +123,12 @@ class Vertex(object): See: https://cloud.google.com/vision/reference/rest/v1/images/annotate#Vertex + + :type x_coordinate: float + :param x_coordinate: X position coordinate. + + :type y_coordinate: float + :param y_coordinate: Y position coordinate. """ def __init__(self, x_coordinate, y_coordinate): self._x_coordinate = x_coordinate diff --git a/google/cloud/vision/image.py b/google/cloud/vision/image.py index 3becd5b90257..cbe0d196e2cd 100644 --- a/google/cloud/vision/image.py +++ b/google/cloud/vision/image.py @@ -18,6 +18,7 @@ from base64 import b64encode from google.cloud._helpers import _to_bytes +from google.cloud.vision.entity import EntityAnnotation from google.cloud.vision.face import Face from google.cloud.vision.feature import Feature from google.cloud.vision.feature import FeatureTypes @@ -98,3 +99,22 @@ def detect_faces(self, limit=10): faces.append(face) return faces + + def detect_logos(self, limit=10): + """Detect logos in an image. + + :type limit: int + :param limit: The maximum number of logos to find. + + :rtype: list + :returns: List of + :class:`~google.cloud.vision.entity.EntityAnnotation`. + """ + logos = [] + logo_detection_feature = Feature(FeatureTypes.LOGO_DETECTION, limit) + result = self.client.annotate(self, [logo_detection_feature]) + for logo_response in result['logoAnnotations']: + logo = EntityAnnotation.from_api_repr(logo_response) + logos.append(logo) + + return logos diff --git a/unit_tests/vision/_fixtures.py b/unit_tests/vision/_fixtures.py new file mode 100644 index 000000000000..94e2622b3e51 --- /dev/null +++ b/unit_tests/vision/_fixtures.py @@ -0,0 +1,1470 @@ +LOGO_DETECTION_RESPONSE = { + 'responses': [ + { + 'logoAnnotations': [ + { + 'mid': '/m/05b5c', + 'description': 'Brand1', + 'score': 0.63192177, + 'boundingPoly': { + 'vertices': [ + { + 'x': 78, + 'y': 162 + }, + { + 'x': 282, + 'y': 162 + }, + { + 'x': 282, + 'y': 211 + }, + { + 'x': 78, + 'y': 211 + } + ] + } + }, + { + 'mid': '/m/0fpzzp', + 'description': 'Brand2', + 'score': 0.5492993, + 'boundingPoly': { + 'vertices': [ + { + 'x': 310, + 'y': 209 + }, + { + 'x': 477, + 'y': 209 + }, + { + 'x': 477, + 'y': 282 + }, + { + 'x': 310, + 'y': 282 + } + ] + } + } + ] + } + ] +} + +FACE_DETECTION_RESPONSE = { + 'responses': [{ + 'faceAnnotations': [{ + 'headwearLikelihood': 'VERY_UNLIKELY', + 'panAngle': 6.027647, + 'underExposedLikelihood': 'VERY_UNLIKELY', + 'landmarkingConfidence': 0.54453093, + 'detectionConfidence': 0.9863683, + 'joyLikelihood': 'VERY_LIKELY', + 'landmarks': [{ + 'position': { + 'y': 482.69385, + 'x': 1004.8003, + 'z': 0.0016593217 + }, + 'type': 'LEFT_EYE' + }, { + 'position': { + 'y': 470.90149, + 'x': 1218.9751, + 'z': 20.597967 + }, + 'type': 'RIGHT_EYE' + }, { + 'position': { + 'y': 441.46521, + 'x': 934.25629, + 'z': -1.1400928 + }, + 'type': 'LEFT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 449.2872, + 'x': 1059.306, + 'z': -47.195843 + }, + 'type': 'RIGHT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 446.05408, + 'x': 1163.678, + 'z': -37.211197 + }, + 'type': 'LEFT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 424.18341, + 'x': 1285.0209, + 'z': 34.844131 + }, + 'type': 'RIGHT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 485.18387, + 'x': 1113.4325, + 'z': -32.579361 + }, + 'type': 'MIDPOINT_BETWEEN_EYES' + }, { + 'position': { + 'y': 620.27032, + 'x': 1122.5671, + 'z': -51.019524 + }, + 'type': 'NOSE_TIP' + }, { + 'position': { + 'y': 674.32526, + 'x': 1117.0417, + 'z': 17.330631 + }, + 'type': 'UPPER_LIP' + }, { + 'position': { + 'y': 737.29736, + 'x': 1115.7112, + 'z': 54.076469 + }, + 'type': 'LOWER_LIP' + }, { + 'position': { + 'y': 680.62927, + 'x': 1017.0475, + 'z': 72.948006 + }, + 'type': 'MOUTH_LEFT' + }, { + 'position': { + 'y': 681.53552, + 'x': 1191.5186, + 'z': 87.198334 + }, + 'type': 'MOUTH_RIGHT' + }, { + 'position': { + 'y': 702.3808, + 'x': 1115.4193, + 'z': 42.56889 + }, + 'type': 'MOUTH_CENTER' + }, { + 'position': { + 'y': 606.68555, + 'x': 1169.0006, + 'z': 33.98217 + }, + 'type': 'NOSE_BOTTOM_RIGHT' + }, { + 'position': { + 'y': 612.71509, + 'x': 1053.9476, + 'z': 23.409685 + }, + 'type': 'NOSE_BOTTOM_LEFT' + }, { + 'position': { + 'y': 634.95532, + 'x': 1116.6818, + 'z': 3.386874 + }, + 'type': 'NOSE_BOTTOM_CENTER' + }, { + 'position': { + 'y': 476.70197, + 'x': 1009.2689, + 'z': -16.84004 + }, + 'type': 'LEFT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 491.64874, + 'x': 1049.3926, + 'z': 7.0493474 + }, + 'type': 'LEFT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 499.426, + 'x': 1003.9925, + 'z': 3.5417991 + }, + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 482.37302, + 'x': 964.48242, + 'z': 14.96223 + }, + 'type': 'LEFT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 487.90195, + 'x': 1005.3607, + 'z': -4.7375555 + }, + 'type': 'LEFT_EYE_PUPIL' + }, { + 'position': { + 'y': 468.33276, + 'x': 1212.7329, + 'z': 3.5585577 + }, + 'type': 'RIGHT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 470.92487, + 'x': 1251.7043, + 'z': 43.794273 + }, + 'type': 'RIGHT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 486.98676, + 'x': 1217.4629, + 'z': 23.580008 + }, + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 482.41071, + 'x': 1173.4624, + 'z': 18.852427 + }, + 'type': 'RIGHT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 479.32739, + 'x': 1213.9757, + 'z': 16.041821 + }, + 'type': 'RIGHT_EYE_PUPIL' + }, { + 'position': { + 'y': 424.38797, + 'x': 1001.2206, + 'z': -46.463905 + }, + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 415.33655, + 'x': 1221.9457, + 'z': -24.29454 + }, + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 506.88251, + 'x': 851.96124, + 'z': 257.9054 + }, + 'type': 'LEFT_EAR_TRAGION' + }, { + 'position': { + 'y': 487.9679, + 'x': 1313.8328, + 'z': 304.29816 + }, + 'type': 'RIGHT_EAR_TRAGION' + }, { + 'position': { + 'y': 447.98254, + 'x': 1114.1573, + 'z': -50.620598 + }, + 'type': 'FOREHEAD_GLABELLA' + }, { + 'position': { + 'y': 815.3302, + 'x': 1113.27, + 'z': 109.69422 + }, + 'type': 'CHIN_GNATHION' + }, { + 'position': { + 'y': 656.20123, + 'x': 884.34106, + 'z': 223.19124 + }, + 'type': 'CHIN_LEFT_GONION' + }, { + 'position': { + 'y': 639.291, + 'x': 1301.2404, + 'z': 265.00647 + }, + 'type': 'CHIN_RIGHT_GONION' + }], + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': -18.412321, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 58, + 'x': 748 + }, { + 'y': 58, + 'x': 1430 + }, { + 'y': 851, + 'x': 1430 + }, { + 'y': 851, + 'x': 748 + }] + }, + 'rollAngle': -0.43419784, + 'blurredLikelihood': 'VERY_UNLIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 310, + 'x': 845 + }, { + 'y': 310, + 'x': 1379 + }, { + 'y': 844, + 'x': 1379 + }, { + 'y': 844, + 'x': 845 + }] + } + }, { + 'headwearLikelihood': 'VERY_UNLIKELY', + 'panAngle': -12.609346, + 'underExposedLikelihood': 'VERY_UNLIKELY', + 'landmarkingConfidence': 0.56890666, + 'detectionConfidence': 0.96333671, + 'joyLikelihood': 'VERY_LIKELY', + 'landmarks': [{ + 'position': { + 'y': 604.24847, + 'x': 1655.8817, + 'z': -0.0023633335 + }, + 'type': 'LEFT_EYE' + }, { + 'position': { + 'y': 590.82428, + 'x': 1797.3677, + 'z': -30.984835 + }, + 'type': 'RIGHT_EYE' + }, { + 'position': { + 'y': 574.40173, + 'x': 1609.9617, + 'z': 14.634346 + }, + 'type': 'LEFT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 576.57483, + 'x': 1682.0824, + 'z': -41.733879 + }, + 'type': 'RIGHT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 571.701, + 'x': 1749.7633, + 'z': -56.105503 + }, + 'type': 'LEFT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 556.67511, + 'x': 1837.4333, + 'z': -35.228374 + }, + 'type': 'RIGHT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 600.41345, + 'x': 1720.1719, + 'z': -44.4393 + }, + 'type': 'MIDPOINT_BETWEEN_EYES' + }, { + 'position': { + 'y': 691.66907, + 'x': 1720.0095, + 'z': -63.878113 + }, + 'type': 'NOSE_TIP' + }, { + 'position': { + 'y': 731.63239, + 'x': 1733.2758, + 'z': -20.964622 + }, + 'type': 'UPPER_LIP' + }, { + 'position': { + 'y': 774.79138, + 'x': 1740.1494, + 'z': -0.038273316 + }, + 'type': 'LOWER_LIP' + }, { + 'position': { + 'y': 739.80981, + 'x': 1673.0156, + 'z': 35.655769 + }, + 'type': 'MOUTH_LEFT' + }, { + 'position': { + 'y': 728.8186, + 'x': 1808.8899, + 'z': 9.5512733 + }, + 'type': 'MOUTH_RIGHT' + }, { + 'position': { + 'y': 753.71118, + 'x': 1738.0863, + 'z': -5.2711153 + }, + 'type': 'MOUTH_CENTER' + }, { + 'position': { + 'y': 684.97522, + 'x': 1770.2415, + 'z': -18.243216 + }, + 'type': 'NOSE_BOTTOM_RIGHT' + }, { + 'position': { + 'y': 695.69922, + 'x': 1693.4669, + 'z': -0.6566487 + }, + 'type': 'NOSE_BOTTOM_LEFT' + }, { + 'position': { + 'y': 704.46063, + 'x': 1729.86, + 'z': -28.144602 + }, + 'type': 'NOSE_BOTTOM_CENTER' + }, { + 'position': { + 'y': 597.93713, + 'x': 1654.082, + 'z': -11.508363 + }, + 'type': 'LEFT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 605.889, + 'x': 1684.0094, + 'z': -5.0379925 + }, + 'type': 'LEFT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 614.40448, + 'x': 1656.4753, + 'z': 1.001922 + }, + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 604.11292, + 'x': 1632.2733, + 'z': 18.163708 + }, + 'type': 'LEFT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 606.02026, + 'x': 1654.1372, + 'z': -3.3510325 + }, + 'type': 'LEFT_EYE_PUPIL' + }, { + 'position': { + 'y': 588.00885, + 'x': 1790.3329, + 'z': -41.150127 + }, + 'type': 'RIGHT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 590.46307, + 'x': 1824.5522, + 'z': -23.20849 + }, + 'type': 'RIGHT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 601.75946, + 'x': 1797.9852, + 'z': -29.095766 + }, + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 598.66449, + 'x': 1768.7595, + 'z': -23.117319 + }, + 'type': 'RIGHT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 595.84918, + 'x': 1794.0195, + 'z': -33.897068 + }, + 'type': 'RIGHT_EYE_PUPIL' + }, { + 'position': { + 'y': 561.08679, + 'x': 1641.9266, + 'z': -26.653444 + }, + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 550.38129, + 'x': 1789.6267, + 'z': -58.874447 + }, + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 632.54456, + 'x': 1611.1659, + 'z': 198.83691 + }, + 'type': 'LEFT_EAR_TRAGION' + }, { + 'position': { + 'y': 610.1615, + 'x': 1920.511, + 'z': 131.28908 + }, + 'type': 'RIGHT_EAR_TRAGION' + }, { + 'position': { + 'y': 574.28448, + 'x': 1714.6324, + 'z': -54.497036 + }, + 'type': 'FOREHEAD_GLABELLA' + }, { + 'position': { + 'y': 830.93884, + 'x': 1752.2703, + 'z': 33.332912 + }, + 'type': 'CHIN_GNATHION' + }, { + 'position': { + 'y': 732.33936, + 'x': 1626.519, + 'z': 162.6319 + }, + 'type': 'CHIN_LEFT_GONION' + }, { + 'position': { + 'y': 712.21118, + 'x': 1905.7007, + 'z': 101.86344 + }, + 'type': 'CHIN_RIGHT_GONION' + }], + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': -13.636207, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 319, + 'x': 1524 + }, { + 'y': 319, + 'x': 1959 + }, { + 'y': 859, + 'x': 1959 + }, { + 'y': 859, + 'x': 1524 + }] + }, + 'rollAngle': -7.1766233, + 'blurredLikelihood': 'VERY_UNLIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 485, + 'x': 1559 + }, { + 'y': 485, + 'x': 1920 + }, { + 'y': 846, + 'x': 1920 + }, { + 'y': 846, + 'x': 1559 + }] + } + }, { + 'headwearLikelihood': 'VERY_UNLIKELY', + 'panAngle': 8.7634687, + 'underExposedLikelihood': 'VERY_UNLIKELY', + 'landmarkingConfidence': 0.45491594, + 'detectionConfidence': 0.98870116, + 'joyLikelihood': 'VERY_LIKELY', + 'landmarks': [{ + 'position': { + 'y': 678.57886, + 'x': 397.22269, + 'z': 0.00052442803 + }, + 'type': 'LEFT_EYE' + }, { + 'position': { + 'y': 671.90381, + 'x': 515.38159, + 'z': 17.843918 + }, + 'type': 'RIGHT_EYE' + }, { + 'position': { + 'y': 657.13904, + 'x': 361.41068, + 'z': 6.1270714 + }, + 'type': 'LEFT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 649.82916, + 'x': 432.9726, + 'z': -16.12303 + }, + 'type': 'RIGHT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 646.04272, + 'x': 487.78485, + 'z': -7.638854 + }, + 'type': 'LEFT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 642.4032, + 'x': 549.46954, + 'z': 35.154259 + }, + 'type': 'RIGHT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 672.44031, + 'x': 462.86993, + 'z': -14.413016 + }, + 'type': 'MIDPOINT_BETWEEN_EYES' + }, { + 'position': { + 'y': 736.5896, + 'x': 474.0661, + 'z': -50.206612 + }, + 'type': 'NOSE_TIP' + }, { + 'position': { + 'y': 775.34973, + 'x': 472.54224, + 'z': -25.24843 + }, + 'type': 'UPPER_LIP' + }, { + 'position': { + 'y': 820.41418, + 'x': 474.41162, + 'z': -18.226196 + }, + 'type': 'LOWER_LIP' + }, { + 'position': { + 'y': 797.35547, + 'x': 415.29095, + 'z': 0.069621459 + }, + 'type': 'MOUTH_LEFT' + }, { + 'position': { + 'y': 786.58917, + 'x': 519.26709, + 'z': 13.945135 + }, + 'type': 'MOUTH_RIGHT' + }, { + 'position': { + 'y': 798.462, + 'x': 472.48071, + 'z': -17.317541 + }, + 'type': 'MOUTH_CENTER' + }, { + 'position': { + 'y': 742.13464, + 'x': 498.90826, + 'z': -1.8338414 + }, + 'type': 'NOSE_BOTTOM_RIGHT' + }, { + 'position': { + 'y': 747.218, + 'x': 438.95078, + 'z': -11.851667 + }, + 'type': 'NOSE_BOTTOM_LEFT' + }, { + 'position': { + 'y': 754.20105, + 'x': 472.47375, + 'z': -24.760784 + }, + 'type': 'NOSE_BOTTOM_CENTER' + }, { + 'position': { + 'y': 672.1994, + 'x': 403.39957, + 'z': -6.9005938 + }, + 'type': 'LEFT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 679.914, + 'x': 425.36029, + 'z': 4.3264537 + }, + 'type': 'LEFT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 687.11792, + 'x': 401.66464, + 'z': -0.79697126 + }, + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 682.9585, + 'x': 378.93005, + 'z': 7.3909378 + }, + 'type': 'LEFT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 680.40326, + 'x': 401.7229, + 'z': -2.7444897 + }, + 'type': 'LEFT_EYE_PUPIL' + }, { + 'position': { + 'y': 663.39496, + 'x': 516.03217, + 'z': 10.454485 + }, + 'type': 'RIGHT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 670.74463, + 'x': 536.45978, + 'z': 31.652559 + }, + 'type': 'RIGHT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 679.21289, + 'x': 517.50879, + 'z': 16.653259 + }, + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 676.06976, + 'x': 495.27335, + 'z': 14.956539 + }, + 'type': 'RIGHT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 671.41052, + 'x': 517.3429, + 'z': 15.007857 + }, + 'type': 'RIGHT_EYE_PUPIL' + }, { + 'position': { + 'y': 639.23633, + 'x': 396.8494, + 'z': -12.132922 + }, + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 629.66724, + 'x': 518.96332, + 'z': 6.7055798 + }, + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 750.20837, + 'x': 313.60855, + 'z': 127.8474 + }, + 'type': 'LEFT_EAR_TRAGION' + }, { + 'position': { + 'y': 728.68243, + 'x': 570.95, + 'z': 166.43564 + }, + 'type': 'RIGHT_EAR_TRAGION' + }, { + 'position': { + 'y': 646.05042, + 'x': 460.94397, + 'z': -16.196959 + }, + 'type': 'FOREHEAD_GLABELLA' + }, { + 'position': { + 'y': 869.36255, + 'x': 476.69009, + 'z': -4.4716644 + }, + 'type': 'CHIN_GNATHION' + }, { + 'position': { + 'y': 818.48083, + 'x': 340.65454, + 'z': 80.163544 + }, + 'type': 'CHIN_LEFT_GONION' + }, { + 'position': { + 'y': 800.17029, + 'x': 571.60297, + 'z': 115.88489 + }, + 'type': 'CHIN_RIGHT_GONION' + }], + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': 2.1818738, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 481, + 'x': 257 + }, { + 'y': 481, + 'x': 636 + }, { + 'y': 922, + 'x': 636 + }, { + 'y': 922, + 'x': 257 + }] + }, + 'rollAngle': -4.8415074, + 'blurredLikelihood': 'VERY_UNLIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 597, + 'x': 315 + }, { + 'y': 597, + 'x': 593 + }, { + 'y': 874, + 'x': 593 + }, { + 'y': 874, + 'x': 315 + }] + } + }, { + 'headwearLikelihood': 'VERY_UNLIKELY', + 'panAngle': 13.486016, + 'underExposedLikelihood': 'VERY_UNLIKELY', + 'landmarkingConfidence': 0.22890881, + 'detectionConfidence': 0.91653949, + 'joyLikelihood': 'LIKELY', + 'landmarks': [{ + 'position': { + 'y': 549.30334, + 'x': 9.7225485, + 'z': 0.0014079071 + }, + 'type': 'LEFT_EYE' + }, { + 'position': { + 'y': 539.7489, + 'x': 128.87411, + 'z': 28.692257 + }, + 'type': 'RIGHT_EYE' + }, { + 'position': { + 'y': 523.62103, + 'x': -35.406662, + 'z': -0.67885911 + }, + 'type': 'LEFT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 519.99487, + 'x': 42.973644, + 'z': -18.105515 + }, + 'type': 'RIGHT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 514.23407, + 'x': 103.02193, + 'z': -4.1667485 + }, + 'type': 'LEFT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 505.69614, + 'x': 165.63609, + 'z': 47.583176 + }, + 'type': 'RIGHT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 540.9787, + 'x': 76.066139, + 'z': -11.183347 + }, + 'type': 'MIDPOINT_BETWEEN_EYES' + }, { + 'position': { + 'y': 615.48669, + 'x': 89.695564, + 'z': -41.252846 + }, + 'type': 'NOSE_TIP' + }, { + 'position': { + 'y': 658.39246, + 'x': 85.935593, + 'z': -9.70177 + }, + 'type': 'UPPER_LIP' + }, { + 'position': { + 'y': 703.04309, + 'x': 87.266853, + 'z': 2.6370313 + }, + 'type': 'LOWER_LIP' + }, { + 'position': { + 'y': 678.54712, + 'x': 31.584759, + 'z': 12.874522 + }, + 'type': 'MOUTH_LEFT' + }, { + 'position': { + 'y': 670.44092, + 'x': 126.54009, + 'z': 35.510525 + }, + 'type': 'MOUTH_RIGHT' + }, { + 'position': { + 'y': 677.92883, + 'x': 85.152267, + 'z': 0.89151889 + }, + 'type': 'MOUTH_CENTER' + }, { + 'position': { + 'y': 618.41052, + 'x': 112.767, + 'z': 14.021111 + }, + 'type': 'NOSE_BOTTOM_RIGHT' + }, { + 'position': { + 'y': 624.28644, + 'x': 45.776546, + 'z': -2.0218573 + }, + 'type': 'NOSE_BOTTOM_LEFT' + }, { + 'position': { + 'y': 632.9657, + 'x': 84.253586, + 'z': -12.025499 + }, + 'type': 'NOSE_BOTTOM_CENTER' + }, { + 'position': { + 'y': 541.79987, + 'x': 11.081995, + 'z': -8.7047234 + }, + 'type': 'LEFT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 549.57306, + 'x': 35.396069, + 'z': 6.4817863 + }, + 'type': 'LEFT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 557.55121, + 'x': 10.446005, + 'z': -0.37798333 + }, + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 551.75134, + 'x': -16.862394, + 'z': 5.4017038 + }, + 'type': 'LEFT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 550.14355, + 'x': 8.5758247, + 'z': -3.3803346 + }, + 'type': 'LEFT_EYE_PUPIL' + }, { + 'position': { + 'y': 531.02594, + 'x': 131.48265, + 'z': 20.201307 + }, + 'type': 'RIGHT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 536.71674, + 'x': 151.31306, + 'z': 45.753532 + }, + 'type': 'RIGHT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 547.00037, + 'x': 130.27722, + 'z': 28.447813 + }, + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 542.38531, + 'x': 106.59242, + 'z': 23.77187 + }, + 'type': 'RIGHT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 539.12781, + 'x': 132.16141, + 'z': 26.180428 + }, + 'type': 'RIGHT_EYE_PUPIL' + }, { + 'position': { + 'y': 506.64093, + 'x': 4.8589344, + 'z': -18.679537 + }, + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 494.94244, + 'x': 135.53185, + 'z': 12.703153 + }, + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 609.03503, + 'x': -98.89212, + 'z': 134.96341 + }, + 'type': 'LEFT_EAR_TRAGION' + }, { + 'position': { + 'y': 584.60681, + 'x': 174.55208, + 'z': 200.56409 + }, + 'type': 'RIGHT_EAR_TRAGION' + }, { + 'position': { + 'y': 514.88513, + 'x': 74.575394, + 'z': -15.91002 + }, + 'type': 'FOREHEAD_GLABELLA' + }, { + 'position': { + 'y': 755.372, + 'x': 86.603539, + 'z': 23.596317 + }, + 'type': 'CHIN_GNATHION' + }, { + 'position': { + 'y': 689.8385, + 'x': -67.949554, + 'z': 94.833694 + }, + 'type': 'CHIN_LEFT_GONION' + }, { + 'position': { + 'y': 667.89325, + 'x': 179.19363, + 'z': 154.18192 + }, + 'type': 'CHIN_RIGHT_GONION' + }], + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': -4.1819687, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 322 + }, { + 'y': 322, + 'x': 252 + }, { + 'y': 800, + 'x': 252 + }, { + 'y': 800 + }] + }, + 'rollAngle': -4.1248608, + 'blurredLikelihood': 'LIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 450 + }, { + 'y': 450, + 'x': 235 + }, { + 'y': 745, + 'x': 235 + }, { + 'y': 745 + }] + } + }, { + 'headwearLikelihood': 'VERY_UNLIKELY', + 'panAngle': 4.0344138, + 'underExposedLikelihood': 'VERY_UNLIKELY', + 'landmarkingConfidence': 0.16798845, + 'detectionConfidence': 0.7605139, + 'joyLikelihood': 'VERY_LIKELY', + 'landmarks': [{ + 'position': { + 'y': 637.85211, + 'x': 676.09375, + 'z': 4.3306696e-05 + }, + 'type': 'LEFT_EYE' + }, { + 'position': { + 'y': 637.43292, + 'x': 767.7132, + 'z': 6.4413033 + }, + 'type': 'RIGHT_EYE' + }, { + 'position': { + 'y': 614.27075, + 'x': 642.07782, + 'z': 3.731837 + }, + 'type': 'LEFT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 617.27216, + 'x': 700.90112, + 'z': -19.774208 + }, + 'type': 'RIGHT_OF_LEFT_EYEBROW' + }, { + 'position': { + 'y': 617.15649, + 'x': 747.60974, + 'z': -16.511871 + }, + 'type': 'LEFT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 614.018, + 'x': 802.60638, + 'z': 14.954031 + }, + 'type': 'RIGHT_OF_RIGHT_EYEBROW' + }, { + 'position': { + 'y': 638.11755, + 'x': 724.42511, + 'z': -16.930967 + }, + 'type': 'MIDPOINT_BETWEEN_EYES' + }, { + 'position': { + 'y': 696.08392, + 'x': 725.82532, + 'z': -38.252609 + }, + 'type': 'NOSE_TIP' + }, { + 'position': { + 'y': 727.826, + 'x': 724.0116, + 'z': -11.615328 + }, + 'type': 'UPPER_LIP' + }, { + 'position': { + 'y': 760.22595, + 'x': 723.30157, + 'z': -0.454926 + }, + 'type': 'LOWER_LIP' + }, { + 'position': { + 'y': 738.67548, + 'x': 684.35724, + 'z': 13.192401 + }, + 'type': 'MOUTH_LEFT' + }, { + 'position': { + 'y': 738.53015, + 'x': 759.91022, + 'z': 18.485643 + }, + 'type': 'MOUTH_RIGHT' + }, { + 'position': { + 'y': 742.42737, + 'x': 723.45239, + 'z': -2.4991846 + }, + 'type': 'MOUTH_CENTER' + }, { + 'position': { + 'y': 698.4281, + 'x': 749.50385, + 'z': 1.1831931 + }, + 'type': 'NOSE_BOTTOM_RIGHT' + }, { + 'position': { + 'y': 698.48151, + 'x': 696.923, + 'z': -2.4809308 + }, + 'type': 'NOSE_BOTTOM_LEFT' + }, { + 'position': { + 'y': 708.10651, + 'x': 724.18506, + 'z': -14.418536 + }, + 'type': 'NOSE_BOTTOM_CENTER' + }, { + 'position': { + 'y': 632.12128, + 'x': 675.22388, + 'z': -7.2390652 + }, + 'type': 'LEFT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 638.59021, + 'x': 694.03516, + 'z': 1.7715795 + }, + 'type': 'LEFT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 644.33356, + 'x': 674.92206, + 'z': -0.037067439 + }, + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 637.16479, + 'x': 655.035, + 'z': 7.4372306 + }, + 'type': 'LEFT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 638.18683, + 'x': 673.39447, + 'z': -2.4558623 + }, + 'type': 'LEFT_EYE_PUPIL' + }, { + 'position': { + 'y': 631.96063, + 'x': 771.31744, + 'z': -0.51439536 + }, + 'type': 'RIGHT_EYE_TOP_BOUNDARY' + }, { + 'position': { + 'y': 636.94287, + 'x': 789.29443, + 'z': 16.814001 + }, + 'type': 'RIGHT_EYE_RIGHT_CORNER' + }, { + 'position': { + 'y': 644.21619, + 'x': 770.13458, + 'z': 6.6525826 + }, + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' + }, { + 'position': { + 'y': 638.75732, + 'x': 752.51831, + 'z': 5.8927159 + }, + 'type': 'RIGHT_EYE_LEFT_CORNER' + }, { + 'position': { + 'y': 638.06738, + 'x': 772.04718, + 'z': 4.350193 + }, + 'type': 'RIGHT_EYE_PUPIL' + }, { + 'position': { + 'y': 604.87769, + 'x': 671.68707, + 'z': -15.778968 + }, + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 604.71191, + 'x': 775.98663, + 'z': -8.4828024 + }, + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' + }, { + 'position': { + 'y': 670.40063, + 'x': 605.07721, + 'z': 119.27386 + }, + 'type': 'LEFT_EAR_TRAGION' + }, { + 'position': { + 'y': 669.99823, + 'x': 823.42841, + 'z': 134.54482 + }, + 'type': 'RIGHT_EAR_TRAGION' + }, { + 'position': { + 'y': 616.47058, + 'x': 724.54547, + 'z': -21.861612 + }, + 'type': 'FOREHEAD_GLABELLA' + }, { + 'position': { + 'y': 801.31934, + 'x': 722.071, + 'z': 18.37034 + }, + 'type': 'CHIN_GNATHION' + }, { + 'position': { + 'y': 736.57159, + 'x': 617.91388, + 'z': 88.713562 + }, + 'type': 'CHIN_LEFT_GONION' + }, { + 'position': { + 'y': 736.21118, + 'x': 815.234, + 'z': 102.52047 + }, + 'type': 'CHIN_RIGHT_GONION' + }], + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': -7.0173812, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 459, + 'x': 557 + }, { + 'y': 459, + 'x': 875 + }, { + 'y': 829, + 'x': 875 + }, { + 'y': 829, + 'x': 557 + }] + }, + 'rollAngle': 0.38634345, + 'blurredLikelihood': 'LIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 570, + 'x': 612 + }, { + 'y': 570, + 'x': 837 + }, { + 'y': 795, + 'x': 837 + }, { + 'y': 795, + 'x': 612 + }] + } + }] + }] +} diff --git a/unit_tests/vision/test_client.py b/unit_tests/vision/test_client.py index 83d7c24595fc..d433f91b8390 100644 --- a/unit_tests/vision/test_client.py +++ b/unit_tests/vision/test_client.py @@ -40,7 +40,7 @@ def test_ctor(self): def test_face_annotation(self): from google.cloud.vision.feature import Feature, FeatureTypes - from google.cloud.vision._fixtures import FACE_DETECTION_RESPONSE + from unit_tests.vision._fixtures import FACE_DETECTION_RESPONSE RETURNED = FACE_DETECTION_RESPONSE REQUEST = { @@ -82,7 +82,7 @@ def test_image_with_client(self): def test_face_detection_from_source(self): from google.cloud.vision.face import Face - from google.cloud.vision._fixtures import FACE_DETECTION_RESPONSE + from unit_tests.vision._fixtures import FACE_DETECTION_RESPONSE RETURNED = FACE_DETECTION_RESPONSE credentials = _Credentials() client = self._makeOne(project=self.PROJECT, credentials=credentials) @@ -99,7 +99,7 @@ def test_face_detection_from_source(self): def test_face_detection_from_content(self): from google.cloud.vision.face import Face - from google.cloud.vision._fixtures import FACE_DETECTION_RESPONSE + from unit_tests.vision._fixtures import FACE_DETECTION_RESPONSE RETURNED = FACE_DETECTION_RESPONSE credentials = _Credentials() client = self._makeOne(project=self.PROJECT, credentials=credentials) @@ -114,6 +114,40 @@ def test_face_detection_from_content(self): image_request['image']['content']) self.assertEqual(5, image_request['features'][0]['maxResults']) + def test_logo_detection_from_source(self): + from google.cloud.vision.entity import EntityAnnotation + from unit_tests.vision._fixtures import LOGO_DETECTION_RESPONSE + RETURNED = LOGO_DETECTION_RESPONSE + credentials = _Credentials() + client = self._makeOne(project=self.PROJECT, credentials=credentials) + client.connection = _Connection(RETURNED) + + image = client.image(source_uri=_IMAGE_SOURCE) + logos = image.detect_logos(limit=3) + self.assertEqual(2, len(logos)) + self.assertTrue(isinstance(logos[0], EntityAnnotation)) + image_request = client.connection._requested[0]['data']['requests'][0] + self.assertEqual(_IMAGE_SOURCE, + image_request['image']['source']['gcs_image_uri']) + self.assertEqual(3, image_request['features'][0]['maxResults']) + + def test_logo_detection_from_content(self): + from google.cloud.vision.entity import EntityAnnotation + from unit_tests.vision._fixtures import LOGO_DETECTION_RESPONSE + RETURNED = LOGO_DETECTION_RESPONSE + credentials = _Credentials() + client = self._makeOne(project=self.PROJECT, credentials=credentials) + client.connection = _Connection(RETURNED) + + image = client.image(content=_IMAGE_CONTENT) + logos = image.detect_logos(limit=5) + self.assertEqual(2, len(logos)) + self.assertTrue(isinstance(logos[0], EntityAnnotation)) + image_request = client.connection._requested[0]['data']['requests'][0] + self.assertEqual(self.B64_IMAGE_CONTENT, + image_request['image']['content']) + self.assertEqual(5, image_request['features'][0]['maxResults']) + class TestVisionRequest(unittest.TestCase): def _getTargetClass(self): diff --git a/unit_tests/vision/test_entity.py b/unit_tests/vision/test_entity.py new file mode 100644 index 000000000000..63e61ce7e546 --- /dev/null +++ b/unit_tests/vision/test_entity.py @@ -0,0 +1,33 @@ +# 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. + +import unittest + + +class TestEntityAnnotation(unittest.TestCase): + def _getTargetClass(self): + from google.cloud.vision.entity import EntityAnnotation + return EntityAnnotation + + def test_logo_annotation(self): + from unit_tests.vision._fixtures import LOGO_DETECTION_RESPONSE + + LOGO = LOGO_DETECTION_RESPONSE['responses'][0]['logoAnnotations'][0] + entity_class = self._getTargetClass() + logo = entity_class.from_api_repr(LOGO) + + self.assertEqual('/m/05b5c', logo.mid) + self.assertEqual('Brand1', logo.description) + self.assertEqual(0.63192177, logo.score) + self.assertEqual(162, logo.bounds.vertices[0].y_coordinate)