google-cloud-vision 0.22.1 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,420 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/cloud/vision/annotation/vertex"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Vision
21
+ class Annotation
22
+ ##
23
+ # # Web
24
+ #
25
+ # Relevant information for the image from the Internet.
26
+ #
27
+ # See {Annotation#web}.
28
+ #
29
+ # @example
30
+ # require "google/cloud/vision"
31
+ #
32
+ # vision = Google::Cloud::Vision.new
33
+ #
34
+ # image = vision.image "path/to/landmark.jpg"
35
+ #
36
+ # web = image.web
37
+ #
38
+ # entity = web.entities.first
39
+ # entity.entity_id #=> "/m/019dvv"
40
+ # entity.score #=> 107.34591674804688
41
+ # entity.description #=> "Mount Rushmore National Memorial"
42
+ #
43
+ # full_matching_image = web.full_matching_images.first
44
+ # full_matching_image.url #=> "http://example.com/images/123.jpg"
45
+ # full_matching_image.score #=> 0.10226666927337646
46
+ #
47
+ # page_with_matching_images = web.pages_with_matching_images.first
48
+ # page_with_matching_images.url #=> "http://example.com/posts/123"
49
+ # page_with_matching_images.score #=> 8.114753723144531
50
+ #
51
+ class Web
52
+ ##
53
+ # @private The WebDetection GRPC object.
54
+ attr_accessor :grpc
55
+
56
+ ##
57
+ # @private Creates a new Web instance.
58
+ def initialize
59
+ @grpc = nil
60
+ end
61
+
62
+ ##
63
+ # Deduced entities from similar images on the Internet.
64
+ #
65
+ # @return [Array<Google::Cloud::Vision::Annotation::Web::Entity>]
66
+ #
67
+ def entities
68
+ @entities ||= Array(@grpc.web_entities).map do |e|
69
+ Entity.from_grpc e
70
+ end
71
+ end
72
+
73
+ ##
74
+ # Fully matching images from the Internet. They're definite neardups
75
+ # and most often a copy of the query image with merely a size change.
76
+ #
77
+ # @return [Array<Google::Cloud::Vision::Annotation::Web::Image>]
78
+ #
79
+ def full_matching_images
80
+ images = @grpc.full_matching_images
81
+ @full_matching_images ||= Array(images).map do |i|
82
+ Image.from_grpc i
83
+ end
84
+ end
85
+
86
+ ##
87
+ # Partial matching images from the Internet. Those images are similar
88
+ # enough to share some key-point features. For example an original
89
+ # image will likely have partial matching for its crops.
90
+ #
91
+ # @return [Array<Google::Cloud::Vision::Annotation::Web::Image>]
92
+ #
93
+ def partial_matching_images
94
+ images = @grpc.partial_matching_images
95
+ @partial_matching_images ||= Array(images).map do |i|
96
+ Image.from_grpc i
97
+ end
98
+ end
99
+
100
+ ##
101
+ # Web pages containing the matching images from the Internet.
102
+ #
103
+ # @return [Array<Google::Cloud::Vision::Annotation::Web::Page>]
104
+ #
105
+ def pages_with_matching_images
106
+ pages = @grpc.pages_with_matching_images
107
+ @pages_with_matching_images ||= Array(pages).map do |p|
108
+ Page.from_grpc p
109
+ end
110
+ end
111
+
112
+ ##
113
+ # Deeply converts object to a hash. All keys will be symbolized.
114
+ #
115
+ # @return [Hash]
116
+ #
117
+ def to_h
118
+ {
119
+ entities: entities.map(&:to_h),
120
+ full_matching_images: full_matching_images.map(&:to_h),
121
+ partial_matching_images: partial_matching_images.map(&:to_h),
122
+ pages_with_matching_images: pages_with_matching_images.map(&:to_h)
123
+ }
124
+ end
125
+
126
+ # @private
127
+ def to_s
128
+ # Keep console output low by not showing all sub-objects.
129
+ format "(entities: %i, full_matching_images: %i," \
130
+ " partial_matching_images: %i," \
131
+ " pages_with_matching_images: %i)",
132
+ entities.count, full_matching_images.count,
133
+ partial_matching_images.count,
134
+ pages_with_matching_images.count
135
+ end
136
+
137
+ # @private
138
+ def inspect
139
+ "#<#{self.class.name} #{self}>"
140
+ end
141
+
142
+ ##
143
+ # @private New Annotation::Face from a GRPC object.
144
+ def self.from_grpc grpc
145
+ new.tap { |f| f.instance_variable_set :@grpc, grpc }
146
+ end
147
+
148
+ ##
149
+ # # Entity
150
+ #
151
+ # Entity deduced from similar images on the Internet.
152
+ #
153
+ # See {Web}.
154
+ #
155
+ # @example
156
+ # require "google/cloud/vision"
157
+ #
158
+ # vision = Google::Cloud::Vision.new
159
+ #
160
+ # image = vision.image "path/to/landmark.jpg"
161
+ #
162
+ # web = image.web
163
+ #
164
+ # entity = web.entities.first
165
+ # entity.entity_id #=> "/m/019dvv"
166
+ # entity.score #=> 107.34591674804688
167
+ # entity.description #=> "Mount Rushmore National Memorial"
168
+ #
169
+ class Entity
170
+ ##
171
+ # @private The WebEntity GRPC object.
172
+ attr_accessor :grpc
173
+
174
+ ##
175
+ # @private Creates a new Entity instance.
176
+ def initialize
177
+ @grpc = nil
178
+ end
179
+
180
+ ##
181
+ # Opaque entity ID.
182
+ #
183
+ # @return [String]
184
+ #
185
+ def entity_id
186
+ @grpc.entity_id
187
+ end
188
+
189
+ ##
190
+ # Overall relevancy score for the entity. Not normalized and not
191
+ # comparable across different image queries.
192
+ #
193
+ # @return [Float]
194
+ #
195
+ def score
196
+ @grpc.score
197
+ end
198
+
199
+ ##
200
+ # Canonical description of the entity, in English.
201
+ #
202
+ # @return [String]
203
+ #
204
+ def description
205
+ @grpc.description
206
+ end
207
+
208
+ ##
209
+ # Returns the object's property values as an array.
210
+ #
211
+ # @return [Array]
212
+ #
213
+ def to_a
214
+ [entity_id, score, description]
215
+ end
216
+
217
+ ##
218
+ # Converts object to a hash. All keys will be symbolized.
219
+ #
220
+ # @return [Hash]
221
+ #
222
+ def to_h
223
+ { entity_id: entity_id, score: score, description: description }
224
+ end
225
+
226
+ # @private
227
+ def to_s
228
+ format "(entity_id: %s, score: %s, description: %s)",
229
+ entity_id.inspect, score.inspect, description.inspect
230
+ end
231
+
232
+ # @private
233
+ def inspect
234
+ "#<Web::Entity #{self}>"
235
+ end
236
+
237
+ ##
238
+ # @private New Google::Cloud::Vision::Annotation::Web::Entity from
239
+ # a GRPC object.
240
+ def self.from_grpc grpc
241
+ new.tap { |f| f.instance_variable_set :@grpc, grpc }
242
+ end
243
+ end
244
+
245
+ ##
246
+ # # Image
247
+ #
248
+ # Metadata for online images.
249
+ #
250
+ # See {Web}.
251
+ #
252
+ # @example
253
+ # require "google/cloud/vision"
254
+ #
255
+ # vision = Google::Cloud::Vision.new
256
+ #
257
+ # image = vision.image "path/to/landmark.jpg"
258
+ #
259
+ # web = image.web
260
+ #
261
+ # full_matching_image = web.full_matching_images.first
262
+ # full_matching_image.url #=> "http://example.com/images/123.jpg"
263
+ # full_matching_image.score #=> 0.10226666927337646
264
+ #
265
+ class Image
266
+ ##
267
+ # @private The WebImage GRPC object.
268
+ attr_accessor :grpc
269
+
270
+ ##
271
+ # @private Creates a new Image instance.
272
+ def initialize
273
+ @grpc = nil
274
+ end
275
+
276
+ ##
277
+ # The result image URL.
278
+ #
279
+ # @return [String]
280
+ #
281
+ def url
282
+ @grpc.url
283
+ end
284
+
285
+ ##
286
+ # Overall relevancy score for the image. Not normalized and not
287
+ # comparable across different image queries.
288
+ #
289
+ # @return [Float]
290
+ #
291
+ def score
292
+ @grpc.score
293
+ end
294
+
295
+ ##
296
+ # Returns the object's property values as an array.
297
+ #
298
+ # @return [Array]
299
+ #
300
+ def to_a
301
+ [url, score]
302
+ end
303
+
304
+ ##
305
+ # Converts object to a hash. All keys will be symbolized.
306
+ #
307
+ # @return [Hash]
308
+ #
309
+ def to_h
310
+ { url: url, score: score }
311
+ end
312
+
313
+ # @private
314
+ def to_s
315
+ format "(url: %s, score: %s)", url.inspect, score.inspect
316
+ end
317
+
318
+ # @private
319
+ def inspect
320
+ "#<Web::Image #{self}>"
321
+ end
322
+
323
+ ##
324
+ # @private New Google::Cloud::Vision::Annotation::Web::Image from
325
+ # a GRPC object.
326
+ def self.from_grpc grpc
327
+ new.tap { |f| f.instance_variable_set :@grpc, grpc }
328
+ end
329
+ end
330
+
331
+ ##
332
+ # # Page
333
+ #
334
+ # Metadata for web pages.
335
+ #
336
+ # See {Web}.
337
+ #
338
+ # @example
339
+ # require "google/cloud/vision"
340
+ #
341
+ # vision = Google::Cloud::Vision.new
342
+ #
343
+ # image = vision.image "path/to/landmark.jpg"
344
+ #
345
+ # web = image.web
346
+ #
347
+ # page_with_matching_images = web.pages_with_matching_images.first
348
+ # page_with_matching_images.url #=> "http://example.com/posts/123"
349
+ # page_with_matching_images.score #=> 8.114753723144531
350
+ #
351
+ class Page
352
+ ##
353
+ # @private The WebPage GRPC object.
354
+ attr_accessor :grpc
355
+
356
+ ##
357
+ # @private Creates a new Page instance.
358
+ def initialize
359
+ @grpc = nil
360
+ end
361
+
362
+ ##
363
+ # The result web page URL.
364
+ #
365
+ # @return [String]
366
+ #
367
+ def url
368
+ @grpc.url
369
+ end
370
+
371
+ ##
372
+ # Overall relevancy score for the web page. Not normalized and not
373
+ # comparable across different image queries.
374
+ #
375
+ # @return [Float]
376
+ #
377
+ def score
378
+ @grpc.score
379
+ end
380
+
381
+ ##
382
+ # Returns the object's property values as an array.
383
+ #
384
+ # @return [Array]
385
+ #
386
+ def to_a
387
+ [url, score]
388
+ end
389
+
390
+ ##
391
+ # Converts object to a hash. All keys will be symbolized.
392
+ #
393
+ # @return [Hash]
394
+ #
395
+ def to_h
396
+ { url: url, score: score }
397
+ end
398
+
399
+ # @private
400
+ def to_s
401
+ format "(url: %s, score: %s)", url.inspect, score.inspect
402
+ end
403
+
404
+ # @private
405
+ def inspect
406
+ "#<Web::Page #{self}>"
407
+ end
408
+
409
+ ##
410
+ # @private New Google::Cloud::Vision::Annotation::Web::Page from
411
+ # a GRPC object.
412
+ def self.from_grpc grpc
413
+ new.tap { |f| f.instance_variable_set :@grpc, grpc }
414
+ end
415
+ end
416
+ end
417
+ end
418
+ end
419
+ end
420
+ end
@@ -25,6 +25,11 @@ module Google
25
25
  #
