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,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