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,126 @@
1
+ # Copyright 2017 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
+ # # CropHint
24
+ #
25
+ # A single crop hint that is used to generate new crops when serving
26
+ # images.
27
+ #
28
+ # @example
29
+ # require "google/cloud/vision"
30
+ #
31
+ # vision = Google::Cloud::Vision.new
32
+ #
33
+ # image = vision.image "path/to/face.jpg"
34
+ #
35
+ # crop_hints = image.crop_hints
36
+ # crop_hints.count #=> 1
37
+ # crop_hint = crop_hints.first
38
+ #
39
+ # crop_hint.bounds.count #=> 4
40
+ # crop_hint.bounds[0].x #=> 1
41
+ # crop_hint.bounds[0].y #=> 0
42
+ # crop_hint.bounds[1].x #=> 511
43
+ # crop_hint.bounds[1].y #=> 0
44
+ # crop_hint.bounds[2].x #=> 511
45
+ # crop_hint.bounds[2].y #=> 383
46
+ # crop_hint.bounds[3].x #=> 0
47
+ # crop_hint.bounds[3].y #=> 383
48
+ #
49
+ # crop_hint.confidence #=> 1.0
50
+ # crop_hint.importance_fraction #=> 1.0399999618530273
51
+ #
52
+ class CropHint
53
+ ##
54
+ # @private The Google::Cloud::Vision::V1::CropHint GRPC object.
55
+ attr_accessor :grpc
56
+
57
+ ##
58
+ # @private Creates a new Entity instance.
59
+ def initialize
60
+ @grpc = nil
61
+ end
62
+
63
+ ##
64
+ # The bounding polygon for the crop region. The coordinates of the
65
+ # bounding box are in the original image's scale.
66
+ #
67
+ # @return [Array<Vertex>] An array of vertices.
68
+ #
69
+ def bounds
70
+ return [] unless @grpc.bounding_poly
71
+ @bounds ||= Array(@grpc.bounding_poly.vertices).map do |v|
72
+ Vertex.from_grpc v
73
+ end
74
+ end
75
+
76
+ ##
77
+ # The confidence of this being a salient region.
78
+ #
79
+ # @return [Float] A value in the range [0, 1].
80
+ #
81
+ def confidence
82
+ @grpc.confidence
83
+ end
84
+
85
+ ##
86
+ # The fraction of importance of this salient region with respect to
87
+ # the original image.
88
+ #
89
+ # @return [Float]
90
+ #
91
+ def importance_fraction
92
+ @grpc.importance_fraction
93
+ end
94
+
95
+ ##
96
+ # Deeply converts object to a hash. All keys will be symbolized.
97
+ #
98
+ # @return [Hash]
99
+ #
100
+ def to_h
101
+ { bounds: bounds.map(&:to_h), confidence: confidence,
102
+ importance_fraction: importance_fraction }
103
+ end
104
+
105
+ # @private
106
+ def to_s
107
+ tmplt = "bounds: %i, confidence: %s, importance_fraction: %s"
108
+ format tmplt, bounds.count, confidence.inspect,
109
+ importance_fraction.inspect
110
+ end
111
+
112
+ # @private
113
+ def inspect
114
+ "#<#{self.class.name} #{self}>"
115
+ end
116
+
117
+ ##
118
+ # @private New Annotation::Entity from a GRPC object.
119
+ def self.from_grpc grpc
120
+ new.tap { |f| f.instance_variable_set :@grpc, grpc }
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -22,7 +22,11 @@ module Google
22
22
  ##
23
23
  # # Text
24
24
  #
25
- # The result of text, or optical character recognition (OCR), detection.
25
+ # The results from either the `TEXT_DETECTION` feature (OCR for shorter
26
+ # documents with sparse text) or the `DOCUMENT_TEXT_DETECTION` feature
27
+ # (OCR for longer documents with dense text). Optional. Contains
28
+ # structured representations of OCR extracted text, as well as
29
+ # the entire UTF-8 text as a string.
26
30
  #