26
26
  # Represents an image for the Vision service.
27
27
  #
28
+ # An Image instance can be created from a string file path, publicly-
29
+ # accessible image HTTP/HTTPS URL, or Cloud Storage URI of the form
30
+ # `"gs://bucketname/path/to/image_filename"`; or a File, IO, StringIO, or
31
+ # Tempfile instance; or an instance of Google::Cloud::Storage::File.
32
+ #
28
33
  # See {Project#image}.
29
34
  #
30
35
  # The Cloud Vision API supports a variety of image file formats, including
@@ -52,7 +57,7 @@ module Google
52
57
  # image.context.languages = ["en"]
53
58
  #
54
59
  # text = image.text
55
- # text.words.count #=> 28
60
+ # text.pages.count #=> 1
56
61
  #
57
62
  class Image
58
63
  # Returns the image context for the image, which accepts metadata values
@@ -107,7 +112,7 @@ module Google
107
112
  # vertex.x #=> 28
108
113
  # vertex.y #=> 40
109
114
  #
110
- def faces max_results = Google::Cloud::Vision.default_max_faces
115
+ def faces max_results = Vision.default_max_faces
111
116
  ensure_vision!
112
117
  annotation = @vision.mark self, faces: max_results
113
118
  annotation.faces
@@ -146,7 +151,7 @@ module Google
146
151
  # landmark.description #=> "Mount Rushmore"
