google-cloud-vision 0.22.1 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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