27
31
  # @example
28
32
  # require "google/cloud/vision"
@@ -32,11 +36,20 @@ module Google
32
36
  # image = vision.image "path/to/text.png"
33
37
  #
34
38
  # text = image.text
35
- # text.locale #=> "en"
36
- # text.words.count #=> 28
39
+ #
37
40
  # text.text
38
41
  # # "Google Cloud Client for Ruby an idiomatic, intuitive... "
39
42
  #
43
+ # text.words[0].text #=> "Google"
44
+ # text.words[0].bounds.count #=> 4
45
+ # vertex = text.words[0].bounds.first
46
+ # vertex.x #=> 13
47
+ # vertex.y #=> 8
48
+ #
49
+ # # Use `pages` to access a full structural representation
50
+ # text.pages[0].blocks[0].paragraphs[0].words[0].symbols[0].text
51
+ # #=> "G"
52
+ #
40
53
  class Text
41
54
  ##
42
55
  # @private The EntityAnnotation GRPC object.
@@ -90,6 +103,20 @@ module Google
90
103
  @words
91
104
  end
92
105
 
106
+ ##
107
+ # Each page in the detected text, with the metadata for each page.
108
+ # Contains a structured representation of OCR extracted text.
109
+ # The hierarchy of an OCR extracted text structure is like this:
110
+ # Page -> Block -> Paragraph -> Word -> Symbol
111
+ # Each structural component, starting from Page, may further have its
112
+ # own properties. Properties describe detected languages, breaks etc..
113
+ #
114
+ # @return [Array<Page>]
115
+ #
116
+ def pages
117
+ @pages
118
+ end
119
+
93
120
  ##
94
121
  # Deeply converts object to a hash. All keys will be symbolized.
95
122
  #
@@ -97,7 +124,7 @@ module Google
97
124
  #
98
125
  def to_h
99
126
  { text: text, locale: locale, bounds: bounds.map(&:to_h),
100
- words: words.map(&:to_h) }
127
+ words: words.map(&:to_h), pages: pages.map(&:to_h) }
101
128
  end
102
129
 
103
130
  # @private
@@ -112,27 +139,35 @@ module Google
112
139
 
113
140
  # @private
114
141
  def inspect
115
- format "#<Text text: %s, locale: %s, bounds: %i, words: %i>",
116
- text.inspect, locale.inspect, bounds.count, words.count
142
+ format "#<Text text: %s, locale: %s, bounds: %i, words: %i," \
143
+ " pages: %i>", text.inspect, locale.inspect, bounds.count,
144
+ words.count, pages.count
117
145
  end
118
146
 
119
147
  ##
120
- # @private New Annotation::Text from an array of GRPC
121
- # objects.
122
- def self.from_grpc grpc_list
123
- text, *words = Array grpc_list
148
+ # @private Create a new Annotation::Text by merging two GRPC models of
149
+ # text representation.
150
+ def self.from_grpc grpc_text_annotations, grpc_full_text_annotation
151
+ text, *words = Array grpc_text_annotations
124
152
  return nil if text.nil?
153
+
154
+ # Since text is taken from grpc_text_annotations, do not use text
155
+ # from grpc_full_text_annotation in this merged model.
156
+ # Instead, just take the pages.
157
+ pages = grpc_full_text_annotation.pages
125
158
  new.tap do |t|
126
159
  t.instance_variable_set :@grpc, text
127
160
  t.instance_variable_set :@words,
128
161
  words.map { |w| Word.from_grpc w }
162
+ t.instance_variable_set :@pages,
163
+ pages.map { |p| Page.from_grpc p }
129
164
  end
130
165
  end
131
166
 
132
167
  ##
133
168
  # # Word
134
169
  #
135
- # A word within a detected text (OCR). See {Text}.
170
+ # A word within a detected text (OCR). See {Text#words}.
136
171
  #
