Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add loading image from filename. #2778

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 57 additions & 11 deletions docs/vision-usage.rst
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand Down
27 changes: 20 additions & 7 deletions vision/google/cloud/vision/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,35 @@ 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.

:type client: :class:`~google.cloud.vision.client.Client`
: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):

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

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()

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.


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.
Expand Down
1 change: 1 addition & 0 deletions vision/tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ localdeps =
pip install --quiet --upgrade {toxinidir}/../core
deps =
{toxinidir}/../core
mock

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

pytest
covercmd =
py.test --quiet \
Expand Down
42 changes: 36 additions & 6 deletions vision/unit_tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)