diff --git a/docs/index.rst b/docs/index.rst index edcf978c47f7..d5a1351aa96f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -151,6 +151,7 @@ vision-client vision-image vision-feature + vision-face .. toctree:: :maxdepth: 0 diff --git a/docs/vision-face.rst b/docs/vision-face.rst new file mode 100644 index 000000000000..56f541399132 --- /dev/null +++ b/docs/vision-face.rst @@ -0,0 +1,10 @@ +Vision Face +=========== + +Face +~~~~ + +.. automodule:: google.cloud.vision.face + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/vision-image.rst b/docs/vision-image.rst index e22be7161d22..491097c3ff31 100644 --- a/docs/vision-image.rst +++ b/docs/vision-image.rst @@ -8,3 +8,19 @@ Image :members: :undoc-members: :show-inheritance: + +Geometry +~~~~~~~~ + +.. automodule:: google.cloud.vision.geometry + :members: + :undoc-members: + :show-inheritance: + +Likelihood +~~~~~~~~~~ + +.. automodule:: google.cloud.vision.likelihood + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/vision-usage.rst b/docs/vision-usage.rst index 461ee9e19e27..a4555f8569e6 100644 --- a/docs/vision-usage.rst +++ b/docs/vision-usage.rst @@ -38,30 +38,29 @@ Annotate a single image .. code-block:: python + >>> import io >>> from google.cloud import vision >>> client = vision.Client() - >>> image = client.image('./image.png') + >>> with io.open('./image.png', 'rb') as image_file: + ... image = client.image(content=image_file.read()) >>> faces = image.detect_faces(limit=10) + >>> faces[0].landmarks.left_eye.position.x_coordinate + ... 1004.8003 Annotate multiple images ~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python - >>> first_image = client.image('./image.jpg') - >>> second_image = client.image('gs://my-storage-bucket/image2.jpg') - >>> with client.batch(): - ... labels = first_image.detect_labels() - ... faces = second_image.detect_faces(limit=10) - -or - -.. code-block:: python - - >>> images = [] - >>> images.append(client.image('./image.jpg')) - >>> images.append(client.image('gs://my-storage-bucket/image2.jpg')) - >>> faces = client.detect_faces_multi(images, limit=10) + >>> import io + >>> from gcloud import vision + >>> client = vision.Client() + >>> with io.open('./image.png', 'rb') as image_file: + ... image_one = client.image(content=image_file.read()) + >>> image_two = client.image(source_uri='gs://my-storage-bucket/image.jpg') + >>> with client.batch(): + ... labels = image_one.detect_labels() + ... faces = image_two.detect_faces(limit=10) No results returned ~~~~~~~~~~~~~~~~~~~ @@ -72,7 +71,7 @@ Failing annotations return no results for the feature type requested. >>> from google.cloud import vision >>> client = vision.Client() - >>> image = client.image('./image.jpg') + >>> image = client.image(source_uri='gs://my-storage-bucket/image.jpg') >>> logos = image.detect_logos(limit=10) >>> logos [] @@ -86,9 +85,13 @@ You can call the detection method manually. .. code-block:: python >>> from google.cloud import vision + >>> from google.cloud.vision.image import Feature + >>> from google.cloud.vision.image import FeatureTypes >>> client = vision.Client() - >>> image = client.image('gs://my-test-bucket/image.jpg') - >>> faces = image.detect(type=vision.FACE_DETECTION, limit=10) + >>> 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) Face Detection ~~~~~~~~~~~~~~ @@ -102,11 +105,11 @@ see: https://cloud.google.com/vision/reference/rest/v1/images/annotate#type_1 >>> from google.cloud import vision >>> client = vision.Client() - >>> image = client.image('./image.jpg') + >>> image = client.image(source_uri='gs://my-test-bucket/image.jpg') >>> faces = image.detect_faces(limit=10) - >>> faces[0].landmarks[0].type + >>> faces[0].landmarks.left_eye.landmark_type 'LEFT_EYE' - >>> faces[0].landmarks[0].position.x + >>> faces[0].landmarks.left_eye.position.x_coordinate 1301.2404 >>> faces[0].detection_confidence 0.9863683 @@ -128,7 +131,7 @@ attempt to identify those objects. >>> from google.cloud import vision >>> client = vision.Client() - >>> image = client.image('./image.jpg') + >>> image = client.image(source_uri='gs://my-storage-bucket/image.jpg') >>> labels = image.detect_labels(limit=3) >>> labels[0].description 'automobile' @@ -155,9 +158,9 @@ locations if available. -33.857123 >>> landmarks[0].locations[0].longitude 151.213921 - >>> landmarks[0].bounding_poly.vertices[0].x + >>> landmarks[0].bounding_poly.vertices[0].x_coordinate 78 - >>> landmarks[0].bounding_poly.vertices[0].y + >>> landmarks[0].bounding_poly.vertices[0].y_coordinate 162 Logo Detection @@ -175,9 +178,9 @@ Google Vision can also attempt to detect company and brand logos in images. 'Google' >>> logos[0].score 0.9795432 - >>> logos[0].bounding_poly.vertices[0].x + >>> logos[0].bounding_poly.vertices[0].x_coordinate 78 - >>> logos[0].bounding_poly.vertices[0].y + >>> logos[0].bounding_poly.vertices[0].y_coordinate 62 Safe Search Detection diff --git a/google/cloud/vision/__init__.py b/google/cloud/vision/__init__.py index fb7f2dfc2d0a..f25370f337b7 100644 --- a/google/cloud/vision/__init__.py +++ b/google/cloud/vision/__init__.py @@ -13,3 +13,4 @@ # limitations under the License. """Google Cloud Vision API package.""" +from google.cloud.vision.client import Client diff --git a/google/cloud/vision/_fixtures.py b/google/cloud/vision/_fixtures.py index 319283606cbc..ea97e4ea18ce 100644 --- a/google/cloud/vision/_fixtures.py +++ b/google/cloud/vision/_fixtures.py @@ -1,21 +1,21 @@ LABEL_DETECTION_RESPONSE = { - "responses": [ + 'responses': [ { - "labelAnnotations": [ + 'labelAnnotations': [ { - "mid": "/m/0k4j", - "description": "automobile", - "score": 0.9776855 + 'mid': '/m/0k4j', + 'description': 'automobile', + 'score': 0.9776855 }, { - "mid": "/m/07yv9", - "description": "vehicle", - "score": 0.947987 + 'mid': '/m/07yv9', + 'description': 'vehicle', + 'score': 0.947987 }, { - "mid": "/m/07r04", - "description": "truck", - "score": 0.88429511 + 'mid': '/m/07r04', + 'description': 'truck', + 'score': 0.88429511 } ] } @@ -23,1411 +23,1411 @@ } 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 + '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" + 'type': 'LEFT_EYE' }, { - "position": { - "y": 470.90149, - "x": 1218.9751, - "z": 20.597967 + 'position': { + 'y': 470.90149, + 'x': 1218.9751, + 'z': 20.597967 }, - "type": "RIGHT_EYE" + 'type': 'RIGHT_EYE' }, { - "position": { - "y": 441.46521, - "x": 934.25629, - "z": -1.1400928 + 'position': { + 'y': 441.46521, + 'x': 934.25629, + 'z': -1.1400928 }, - "type": "LEFT_OF_LEFT_EYEBROW" + 'type': 'LEFT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 449.2872, - "x": 1059.306, - "z": -47.195843 + 'position': { + 'y': 449.2872, + 'x': 1059.306, + 'z': -47.195843 }, - "type": "RIGHT_OF_LEFT_EYEBROW" + 'type': 'RIGHT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 446.05408, - "x": 1163.678, - "z": -37.211197 + 'position': { + 'y': 446.05408, + 'x': 1163.678, + 'z': -37.211197 }, - "type": "LEFT_OF_RIGHT_EYEBROW" + 'type': 'LEFT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 424.18341, - "x": 1285.0209, - "z": 34.844131 + 'position': { + 'y': 424.18341, + 'x': 1285.0209, + 'z': 34.844131 }, - "type": "RIGHT_OF_RIGHT_EYEBROW" + 'type': 'RIGHT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 485.18387, - "x": 1113.4325, - "z": -32.579361 + 'position': { + 'y': 485.18387, + 'x': 1113.4325, + 'z': -32.579361 }, - "type": "MIDPOINT_BETWEEN_EYES" + 'type': 'MIDPOINT_BETWEEN_EYES' }, { - "position": { - "y": 620.27032, - "x": 1122.5671, - "z": -51.019524 + 'position': { + 'y': 620.27032, + 'x': 1122.5671, + 'z': -51.019524 }, - "type": "NOSE_TIP" + 'type': 'NOSE_TIP' }, { - "position": { - "y": 674.32526, - "x": 1117.0417, - "z": 17.330631 + 'position': { + 'y': 674.32526, + 'x': 1117.0417, + 'z': 17.330631 }, - "type": "UPPER_LIP" + 'type': 'UPPER_LIP' }, { - "position": { - "y": 737.29736, - "x": 1115.7112, - "z": 54.076469 + 'position': { + 'y': 737.29736, + 'x': 1115.7112, + 'z': 54.076469 }, - "type": "LOWER_LIP" + 'type': 'LOWER_LIP' }, { - "position": { - "y": 680.62927, - "x": 1017.0475, - "z": 72.948006 + 'position': { + 'y': 680.62927, + 'x': 1017.0475, + 'z': 72.948006 }, - "type": "MOUTH_LEFT" + 'type': 'MOUTH_LEFT' }, { - "position": { - "y": 681.53552, - "x": 1191.5186, - "z": 87.198334 + 'position': { + 'y': 681.53552, + 'x': 1191.5186, + 'z': 87.198334 }, - "type": "MOUTH_RIGHT" + 'type': 'MOUTH_RIGHT' }, { - "position": { - "y": 702.3808, - "x": 1115.4193, - "z": 42.56889 + 'position': { + 'y': 702.3808, + 'x': 1115.4193, + 'z': 42.56889 }, - "type": "MOUTH_CENTER" + 'type': 'MOUTH_CENTER' }, { - "position": { - "y": 606.68555, - "x": 1169.0006, - "z": 33.98217 + 'position': { + 'y': 606.68555, + 'x': 1169.0006, + 'z': 33.98217 }, - "type": "NOSE_BOTTOM_RIGHT" + 'type': 'NOSE_BOTTOM_RIGHT' }, { - "position": { - "y": 612.71509, - "x": 1053.9476, - "z": 23.409685 + 'position': { + 'y': 612.71509, + 'x': 1053.9476, + 'z': 23.409685 }, - "type": "NOSE_BOTTOM_LEFT" + 'type': 'NOSE_BOTTOM_LEFT' }, { - "position": { - "y": 634.95532, - "x": 1116.6818, - "z": 3.386874 + 'position': { + 'y': 634.95532, + 'x': 1116.6818, + 'z': 3.386874 }, - "type": "NOSE_BOTTOM_CENTER" + 'type': 'NOSE_BOTTOM_CENTER' }, { - "position": { - "y": 476.70197, - "x": 1009.2689, - "z": -16.84004 + 'position': { + 'y': 476.70197, + 'x': 1009.2689, + 'z': -16.84004 }, - "type": "LEFT_EYE_TOP_BOUNDARY" + 'type': 'LEFT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 491.64874, - "x": 1049.3926, - "z": 7.0493474 + 'position': { + 'y': 491.64874, + 'x': 1049.3926, + 'z': 7.0493474 }, - "type": "LEFT_EYE_RIGHT_CORNER" + 'type': 'LEFT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 499.426, - "x": 1003.9925, - "z": 3.5417991 + 'position': { + 'y': 499.426, + 'x': 1003.9925, + 'z': 3.5417991 }, - "type": "LEFT_EYE_BOTTOM_BOUNDARY" + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 482.37302, - "x": 964.48242, - "z": 14.96223 + 'position': { + 'y': 482.37302, + 'x': 964.48242, + 'z': 14.96223 }, - "type": "LEFT_EYE_LEFT_CORNER" + 'type': 'LEFT_EYE_LEFT_CORNER' }, { - "position": { - "y": 487.90195, - "x": 1005.3607, - "z": -4.7375555 + 'position': { + 'y': 487.90195, + 'x': 1005.3607, + 'z': -4.7375555 }, - "type": "LEFT_EYE_PUPIL" + 'type': 'LEFT_EYE_PUPIL' }, { - "position": { - "y": 468.33276, - "x": 1212.7329, - "z": 3.5585577 + 'position': { + 'y': 468.33276, + 'x': 1212.7329, + 'z': 3.5585577 }, - "type": "RIGHT_EYE_TOP_BOUNDARY" + 'type': 'RIGHT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 470.92487, - "x": 1251.7043, - "z": 43.794273 + 'position': { + 'y': 470.92487, + 'x': 1251.7043, + 'z': 43.794273 }, - "type": "RIGHT_EYE_RIGHT_CORNER" + 'type': 'RIGHT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 486.98676, - "x": 1217.4629, - "z": 23.580008 + 'position': { + 'y': 486.98676, + 'x': 1217.4629, + 'z': 23.580008 }, - "type": "RIGHT_EYE_BOTTOM_BOUNDARY" + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 482.41071, - "x": 1173.4624, - "z": 18.852427 + 'position': { + 'y': 482.41071, + 'x': 1173.4624, + 'z': 18.852427 }, - "type": "RIGHT_EYE_LEFT_CORNER" + 'type': 'RIGHT_EYE_LEFT_CORNER' }, { - "position": { - "y": 479.32739, - "x": 1213.9757, - "z": 16.041821 + 'position': { + 'y': 479.32739, + 'x': 1213.9757, + 'z': 16.041821 }, - "type": "RIGHT_EYE_PUPIL" + 'type': 'RIGHT_EYE_PUPIL' }, { - "position": { - "y": 424.38797, - "x": 1001.2206, - "z": -46.463905 + 'position': { + 'y': 424.38797, + 'x': 1001.2206, + 'z': -46.463905 }, - "type": "LEFT_EYEBROW_UPPER_MIDPOINT" + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 415.33655, - "x": 1221.9457, - "z": -24.29454 + 'position': { + 'y': 415.33655, + 'x': 1221.9457, + 'z': -24.29454 }, - "type": "RIGHT_EYEBROW_UPPER_MIDPOINT" + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 506.88251, - "x": 851.96124, - "z": 257.9054 + 'position': { + 'y': 506.88251, + 'x': 851.96124, + 'z': 257.9054 }, - "type": "LEFT_EAR_TRAGION" + 'type': 'LEFT_EAR_TRAGION' }, { - "position": { - "y": 487.9679, - "x": 1313.8328, - "z": 304.29816 + 'position': { + 'y': 487.9679, + 'x': 1313.8328, + 'z': 304.29816 }, - "type": "RIGHT_EAR_TRAGION" + 'type': 'RIGHT_EAR_TRAGION' }, { - "position": { - "y": 447.98254, - "x": 1114.1573, - "z": -50.620598 + 'position': { + 'y': 447.98254, + 'x': 1114.1573, + 'z': -50.620598 }, - "type": "FOREHEAD_GLABELLA" + 'type': 'FOREHEAD_GLABELLA' }, { - "position": { - "y": 815.3302, - "x": 1113.27, - "z": 109.69422 + 'position': { + 'y': 815.3302, + 'x': 1113.27, + 'z': 109.69422 }, - "type": "CHIN_GNATHION" + 'type': 'CHIN_GNATHION' }, { - "position": { - "y": 656.20123, - "x": 884.34106, - "z": 223.19124 + 'position': { + 'y': 656.20123, + 'x': 884.34106, + 'z': 223.19124 }, - "type": "CHIN_LEFT_GONION" + 'type': 'CHIN_LEFT_GONION' }, { - "position": { - "y": 639.291, - "x": 1301.2404, - "z": 265.00647 + 'position': { + 'y': 639.291, + 'x': 1301.2404, + 'z': 265.00647 }, - "type": "CHIN_RIGHT_GONION" + 'type': 'CHIN_RIGHT_GONION' }], - "sorrowLikelihood": "VERY_UNLIKELY", - "surpriseLikelihood": "VERY_UNLIKELY", - "tiltAngle": -18.412321, - "angerLikelihood": "VERY_UNLIKELY", - "boundingPoly": { - "vertices": [{ - "y": 58, - "x": 748 + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': -18.412321, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 58, + 'x': 748 }, { - "y": 58, - "x": 1430 + 'y': 58, + 'x': 1430 }, { - "y": 851, - "x": 1430 + 'y': 851, + 'x': 1430 }, { - "y": 851, - "x": 748 + 'y': 851, + 'x': 748 }] }, - "rollAngle": -0.43419784, - "blurredLikelihood": "VERY_UNLIKELY", - "fdBoundingPoly": { - "vertices": [{ - "y": 310, - "x": 845 + 'rollAngle': -0.43419784, + 'blurredLikelihood': 'VERY_UNLIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 310, + 'x': 845 }, { - "y": 310, - "x": 1379 + 'y': 310, + 'x': 1379 }, { - "y": 844, - "x": 1379 + 'y': 844, + 'x': 1379 }, { - "y": 844, - "x": 845 + '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 + '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" + 'type': 'LEFT_EYE' }, { - "position": { - "y": 590.82428, - "x": 1797.3677, - "z": -30.984835 + 'position': { + 'y': 590.82428, + 'x': 1797.3677, + 'z': -30.984835 }, - "type": "RIGHT_EYE" + 'type': 'RIGHT_EYE' }, { - "position": { - "y": 574.40173, - "x": 1609.9617, - "z": 14.634346 + 'position': { + 'y': 574.40173, + 'x': 1609.9617, + 'z': 14.634346 }, - "type": "LEFT_OF_LEFT_EYEBROW" + 'type': 'LEFT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 576.57483, - "x": 1682.0824, - "z": -41.733879 + 'position': { + 'y': 576.57483, + 'x': 1682.0824, + 'z': -41.733879 }, - "type": "RIGHT_OF_LEFT_EYEBROW" + 'type': 'RIGHT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 571.701, - "x": 1749.7633, - "z": -56.105503 + 'position': { + 'y': 571.701, + 'x': 1749.7633, + 'z': -56.105503 }, - "type": "LEFT_OF_RIGHT_EYEBROW" + 'type': 'LEFT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 556.67511, - "x": 1837.4333, - "z": -35.228374 + 'position': { + 'y': 556.67511, + 'x': 1837.4333, + 'z': -35.228374 }, - "type": "RIGHT_OF_RIGHT_EYEBROW" + 'type': 'RIGHT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 600.41345, - "x": 1720.1719, - "z": -44.4393 + 'position': { + 'y': 600.41345, + 'x': 1720.1719, + 'z': -44.4393 }, - "type": "MIDPOINT_BETWEEN_EYES" + 'type': 'MIDPOINT_BETWEEN_EYES' }, { - "position": { - "y": 691.66907, - "x": 1720.0095, - "z": -63.878113 + 'position': { + 'y': 691.66907, + 'x': 1720.0095, + 'z': -63.878113 }, - "type": "NOSE_TIP" + 'type': 'NOSE_TIP' }, { - "position": { - "y": 731.63239, - "x": 1733.2758, - "z": -20.964622 + 'position': { + 'y': 731.63239, + 'x': 1733.2758, + 'z': -20.964622 }, - "type": "UPPER_LIP" + 'type': 'UPPER_LIP' }, { - "position": { - "y": 774.79138, - "x": 1740.1494, - "z": -0.038273316 + 'position': { + 'y': 774.79138, + 'x': 1740.1494, + 'z': -0.038273316 }, - "type": "LOWER_LIP" + 'type': 'LOWER_LIP' }, { - "position": { - "y": 739.80981, - "x": 1673.0156, - "z": 35.655769 + 'position': { + 'y': 739.80981, + 'x': 1673.0156, + 'z': 35.655769 }, - "type": "MOUTH_LEFT" + 'type': 'MOUTH_LEFT' }, { - "position": { - "y": 728.8186, - "x": 1808.8899, - "z": 9.5512733 + 'position': { + 'y': 728.8186, + 'x': 1808.8899, + 'z': 9.5512733 }, - "type": "MOUTH_RIGHT" + 'type': 'MOUTH_RIGHT' }, { - "position": { - "y": 753.71118, - "x": 1738.0863, - "z": -5.2711153 + 'position': { + 'y': 753.71118, + 'x': 1738.0863, + 'z': -5.2711153 }, - "type": "MOUTH_CENTER" + 'type': 'MOUTH_CENTER' }, { - "position": { - "y": 684.97522, - "x": 1770.2415, - "z": -18.243216 + 'position': { + 'y': 684.97522, + 'x': 1770.2415, + 'z': -18.243216 }, - "type": "NOSE_BOTTOM_RIGHT" + 'type': 'NOSE_BOTTOM_RIGHT' }, { - "position": { - "y": 695.69922, - "x": 1693.4669, - "z": -0.6566487 + 'position': { + 'y': 695.69922, + 'x': 1693.4669, + 'z': -0.6566487 }, - "type": "NOSE_BOTTOM_LEFT" + 'type': 'NOSE_BOTTOM_LEFT' }, { - "position": { - "y": 704.46063, - "x": 1729.86, - "z": -28.144602 + 'position': { + 'y': 704.46063, + 'x': 1729.86, + 'z': -28.144602 }, - "type": "NOSE_BOTTOM_CENTER" + 'type': 'NOSE_BOTTOM_CENTER' }, { - "position": { - "y": 597.93713, - "x": 1654.082, - "z": -11.508363 + 'position': { + 'y': 597.93713, + 'x': 1654.082, + 'z': -11.508363 }, - "type": "LEFT_EYE_TOP_BOUNDARY" + 'type': 'LEFT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 605.889, - "x": 1684.0094, - "z": -5.0379925 + 'position': { + 'y': 605.889, + 'x': 1684.0094, + 'z': -5.0379925 }, - "type": "LEFT_EYE_RIGHT_CORNER" + 'type': 'LEFT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 614.40448, - "x": 1656.4753, - "z": 1.001922 + 'position': { + 'y': 614.40448, + 'x': 1656.4753, + 'z': 1.001922 }, - "type": "LEFT_EYE_BOTTOM_BOUNDARY" + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 604.11292, - "x": 1632.2733, - "z": 18.163708 + 'position': { + 'y': 604.11292, + 'x': 1632.2733, + 'z': 18.163708 }, - "type": "LEFT_EYE_LEFT_CORNER" + 'type': 'LEFT_EYE_LEFT_CORNER' }, { - "position": { - "y": 606.02026, - "x": 1654.1372, - "z": -3.3510325 + 'position': { + 'y': 606.02026, + 'x': 1654.1372, + 'z': -3.3510325 }, - "type": "LEFT_EYE_PUPIL" + 'type': 'LEFT_EYE_PUPIL' }, { - "position": { - "y": 588.00885, - "x": 1790.3329, - "z": -41.150127 + 'position': { + 'y': 588.00885, + 'x': 1790.3329, + 'z': -41.150127 }, - "type": "RIGHT_EYE_TOP_BOUNDARY" + 'type': 'RIGHT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 590.46307, - "x": 1824.5522, - "z": -23.20849 + 'position': { + 'y': 590.46307, + 'x': 1824.5522, + 'z': -23.20849 }, - "type": "RIGHT_EYE_RIGHT_CORNER" + 'type': 'RIGHT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 601.75946, - "x": 1797.9852, - "z": -29.095766 + 'position': { + 'y': 601.75946, + 'x': 1797.9852, + 'z': -29.095766 }, - "type": "RIGHT_EYE_BOTTOM_BOUNDARY" + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 598.66449, - "x": 1768.7595, - "z": -23.117319 + 'position': { + 'y': 598.66449, + 'x': 1768.7595, + 'z': -23.117319 }, - "type": "RIGHT_EYE_LEFT_CORNER" + 'type': 'RIGHT_EYE_LEFT_CORNER' }, { - "position": { - "y": 595.84918, - "x": 1794.0195, - "z": -33.897068 + 'position': { + 'y': 595.84918, + 'x': 1794.0195, + 'z': -33.897068 }, - "type": "RIGHT_EYE_PUPIL" + 'type': 'RIGHT_EYE_PUPIL' }, { - "position": { - "y": 561.08679, - "x": 1641.9266, - "z": -26.653444 + 'position': { + 'y': 561.08679, + 'x': 1641.9266, + 'z': -26.653444 }, - "type": "LEFT_EYEBROW_UPPER_MIDPOINT" + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 550.38129, - "x": 1789.6267, - "z": -58.874447 + 'position': { + 'y': 550.38129, + 'x': 1789.6267, + 'z': -58.874447 }, - "type": "RIGHT_EYEBROW_UPPER_MIDPOINT" + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 632.54456, - "x": 1611.1659, - "z": 198.83691 + 'position': { + 'y': 632.54456, + 'x': 1611.1659, + 'z': 198.83691 }, - "type": "LEFT_EAR_TRAGION" + 'type': 'LEFT_EAR_TRAGION' }, { - "position": { - "y": 610.1615, - "x": 1920.511, - "z": 131.28908 + 'position': { + 'y': 610.1615, + 'x': 1920.511, + 'z': 131.28908 }, - "type": "RIGHT_EAR_TRAGION" + 'type': 'RIGHT_EAR_TRAGION' }, { - "position": { - "y": 574.28448, - "x": 1714.6324, - "z": -54.497036 + 'position': { + 'y': 574.28448, + 'x': 1714.6324, + 'z': -54.497036 }, - "type": "FOREHEAD_GLABELLA" + 'type': 'FOREHEAD_GLABELLA' }, { - "position": { - "y": 830.93884, - "x": 1752.2703, - "z": 33.332912 + 'position': { + 'y': 830.93884, + 'x': 1752.2703, + 'z': 33.332912 }, - "type": "CHIN_GNATHION" + 'type': 'CHIN_GNATHION' }, { - "position": { - "y": 732.33936, - "x": 1626.519, - "z": 162.6319 + 'position': { + 'y': 732.33936, + 'x': 1626.519, + 'z': 162.6319 }, - "type": "CHIN_LEFT_GONION" + 'type': 'CHIN_LEFT_GONION' }, { - "position": { - "y": 712.21118, - "x": 1905.7007, - "z": 101.86344 + 'position': { + 'y': 712.21118, + 'x': 1905.7007, + 'z': 101.86344 }, - "type": "CHIN_RIGHT_GONION" + 'type': 'CHIN_RIGHT_GONION' }], - "sorrowLikelihood": "VERY_UNLIKELY", - "surpriseLikelihood": "VERY_UNLIKELY", - "tiltAngle": -13.636207, - "angerLikelihood": "VERY_UNLIKELY", - "boundingPoly": { - "vertices": [{ - "y": 319, - "x": 1524 + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': -13.636207, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 319, + 'x': 1524 }, { - "y": 319, - "x": 1959 + 'y': 319, + 'x': 1959 }, { - "y": 859, - "x": 1959 + 'y': 859, + 'x': 1959 }, { - "y": 859, - "x": 1524 + 'y': 859, + 'x': 1524 }] }, - "rollAngle": -7.1766233, - "blurredLikelihood": "VERY_UNLIKELY", - "fdBoundingPoly": { - "vertices": [{ - "y": 485, - "x": 1559 + 'rollAngle': -7.1766233, + 'blurredLikelihood': 'VERY_UNLIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 485, + 'x': 1559 }, { - "y": 485, - "x": 1920 + 'y': 485, + 'x': 1920 }, { - "y": 846, - "x": 1920 + 'y': 846, + 'x': 1920 }, { - "y": 846, - "x": 1559 + '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 + '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" + 'type': 'LEFT_EYE' }, { - "position": { - "y": 671.90381, - "x": 515.38159, - "z": 17.843918 + 'position': { + 'y': 671.90381, + 'x': 515.38159, + 'z': 17.843918 }, - "type": "RIGHT_EYE" + 'type': 'RIGHT_EYE' }, { - "position": { - "y": 657.13904, - "x": 361.41068, - "z": 6.1270714 + 'position': { + 'y': 657.13904, + 'x': 361.41068, + 'z': 6.1270714 }, - "type": "LEFT_OF_LEFT_EYEBROW" + 'type': 'LEFT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 649.82916, - "x": 432.9726, - "z": -16.12303 + 'position': { + 'y': 649.82916, + 'x': 432.9726, + 'z': -16.12303 }, - "type": "RIGHT_OF_LEFT_EYEBROW" + 'type': 'RIGHT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 646.04272, - "x": 487.78485, - "z": -7.638854 + 'position': { + 'y': 646.04272, + 'x': 487.78485, + 'z': -7.638854 }, - "type": "LEFT_OF_RIGHT_EYEBROW" + 'type': 'LEFT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 642.4032, - "x": 549.46954, - "z": 35.154259 + 'position': { + 'y': 642.4032, + 'x': 549.46954, + 'z': 35.154259 }, - "type": "RIGHT_OF_RIGHT_EYEBROW" + 'type': 'RIGHT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 672.44031, - "x": 462.86993, - "z": -14.413016 + 'position': { + 'y': 672.44031, + 'x': 462.86993, + 'z': -14.413016 }, - "type": "MIDPOINT_BETWEEN_EYES" + 'type': 'MIDPOINT_BETWEEN_EYES' }, { - "position": { - "y": 736.5896, - "x": 474.0661, - "z": -50.206612 + 'position': { + 'y': 736.5896, + 'x': 474.0661, + 'z': -50.206612 }, - "type": "NOSE_TIP" + 'type': 'NOSE_TIP' }, { - "position": { - "y": 775.34973, - "x": 472.54224, - "z": -25.24843 + 'position': { + 'y': 775.34973, + 'x': 472.54224, + 'z': -25.24843 }, - "type": "UPPER_LIP" + 'type': 'UPPER_LIP' }, { - "position": { - "y": 820.41418, - "x": 474.41162, - "z": -18.226196 + 'position': { + 'y': 820.41418, + 'x': 474.41162, + 'z': -18.226196 }, - "type": "LOWER_LIP" + 'type': 'LOWER_LIP' }, { - "position": { - "y": 797.35547, - "x": 415.29095, - "z": 0.069621459 + 'position': { + 'y': 797.35547, + 'x': 415.29095, + 'z': 0.069621459 }, - "type": "MOUTH_LEFT" + 'type': 'MOUTH_LEFT' }, { - "position": { - "y": 786.58917, - "x": 519.26709, - "z": 13.945135 + 'position': { + 'y': 786.58917, + 'x': 519.26709, + 'z': 13.945135 }, - "type": "MOUTH_RIGHT" + 'type': 'MOUTH_RIGHT' }, { - "position": { - "y": 798.462, - "x": 472.48071, - "z": -17.317541 + 'position': { + 'y': 798.462, + 'x': 472.48071, + 'z': -17.317541 }, - "type": "MOUTH_CENTER" + 'type': 'MOUTH_CENTER' }, { - "position": { - "y": 742.13464, - "x": 498.90826, - "z": -1.8338414 + 'position': { + 'y': 742.13464, + 'x': 498.90826, + 'z': -1.8338414 }, - "type": "NOSE_BOTTOM_RIGHT" + 'type': 'NOSE_BOTTOM_RIGHT' }, { - "position": { - "y": 747.218, - "x": 438.95078, - "z": -11.851667 + 'position': { + 'y': 747.218, + 'x': 438.95078, + 'z': -11.851667 }, - "type": "NOSE_BOTTOM_LEFT" + 'type': 'NOSE_BOTTOM_LEFT' }, { - "position": { - "y": 754.20105, - "x": 472.47375, - "z": -24.760784 + 'position': { + 'y': 754.20105, + 'x': 472.47375, + 'z': -24.760784 }, - "type": "NOSE_BOTTOM_CENTER" + 'type': 'NOSE_BOTTOM_CENTER' }, { - "position": { - "y": 672.1994, - "x": 403.39957, - "z": -6.9005938 + 'position': { + 'y': 672.1994, + 'x': 403.39957, + 'z': -6.9005938 }, - "type": "LEFT_EYE_TOP_BOUNDARY" + 'type': 'LEFT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 679.914, - "x": 425.36029, - "z": 4.3264537 + 'position': { + 'y': 679.914, + 'x': 425.36029, + 'z': 4.3264537 }, - "type": "LEFT_EYE_RIGHT_CORNER" + 'type': 'LEFT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 687.11792, - "x": 401.66464, - "z": -0.79697126 + 'position': { + 'y': 687.11792, + 'x': 401.66464, + 'z': -0.79697126 }, - "type": "LEFT_EYE_BOTTOM_BOUNDARY" + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 682.9585, - "x": 378.93005, - "z": 7.3909378 + 'position': { + 'y': 682.9585, + 'x': 378.93005, + 'z': 7.3909378 }, - "type": "LEFT_EYE_LEFT_CORNER" + 'type': 'LEFT_EYE_LEFT_CORNER' }, { - "position": { - "y": 680.40326, - "x": 401.7229, - "z": -2.7444897 + 'position': { + 'y': 680.40326, + 'x': 401.7229, + 'z': -2.7444897 }, - "type": "LEFT_EYE_PUPIL" + 'type': 'LEFT_EYE_PUPIL' }, { - "position": { - "y": 663.39496, - "x": 516.03217, - "z": 10.454485 + 'position': { + 'y': 663.39496, + 'x': 516.03217, + 'z': 10.454485 }, - "type": "RIGHT_EYE_TOP_BOUNDARY" + 'type': 'RIGHT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 670.74463, - "x": 536.45978, - "z": 31.652559 + 'position': { + 'y': 670.74463, + 'x': 536.45978, + 'z': 31.652559 }, - "type": "RIGHT_EYE_RIGHT_CORNER" + 'type': 'RIGHT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 679.21289, - "x": 517.50879, - "z": 16.653259 + 'position': { + 'y': 679.21289, + 'x': 517.50879, + 'z': 16.653259 }, - "type": "RIGHT_EYE_BOTTOM_BOUNDARY" + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 676.06976, - "x": 495.27335, - "z": 14.956539 + 'position': { + 'y': 676.06976, + 'x': 495.27335, + 'z': 14.956539 }, - "type": "RIGHT_EYE_LEFT_CORNER" + 'type': 'RIGHT_EYE_LEFT_CORNER' }, { - "position": { - "y": 671.41052, - "x": 517.3429, - "z": 15.007857 + 'position': { + 'y': 671.41052, + 'x': 517.3429, + 'z': 15.007857 }, - "type": "RIGHT_EYE_PUPIL" + 'type': 'RIGHT_EYE_PUPIL' }, { - "position": { - "y": 639.23633, - "x": 396.8494, - "z": -12.132922 + 'position': { + 'y': 639.23633, + 'x': 396.8494, + 'z': -12.132922 }, - "type": "LEFT_EYEBROW_UPPER_MIDPOINT" + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 629.66724, - "x": 518.96332, - "z": 6.7055798 + 'position': { + 'y': 629.66724, + 'x': 518.96332, + 'z': 6.7055798 }, - "type": "RIGHT_EYEBROW_UPPER_MIDPOINT" + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 750.20837, - "x": 313.60855, - "z": 127.8474 + 'position': { + 'y': 750.20837, + 'x': 313.60855, + 'z': 127.8474 }, - "type": "LEFT_EAR_TRAGION" + 'type': 'LEFT_EAR_TRAGION' }, { - "position": { - "y": 728.68243, - "x": 570.95, - "z": 166.43564 + 'position': { + 'y': 728.68243, + 'x': 570.95, + 'z': 166.43564 }, - "type": "RIGHT_EAR_TRAGION" + 'type': 'RIGHT_EAR_TRAGION' }, { - "position": { - "y": 646.05042, - "x": 460.94397, - "z": -16.196959 + 'position': { + 'y': 646.05042, + 'x': 460.94397, + 'z': -16.196959 }, - "type": "FOREHEAD_GLABELLA" + 'type': 'FOREHEAD_GLABELLA' }, { - "position": { - "y": 869.36255, - "x": 476.69009, - "z": -4.4716644 + 'position': { + 'y': 869.36255, + 'x': 476.69009, + 'z': -4.4716644 }, - "type": "CHIN_GNATHION" + 'type': 'CHIN_GNATHION' }, { - "position": { - "y": 818.48083, - "x": 340.65454, - "z": 80.163544 + 'position': { + 'y': 818.48083, + 'x': 340.65454, + 'z': 80.163544 }, - "type": "CHIN_LEFT_GONION" + 'type': 'CHIN_LEFT_GONION' }, { - "position": { - "y": 800.17029, - "x": 571.60297, - "z": 115.88489 + 'position': { + 'y': 800.17029, + 'x': 571.60297, + 'z': 115.88489 }, - "type": "CHIN_RIGHT_GONION" + 'type': 'CHIN_RIGHT_GONION' }], - "sorrowLikelihood": "VERY_UNLIKELY", - "surpriseLikelihood": "VERY_UNLIKELY", - "tiltAngle": 2.1818738, - "angerLikelihood": "VERY_UNLIKELY", - "boundingPoly": { - "vertices": [{ - "y": 481, - "x": 257 + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': 2.1818738, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 481, + 'x': 257 }, { - "y": 481, - "x": 636 + 'y': 481, + 'x': 636 }, { - "y": 922, - "x": 636 + 'y': 922, + 'x': 636 }, { - "y": 922, - "x": 257 + 'y': 922, + 'x': 257 }] }, - "rollAngle": -4.8415074, - "blurredLikelihood": "VERY_UNLIKELY", - "fdBoundingPoly": { - "vertices": [{ - "y": 597, - "x": 315 + 'rollAngle': -4.8415074, + 'blurredLikelihood': 'VERY_UNLIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 597, + 'x': 315 }, { - "y": 597, - "x": 593 + 'y': 597, + 'x': 593 }, { - "y": 874, - "x": 593 + 'y': 874, + 'x': 593 }, { - "y": 874, - "x": 315 + '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 + '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" + 'type': 'LEFT_EYE' }, { - "position": { - "y": 539.7489, - "x": 128.87411, - "z": 28.692257 + 'position': { + 'y': 539.7489, + 'x': 128.87411, + 'z': 28.692257 }, - "type": "RIGHT_EYE" + 'type': 'RIGHT_EYE' }, { - "position": { - "y": 523.62103, - "x": -35.406662, - "z": -0.67885911 + 'position': { + 'y': 523.62103, + 'x': -35.406662, + 'z': -0.67885911 }, - "type": "LEFT_OF_LEFT_EYEBROW" + 'type': 'LEFT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 519.99487, - "x": 42.973644, - "z": -18.105515 + 'position': { + 'y': 519.99487, + 'x': 42.973644, + 'z': -18.105515 }, - "type": "RIGHT_OF_LEFT_EYEBROW" + 'type': 'RIGHT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 514.23407, - "x": 103.02193, - "z": -4.1667485 + 'position': { + 'y': 514.23407, + 'x': 103.02193, + 'z': -4.1667485 }, - "type": "LEFT_OF_RIGHT_EYEBROW" + 'type': 'LEFT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 505.69614, - "x": 165.63609, - "z": 47.583176 + 'position': { + 'y': 505.69614, + 'x': 165.63609, + 'z': 47.583176 }, - "type": "RIGHT_OF_RIGHT_EYEBROW" + 'type': 'RIGHT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 540.9787, - "x": 76.066139, - "z": -11.183347 + 'position': { + 'y': 540.9787, + 'x': 76.066139, + 'z': -11.183347 }, - "type": "MIDPOINT_BETWEEN_EYES" + 'type': 'MIDPOINT_BETWEEN_EYES' }, { - "position": { - "y": 615.48669, - "x": 89.695564, - "z": -41.252846 + 'position': { + 'y': 615.48669, + 'x': 89.695564, + 'z': -41.252846 }, - "type": "NOSE_TIP" + 'type': 'NOSE_TIP' }, { - "position": { - "y": 658.39246, - "x": 85.935593, - "z": -9.70177 + 'position': { + 'y': 658.39246, + 'x': 85.935593, + 'z': -9.70177 }, - "type": "UPPER_LIP" + 'type': 'UPPER_LIP' }, { - "position": { - "y": 703.04309, - "x": 87.266853, - "z": 2.6370313 + 'position': { + 'y': 703.04309, + 'x': 87.266853, + 'z': 2.6370313 }, - "type": "LOWER_LIP" + 'type': 'LOWER_LIP' }, { - "position": { - "y": 678.54712, - "x": 31.584759, - "z": 12.874522 + 'position': { + 'y': 678.54712, + 'x': 31.584759, + 'z': 12.874522 }, - "type": "MOUTH_LEFT" + 'type': 'MOUTH_LEFT' }, { - "position": { - "y": 670.44092, - "x": 126.54009, - "z": 35.510525 + 'position': { + 'y': 670.44092, + 'x': 126.54009, + 'z': 35.510525 }, - "type": "MOUTH_RIGHT" + 'type': 'MOUTH_RIGHT' }, { - "position": { - "y": 677.92883, - "x": 85.152267, - "z": 0.89151889 + 'position': { + 'y': 677.92883, + 'x': 85.152267, + 'z': 0.89151889 }, - "type": "MOUTH_CENTER" + 'type': 'MOUTH_CENTER' }, { - "position": { - "y": 618.41052, - "x": 112.767, - "z": 14.021111 + 'position': { + 'y': 618.41052, + 'x': 112.767, + 'z': 14.021111 }, - "type": "NOSE_BOTTOM_RIGHT" + 'type': 'NOSE_BOTTOM_RIGHT' }, { - "position": { - "y": 624.28644, - "x": 45.776546, - "z": -2.0218573 + 'position': { + 'y': 624.28644, + 'x': 45.776546, + 'z': -2.0218573 }, - "type": "NOSE_BOTTOM_LEFT" + 'type': 'NOSE_BOTTOM_LEFT' }, { - "position": { - "y": 632.9657, - "x": 84.253586, - "z": -12.025499 + 'position': { + 'y': 632.9657, + 'x': 84.253586, + 'z': -12.025499 }, - "type": "NOSE_BOTTOM_CENTER" + 'type': 'NOSE_BOTTOM_CENTER' }, { - "position": { - "y": 541.79987, - "x": 11.081995, - "z": -8.7047234 + 'position': { + 'y': 541.79987, + 'x': 11.081995, + 'z': -8.7047234 }, - "type": "LEFT_EYE_TOP_BOUNDARY" + 'type': 'LEFT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 549.57306, - "x": 35.396069, - "z": 6.4817863 + 'position': { + 'y': 549.57306, + 'x': 35.396069, + 'z': 6.4817863 }, - "type": "LEFT_EYE_RIGHT_CORNER" + 'type': 'LEFT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 557.55121, - "x": 10.446005, - "z": -0.37798333 + 'position': { + 'y': 557.55121, + 'x': 10.446005, + 'z': -0.37798333 }, - "type": "LEFT_EYE_BOTTOM_BOUNDARY" + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 551.75134, - "x": -16.862394, - "z": 5.4017038 + 'position': { + 'y': 551.75134, + 'x': -16.862394, + 'z': 5.4017038 }, - "type": "LEFT_EYE_LEFT_CORNER" + 'type': 'LEFT_EYE_LEFT_CORNER' }, { - "position": { - "y": 550.14355, - "x": 8.5758247, - "z": -3.3803346 + 'position': { + 'y': 550.14355, + 'x': 8.5758247, + 'z': -3.3803346 }, - "type": "LEFT_EYE_PUPIL" + 'type': 'LEFT_EYE_PUPIL' }, { - "position": { - "y": 531.02594, - "x": 131.48265, - "z": 20.201307 + 'position': { + 'y': 531.02594, + 'x': 131.48265, + 'z': 20.201307 }, - "type": "RIGHT_EYE_TOP_BOUNDARY" + 'type': 'RIGHT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 536.71674, - "x": 151.31306, - "z": 45.753532 + 'position': { + 'y': 536.71674, + 'x': 151.31306, + 'z': 45.753532 }, - "type": "RIGHT_EYE_RIGHT_CORNER" + 'type': 'RIGHT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 547.00037, - "x": 130.27722, - "z": 28.447813 + 'position': { + 'y': 547.00037, + 'x': 130.27722, + 'z': 28.447813 }, - "type": "RIGHT_EYE_BOTTOM_BOUNDARY" + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 542.38531, - "x": 106.59242, - "z": 23.77187 + 'position': { + 'y': 542.38531, + 'x': 106.59242, + 'z': 23.77187 }, - "type": "RIGHT_EYE_LEFT_CORNER" + 'type': 'RIGHT_EYE_LEFT_CORNER' }, { - "position": { - "y": 539.12781, - "x": 132.16141, - "z": 26.180428 + 'position': { + 'y': 539.12781, + 'x': 132.16141, + 'z': 26.180428 }, - "type": "RIGHT_EYE_PUPIL" + 'type': 'RIGHT_EYE_PUPIL' }, { - "position": { - "y": 506.64093, - "x": 4.8589344, - "z": -18.679537 + 'position': { + 'y': 506.64093, + 'x': 4.8589344, + 'z': -18.679537 }, - "type": "LEFT_EYEBROW_UPPER_MIDPOINT" + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 494.94244, - "x": 135.53185, - "z": 12.703153 + 'position': { + 'y': 494.94244, + 'x': 135.53185, + 'z': 12.703153 }, - "type": "RIGHT_EYEBROW_UPPER_MIDPOINT" + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 609.03503, - "x": -98.89212, - "z": 134.96341 + 'position': { + 'y': 609.03503, + 'x': -98.89212, + 'z': 134.96341 }, - "type": "LEFT_EAR_TRAGION" + 'type': 'LEFT_EAR_TRAGION' }, { - "position": { - "y": 584.60681, - "x": 174.55208, - "z": 200.56409 + 'position': { + 'y': 584.60681, + 'x': 174.55208, + 'z': 200.56409 }, - "type": "RIGHT_EAR_TRAGION" + 'type': 'RIGHT_EAR_TRAGION' }, { - "position": { - "y": 514.88513, - "x": 74.575394, - "z": -15.91002 + 'position': { + 'y': 514.88513, + 'x': 74.575394, + 'z': -15.91002 }, - "type": "FOREHEAD_GLABELLA" + 'type': 'FOREHEAD_GLABELLA' }, { - "position": { - "y": 755.372, - "x": 86.603539, - "z": 23.596317 + 'position': { + 'y': 755.372, + 'x': 86.603539, + 'z': 23.596317 }, - "type": "CHIN_GNATHION" + 'type': 'CHIN_GNATHION' }, { - "position": { - "y": 689.8385, - "x": -67.949554, - "z": 94.833694 + 'position': { + 'y': 689.8385, + 'x': -67.949554, + 'z': 94.833694 }, - "type": "CHIN_LEFT_GONION" + 'type': 'CHIN_LEFT_GONION' }, { - "position": { - "y": 667.89325, - "x": 179.19363, - "z": 154.18192 + 'position': { + 'y': 667.89325, + 'x': 179.19363, + 'z': 154.18192 }, - "type": "CHIN_RIGHT_GONION" + 'type': 'CHIN_RIGHT_GONION' }], - "sorrowLikelihood": "VERY_UNLIKELY", - "surpriseLikelihood": "VERY_UNLIKELY", - "tiltAngle": -4.1819687, - "angerLikelihood": "VERY_UNLIKELY", - "boundingPoly": { - "vertices": [{ - "y": 322 + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': -4.1819687, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 322 }, { - "y": 322, - "x": 252 + 'y': 322, + 'x': 252 }, { - "y": 800, - "x": 252 + 'y': 800, + 'x': 252 }, { - "y": 800 + 'y': 800 }] }, - "rollAngle": -4.1248608, - "blurredLikelihood": "LIKELY", - "fdBoundingPoly": { - "vertices": [{ - "y": 450 + 'rollAngle': -4.1248608, + 'blurredLikelihood': 'LIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 450 }, { - "y": 450, - "x": 235 + 'y': 450, + 'x': 235 }, { - "y": 745, - "x": 235 + 'y': 745, + 'x': 235 }, { - "y": 745 + '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 + '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" + 'type': 'LEFT_EYE' }, { - "position": { - "y": 637.43292, - "x": 767.7132, - "z": 6.4413033 + 'position': { + 'y': 637.43292, + 'x': 767.7132, + 'z': 6.4413033 }, - "type": "RIGHT_EYE" + 'type': 'RIGHT_EYE' }, { - "position": { - "y": 614.27075, - "x": 642.07782, - "z": 3.731837 + 'position': { + 'y': 614.27075, + 'x': 642.07782, + 'z': 3.731837 }, - "type": "LEFT_OF_LEFT_EYEBROW" + 'type': 'LEFT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 617.27216, - "x": 700.90112, - "z": -19.774208 + 'position': { + 'y': 617.27216, + 'x': 700.90112, + 'z': -19.774208 }, - "type": "RIGHT_OF_LEFT_EYEBROW" + 'type': 'RIGHT_OF_LEFT_EYEBROW' }, { - "position": { - "y": 617.15649, - "x": 747.60974, - "z": -16.511871 + 'position': { + 'y': 617.15649, + 'x': 747.60974, + 'z': -16.511871 }, - "type": "LEFT_OF_RIGHT_EYEBROW" + 'type': 'LEFT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 614.018, - "x": 802.60638, - "z": 14.954031 + 'position': { + 'y': 614.018, + 'x': 802.60638, + 'z': 14.954031 }, - "type": "RIGHT_OF_RIGHT_EYEBROW" + 'type': 'RIGHT_OF_RIGHT_EYEBROW' }, { - "position": { - "y": 638.11755, - "x": 724.42511, - "z": -16.930967 + 'position': { + 'y': 638.11755, + 'x': 724.42511, + 'z': -16.930967 }, - "type": "MIDPOINT_BETWEEN_EYES" + 'type': 'MIDPOINT_BETWEEN_EYES' }, { - "position": { - "y": 696.08392, - "x": 725.82532, - "z": -38.252609 + 'position': { + 'y': 696.08392, + 'x': 725.82532, + 'z': -38.252609 }, - "type": "NOSE_TIP" + 'type': 'NOSE_TIP' }, { - "position": { - "y": 727.826, - "x": 724.0116, - "z": -11.615328 + 'position': { + 'y': 727.826, + 'x': 724.0116, + 'z': -11.615328 }, - "type": "UPPER_LIP" + 'type': 'UPPER_LIP' }, { - "position": { - "y": 760.22595, - "x": 723.30157, - "z": -0.454926 + 'position': { + 'y': 760.22595, + 'x': 723.30157, + 'z': -0.454926 }, - "type": "LOWER_LIP" + 'type': 'LOWER_LIP' }, { - "position": { - "y": 738.67548, - "x": 684.35724, - "z": 13.192401 + 'position': { + 'y': 738.67548, + 'x': 684.35724, + 'z': 13.192401 }, - "type": "MOUTH_LEFT" + 'type': 'MOUTH_LEFT' }, { - "position": { - "y": 738.53015, - "x": 759.91022, - "z": 18.485643 + 'position': { + 'y': 738.53015, + 'x': 759.91022, + 'z': 18.485643 }, - "type": "MOUTH_RIGHT" + 'type': 'MOUTH_RIGHT' }, { - "position": { - "y": 742.42737, - "x": 723.45239, - "z": -2.4991846 + 'position': { + 'y': 742.42737, + 'x': 723.45239, + 'z': -2.4991846 }, - "type": "MOUTH_CENTER" + 'type': 'MOUTH_CENTER' }, { - "position": { - "y": 698.4281, - "x": 749.50385, - "z": 1.1831931 + 'position': { + 'y': 698.4281, + 'x': 749.50385, + 'z': 1.1831931 }, - "type": "NOSE_BOTTOM_RIGHT" + 'type': 'NOSE_BOTTOM_RIGHT' }, { - "position": { - "y": 698.48151, - "x": 696.923, - "z": -2.4809308 + 'position': { + 'y': 698.48151, + 'x': 696.923, + 'z': -2.4809308 }, - "type": "NOSE_BOTTOM_LEFT" + 'type': 'NOSE_BOTTOM_LEFT' }, { - "position": { - "y": 708.10651, - "x": 724.18506, - "z": -14.418536 + 'position': { + 'y': 708.10651, + 'x': 724.18506, + 'z': -14.418536 }, - "type": "NOSE_BOTTOM_CENTER" + 'type': 'NOSE_BOTTOM_CENTER' }, { - "position": { - "y": 632.12128, - "x": 675.22388, - "z": -7.2390652 + 'position': { + 'y': 632.12128, + 'x': 675.22388, + 'z': -7.2390652 }, - "type": "LEFT_EYE_TOP_BOUNDARY" + 'type': 'LEFT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 638.59021, - "x": 694.03516, - "z": 1.7715795 + 'position': { + 'y': 638.59021, + 'x': 694.03516, + 'z': 1.7715795 }, - "type": "LEFT_EYE_RIGHT_CORNER" + 'type': 'LEFT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 644.33356, - "x": 674.92206, - "z": -0.037067439 + 'position': { + 'y': 644.33356, + 'x': 674.92206, + 'z': -0.037067439 }, - "type": "LEFT_EYE_BOTTOM_BOUNDARY" + 'type': 'LEFT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 637.16479, - "x": 655.035, - "z": 7.4372306 + 'position': { + 'y': 637.16479, + 'x': 655.035, + 'z': 7.4372306 }, - "type": "LEFT_EYE_LEFT_CORNER" + 'type': 'LEFT_EYE_LEFT_CORNER' }, { - "position": { - "y": 638.18683, - "x": 673.39447, - "z": -2.4558623 + 'position': { + 'y': 638.18683, + 'x': 673.39447, + 'z': -2.4558623 }, - "type": "LEFT_EYE_PUPIL" + 'type': 'LEFT_EYE_PUPIL' }, { - "position": { - "y": 631.96063, - "x": 771.31744, - "z": -0.51439536 + 'position': { + 'y': 631.96063, + 'x': 771.31744, + 'z': -0.51439536 }, - "type": "RIGHT_EYE_TOP_BOUNDARY" + 'type': 'RIGHT_EYE_TOP_BOUNDARY' }, { - "position": { - "y": 636.94287, - "x": 789.29443, - "z": 16.814001 + 'position': { + 'y': 636.94287, + 'x': 789.29443, + 'z': 16.814001 }, - "type": "RIGHT_EYE_RIGHT_CORNER" + 'type': 'RIGHT_EYE_RIGHT_CORNER' }, { - "position": { - "y": 644.21619, - "x": 770.13458, - "z": 6.6525826 + 'position': { + 'y': 644.21619, + 'x': 770.13458, + 'z': 6.6525826 }, - "type": "RIGHT_EYE_BOTTOM_BOUNDARY" + 'type': 'RIGHT_EYE_BOTTOM_BOUNDARY' }, { - "position": { - "y": 638.75732, - "x": 752.51831, - "z": 5.8927159 + 'position': { + 'y': 638.75732, + 'x': 752.51831, + 'z': 5.8927159 }, - "type": "RIGHT_EYE_LEFT_CORNER" + 'type': 'RIGHT_EYE_LEFT_CORNER' }, { - "position": { - "y": 638.06738, - "x": 772.04718, - "z": 4.350193 + 'position': { + 'y': 638.06738, + 'x': 772.04718, + 'z': 4.350193 }, - "type": "RIGHT_EYE_PUPIL" + 'type': 'RIGHT_EYE_PUPIL' }, { - "position": { - "y": 604.87769, - "x": 671.68707, - "z": -15.778968 + 'position': { + 'y': 604.87769, + 'x': 671.68707, + 'z': -15.778968 }, - "type": "LEFT_EYEBROW_UPPER_MIDPOINT" + 'type': 'LEFT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 604.71191, - "x": 775.98663, - "z": -8.4828024 + 'position': { + 'y': 604.71191, + 'x': 775.98663, + 'z': -8.4828024 }, - "type": "RIGHT_EYEBROW_UPPER_MIDPOINT" + 'type': 'RIGHT_EYEBROW_UPPER_MIDPOINT' }, { - "position": { - "y": 670.40063, - "x": 605.07721, - "z": 119.27386 + 'position': { + 'y': 670.40063, + 'x': 605.07721, + 'z': 119.27386 }, - "type": "LEFT_EAR_TRAGION" + 'type': 'LEFT_EAR_TRAGION' }, { - "position": { - "y": 669.99823, - "x": 823.42841, - "z": 134.54482 + 'position': { + 'y': 669.99823, + 'x': 823.42841, + 'z': 134.54482 }, - "type": "RIGHT_EAR_TRAGION" + 'type': 'RIGHT_EAR_TRAGION' }, { - "position": { - "y": 616.47058, - "x": 724.54547, - "z": -21.861612 + 'position': { + 'y': 616.47058, + 'x': 724.54547, + 'z': -21.861612 }, - "type": "FOREHEAD_GLABELLA" + 'type': 'FOREHEAD_GLABELLA' }, { - "position": { - "y": 801.31934, - "x": 722.071, - "z": 18.37034 + 'position': { + 'y': 801.31934, + 'x': 722.071, + 'z': 18.37034 }, - "type": "CHIN_GNATHION" + 'type': 'CHIN_GNATHION' }, { - "position": { - "y": 736.57159, - "x": 617.91388, - "z": 88.713562 + 'position': { + 'y': 736.57159, + 'x': 617.91388, + 'z': 88.713562 }, - "type": "CHIN_LEFT_GONION" + 'type': 'CHIN_LEFT_GONION' }, { - "position": { - "y": 736.21118, - "x": 815.234, - "z": 102.52047 + 'position': { + 'y': 736.21118, + 'x': 815.234, + 'z': 102.52047 }, - "type": "CHIN_RIGHT_GONION" + 'type': 'CHIN_RIGHT_GONION' }], - "sorrowLikelihood": "VERY_UNLIKELY", - "surpriseLikelihood": "VERY_UNLIKELY", - "tiltAngle": -7.0173812, - "angerLikelihood": "VERY_UNLIKELY", - "boundingPoly": { - "vertices": [{ - "y": 459, - "x": 557 + 'sorrowLikelihood': 'VERY_UNLIKELY', + 'surpriseLikelihood': 'VERY_UNLIKELY', + 'tiltAngle': -7.0173812, + 'angerLikelihood': 'VERY_UNLIKELY', + 'boundingPoly': { + 'vertices': [{ + 'y': 459, + 'x': 557 }, { - "y": 459, - "x": 875 + 'y': 459, + 'x': 875 }, { - "y": 829, - "x": 875 + 'y': 829, + 'x': 875 }, { - "y": 829, - "x": 557 + 'y': 829, + 'x': 557 }] }, - "rollAngle": 0.38634345, - "blurredLikelihood": "LIKELY", - "fdBoundingPoly": { - "vertices": [{ - "y": 570, - "x": 612 + 'rollAngle': 0.38634345, + 'blurredLikelihood': 'LIKELY', + 'fdBoundingPoly': { + 'vertices': [{ + 'y': 570, + 'x': 612 }, { - "y": 570, - "x": 837 + 'y': 570, + 'x': 837 }, { - "y": 795, - "x": 837 + 'y': 795, + 'x': 837 }, { - "y": 795, - "x": 612 + 'y': 795, + 'x': 612 }] } }] diff --git a/google/cloud/vision/client.py b/google/cloud/vision/client.py index a955b48da9e3..2334bd24c447 100644 --- a/google/cloud/vision/client.py +++ b/google/cloud/vision/client.py @@ -24,7 +24,7 @@ class VisionRequest(object): """Request container with image and features information to annotate. - :type features: list of :class:`gcoud.vision.feature.Feature`. + :type features: list of :class:`~gcoud.vision.feature.Feature`. :param features: The features that dictate which annotations to run. :type image: bytes @@ -89,7 +89,7 @@ def annotate(self, image, features): :param image: A string which can be a URL, a Google Cloud Storage path, or a byte stream of the image. - :type features: list of :class:`google.cloud.vision.feature.Feature` + :type features: list of :class:`~google.cloud.vision.feature.Feature` :param features: The type of detection that the Vision API should use to determine image attributes. Pricing is based on the number of Feature Types. @@ -98,8 +98,7 @@ def annotate(self, image, features): :rtype: dict :returns: List of annotations. """ - img = Image(image, self) - request = VisionRequest(img, features) + request = VisionRequest(image, features) data = {'requests': [request.as_dict()]} response = self.connection.api_request(method='POST', @@ -107,3 +106,17 @@ def annotate(self, image, features): data=data) return response['responses'][0] + + def image(self, content=None, source_uri=None): + """Get instance of Image using current client. + + :type content: bytes + :param content: Byte stream of an image. + + :type source_uri: str + :param source_uri: Google Cloud Storage URI of image. + + :rtype: :class:`~google.cloud.vision.image.Image` + :returns: Image instance with the current client attached. + """ + return Image(client=self, content=content, source_uri=source_uri) diff --git a/google/cloud/vision/face.py b/google/cloud/vision/face.py new file mode 100644 index 000000000000..a071af0a04d4 --- /dev/null +++ b/google/cloud/vision/face.py @@ -0,0 +1,403 @@ +# 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. + +"""Face class representing the Vision API's face detection response.""" + + +from google.cloud.vision.geometry import BoundsBase +from google.cloud.vision.likelihood import Likelihood +from google.cloud.vision.geometry import Position + + +class Angles(object): + """Angles representing the positions of a face.""" + def __init__(self, roll, pan, tilt): + self._roll = roll + self._pan = pan + self._tilt = tilt + + @classmethod + def from_api_repr(cls, response): + """Factory: construct the angles from an Vision API response. + + :rtype: :class:`~google.cloud.vision.face.Angles` + :returns: An `Angles` instance with data parsed from `response`. + """ + roll = response['rollAngle'] + pan = response['panAngle'] + tilt = response['tiltAngle'] + + return cls(roll, pan, tilt) + + @property + def roll(self): + """Roll angle of face. + + :rtype: float + :returns: Roll angle of face in degrees. + """ + return self._roll + + @property + def pan(self): + """Pan angle of face. + + :rtype: float + :returns: Pan angle of face in degrees. + """ + return self._pan + + @property + def tilt(self): + """Tilt angle of face. + + :rtype: float + :returns: Tilt angle of face in degrees. + """ + return self._tilt + + +class Bounds(BoundsBase): + """The bounding polygon of the entire face.""" + + +class Emotions(object): + """Emotions displayed by the face detected in an image.""" + def __init__(self, joy_likelihood, sorrow_likelihood, + surprise_likelihood, anger_likelihood): + self._joy_likelihood = joy_likelihood + self._sorrow_likelihood = sorrow_likelihood + self._surprise_likelihood = surprise_likelihood + self._anger_likelihood = anger_likelihood + + @classmethod + def from_api_repr(cls, response): + """Factory: construct `Emotions` from Vision API response. + + :type response: dict + :param response: Response dictionary representing a face. + + :rtype: :class:`~google.cloud.vision.face.Emotions` + :returns: Populated instance of `Emotions`. + """ + joy_likelihood = getattr(Likelihood, response['joyLikelihood']) + sorrow_likelihood = getattr(Likelihood, response['sorrowLikelihood']) + surprise_likelihood = getattr(Likelihood, + response['surpriseLikelihood']) + anger_likelihood = getattr(Likelihood, response['angerLikelihood']) + + return cls(joy_likelihood, sorrow_likelihood, surprise_likelihood, + anger_likelihood) + + @property + def joy_likelihood(self): + """Likelihood of joy in detected face. + + :rtype: str + :returns: String derived from + :class:`~google.cloud.vision.face.Likelihood`. + """ + return self._joy_likelihood + + @property + def sorrow_likelihood(self): + """Likelihood of sorrow in detected face. + + :rtype: str + :returns: String derived from + :class:`~google.cloud.vision.face.Likelihood`. + """ + return self._sorrow_likelihood + + @property + def surprise_likelihood(self): + """Likelihood of surprise in detected face. + + :rtype: str + :returns: String derived from + :class:`~google.cloud.vision.face.Likelihood`. + """ + return self._surprise_likelihood + + @property + def anger_likelihood(self): + """Likelihood of anger in detected face. + + :rtype: str + :returns: String derived from + :class:`~google.cloud.vision.face.Likelihood`. + """ + return self._anger_likelihood + + +class Face(object): + """Representation of a face found by the Vision API""" + + def __init__(self, angles, bounds, detection_confidence, emotions, + fd_bounds, headwear_likelihood, image_properties, landmarks, + landmarking_confidence): + self._angles = angles + self._bounds = bounds + self._detection_confidence = detection_confidence + self._emotions = emotions + self._fd_bounds = fd_bounds + self._headwear_likelihood = headwear_likelihood + self._landmarks = landmarks + self._landmarking_confidence = landmarking_confidence + self._image_properties = image_properties + + @classmethod + def from_api_repr(cls, response): + """Factory: construct an instance of a Face from an API response + + :type response: dict + :param response: Face annotation dict returned from the Vision API. + + :rtype: :class:`~google.cloud.vision.face.Face` + :returns: A instance of `Face` with data parsed from `response`. + """ + angles = Angles.from_api_repr(response) + bounds = Bounds.from_api_repr(response['boundingPoly']) + detection_confidence = response['detectionConfidence'] + emotions = Emotions.from_api_repr(response) + fd_bounds = FDBounds.from_api_repr(response['fdBoundingPoly']) + headwear_likelihood = getattr(Likelihood, + response['headwearLikelihood']) + image_properties = FaceImageProperties.from_api_repr(response) + landmarks = Landmarks(response['landmarks']) + landmarking_confidence = response['landmarkingConfidence'] + + return cls(angles, bounds, detection_confidence, emotions, fd_bounds, + headwear_likelihood, image_properties, landmarks, + landmarking_confidence) + + @property + def angles(self): + """Accessor to the pan, tilt and roll angles of a Face. + + :rtype: :class:`~google.cloud.vision.face.Angles` + :returns: Pan, tilt and roll angles of the detected face. + """ + + return self._angles + + @property + def bounds(self): + """Accessor to the bounding poly information of the detected face. + + :rtype: :class:`~google.cloud.vision.face.Bounds` + :returns: An instance of ``Bounds`` which has a list of vertices. + """ + return self._bounds + + @property + def detection_confidence(self): + """Face detection confidence score determined by the Vision API. + + :rtype: float + :returns: Float representation of confidence ranging from 0 to 1. + """ + return self._detection_confidence + + @property + def emotions(self): + """Accessor to the possible emotions expressed in the detected face. + + :rtype: :class:`~google.cloud.vision.face.Emotions` + :returns: An instance of ``Emotions`` with joy, sorrow, anger, surprise + likelihood. + """ + return self._emotions + + @property + def fd_bounds(self): + """Accessor to the skin area bounding poly of the detected face. + + :rtype: :class:`~google.cloud.vision.image.FDBounds` + :returns: An instance of ``FDBounds`` which has a list of vertices. + """ + return self._fd_bounds + + @property + def headwear_likelihood(self): + """Headwear likelihood. + + :rtype: :class:`~google.cloud.vision.face.Likelihood` + :returns: String representing the likelihood based on + :class:`~google.cloud.vision.face.Likelihood` + """ + return self._headwear_likelihood + + @property + def image_properties(self): + """Image properties from imaged used in face detection. + + :rtype: :class:`~google.cloud.vision.face.FaceImageProperties` + :returns: ``FaceImageProperties`` object with image properties. + """ + return self._image_properties + + @property + def landmarks(self): + """Accessor to the facial landmarks detected in a face. + + :rtype: :class:`~google.cloud.vision.face.Landmarks` + :returns: ``Landmarks`` object with facial landmarks as properies. + """ + return self._landmarks + + @property + def landmarking_confidence(self): + """Landmarking confidence score determinged by the Vision API. + + :rtype: float + :returns: Float representing the confidence of the Vision API in + determining the landmarks on a face. + """ + return self._landmarking_confidence + + +class FaceImageProperties(object): + """A representation of the image properties from face detection.""" + def __init__(self, blurred_likelihood, underexposed_likelihood): + self._blurred_likelihood = blurred_likelihood + self._underexposed_likelihood = underexposed_likelihood + + @classmethod + def from_api_repr(cls, response): + """Factory: construct image properties from image. + + :rtype: :class:`~google.cloud.vision.face.FaceImageProperties` + :returns: Instance populated with image property data. + """ + blurred_likelihood = getattr(Likelihood, + response['blurredLikelihood']) + underexposed_likelihood = getattr(Likelihood, + response['underExposedLikelihood']) + + return cls(blurred_likelihood, underexposed_likelihood) + + @property + def blurred_likelihood(self): + """Likelihood of the image being blurred. + + :rtype: str + :returns: String representation derived from + :class:`~google.cloud.vision.face.Position`. + """ + return self._blurred_likelihood + + @property + def underexposed_likelihood(self): + """Likelihood that the image used for detection was underexposed. + + :rtype: str + :returns: String representation derived from + :class:`~google.cloud.vision.face.Position`. + """ + return self._underexposed_likelihood + + +class FaceLandmarkTypes(object): + """A representation of the face detection landmark types. + + See: + https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1 + """ + UNKNOWN_LANDMARK = 'UNKNOWN_LANDMARK' + LEFT_EYE = 'LEFT_EYE' + RIGHT_EYE = 'RIGHT_EYE' + LEFT_OF_LEFT_EYEBROW = 'LEFT_OF_LEFT_EYEBROW' + RIGHT_OF_LEFT_EYEBROW = 'RIGHT_OF_LEFT_EYEBROW' + LEFT_OF_RIGHT_EYEBROW = 'LEFT_OF_RIGHT_EYEBROW' + RIGHT_OF_RIGHT_EYEBROW = 'RIGHT_OF_RIGHT_EYEBROW' + MIDPOINT_BETWEEN_EYES = 'MIDPOINT_BETWEEN_EYES' + NOSE_TIP = 'NOSE_TIP' + UPPER_LIP = 'UPPER_LIP' + LOWER_LIP = 'LOWER_LIP' + MOUTH_LEFT = 'MOUTH_LEFT' + MOUTH_RIGHT = 'MOUTH_RIGHT' + MOUTH_CENTER = 'MOUTH_CENTER' + NOSE_BOTTOM_RIGHT = 'NOSE_BOTTOM_RIGHT' + NOSE_BOTTOM_LEFT = 'NOSE_BOTTOM_LEFT' + NOSE_BOTTOM_CENTER = 'NOSE_BOTTOM_CENTER' + LEFT_EYE_TOP_BOUNDARY = 'LEFT_EYE_TOP_BOUNDARY' + LEFT_EYE_RIGHT_CORNER = 'LEFT_EYE_RIGHT_CORNER' + LEFT_EYE_BOTTOM_BOUNDARY = 'LEFT_EYE_BOTTOM_BOUNDARY' + LEFT_EYE_LEFT_CORNER = 'LEFT_EYE_LEFT_CORNER' + RIGHT_EYE_TOP_BOUNDARY = 'RIGHT_EYE_TOP_BOUNDARY' + RIGHT_EYE_RIGHT_CORNER = 'RIGHT_EYE_RIGHT_CORNER' + RIGHT_EYE_BOTTOM_BOUNDARY = 'RIGHT_EYE_BOTTOM_BOUNDARY' + RIGHT_EYE_LEFT_CORNER = 'RIGHT_EYE_LEFT_CORNER' + LEFT_EYEBROW_UPPER_MIDPOINT = 'LEFT_EYEBROW_UPPER_MIDPOINT' + RIGHT_EYEBROW_UPPER_MIDPOINT = 'RIGHT_EYEBROW_UPPER_MIDPOINT' + LEFT_EAR_TRAGION = 'LEFT_EAR_TRAGION' + RIGHT_EAR_TRAGION = 'RIGHT_EAR_TRAGION' + LEFT_EYE_PUPIL = 'LEFT_EYE_PUPIL' + RIGHT_EYE_PUPIL = 'RIGHT_EYE_PUPIL' + FOREHEAD_GLABELLA = 'FOREHEAD_GLABELLA' + CHIN_GNATHION = 'CHIN_GNATHION' + CHIN_LEFT_GONION = 'CHIN_LEFT_GONION' + CHIN_RIGHT_GONION = 'CHIN_RIGHT_GONION' + + +class FDBounds(BoundsBase): + """The bounding polygon of just the skin portion of the face.""" + + +class Landmark(object): + """A face-specific landmark (for example, a face feature, left eye).""" + def __init__(self, position, landmark_type): + self._position = position + self._landmark_type = landmark_type + + @classmethod + def from_api_repr(cls, response_landmark): + """Factory: construct an instance of a Landmark from a response. + + :type response_landmark: dict + :param response_landmark: Landmark representation from Vision API. + + :rtype: :class:`~google.cloud.vision.face.Landmark` + :returns: Populated instance of `Landmark`. + """ + position = Position.from_api_repr(response_landmark['position']) + landmark_type = getattr(FaceLandmarkTypes, response_landmark['type']) + return cls(position, landmark_type) + + @property + def position(self): + """Landmark position on face. + + :rtype: :class:`~google.cloud.vision.face.Position` + :returns: Instance of `Position` with landmark coordinates. + """ + return self._position + + @property + def landmark_type(self): + """Landmark type of facial feature. + + :rtype: str + :returns: String representation of facial landmark type. + """ + return self._landmark_type + + +class Landmarks(object): + """Landmarks detected on a face represented as properties.""" + def __init__(self, landmarks): + for landmark_response in landmarks: + landmark = Landmark.from_api_repr(landmark_response) + setattr(self, landmark.landmark_type.lower(), landmark) diff --git a/google/cloud/vision/feature.py b/google/cloud/vision/feature.py index 78f7abf0434a..3c5ac6d1b24d 100644 --- a/google/cloud/vision/feature.py +++ b/google/cloud/vision/feature.py @@ -34,7 +34,7 @@ class Feature(object): :type feature_type: str :param feature_type: String representation of - :class:`google.cloud.vision.feature.FeatureType`. + :class:`~google.cloud.vision.feature.FeatureType`. :type max_results: int :param max_results: Number of results to return for the specified diff --git a/google/cloud/vision/geometry.py b/google/cloud/vision/geometry.py new file mode 100644 index 000000000000..eeb1795a015f --- /dev/null +++ b/google/cloud/vision/geometry.py @@ -0,0 +1,126 @@ +# 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. + +"""Geometry and other generic classes used by the Vision API.""" + + +class BoundsBase(object): + """Base class for handling bounds with vertices.""" + def __init__(self, vertices): + self._vertices = vertices + + @classmethod + def from_api_repr(cls, response_vertices): + """Factory: construct BoundsBase instance from Vision API response. + + :type response_vertices: dict + :param response_vertices: List of vertices. + + :rtype: :class:`~google.cloud.vision.geometry.BoundsBase` + :returns: Instance of BoundsBase with populated verticies. + """ + vertices = [] + for vertex in response_vertices['vertices']: + vertices.append(Vertex(vertex.get('x', None), + vertex.get('y', None))) + return cls(vertices) + + @property + def vertices(self): + """List of vertices. + + :rtype: list + :returns: List of populated vertices. + """ + return self._vertices + + +class Position(object): + """A 3D position in the image. + + See: + https://cloud.google.com/vision/reference/rest/v1/images/annotate#Position + """ + def __init__(self, x_coordinate, y_coordinate, z_coordinate): + self._x_coordinate = x_coordinate + self._y_coordinate = y_coordinate + self._z_coordinate = z_coordinate + + @classmethod + def from_api_repr(cls, response_position): + """Factory: construct 3D position from API response. + + :rtype: :class:`~google.cloud.vision.geometry.Position` + :returns: `Position` constructed with 3D points from API response. + """ + x_coordinate = response_position['x'] + y_coordinate = response_position['y'] + z_coordinate = response_position['z'] + return cls(x_coordinate, y_coordinate, z_coordinate) + + @property + def x_coordinate(self): + """X position coordinate. + + :rtype: float + :returns: X position coordinate. + """ + return self._x_coordinate + + @property + def y_coordinate(self): + """Y position coordinate. + + :rtype: float + :returns: Y position coordinate. + """ + return self._y_coordinate + + @property + def z_coordinate(self): + """Z position coordinate. + + :rtype: float + :returns: Z position coordinate. + """ + return self._z_coordinate + + +class Vertex(object): + """A vertex represents a 2D point in the image. + + See: + https://cloud.google.com/vision/reference/rest/v1/images/annotate#Vertex + """ + def __init__(self, x_coordinate, y_coordinate): + self._x_coordinate = x_coordinate + self._y_coordinate = y_coordinate + + @property + def x_coordinate(self): + """X position coordinate. + + :rtype: float + :returns: X position coordinate. + """ + return self._x_coordinate + + @property + def y_coordinate(self): + """Y position coordinate. + + :rtype: float + :returns: Y position coordinate. + """ + return self._y_coordinate diff --git a/google/cloud/vision/image.py b/google/cloud/vision/image.py index 505441e04b7e..3becd5b90257 100644 --- a/google/cloud/vision/image.py +++ b/google/cloud/vision/image.py @@ -18,32 +18,40 @@ from base64 import b64encode from google.cloud._helpers import _to_bytes -from google.cloud._helpers import _bytes_to_unicode +from google.cloud.vision.face import Face +from google.cloud.vision.feature import Feature +from google.cloud.vision.feature import FeatureTypes class Image(object): """Image representation containing information to be annotate. - :type image_source: str - :param image_source: A string which can a Google Cloud Storage URI, or - a byte stream of the image. + :type content: bytes + :param content: Byte stream of an image. - :type client: :class:`Client` + :type source_uri: str + :param source_uri: Google Cloud Storage URI of image. + + :type client: :class:`~google.cloud.vision.client.Client` :param client: Instance of Vision client. """ - def __init__(self, image_source, client): + def __init__(self, client, content=None, source_uri=None): self.client = client self._content = None self._source = None - if _bytes_to_unicode(image_source).startswith('gs://'): - self._source = image_source + if source_uri: + self._source = source_uri else: - self._content = b64encode(_to_bytes(image_source)) + self._content = b64encode(_to_bytes(content)) def as_dict(self): - """Generate dictionary structure for request""" + """Generate dictionary structure for request. + + :rtype: dict + :returns: Dictionary with source information for image. + """ if self.content: return { 'content': self.content @@ -57,10 +65,36 @@ def as_dict(self): @property def content(self): - """Base64 encoded image content""" + """Base64 encoded image content. + + :rtype: str + :returns: Base64 encoded image bytes. + """ return self._content @property def source(self): - """Google Cloud Storage URI""" + """Google Cloud Storage URI. + + :rtype: str + :returns: String of Google Cloud Storage URI. + """ return self._source + + def detect_faces(self, limit=10): + """Detect faces in image. + + :type limit: int + :param limit: The number of faces to try and detect. + + :rtype: list + :returns: List of :class:`~google.cloud.vision.face.Face`. + """ + faces = [] + face_detection_feature = Feature(FeatureTypes.FACE_DETECTION, limit) + result = self.client.annotate(self, [face_detection_feature]) + for face_response in result['faceAnnotations']: + face = Face.from_api_repr(face_response) + faces.append(face) + + return faces diff --git a/google/cloud/vision/likelihood.py b/google/cloud/vision/likelihood.py new file mode 100644 index 000000000000..63c63b955d58 --- /dev/null +++ b/google/cloud/vision/likelihood.py @@ -0,0 +1,29 @@ +# 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. + +"""Likelihood constants returned from Vision API.""" + + +class Likelihood(object): + """A representation of likelihood to give stable results across upgrades. + + See: + https://cloud.google.com/vision/reference/rest/v1/images/annotate#likelihood + """ + UNKNOWN = 'UNKNOWN' + VERY_UNLIKELY = 'VERY_UNLIKELY' + UNLIKELY = 'UNLIKELY' + POSSIBLE = 'POSSIBLE' + LIKELY = 'LIKELY' + VERY_LIKELY = 'VERY_LIKELY' diff --git a/unit_tests/vision/test_client.py b/unit_tests/vision/test_client.py index d7c92b1f63c3..83d7c24595fc 100644 --- a/unit_tests/vision/test_client.py +++ b/unit_tests/vision/test_client.py @@ -13,17 +13,18 @@ # limitations under the License. -import base64 import unittest from google.cloud._helpers import _to_bytes +_IMAGE_CONTENT = _to_bytes('/9j/4QNURXhpZgAASUkq') +_IMAGE_SOURCE = 'gs://some/image.jpg' + class TestClient(unittest.TestCase): + import base64 PROJECT = 'PROJECT' - IMAGE_SOURCE = 'gs://some/image.jpg' - IMAGE_CONTENT = _to_bytes('/9j/4QNURXhpZgAASUkq') - B64_IMAGE_CONTENT = base64.b64encode(IMAGE_CONTENT) + B64_IMAGE_CONTENT = base64.b64encode(_IMAGE_CONTENT) def _getTargetClass(self): from google.cloud.vision.client import Client @@ -36,9 +37,9 @@ def test_ctor(self): creds = _Credentials() client = self._makeOne(project=self.PROJECT, credentials=creds) self.assertEqual(client.project, self.PROJECT) - self.assertTrue('annotate' in dir(client)) def test_face_annotation(self): + from google.cloud.vision.feature import Feature, FeatureTypes from google.cloud.vision._fixtures import FACE_DETECTION_RESPONSE RETURNED = FACE_DETECTION_RESPONSE @@ -61,22 +62,60 @@ def test_face_annotation(self): client = self._makeOne(project=self.PROJECT, credentials=credentials) client.connection = _Connection(RETURNED) - from google.cloud.vision.feature import Feature, FeatureTypes - features = [Feature(feature_type=FeatureTypes.FACE_DETECTION, max_results=3)] - - response = client.annotate(self.IMAGE_CONTENT, features) + image = client.image(content=_IMAGE_CONTENT) + response = client.annotate(image, features) self.assertEqual(REQUEST, client.connection._requested[0]['data']) - self.assertTrue('faceAnnotations' in response) + def test_image_with_client(self): + from google.cloud.vision.image import Image -class TestVisionRequest(unittest.TestCase): - _IMAGE_CONTENT = _to_bytes('/9j/4QNURXhpZgAASUkq') + credentials = _Credentials() + client = self._makeOne(project=self.PROJECT, + credentials=credentials) + image = client.image(source_uri=_IMAGE_SOURCE) + self.assertTrue(isinstance(image, Image)) + def test_face_detection_from_source(self): + from google.cloud.vision.face import Face + from google.cloud.vision._fixtures import FACE_DETECTION_RESPONSE + RETURNED = FACE_DETECTION_RESPONSE + credentials = _Credentials() + client = self._makeOne(project=self.PROJECT, credentials=credentials) + client.connection = _Connection(RETURNED) + + image = client.image(source_uri=_IMAGE_SOURCE) + faces = image.detect_faces(limit=3) + self.assertEqual(5, len(faces)) + self.assertTrue(isinstance(faces[0], Face)) + 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_face_detection_from_content(self): + from google.cloud.vision.face import Face + from google.cloud.vision._fixtures import FACE_DETECTION_RESPONSE + RETURNED = FACE_DETECTION_RESPONSE + credentials = _Credentials() + client = self._makeOne(project=self.PROJECT, credentials=credentials) + client.connection = _Connection(RETURNED) + + image = client.image(content=_IMAGE_CONTENT) + faces = image.detect_faces(limit=5) + self.assertEqual(5, len(faces)) + self.assertTrue(isinstance(faces[0], Face)) + 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): from google.cloud.vision.client import VisionRequest return VisionRequest @@ -86,22 +125,17 @@ def _makeOne(self, *args, **kw): def test_make_vision_request(self): from google.cloud.vision.feature import Feature, FeatureTypes + feature = Feature(feature_type=FeatureTypes.FACE_DETECTION, max_results=3) - vision_request = self._makeOne(self._IMAGE_CONTENT, feature) - - self.assertEqual(self._IMAGE_CONTENT, vision_request.image) - self.assertEqual(FeatureTypes.FACE_DETECTION, - vision_request.features[0].feature_type) - - vision_request = self._makeOne(self._IMAGE_CONTENT, [feature]) - - self.assertEqual(self._IMAGE_CONTENT, vision_request.image) + vision_request = self._makeOne(_IMAGE_CONTENT, feature) + self.assertEqual(_IMAGE_CONTENT, vision_request.image) self.assertEqual(FeatureTypes.FACE_DETECTION, vision_request.features[0].feature_type) + def test_make_vision_request_with_bad_feature(self): with self.assertRaises(TypeError): - self._makeOne(self._IMAGE_CONTENT, 'nonsensefeature') + self._makeOne(_IMAGE_CONTENT, 'nonsensefeature') class _Credentials(object): diff --git a/unit_tests/vision/test_face.py b/unit_tests/vision/test_face.py new file mode 100644 index 000000000000..8cdf8d393741 --- /dev/null +++ b/unit_tests/vision/test_face.py @@ -0,0 +1,76 @@ +# 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 TestFace(unittest.TestCase): + def _getTargetClass(self): + from google.cloud.vision.face import Face + return Face + + def setUp(self): + from google.cloud.vision._fixtures import FACE_DETECTION_RESPONSE + self.FACE_ANNOTATIONS = FACE_DETECTION_RESPONSE['responses'][0] + self.face_class = self._getTargetClass() + self.face = self.face_class.from_api_repr( + self.FACE_ANNOTATIONS['faceAnnotations'][0]) + + def test_face_landmarks(self): + self.assertEqual(0.54453093, self.face.landmarking_confidence) + self.assertEqual(0.9863683, self.face.detection_confidence) + self.assertTrue(hasattr(self.face.landmarks, 'left_eye')) + self.assertEqual(1004.8003, + self.face.landmarks.left_eye.position.x_coordinate) + self.assertEqual(482.69385, + self.face.landmarks.left_eye.position.y_coordinate) + self.assertEqual(0.0016593217, + self.face.landmarks.left_eye.position.z_coordinate) + self.assertEqual('LEFT_EYE', + self.face.landmarks.left_eye.landmark_type) + + def test_facial_emotions(self): + from google.cloud.vision.face import Likelihood + self.assertEqual(Likelihood.VERY_LIKELY, + self.face.emotions.joy_likelihood) + self.assertEqual(Likelihood.VERY_UNLIKELY, + self.face.emotions.sorrow_likelihood) + self.assertEqual(Likelihood.VERY_UNLIKELY, + self.face.emotions.surprise_likelihood) + self.assertEqual(Likelihood.VERY_UNLIKELY, + self.face.emotions.anger_likelihood) + + def test_faciale_angles(self): + self.assertEqual(-0.43419784, self.face.angles.roll) + self.assertEqual(6.027647, self.face.angles.pan) + self.assertEqual(-18.412321, self.face.angles.tilt) + + def test_face_headware_and_blur_and_underexposed(self): + from google.cloud.vision.face import Likelihood + self.assertEqual(Likelihood.VERY_UNLIKELY, + self.face.image_properties.blurred_likelihood) + self.assertEqual(Likelihood.VERY_UNLIKELY, + self.face.headwear_likelihood) + self.assertEqual(Likelihood.VERY_UNLIKELY, + self.face.image_properties.underexposed_likelihood) + + def test_face_bounds(self): + self.assertEqual(4, len(self.face.bounds.vertices)) + self.assertEqual(748, self.face.bounds.vertices[0].x_coordinate) + self.assertEqual(58, self.face.bounds.vertices[0].y_coordinate) + + def test_facial_skin_bounds(self): + self.assertEqual(4, len(self.face.fd_bounds.vertices)) + self.assertEqual(845, self.face.fd_bounds.vertices[0].x_coordinate) + self.assertEqual(310, self.face.fd_bounds.vertices[0].y_coordinate) diff --git a/unit_tests/vision/test_image.py b/unit_tests/vision/test_image.py index 7f7f3a77490c..aa359b31a5dd 100644 --- a/unit_tests/vision/test_image.py +++ b/unit_tests/vision/test_image.py @@ -32,7 +32,7 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_image_source_type_content(self): - image = self._makeOne(self._IMAGE_CONTENT, self._CLIENT_MOCK) + image = self._makeOne(self._CLIENT_MOCK, content=self._IMAGE_CONTENT) _AS_DICT = { 'content': self._B64_IMAGE_CONTENT @@ -43,7 +43,7 @@ def test_image_source_type_content(self): self.assertEqual(_AS_DICT, image.as_dict()) def test_image_source_type_google_cloud_storage(self): - image = self._makeOne(self._IMAGE_SOURCE, self._CLIENT_MOCK) + image = self._makeOne(self._CLIENT_MOCK, source_uri=self._IMAGE_SOURCE) _AS_DICT = { 'source': { @@ -56,13 +56,13 @@ def test_image_source_type_google_cloud_storage(self): self.assertEqual(_AS_DICT, image.as_dict()) def test_cannot_set_both_source_and_content(self): - image = self._makeOne(self._IMAGE_CONTENT, self._CLIENT_MOCK) + image = self._makeOne(self._CLIENT_MOCK, content=self._IMAGE_CONTENT) self.assertEqual(self._B64_IMAGE_CONTENT, image.content) with self.assertRaises(AttributeError): image.source = self._IMAGE_SOURCE - image = self._makeOne(self._IMAGE_SOURCE, self._CLIENT_MOCK) + image = self._makeOne(self._CLIENT_MOCK, source_uri=self._IMAGE_SOURCE) self.assertEqual(self._IMAGE_SOURCE, image.source) with self.assertRaises(AttributeError): image.content = self._IMAGE_CONTENT