Skip to content

Commit f4cb0ee

Browse files
Vision 1.1 API Client (#3069)
1 parent a3596d6 commit f4cb0ee

File tree

27 files changed

+1740
-32
lines changed

27 files changed

+1740
-32
lines changed

docs/index.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,14 @@
158158
vision-batch
159159
vision-client
160160
vision-color
161+
vision-crop-hint
161162
vision-entity
162163
vision-feature
163164
vision-face
164165
vision-image
165166
vision-safe-search
167+
vision-text
168+
vision-web
166169

167170
.. toctree::
168171
:maxdepth: 0

docs/vision-crop-hint.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Vision Crop Hint
2+
================
3+
4+
Crop Hint
5+
~~~~~~~~~
6+
7+
.. automodule:: google.cloud.vision.crop_hint
8+
:members:
9+
:undoc-members:
10+
:show-inheritance:

docs/vision-text.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Vision Full Text
2+
================
3+
4+
Full Text Annotation
5+
~~~~~~~~~~~~~~~~~~~~
6+
7+
.. automodule:: google.cloud.vision.text
8+
:members:
9+
:undoc-members:
10+
:show-inheritance:

docs/vision-usage.rst

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,30 @@ You can call the detection method manually.
9898
'github'
9999
100100
101+
**********
102+
Crop Hints
103+
**********
104+
105+
:meth:`~google.cloud.vision.image.Image.detect_crop_hints` will attempt to find
106+
boundaries that contain interesting data which can be used to crop an image.
107+
108+
.. code-block:: python
109+
110+
>>> from google.cloud import vision
111+
>>> client = vision.Client()
112+
>>> image = client.image(source_uri='gs://my-test-bucket/image.jpg')
113+
>>> crop_hints = image.detect_crop_hints(aspect_ratios=[1.3333], limit=2)
114+
>>> first_hint = crop_hints[0]
115+
>>> first_hint.bounds.vertices[0].x_coordinate
116+
77
117+
>>> first_hint.bounds.vertices[0].y_coordinate
118+
102
119+
>>> first_hint.confidence
120+
0.5
121+
>>> first_hint.importance_fraction
122+
1.22000002861
123+
124+
101125
**************
102126
Face Detection
103127
**************
@@ -317,6 +341,43 @@ Multiple images can be processed with a single request by passing
317341
<Likelihood.VERY_LIKELY: 'POSSIBLE'>
318342
319343
344+
*************
345+
Web Detection
346+
*************
347+
348+
:meth:`~google.cloud.vision.image.Image.detect_web` search for images on the
349+
web that are similar to the image you have.
350+
351+
.. code-block:: python
352+
353+
>>> from google.cloud import vision
354+
>>> client = vision.Client()
355+
>>> with open('./image.jpg', 'rb') as image_file:
356+
... image = client.image(content=image_file.read())
357+
>>> web_images = image.detect_web(limit=2)
358+
>>> for full_matching_image in web_images.full_matching_images:
359+
... print('=' * 20)
360+
... print(full_matching_image.url)
361+
====================
362+
'https://example.com/image.jpg'
363+
>>> for partial_matching_image in web_images.partial_matching_images:
364+
... print('=' * 20)
365+
... print(partial_matching_image.url)
366+
====================
367+
>>> for page_with_matching_images in web_images.pages_with_matching_images:
368+
... print('=' * 20)
369+
... print(page_with_matching_images.url)
370+
====================
371+
'https://example.com/portfolio/'
372+
>>> for entity in web_images.web_entities:
373+
... print('=' * 20)
374+
... print(entity.description)
375+
====================
376+
'Mount Rushmore National Memorial'
377+
====================
378+
'Landmark'
379+
380+
320381
****************
321382
No results found
322383
****************

docs/vision-web.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Vision Web Annotations
2+
======================
3+
4+
Web Annotations
5+
~~~~~~~~~~~~~~~
6+
7+
.. automodule:: google.cloud.vision.web
8+
:members:
9+
:undoc-members:
10+
:show-inheritance:

logging/google/cloud/logging/logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def log_text(self, text, client=None, labels=None, insert_id=None,
171171
See:
172172
https://cloud.google.com/logging/docs/api/reference/rest/v2/entries/write
173173
174-
:type text: text
174+
:type text: str
175175
:param text: the log message.
176176
177177
:type client: :class:`~google.cloud.logging.client.Client` or

storage/google/cloud/storage/blob.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ def upload_from_string(self, data, content_type='text/plain', client=None):
659659
`lifecycle <https://cloud.google.com/storage/docs/lifecycle>`_
660660
API documents for details.
661661
662-
:type data: bytes or text
662+
:type data: bytes or str
663663
:param data: The data to store in this blob. If the value is
664664
text, it will be encoded as UTF-8.
665665

system_tests/data/full-text.jpg

1.11 MB
Loading

system_tests/vision.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
LABEL_FILE = os.path.join(_SYS_TESTS_DIR, 'data', 'car.jpg')
3636
LANDMARK_FILE = os.path.join(_SYS_TESTS_DIR, 'data', 'landmark.jpg')
3737
TEXT_FILE = os.path.join(_SYS_TESTS_DIR, 'data', 'text.jpg')
38+
FULL_TEXT_FILE = os.path.join(_SYS_TESTS_DIR, 'data', 'full-text.jpg')
3839

3940

4041
class Config(object):
@@ -80,6 +81,107 @@ def _pb_not_implemented_skip(self, message):
8081
self.skipTest(message)
8182

8283

84+
class TestVisionFullText(unittest.TestCase):
85+
def setUp(self):
86+
self.to_delete_by_case = []
87+
88+
def tearDown(self):
89+
for value in self.to_delete_by_case:
90+
value.delete()
91+
92+
def _assert_full_text(self, full_text):
93+
from google.cloud.vision.text import TextAnnotation
94+
95+
self.assertIsInstance(full_text, TextAnnotation)
96+
self.assertIsInstance(full_text.text, six.text_type)
97+
self.assertEqual(len(full_text.pages), 1)
98+
self.assertIsInstance(full_text.pages[0].width, int)
99+
self.assertIsInstance(full_text.pages[0].height, int)
100+
101+
def test_detect_full_text_content(self):
102+
client = Config.CLIENT
103+
with open(FULL_TEXT_FILE, 'rb') as image_file:
104+
image = client.image(content=image_file.read())
105+
full_text = image.detect_full_text()
106+
self._assert_full_text(full_text)
107+
108+
def test_detect_full_text_filename(self):
109+
client = Config.CLIENT
110+
image = client.image(filename=FULL_TEXT_FILE)
111+
full_text = image.detect_full_text()
112+
self._assert_full_text(full_text)
113+
114+
def test_detect_full_text_gcs(self):
115+
bucket_name = Config.TEST_BUCKET.name
116+
blob_name = 'full-text.jpg'
117+
blob = Config.TEST_BUCKET.blob(blob_name)
118+
self.to_delete_by_case.append(blob) # Clean-up.
119+
with open(FULL_TEXT_FILE, 'rb') as file_obj:
120+
blob.upload_from_file(file_obj)
121+
122+
source_uri = 'gs://%s/%s' % (bucket_name, blob_name)
123+
124+
client = Config.CLIENT
125+
image = client.image(source_uri=source_uri)
126+
full_text = image.detect_full_text()
127+
self._assert_full_text(full_text)
128+
129+
130+
class TestVisionClientCropHint(BaseVisionTestCase):
131+
def setUp(self):
132+
self.to_delete_by_case = []
133+
134+
def tearDown(self):
135+
for value in self.to_delete_by_case:
136+
value.delete()
137+
138+
def _assert_crop_hint(self, hint):
139+
from google.cloud.vision.crop_hint import CropHint
140+
from google.cloud.vision.geometry import Bounds
141+
142+
self.assertIsInstance(hint, CropHint)
143+
self.assertIsInstance(hint.bounds, Bounds)
144+
self.assertGreater(hint.bounds.vertices, 1)
145+
self.assertIsInstance(hint.confidence, (int, float))
146+
self.assertIsInstance(hint.importance_fraction, float)
147+
148+
def test_detect_crop_hints_content(self):
149+
client = Config.CLIENT
150+
with open(FACE_FILE, 'rb') as image_file:
151+
image = client.image(content=image_file.read())
152+
crop_hints = image.detect_crop_hints(
153+
aspect_ratios=[1.3333, 1.7777], limit=2)
154+
self.assertEqual(len(crop_hints), 2)
155+
for hint in crop_hints:
156+
self._assert_crop_hint(hint)
157+
158+
def test_detect_crop_hints_filename(self):
159+
client = Config.CLIENT
160+
image = client.image(filename=FACE_FILE)
161+
crop_hints = image.detect_crop_hints(
162+
aspect_ratios=[1.3333, 1.7777], limit=2)
163+
self.assertEqual(len(crop_hints), 2)
164+
for hint in crop_hints:
165+
self._assert_crop_hint(hint)
166+
167+
def test_detect_crop_hints_gcs(self):
168+
bucket_name = Config.TEST_BUCKET.name
169+
blob_name = 'faces.jpg'
170+
blob = Config.TEST_BUCKET.blob(blob_name)
171+
self.to_delete_by_case.append(blob) # Clean-up.
172+
with open(FACE_FILE, 'rb') as file_obj:
173+
blob.upload_from_file(file_obj)
174+
175+
source_uri = 'gs://%s/%s' % (bucket_name, blob_name)
176+
client = Config.CLIENT
177+
image = client.image(source_uri=source_uri)
178+
crop_hints = image.detect_crop_hints(
179+
aspect_ratios=[1.3333, 1.7777], limit=2)
180+
self.assertEqual(len(crop_hints), 2)
181+
for hint in crop_hints:
182+
self._assert_crop_hint(hint)
183+
184+
83185
class TestVisionClientLogo(unittest.TestCase):
84186
def setUp(self):
85187
self.to_delete_by_case = []
@@ -559,3 +661,82 @@ def test_batch_detect_gcs(self):
559661

560662
self.assertEqual(len(results[1].logos), 0)
561663
self.assertEqual(len(results[1].faces), 2)
664+
665+
666+
class TestVisionWebAnnotation(BaseVisionTestCase):
667+
def setUp(self):
668+
self.to_delete_by_case = []
669+
670+
def tearDown(self):
671+
for value in self.to_delete_by_case:
672+
value.delete()
673+
674+
def _assert_web_entity(self, web_entity):
675+
from google.cloud.vision.web import WebEntity
676+
677+
self.assertIsInstance(web_entity, WebEntity)
678+
self.assertIsInstance(web_entity.entity_id, six.text_type)
679+
self.assertIsInstance(web_entity.score, float)
680+
self.assertIsInstance(web_entity.description, six.text_type)
681+
682+
def _assert_web_image(self, web_image):
683+
from google.cloud.vision.web import WebImage
684+
685+
self.assertIsInstance(web_image, WebImage)
686+
self.assertIsInstance(web_image.url, six.text_type)
687+
self.assertIsInstance(web_image.score, float)
688+
689+
def _assert_web_page(self, web_page):
690+
from google.cloud.vision.web import WebPage
691+
692+
self.assertIsInstance(web_page, WebPage)
693+
self.assertIsInstance(web_page.url, six.text_type)
694+
self.assertIsInstance(web_page.score, float)
695+
696+
def _assert_web_images(self, web_images, limit):
697+
self.assertEqual(len(web_images.web_entities), limit)
698+
for web_entity in web_images.web_entities:
699+
self._assert_web_entity(web_entity)
700+
701+
self.assertEqual(len(web_images.full_matching_images), limit)
702+
for web_image in web_images.full_matching_images:
703+
self._assert_web_image(web_image)
704+
705+
self.assertEqual(len(web_images.partial_matching_images), limit)
706+
for web_image in web_images.partial_matching_images:
707+
self._assert_web_image(web_image)
708+
709+
self.assertEqual(len(web_images.pages_with_matching_images), limit)
710+
for web_page in web_images.pages_with_matching_images:
711+
self._assert_web_page(web_page)
712+
713+
def test_detect_web_images_from_content(self):
714+
client = Config.CLIENT
715+
with open(LANDMARK_FILE, 'rb') as image_file:
716+
image = client.image(content=image_file.read())
717+
limit = 5
718+
web_images = image.detect_web(limit=limit)
719+
self._assert_web_images(web_images, limit)
720+
721+
def test_detect_web_images_from_gcs(self):
722+
client = Config.CLIENT
723+
bucket_name = Config.TEST_BUCKET.name
724+
blob_name = 'landmark.jpg'
725+
blob = Config.TEST_BUCKET.blob(blob_name)
726+
self.to_delete_by_case.append(blob) # Clean-up.
727+
with open(LANDMARK_FILE, 'rb') as file_obj:
728+
blob.upload_from_file(file_obj)
729+
730+
source_uri = 'gs://%s/%s' % (bucket_name, blob_name)
731+
732+
image = client.image(source_uri=source_uri)
733+
limit = 5
734+
web_images = image.detect_web(limit=limit)
735+
self._assert_web_images(web_images, limit)
736+
737+
def test_detect_web_images_from_filename(self):
738+
client = Config.CLIENT
739+
image = client.image(filename=LANDMARK_FILE)
740+
limit = 5
741+
web_images = image.detect_web(limit=limit)
742+
self._assert_web_images(web_images, limit)

0 commit comments

Comments
 (0)