137
172
  # @example
138
173
  # require "google/cloud/vision"
@@ -215,6 +250,749 @@ module Google
215
250
  new.tap { |w| w.instance_variable_set :@grpc, grpc }
216
251
  end
217
252
  end
253
+
254
+ ##
255
+ # # Page
256
+ #
257
+ # A page within a detected text (OCR). See {Text#pages}.
258
+ #
259
+ # @example
260
+ # require "google/cloud/vision"
261
+ #
262
+ # vision = Google::Cloud::Vision.new
263
+ #
264
+ # image = vision.image "path/to/text.png"
265
+ #
266
+ # text = image.text
267
+ #
268
+ # page = text.pages.first
269
+ #
270
+ # page.languages.first.code #=> "en"
271
+ # page.wont_be :prefix_break?
272
+ # page.width #=> 400
273
+ # page.height #=> 80
274
+ # page.blocks.count #=> 1
275
+ #
276
+ class Page
277
+ ##
278
+ # @private The EntityAnnotation GRPC object.
279
+ attr_accessor :grpc
280
+
281
+ ##
282
+ # @private Creates a new Page instance.
283
+ def initialize
284
+ @grpc = nil
285
+ end
286
+
287
+ ##
288
+ # A list of detected languages together with confidence.
289
+ #
290
+ # @return [Array<Language>]
291
+ #
292
+ def languages
293
+ @languages ||= Array(@grpc.property.detected_languages).map do |l|
294
+ Language.from_grpc l
295
+ end
296
+ end
297
+
298
+ ##
299
+ # The type of a detected break at the start or end of the page.
300
+ #
301
+ # @return [::Symbol]
302
+ #
303
+ def break_type
304
+ @grpc.property.detected_break &&
305
+ @grpc.property.detected_break.type.to_sym
306
+ end
307
+
308
+ ##
309
+ # True if a detected break prepends the page.
310
+ #
311
+ # @return [Boolean]
312
+ #
313
+ def prefix_break?
314
+ @grpc.property.detected_break &&
315
+ @grpc.property.detected_break.is_prefix
316
+ end
317
+
318
+ ##
319
+ # Page width in pixels.
320
+ #
321
+ # @return [Integer]
322
+ #
323
+ def width
324
+ @grpc.width
325
+ end
326
+
327
+ ##
328
+ # Page height in pixels.
329
+ #
330
+ # @return [Integer]
331
+ #
332
+ def height
333
+ @grpc.height
334
+ end
335
+
336
+ ##
337
+ # List of blocks of text, images etc on this page.
338
+ #
339
+ # @return [Array<Block>]
340
+ #
341
+ def blocks
342
+ @blocks ||= Array(@grpc.blocks).map do |b|
343
+ Block.from_grpc b
344
+ end
345
+ end
346
+
347
+ ##
348
+ # Deeply converts object to a hash. All keys will be symbolized.
349
+ #
350
+ # @return [Hash]
351
+ #
352
+ def to_h
353
+ { languages: languages.map(&:to_h), break_type: break_type,
354
+ prefix_break: prefix_break?, width: width, height: height,
355
+ blocks: blocks.map(&:to_h) }
356
+ end
357
+
358
+ # @private
359
+ def to_s
360
+ tmplt = "languages: %s, break_type: %s, prefix_break: %s," \
361
+ " width: %s, height: %s, blocks: %i"
362
+ format tmplt, languages.inspect, break_type, prefix_break?, width,
363
+ height, blocks.count
364
+ end
365
+
366
+ # @private
367
+ def inspect
368
+ "#<#{self.class.name} #{self}>"
369
+ end
370
+
371
+ ##
372
+ # @private New Annotation::Text::Page from a GRPC
373
+ # object.
374
+ def self.from_grpc grpc
375
+ new.tap { |w| w.instance_variable_set :@grpc, grpc }
376
+ end
377
+
378
+ ##
379
+ # # Block
380
+ #
381
+ # A logical element on the page. See {Page}.
382
+ #
383
+ # @example
384
+ # require "google/cloud/vision"
385
+ #
386
+ # vision = Google::Cloud::Vision.new
387
+ #
388
+ # image = vision.image "path/to/text.png"
389
+ # text = image.text
390
+ #
391
+ # block = text.pages[0].blocks.first
392
+ #
393
+ # block.languages.first.code #=> "en"
394
+ # block.bounds.count #=> 4
395
+ # block.paragraphs.count #=> 1
396
+ #
397
+ class Block
398
+ ##
399
+ # @private The EntityAnnotation GRPC object.
400
+ attr_accessor :grpc
401
+
402
+ ##
403
+ # @private Creates a new Block instance.
404
+ def initialize
405
+ @grpc = nil
406
+ end
407
+
408
+ ##
409
+ # A list of detected languages together with confidence.
410
+ #
411
+ # @return [Array<Language>]
412
+ #
413
+ def languages
414
+ detected_languages = @grpc.property.detected_languages
415
+ @languages ||= Array(detected_languages).map do |l|
416
+ Language.from_grpc l
417
+ end
418
+ end
419
+
420
+ ##
421
+ # The type of a detected break at the start or end of the page.
422
+ #
423
+ # @return [::Symbol]
424
+ #
425
+ def break_type
426
+ @grpc.property.detected_break &&
427
+ @grpc.property.detected_break.type.to_sym
428
+ end
429
+
430
+ ##
431
+ # True if a detected break prepends the page.
432
+ #
433
+ # @return [Boolean]
434
+ #
435
+ def prefix_break?
436
+ @grpc.property.detected_break &&
437
+ @grpc.property.detected_break.is_prefix
438
+ end
439
+
440
+ ##
441
+ # The bounding box for the block.
442
+ # The vertices are in the order of top-left, top-right,
443
+ # bottom-right, bottom-left. When a rotation of the bounding box
444
+ # is detected the rotation is represented as around the top-left
445
+ # corner as defined when the text is read in the 'natural'
446
+ # orientation.
447
+ # For example:
448
+ # * when the text is horizontal it might look like:
449
+ # 0----1
450
+ # | |
451
+ # 3----2
452
+ # * when rotated 180 degrees around the top-left corner it
453
+ # becomes:
454
+ # 2----3
455
+ # | |
456
+ # 1----0
457
+ # and the vertice order will still be (0, 1, 2, 3).
458
+ #
459
+ # @return [Array<Vertex>]
460
+ #
461
+ def bounds
462
+ return [] unless @grpc.bounding_box
463
+ @bounds ||= Array(@grpc.bounding_box.vertices).map do |v|
464
+ Vertex.from_grpc v
465
+ end
466
+ end
467
+
468
+ ##
469
+ # List of paragraphs in this block (if this block is of type
470
+ # text).
471
+ #
472
+ # @return [Array<Paragraph>]
473
+ #
474
+ def paragraphs
475
+ @paragraphs ||= Array(@grpc.paragraphs).map do |b|
476
+ Paragraph.from_grpc b
477
+ end
478
+ end
479
+
480
+ ##
481
+ # Detected block type (text, image etc) for the block.
482
+ #
483
+ # @return [::Symbol]
484
+ #
485
+ def block_type
486
+ @grpc.block_type.to_sym
487
+ end
488
+
489
+ ##
490
+ # Deeply converts object to a hash. All keys will be symbolized.
491
+ #
492
+ # @return [Hash]
493
+ #
494
+ def to_h
495
+ { languages: languages.map(&:to_h), break_type: break_type,
496
+ prefix_break: prefix_break?, bounds: bounds.map(&:to_h),
497
+ paragraphs: paragraphs.map(&:to_h), block_type: block_type }
498
+ end
499
+
500
+ # @private
501
+ def to_s
502
+ tmplt = "languages: %s, break_type: %s, prefix_break: %s," \
503
+ " bounds: %i, paragraphs: %i, block_type: %s"
504
+ format tmplt, languages.inspect, break_type, prefix_break?,
505
+ bounds.count, paragraphs.count, block_type
506
+ end
507
+
508
+ # @private
509
+ def inspect
510
+ "#<#{self.class.name} #{self}>"
511
+ end
512
+
513
+ ##
514
+ # @private New Annotation::Text::Page::Block from a GRPC
515
+ # object.
516
+ def self.from_grpc grpc
517
+ new.tap { |w| w.instance_variable_set :@grpc, grpc }
518
+ end
519
+ end
520
+
521
+ ##
522
+ # # Paragraph
523
+ #
524
+ # Structural unit of text representing a number of words in certain
525
+ # order. See {Block}.
526
+ #
527
+ # @example
528
+ # require "google/cloud/vision"
529
+ #
530
+ # vision = Google::Cloud::Vision.new
531
+ #
532
+ # image = vision.image "path/to/text.png"
533
+ # text = image.text
534
+ #
535
+ # paragraph = text.pages[0].blocks[0].paragraphs.first
536
+ #
537
+ # paragraph.languages.first.code #=> "en"
538
+ # paragraph.bounds.count #=> 4
539
+ # paragraph.words.count #=> 10
540
+ #
541
+ class Paragraph
542
+ ##
543
+ # @private The EntityAnnotation GRPC object.
544
+ attr_accessor :grpc
545
+
546
+ ##
547
+ # @private Creates a new Paragraph instance.
548
+ def initialize
549
+ @grpc = nil
550
+ end
551
+
552
+ ##
553
+ # A list of detected languages together with confidence.
554
+ #
555
+ # @return [Array<Language>]
556
+ #
557
+ def languages
558
+ detected_languages = @grpc.property.detected_languages
559
+ @languages ||= Array(detected_languages).map do |l|
560
+ Language.from_grpc l
561
+ end
562
+ end
563
+
564
+ ##
565
+ # The type of a detected break at the start or end of the page.
566
+ #
567
+ # @return [::Symbol]
568
+ #
569
+ def break_type
570
+ @grpc.property.detected_break &&
571
+ @grpc.property.detected_break.type.to_sym
572
+ end
573
+
574
+ ##
575
+ # True if a detected break prepends the page.
576
+ #
577
+ # @return [Boolean]
578
+ #
579
+ def prefix_break?
580
+ @grpc.property.detected_break &&
581
+ @grpc.property.detected_break.is_prefix
582
+ end
583
+
584
+ ##
585
+ # The bounding box for the paragraph.
586
+ # The vertices are in the order of top-left, top-right,
587
+ # bottom-right, bottom-left. When a rotation of the bounding box
588
+ # is detected the rotation is represented as around the top-left
589
+ # corner as defined when the text is read in the 'natural'
590
+ # orientation.
591
+ # For example:
592
+ # * when the text is horizontal it might look like:
593
+ # 0----1
594
+ # | |
595
+ # 3----2
596
+ # * when rotated 180 degrees around the top-left corner it
597
+ # becomes:
598
+ # 2----3
599
+ # | |
600
+ # 1----0
601
+ # and the vertice order will still be (0, 1, 2, 3).
602
+ #
603
+ # @return [Array<Vertex>]
604
+ #
605
+ def bounds
606
+ return [] unless @grpc.bounding_box
607
+ @bounds ||= Array(@grpc.bounding_box.vertices).map do |v|
608
+ Vertex.from_grpc v
609
+ end
610
+ end
611
+
612
+ ##
613
+ # List of words in this paragraph.
614
+ #
615
+ # @return [Array<Word>]
616
+ #
617
+ def words
618
+ @words ||= Array(@grpc.words).map do |b|
619
+ Word.from_grpc b
620
+ end
621
+ end
622
+
623
+ ##
624
+ # Deeply converts object to a hash. All keys will be symbolized.
625
+ #
626
+ # @return [Hash]
627
+ #
628
+ def to_h
629
+ { languages: languages.map(&:to_h), break_type: break_type,
630
+ prefix_break: prefix_break?, bounds: bounds.map(&:to_h),
631
+ words: words.map(&:to_h) }
632
+ end
633
+
634
+ # @private
635
+ def to_s
636
+ tmplt = "languages: %s, break_type: %s, prefix_break: %s," \
637
+ " bounds: %i, words: %i"
638
+ format tmplt, languages.inspect, break_type, prefix_break?,
639
+ bounds.count, words.count
640
+ end
641
+
642
+ # @private
643
+ def inspect
644
+ "#<#{self.class.name} #{self}>"
645
+ end
646
+
647
+ ##
648
+ # @private New Annotation::Text::Page::Paragraph from a GRPC
649
+ # object.
650
+ def self.from_grpc grpc
651
+ new.tap { |w| w.instance_variable_set :@grpc, grpc }
652
+ end
653
+ end
654
+
655
+ ##
656
+ # # Word
657
+ #
658
+ # A word representation. See {Paragraph}.
659
+ #
660
+ # @example
661
+ # require "google/cloud/vision"
662
+ #
663
+ # vision = Google::Cloud::Vision.new
664
+ #
665
+ # image = vision.image "path/to/text.png"
666
+ # text = image.text
667
+ #
668
+ # word = text.pages[0].blocks[0].paragraphs[0].words.first
669
+ #
670
+ # word.languages.first.code #=> "en"
671
+ # word.bounds.count #=> 4
672
+ # word.symbols.count #=> 6
673
+ #
674
+ class Word
675
+ ##
676
+ # @private The EntityAnnotation GRPC object.
677
+ attr_accessor :grpc
678
+
679
+ ##
680
+ # @private Creates a new Word instance.
681
+ def initialize
682
+ @grpc = nil
683
+ end
684
+
685
+ ##
686
+ # A list of detected languages together with confidence.
687
+ #
688
+ # @return [Array<Language>]
689
+ #
690
+ def languages
691
+ detected_languages = @grpc.property.detected_languages
692
+ @languages ||= Array(detected_languages).map do |l|
693
+ Language.from_grpc l
694
+ end
695
+ end
696
+
697
+ ##
698
+ # The type of a detected break at the start or end of the page.
699
+ #
700
+ # @return [::Symbol]
701
+ #
702
+ def break_type
703
+ @grpc.property.detected_break &&
704
+ @grpc.property.detected_break.type.to_sym
705
+ end
706
+
707
+ ##
708
+ # True if a detected break prepends the page.
709
+ #
710
+ # @return [Boolean]
711
+ #
712
+ def prefix_break?
713
+ @grpc.property.detected_break &&
714
+ @grpc.property.detected_break.is_prefix
715
+ end
716
+
717
+ ##
718
+ # The bounding box for the word.
719
+ # The vertices are in the order of top-left, top-right,
720
+ # bottom-right, bottom-left. When a rotation of the bounding box
721
+ # is detected the rotation is represented as around the top-left
722
+ # corner as defined when the text is read in the 'natural'
723
+ # orientation.
724
+ # For example:
725
+ # * when the text is horizontal it might look like:
726
+ # 0----1
727
+ # | |
728
+ # 3----2
729
+ # * when rotated 180 degrees around the top-left corner it
730
+ # becomes:
731
+ # 2----3
732
+ # | |
733
+ # 1----0
734
+ # and the vertice order will still be (0, 1, 2, 3).
735
+ #
736
+ # @return [Array<Vertex>]
737
+ #
738
+ def bounds
739
+ return [] unless @grpc.bounding_box
740
+ @bounds ||= Array(@grpc.bounding_box.vertices).map do |v|
741
+ Vertex.from_grpc v
742
+ end
743
+ end
744
+
745
+ ##
746
+ # List of symbols in the word. The order of the symbols follows
747
+ # the natural reading order.
748
+ #
749
+ # @return [Array<Symbol>]
750
+ #
751
+ def symbols
752
+ @symbols ||= Array(@grpc.symbols).map do |b|
753
+ Symbol.from_grpc b
754
+ end
755
+ end
756
+
757
+ ##
758
+ # Deeply converts object to a hash. All keys will be symbolized.
759
+ #
760
+ # @return [Hash]
761
+ #
762
+ def to_h
763
+ { languages: languages.map(&:to_h), break_type: break_type,
764
+ prefix_break: prefix_break?, bounds: bounds.map(&:to_h),
765
+ symbols: symbols.map(&:to_h) }
766
+ end
767
+
768
+ # @private
769
+ def to_s
770
+ tmplt = "languages: %s, break_type: %s, prefix_break: %s," \
771
+ " bounds: %i, symbols: %i"
772
+ format tmplt, languages.inspect, break_type, prefix_break?,
773
+ bounds.count, symbols.count
774
+ end
775
+
776
+ # @private
777
+ def inspect
778
+ "#<#{self.class.name} #{self}>"
779
+ end
780
+
781
+ ##
782
+ # @private New Annotation::Text::Page::Word from a GRPC
783
+ # object.
784
+ def self.from_grpc grpc
785
+ new.tap { |w| w.instance_variable_set :@grpc, grpc }
786
+ end
787
+ end
788
+
789
+ ##
790
+ # # Symbol
791
+ #
792
+ # A word representation. See {Paragraph}.
793
+ #
794
+ # @example
795
+ # require "google/cloud/vision"
796
+ #
797
+ # vision = Google::Cloud::Vision.new
798
+ #
799
+ # image = vision.image "path/to/text.png"
800
+ # text = image.text
801
+ # page = text.pages.first
802
+ #
803
+ # symbol = page.blocks[0].paragraphs[0].words[0].symbols[0]
804
+ #
805
+ # symbol.languages.first.code #=> "en"
806
+ # symbol.bounds.count #=> 4
807
+ # symbol.text #=> "G"
808
+ #
809
+ class Symbol
810
+ ##
811
+ # @private The EntityAnnotation GRPC object.
812
+ attr_accessor :grpc
813
+
814
+ ##
815
+ # @private Creates a new Symbol instance.
816
+ def initialize
817
+ @grpc = nil
818
+ end
819
+
820
+ ##
821
+ # A list of detected languages together with confidence.
822
+ #
823
+ # @return [Array<Language>]
824
+ #
825
+ def languages
826
+ detected_languages = @grpc.property.detected_languages
827
+ @languages ||= Array(detected_languages).map do |l|
828
+ Language.from_grpc l
829
+ end
830
+ end
831
+
832
+ ##
833
+ # The type of a detected break at the start or end of the page.
834
+ #
835
+ # @return [::Symbol]
836
+ #
837
+ def break_type
838
+ @grpc.property.detected_break &&
839
+ @grpc.property.detected_break.type.to_sym
840
+ end
841
+
842
+ ##
843
+ # True if a detected break prepends the page.
844
+ #
845
+ # @return [Boolean]
846
+ #
847
+ def prefix_break?
848
+ @grpc.property.detected_break &&
849
+ @grpc.property.detected_break.is_prefix
850
+ end
851
+
852
+ ##
853
+ # The bounding box for the symbol.
854
+ # The vertices are in the order of top-left, top-right,
855
+ # bottom-right, bottom-left. When a rotation of the bounding box
856
+ # is detected the rotation is represented as around the top-left
857
+ # corner as defined when the text is read in the 'natural'
858
+ # orientation.
859
+ # For example:
860
+ # * when the text is horizontal it might look like:
861
+ # 0----1
862
+ # | |
863
+ # 3----2
864
+ # * when rotated 180 degrees around the top-left corner it
865
+ # becomes:
866
+ # 2----3
867
+ # | |
868
+ # 1----0
869
+ # and the vertice order will still be (0, 1, 2, 3).
870
+ #
871
+ # @return [Array<Vertex>]
872
+ #
873
+ def bounds
874
+ return [] unless @grpc.bounding_box
875
+ @bounds ||= Array(@grpc.bounding_box.vertices).map do |v|
876
+ Vertex.from_grpc v
877
+ end
878
+ end
879
+
880
+ ##
881
+ # The actual UTF-8 representation of the symbol.
882
+ #
883
+ # @return [String]
884
+ #
885
+ def text
886
+ @grpc.text
887
+ end
888
+
889
+ ##
890
+ # Deeply converts object to a hash. All keys will be symbolized.
891
+ #
892
+ # @return [Hash]
893
+ #
894
+ def to_h
895
+ { languages: languages.map(&:to_h), break_type: break_type,
896
+ prefix_break: prefix_break?, bounds: bounds.map(&:to_h),
897
+ text: text }
898
+ end
899
+
900
+ # @private
901
+ def to_s
902
+ tmplt = "languages: %s, break_type: %s, prefix_break: %s," \
903
+ " bounds: %i, text: %s"
904
+ format tmplt, languages.inspect, break_type, prefix_break?,
905
+ bounds.count, text
906
+ end
907
+
908
+ # @private
909
+ def inspect
910
+ "#<#{self.class.name} #{self}>"
911
+ end
912
+
913
+ ##
914
+ # @private New Annotation::Text::Page::Symbol from a GRPC
915
+ # object.
916
+ def self.from_grpc grpc
917
+ new.tap { |w| w.instance_variable_set :@grpc, grpc }
918
+ end
919
+ end
920
+
921
+ ##
922
+ # # Language
923
+ #
924
+ # A language within a detected text (OCR). See {Text#pages}.
925
+ #
926
+ # @example
927
+ # require "google/cloud/vision"
928
+ #
929
+ # vision = Google::Cloud::Vision.new
930
+ #
931
+ # image = vision.image "path/to/text.png"
932
+ # text = image.text
933
+ # page = text.pages.first
934
+ #
935
+ # language = page.languages.first
936
+ # language.code #=> "en"
937
+ #
938
+ class Language
939
+ ##
940
+ # @private The EntityAnnotation GRPC object.
941
+ attr_accessor :grpc
942
+
943
+ ##
944
+ # @private Creates a new Language instance.
945
+ def initialize
946
+ @grpc = nil
947
+ end
948
+
949
+ ##
950
+ # The language code detected for a structural component.
951
+ #
952
+ # @return [String] The [ISO
953
+ # 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
954
+ # language code.
955
+ #
956
+ def code
957
+ @grpc.language_code
958
+ end
959
+
960
+ ##
961
+ # Confidence of detected language.
962
+ #
963
+ # @return [Float] A value in the range [0,1].
964
+ #
965
+ def confidence
966
+ @grpc.confidence
967
+ end
968
+
969
+ ##
970
+ # Deeply converts object to a hash. All keys will be symbolized.
971
+ #
972
+ # @return [Hash]
973
+ #
974
+ def to_h
975
+ { code: code, confidence: confidence }
976
+ end
977
+
978
+ # @private
979
+ def to_s
980
+ format "code: %s, confidence: %s", code, confidence
981
+ end
982
+
983
+ # @private
984
+ def inspect
985
+ "#<#{self.class.name} #{self}>"
986
+ end
987
+
988
+ ##
989
+ # @private New Annotation::Text::Page::Language from a GRPC
990
+ # object.
991
+ def self.from_grpc grpc
992
+ new.tap { |w| w.instance_variable_set :@grpc, grpc }
993
+ end
994
+ end
995
+ end
218
996
  end
219
997
  end
220
998
  end