147
152
  # landmark.mid #=> "/m/019dvv"
148
153
  #
149
- def landmarks max_results = Google::Cloud::Vision.default_max_landmarks
154
+ def landmarks max_results = Vision.default_max_landmarks
150
155
  ensure_vision!
151
156
  annotation = @vision.mark self, landmarks: max_results
152
157
  annotation.landmarks
@@ -185,7 +190,7 @@ module Google
185
190
  # logo.description #=> "Google"
186
191
  # logo.mid #=> "/m/0b34hf"
187
192
  #
188
- def logos max_results = Google::Cloud::Vision.default_max_logos
193
+ def logos max_results = Vision.default_max_logos
189
194
  ensure_vision!
190
195
  annotation = @vision.mark self, logos: max_results
191
196
  annotation.logos
@@ -225,7 +230,7 @@ module Google
225
230
  # label.description #=> "stone carving"
226
231
  # label.mid #=> "/m/02wtjj"
227
232
  #
228
- def labels max_results = Google::Cloud::Vision.default_max_labels
233
+ def labels max_results = Vision.default_max_labels
229
234
  ensure_vision!
230
235
  annotation = @vision.mark self, labels: max_results
231
236
  annotation.labels
@@ -242,7 +247,8 @@ module Google
242
247
  end
243
248
 
