diff --git a/docs/vision-usage.rst b/docs/vision-usage.rst index 904b8539f3e1..e0a891a20c1f 100644 --- a/docs/vision-usage.rst +++ b/docs/vision-usage.rst @@ -1,8 +1,11 @@ +#################### Using the Vision API -==================== +#################### + +******************************** Authentication and Configuration --------------------------------- +******************************** - For an overview of authentication in ``google-cloud-python``, see :doc:`google-cloud-auth`. @@ -31,8 +34,43 @@ or pass in ``credentials`` and ``project`` explicitly. >>> client = vision.Client(project='my-project', credentials=creds) +***************************************************** +Creating an :class:`~google.cloud.vision.image.Image` +***************************************************** + +The :class:`~google.cloud.vision.image.Image` class is used to load image +data from sources such as a Google Cloud Storage URI, raw bytes, or a file. + + +From a Google Cloud Storage URI +=============================== + +.. code-block:: python + + >>> from google.cloud import vision + >>> client = vision.Client() + >>> image = client.image(source_uri='gs://my-test-bucket/image.jpg') + + +From a filename +=============== + +.. code-block:: python + + >>> image = client.image(filename='image.jpg') + +From raw bytes +============== + +.. code-block:: python + + >>> with open('./image.jpg', 'rb') as image_file: + ... bytes_image = client.image(content=image_file.read()) + + +**************** Manual Detection -~~~~~~~~~~~~~~~~ +**************** You can call the detection method manually. @@ -60,8 +98,9 @@ You can call the detection method manually. 'github' +************** Face Detection -~~~~~~~~~~~~~~ +************** :meth:`~google.cloud.vision.image.Image.detect_faces` will search for faces in an image and return the coordinates in the image of each `landmark type`_ that @@ -87,8 +126,9 @@ was detected. 0.02545464 +*************** Label Detection -~~~~~~~~~~~~~~~ +*************** :meth:`~google.cloud.vision.image.Image.detect_labels` will attempt to label objects in an image. If there is a car, person and a dog in the image, label @@ -107,8 +147,9 @@ certainty from ``0.0 to 1.0``. 0.9863683 +****************** Landmark Detection -~~~~~~~~~~~~~~~~~~ +****************** :meth:`~google.cloud.vision.image.Image.detect_landmarks` will attempt to detect landmarks such as "Mount Rushmore" and the "Sydney Opera House". The API @@ -133,8 +174,9 @@ will also provide their known geographical locations if available. 162 +************** Logo Detection -~~~~~~~~~~~~~~ +************** With :meth:`~google.cloud.vision.image.Image.detect_logos`, you can identify brand logos in an image. Their shape and location in the image can be found by @@ -162,8 +204,9 @@ iterating through the detected logo's ``vertices``. 62 +********************* Safe Search Detection -~~~~~~~~~~~~~~~~~~~~~ +********************* :meth:`~google.cloud.vision.image.Image.detect_safe_search` will try to categorize the entire contents of the image under four categories. @@ -192,8 +235,9 @@ categorize the entire contents of the image under four categories. 'LIKELY' +************** Text Detection -~~~~~~~~~~~~~~ +************** :meth:`~google.cloud.vision.image.Image.detect_text` performs OCR to find text in an image. @@ -213,8 +257,9 @@ in an image. 'some other text in the image' +**************** Image Properties -~~~~~~~~~~~~~~~~ +**************** :meth:`~google.cloud.vision.image.Image.detect_properties` will process the image and determine the dominant colors in the image. @@ -238,8 +283,9 @@ image and determine the dominant colors in the image. 0.758658 +**************** No results found -~~~~~~~~~~~~~~~~ +**************** If no results for the detection performed can be extracted from the image, then an empty list is returned. This behavior is similiar with all detection types. diff --git a/vision/google/cloud/vision/image.py b/vision/google/cloud/vision/image.py index 9f7a2afcdaee..d094b6702537 100644 --- a/vision/google/cloud/vision/image.py +++ b/vision/google/cloud/vision/image.py @@ -30,6 +30,9 @@ class Image(object): :type content: bytes :param content: Byte stream of an image. + :type filename: str + :param filename: Filename to image. + :type source_uri: str :param source_uri: Google Cloud Storage URI of image. @@ -37,15 +40,25 @@ class Image(object): :param client: Instance of Vision client. """ - def __init__(self, client, content=None, source_uri=None): + def __init__(self, client, content=None, filename=None, source_uri=None): + sources = [source for source in (content, filename, source_uri) + if source is not None] + if len(sources) != 1: + raise ValueError( + 'Specify exactly one of "content", "filename", or ' + '"source_uri".') + self.client = client - self._content = None - self._source = None - if source_uri: - self._source = source_uri - else: - self._content = _bytes_to_unicode(b64encode(_to_bytes(content))) + if filename is not None: + with open(filename, 'rb') as file_obj: + content = file_obj.read() + + if content is not None: + content = _bytes_to_unicode(b64encode(_to_bytes(content))) + + self._content = content + self._source = source_uri def as_dict(self): """Generate dictionary structure for request. diff --git a/vision/tox.ini b/vision/tox.ini index a72e9b8216de..e0d8c2b172de 100644 --- a/vision/tox.ini +++ b/vision/tox.ini @@ -7,6 +7,7 @@ localdeps = pip install --quiet --upgrade {toxinidir}/../core deps = {toxinidir}/../core + mock pytest covercmd = py.test --quiet \ diff --git a/vision/unit_tests/test_image.py b/vision/unit_tests/test_image.py index 756e1f517e5a..d9a90e9845ec 100644 --- a/vision/unit_tests/test_image.py +++ b/vision/unit_tests/test_image.py @@ -33,29 +33,44 @@ def _get_target_class(): def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) + def test_must_set_one_source(self): + with self.assertRaises(ValueError): + self._make_one(CLIENT_MOCK) + + with self.assertRaises(ValueError): + self._make_one(CLIENT_MOCK, content=IMAGE_CONTENT, + source_uri=IMAGE_SOURCE) + + with self.assertRaises(ValueError): + self._make_one(CLIENT_MOCK, content=IMAGE_CONTENT, + source_uri=IMAGE_SOURCE, filename='myimage.jpg') + + image = self._make_one(CLIENT_MOCK, content=IMAGE_CONTENT) + self.assertEqual(image.content, B64_IMAGE_CONTENT) + def test_image_source_type_content(self): image = self._make_one(CLIENT_MOCK, content=IMAGE_CONTENT) - _AS_DICT = { - 'content': B64_IMAGE_CONTENT + as_dict = { + 'content': B64_IMAGE_CONTENT, } self.assertEqual(B64_IMAGE_CONTENT, image.content) self.assertEqual(None, image.source) - self.assertEqual(_AS_DICT, image.as_dict()) + self.assertEqual(image.as_dict(), as_dict) def test_image_source_type_google_cloud_storage(self): image = self._make_one(CLIENT_MOCK, source_uri=IMAGE_SOURCE) - _AS_DICT = { + as_dict = { 'source': { - 'gcs_image_uri': IMAGE_SOURCE + 'gcs_image_uri': IMAGE_SOURCE, } } self.assertEqual(IMAGE_SOURCE, image.source) self.assertEqual(None, image.content) - self.assertEqual(_AS_DICT, image.as_dict()) + self.assertEqual(image.as_dict(), as_dict) def test_cannot_set_both_source_and_content(self): image = self._make_one(CLIENT_MOCK, content=IMAGE_CONTENT) @@ -68,3 +83,18 @@ def test_cannot_set_both_source_and_content(self): self.assertEqual(IMAGE_SOURCE, image.source) with self.assertRaises(AttributeError): image.content = IMAGE_CONTENT + + def test_image_from_filename(self): + from mock import mock_open + from mock import patch + + as_dict = { + 'content': B64_IMAGE_CONTENT, + } + + with patch('google.cloud.vision.image.open', + mock_open(read_data=IMAGE_CONTENT)) as m: + image = self._make_one(CLIENT_MOCK, filename='my-image-file.jpg') + m.assert_called_once_with('my-image-file.jpg', 'rb') + self.assertEqual(image.content, B64_IMAGE_CONTENT) + self.assertEqual(image.as_dict(), as_dict)