244
249
  ##
245
- # Performs the `TEXT_DETECTION` (OCR) feature on the image.
250
+ # Performs the `TEXT_DETECTION` feature (OCR for shorter documents with
251
+ # sparse text) on the image.
246
252
  #
247
253
  # @see https://cloud.google.com/vision/docs/pricing Cloud Vision Pricing
248
254
  #
@@ -256,17 +262,64 @@ module Google
256
262
  #
257
263
  # text = image.text
258
264
  #
259
- # text.locale #=> "en"
260
- # text.words.count #=> 28
261
265
  # text.text
262
266
  # # "Google Cloud Client for Ruby an idiomatic, intuitive... "
263
267
  #
268
+ # text.locale #=> "en"
269
+ # text.words.count #=> 28
270
+ # text.words[0].text #=> "Google"
271
+ # text.words[0].bounds.count #=> 4
272
+ # vertex = text.words[0].bounds.first
273
+ # vertex.x #=> 13
274
+ # vertex.y #=> 8
275
+ #
276
+ # # Use `pages` to access a full structural representation
277
+ # page = text.pages.first
278
+ # page.blocks[0].paragraphs[0].words[0].symbols[0].text #=> "G"
279
+ #
280
+ #
264
281
  def text
265
282
  ensure_vision!
266
283
  annotation = @vision.mark self, text: true
267
284
  annotation.text
268
285
  end
269
286
 
287
+ ##
288
+ # Performs the `DOCUMENT_TEXT_DETECTION` feature (OCR for longer
289
+ # documents with dense text) on the image.
290
+ #
291
+ # @see https://cloud.google.com/vision/docs/pricing Cloud Vision Pricing
292
+ #
293
+ # @return [Annotation::Text] The results of document text (OCR)
294
+ # detection.
295
+ #
296
+ # @example
297
+ # require "google/cloud/vision"
298
+ #
299
+ # vision = Google::Cloud::Vision.new
300
+ # image = vision.image "path/to/text.png"
301
+ #
302
+ # text = image.document
303
+ #
304
+ # text.text
305
+ # # "Google Cloud Client for Ruby an idiomatic, intuitive... "
306
+ #
307
+ # text.words[0].text #=> "Google"
308
+ # text.words[0].bounds.count #=> 4
309
+ # vertex = text.words[0].bounds.first
310
+ # vertex.x #=> 13
311
+ # vertex.y #=> 8
312
+ #
313
+ # # Use `pages` to access a full structural representation
314
+ # page = text.pages.first
315
+ # page.blocks[0].paragraphs[0].words[0].symbols[0].text #=> "G"
316
+ #
317
+ def document
318
+ ensure_vision!
319
+ annotation = @vision.mark self, document: true
320
+ annotation.text
321
+ end
322
+
270
323
  ##
271
324
  # Performs the `SAFE_SEARCH_DETECTION` feature on the image.
272
325
  #
@@ -319,6 +372,68 @@ module Google
319
372
  annotation.properties
320
373
  end
321
374
 
375
+ ##
376
+ # Performs the `CROP_HINTS` feature on the image.
377
+ #
378
+ # @see https://cloud.google.com/vision/docs/pricing Cloud Vision Pricing
379
+ #
380
+ # @return [Array<Annotation::CropHint>] The results of crop hints
381
+ # detection.
382
+ #
383
+ # @example
384
+ # require "google/cloud/vision"
385
+ #
386
+ # vision = Google::Cloud::Vision.new
387
+ # image = vision.image "path/to/face.jpg"
388
+ #
389
+ # crop_hints = image.crop_hints
390
+ # crop_hints.count #=> 1
391
+ # crop_hint = crop_hints.first
392
+ #
393
+ # crop_hint.bounds.count #=> 4
394
+ # crop_hint.confidence #=> 1.0
395
+ # crop_hint.importance_fraction #=> 1.0399999618530273
396
+ #
397
+ def crop_hints max_results = Vision.default_max_crop_hints
398
+ ensure_vision!
399
+ annotation = @vision.mark self, crop_hints: max_results
400
+ annotation.crop_hints
401
+ end
402
+
403
+ ##
404
+ # Performs the `WEB_ANNOTATION` feature on the image.
405
+ #
406
+ # @see https://cloud.google.com/vision/docs/pricing Cloud Vision Pricing
407
+ #
408
+ # @return [Annotation::Web] The results of web detection.
409
+ #
410
+ # @example
411
+ # require "google/cloud/vision"
412
+ #
413
+ # vision = Google::Cloud::Vision.new
414
+ # image = vision.image "path/to/face.jpg"
415
+ #
416
+ # web = image.web
417
+ #
418
+ # entity = web.entities.first
419
+ # entity.entity_id #=> "/m/019dvv"
420
+ # entity.score #=> 107.34591674804688
421
+ # entity.description #=> "Mount Rushmore National Memorial"
422
+ #
423
+ # full_matching_image = web.full_matching_images.first
424
+ # full_matching_image.url #=> "http://example.com/images/123.jpg"
425
+ # full_matching_image.score #=> 0.10226666927337646
426
+ #
427
+ # page_with_matching_images = web.pages_with_matching_images.first
428
+ # page_with_matching_images.url #=> "http://example.com/posts/123"
429
+ # page_with_matching_images.score #=> 8.114753723144531
430
+ #
431
+ def web max_results = Vision.default_max_web
432
+ ensure_vision!
433
+ annotation = @vision.mark self, web: max_results
434
+ annotation.web
435
+ end
436
+
322
437
  # @private
323
438
  def to_s
324
439
  @to_s ||= begin
@@ -345,7 +460,7 @@ module Google
345
460
  elsif url?
346
461
  Google::Cloud::Vision::V1::Image.new(
347
462
  source: Google::Cloud::Vision::V1::ImageSource.new(
348
- gcs_image_uri: @url))
463
+ image_uri: @url))
349
464
  else
350
465
  fail ArgumentError, "Unable to use Image with Vision service."
351
466
  end
@@ -361,8 +476,8 @@ module Google
361
476
  source = source.to_gs_url if source.respond_to? :to_gs_url
362
477
  # Everything should be a string from now on
363
478
  source = String source
364
- # Create an Image from the Google Storage URL
365
- return from_url(source, vision) if source.start_with? "gs://"
479
+ # Create an Image from a HTTP/HTTPS URL or Google Storage URL.
480
+ return from_url(source, vision) if url? source
366
481
  # Create an image from a file on the filesystem
367
482
  if File.file? source
368
483
  unless File.readable? source
@@ -386,11 +501,11 @@ module Google
386
501
  end
387
502
 
388
503
  ##
389
- # @private New Image from an IO object.
504
+ # @private New Image from a HTTP/HTTPS URL or Google Storage URL.
390
505
  def self.from_url url, vision
391
506
  url = String url
392
- unless url.start_with? "gs://"
393
- fail ArgumentError, "Cannot create an Image without a Storage URL"
507
+ unless url? url
508
+ fail ArgumentError, "Cannot create an Image without a URL"
394
509
  end
395
510
  new.tap do |i|
396
511
  i.instance_variable_set :@url, url
@@ -398,6 +513,13 @@ module Google
398
513
  end
399
514
  end
400
515
 
516
+ ##
517
+ # @private
518
+ def self.url? url
519
+ regex = %r{\A(http|https|gs):\/\/}
520
+ !regex.match(url).nil?
521
+ end
522
+
401
523
  protected
402
524
 
403
525
  ##
@@ -421,7 +543,14 @@ module Google
421
543
  # latin alphabet a hint is not needed. In rare cases, when the
422
544
  # language of the text in the image is known in advance, setting
423
545
  # this hint will help get better results (although it will hurt a
424
- # great deal if the hint is wrong).
546
+ # great deal if the hint is wrong). For use with {Image#text}.
547
+ # @attr [Array<Float>] aspect_ratios Aspect ratios in floats,
548
+ # representing the ratio of the width to the height of the image. For
549
+ # example, if the desired aspect ratio is 4/3, the corresponding float
550
+ # value should be 1.33333. If not specified, the best possible crop
551
+ # is returned. The number of provided aspect ratios is limited to a
552
+ # maximum of 16; any aspect ratios provided after the 16th are
553
+ # ignored. For use with {Image#crop_hints}.
425
554
  #
426
555
  # @example
427
556
  # require "google/cloud/vision"
@@ -441,13 +570,14 @@ module Google
441
570
  # @return [Area] The lat/long pairs for `latLongRect`.
442
571
  attr_reader :area
443
572
 
444
- attr_accessor :languages
573
+ attr_accessor :languages, :aspect_ratios
445
574
 
446
575
  ##
447
576
  # @private Creates a new Context instance.
448
577
  def initialize
449
578
  @area = Area.new
450
579
  @languages = []
580
+ @aspect_ratios = []
451
581
  end
452
582
 
453
583
  ##
@@ -456,7 +586,7 @@ module Google
456
586
  # @return [Boolean]
457
587
  #
458
588
  def empty?
459
- area.empty? && languages.empty?
589
+ area.empty? && languages.empty? && aspect_ratios.empty?
460
590
  end
461
591
 
462
592
  ##
@@ -467,6 +597,12 @@ module Google
467
597
  args = {}
468
598
  args[:lat_long_rect] = area.to_grpc unless area.empty?
469
599
  args[:language_hints] = languages unless languages.empty?
600
+ unless aspect_ratios.empty?
601
+ crop_params = Google::Cloud::Vision::V1::CropHintsParams.new(
602
+ aspect_ratios: aspect_ratios
603
+ )
604
+ args[:crop_hints_params] = crop_params
605
+ end
470
606
  Google::Cloud::Vision::V1::ImageContext.new args
471
607
  end
472
608