google-cloud-vision 0.20.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,228 @@
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/location"
17
+ require "google/cloud/vision/annotation/vertex"
18
+
19
+ module Google
20
+ module Cloud
21
+ module Vision
22
+ class Annotation
23
+ ##
24
+ # # Entity
25
+ #
26
+ # Represents characteristics of an entity detected in an image. May
27
+ # describe a real-world entity such as a person, place, or thing. May be
28
+ # identified with an entity ID as an entity in the Knowledge Graph (KG).
29
+ #
30
+ # @see https://developers.google.com/knowledge-graph/ Knowledge Graph
31
+ #
32
+ # @example
33
+ # require "google/cloud"
34
+ #
35
+ # gcloud = Google::Cloud.new
36
+ # vision = gcloud.vision
37
+ #
38
+ # image = vision.image "path/to/landmark.jpg"
39
+ #
40
+ # landmark = image.landmark
41
+ # landmark.score #=> 0.91912264
42
+ # landmark.description #=> "Mount Rushmore"
43
+ # landmark.mid #=> "/m/019dvv"
44
+ #
45
+ # @example
46
+ # require "google/cloud"
47
+ #
48
+ # gcloud = Google::Cloud.new
49
+ # vision = gcloud.vision
50
+ #
51
+ # image = vision.image "path/to/logo.jpg"
52
+ #
53
+ # logo = image.logo
54
+ # logo.score #=> 0.70057315
55
+ # logo.description #=> "Google"
56
+ # logo.mid #=> "/m/0b34hf"
57
+ #
58
+ # @example
59
+ # require "google/cloud"
60
+ #
61
+ # gcloud = Google::Cloud.new
62
+ # vision = gcloud.vision
63
+ #
64
+ # image = vision.image "path/to/face.jpg"
65
+ #
66
+ # labels = image.labels
67
+ # labels.count #=> 4
68
+ #
69
+ # label = labels.first
70
+ # label.score #=> 0.9481349
71
+ # label.description #=> "person"
72
+ # label.mid #=> "/m/01g317"
73
+ #
74
+ class Entity
75
+ ##
76
+ # @private The EntityAnnotation Google API Client object.
77
+ attr_accessor :gapi
78
+
79
+ ##
80
+ # @private Creates a new Entity instance.
81
+ def initialize
82
+ @gapi = {}
83
+ end
84
+
85
+ ##
86
+ # Opaque entity ID. Some IDs might be available in Knowledge Graph
87
+ # (KG).
88
+ #
89
+ # @see https://developers.google.com/knowledge-graph/ Knowledge Graph
90
+ #
91
+ # @return [String] The opaque entity ID.
92
+ #
93
+ def mid
94
+ @gapi.mid
95
+ end
96
+
97
+ ##
98
+ # The language code for the locale in which the `description` is
99
+ # expressed.
100
+ #
101
+ # @return [String] The [ISO
102
+ # 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
103
+ # language code.
104
+ #
105
+ def locale
106
+ @gapi.locale
107
+ end
108
+
109
+ ##
110
+ # Entity textual description, expressed in the {#locale} language.
111
+ #
112
+ # @return [String] A description of the entity.
113
+ #
114
+ def description
115
+ @gapi.description
116
+ end
117
+
118
+ ##
119
+ # Overall score of the result.
120
+ #
121
+ # @return [Float] A value in the range [0, 1].
122
+ #
123
+ def score
124
+ @gapi.score
125
+ end
126
+
127
+ ##
128
+ # The accuracy of the entity detection in an image. For example, for
129
+ # an image containing 'Eiffel Tower,' this field represents the
130
+ # confidence that there is a tower in the query image.
131
+ #
132
+ # @return [Float] A value in the range [0, 1].
133
+ #
134
+ def confidence
135
+ @gapi.confidence
136
+ end
137
+
138
+ ##
139
+ # The relevancy of the ICA (Image Content Annotation) label to the
140
+ # image. For example, the relevancy of 'tower' to an image containing
141
+ # 'Eiffel Tower' is likely higher than an image containing a distant
142
+ # towering building, though the confidence that there is a tower may
143
+ # be the same.
144
+ #
145
+ # @return [Float] A value in the range [0, 1].
146
+ #
147
+ def topicality
148
+ @gapi.topicality
149
+ end
150
+
151
+ ##
152
+ # Image region to which this entity belongs. Not filled currently for
153
+ # `labels` detection.
154
+ #
155
+ # @return [Array<Vertex>] An array of vertices.
156
+ #
157
+ def bounds
158
+ return [] unless @gapi.bounding_poly
159
+ @bounds ||= Array(@gapi.bounding_poly.vertices).map do |v|
160
+ Vertex.from_gapi v
161
+ end
162
+ end
163
+
164
+ ##
165
+ # The location information for the detected entity. Multiple Location
166
+ # elements can be present since one location may indicate the location
167
+ # of the scene in the query image, and another the location of the
168
+ # place where the query image was taken. Location information is
169
+ # usually present for landmarks.
170
+ #
171
+ # @return [Array<Location>] An array of locations containing latitude
172
+ # and longitude.
173
+ #
174
+ def locations
175
+ @locations ||= Array(@gapi.locations).map do |l|
176
+ Location.from_gapi l.lat_lng
177
+ end
178
+ end
179
+
180
+ ##
181
+ # Some entities can have additional optional Property fields. For
182
+ # example a different kind of score or string that qualifies the
183
+ # entity. present for landmarks.
184
+ #
185
+ # @return [Hash] A hash containing property names and values.
186
+ #
187
+ def properties
188
+ @properties ||=
189
+ Hash[Array(@gapi.properties).map { |p| [p.name, p.value] }]
190
+ end
191
+
192
+ ##
193
+ # Deeply converts object to a hash. All keys will be symbolized.
194
+ #
195
+ # @return [Hash]
196
+ #
197
+ def to_h
198
+ { mid: mid, locale: locale, description: description,
199
+ score: score, confidence: confidence, topicality: topicality,
200
+ bounds: bounds.map(&:to_h), locations: locations.map(&:to_h),
201
+ properties: properties }
202
+ end
203
+
204
+ # @private
205
+ def to_s
206
+ tmplt = "mid: %s, locale: %s, description: %s, score: %s, " \
207
+ "confidence: %s, topicality: %s, bounds: %i, " \
208
+ "locations: %i, properties: %s"
209
+ format tmplt, mid.inspect, locale.inspect, description.inspect,
210
+ score.inspect, confidence.inspect, topicality.inspect,
211
+ bounds.count, locations.count, properties.inspect
212
+ end
213
+
214
+ # @private
215
+ def inspect
216
+ "#<#{self.class.name} #{self}>"
217
+ end
218
+
219
+ ##
220
+ # @private New Annotation::Entity from a Google API Client object.
221
+ def self.from_gapi gapi
222
+ new.tap { |f| f.instance_variable_set :@gapi, gapi }
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,1512 @@
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
+ # # Face
24
+ #
25
+ # The results of face detection.
26
+ #
27
+ # See {Annotation#faces} and {Annotation#face}.
28
+ #
29
+ # @example
30
+ # require "google/cloud"
31
+ #
32
+ # gcloud = Google::Cloud.new
33
+ # vision = gcloud.vision
34
+ #
35
+ # image = vision.image "path/to/face.jpg"
36
+ #
37
+ # face = image.face
38
+ # face.confidence #=> 0.86162376
39
+ #
40
+ class Face
41
+ ##
42
+ # @private The FaceAnnotation Google API Client object.
43
+ attr_accessor :gapi
44
+
45
+ ##
46
+ # @private Creates a new Face instance.
47
+ def initialize
48
+ @gapi = {}
49
+ end
50
+
51
+ ##
52
+ # The angles of the face, including roll, yaw, and pitch.
53
+ #
54
+ # @return [Angles]
55
+ #
56
+ def angles
57
+ @angles ||= Angles.from_gapi @gapi
58
+ end
59
+
60
+ ##
61
+ # The bounds of the face, including the polygons for the head and
62
+ # face.
63
+ #
64
+ # @return [Bounds]
65
+ #
66
+ def bounds
67
+ @bounds ||= Bounds.from_gapi @gapi
68
+ end
69
+
70
+ ##
71
+ # The landmarks of the face, including the points for the eyes, ears,
72
+ # nose and mouth.
73
+ #
74
+ # @return [Features]
75
+ #
76
+ def features
77
+ @features ||= Features.from_gapi @gapi
78
+ end
79
+
80
+ ##
81
+ # The likelihood of the facial detection, including joy, sorrow,
82
+ # anger, surprise, under_exposed, blurred, and headwear.
83
+ #
84
+ # @return [Likelihood]
85
+ #
86
+ def likelihood
87
+ @likelihood ||= Likelihood.from_gapi @gapi
88
+ end
89
+
90
+ ##
91
+ # The confidence of the facial detection.
92
+ #
93
+ # @return [Float] A value in the range [0, 1].
94
+ #
95
+ def confidence
96
+ @gapi.detection_confidence
97
+ end
98
+
99
+ ##
100
+ # Deeply converts object to a hash. All keys will be symbolized.
101
+ #
102
+ # @return [Hash]
103
+ #
104
+ def to_h
105
+ { angles: angles.to_h, bounds: bounds.to_h, features: features.to_h,
106
+ likelihood: likelihood.to_h }
107
+ end
108
+
109
+ # @private
110
+ def to_s
111
+ # Keep console output low by not showing all sub-objects.
112
+ "(angles, bounds, features, likelihood)"
113
+ end
114
+
115
+ # @private
116
+ def inspect
117
+ "#<#{self.class.name} #{self}>"
118
+ end
119
+
120
+ ##
121
+ # @private New Annotation::Face from a Google API Client object.
122
+ def self.from_gapi gapi
123
+ new.tap { |f| f.instance_variable_set :@gapi, gapi }
124
+ end
125
+
126
+ ##
127
+ # # Angles
128
+ #
129
+ # The orientation of the face relative to the image.
130
+ #
131
+ # See {Face}.
132
+ #
133
+ # @example
134
+ # require "google/cloud"
135
+ #
136
+ # gcloud = Google::Cloud.new
137
+ # vision = gcloud.vision
138
+ #
139
+ # image = vision.image "path/to/face.jpg"
140
+ # face = image.face
141
+ #
142
+ # face.angles.roll #=> -5.1492119
143
+ # face.angles.yaw #=> -4.0695682
144
+ # face.angles.pitch #=> -13.083284
145
+ #
146
+ class Angles
147
+ ##
148
+ # @private The FaceAnnotation Google API Client object.
149
+ attr_accessor :gapi
150
+
151
+ ##
152
+ # @private Creates a new Angles instance.
153
+ def initialize
154
+ @gapi = {}
155
+ end
156
+
157
+ ##
158
+ # Roll angle. Indicates the amount of clockwise/anti-clockwise
159
+ # rotation of the face relative to the image vertical, about the
160
+ # axis perpendicular to the face.
161
+ #
162
+ # @return [Float] A value in the range [-180,180].
163
+ #
164
+ def roll
165
+ @gapi.roll_angle
166
+ end
167
+
168
+ ##
169
+ # Yaw (pan) angle. Indicates the leftward/rightward angle that the
170
+ # face is pointing, relative to the vertical plane perpendicular to
171
+ # the image.
172
+ #
173
+ # @return [Float] A value in the range [-180,180].
174
+ #
175
+ def yaw
176
+ @gapi.pan_angle
177
+ end
178
+ alias_method :pan, :yaw
179
+
180
+ ##
181
+ # Pitch (tilt) angle. Indicates the upwards/downwards angle that the
182
+ # face is pointing relative to the image's horizontal plane.
183
+ #
184
+ # @return [Float] A value in the range [-180,180].
185
+ #
186
+ def pitch
187
+ @gapi.tilt_angle
188
+ end
189
+ alias_method :tilt, :pitch
190
+
191
+ ##
192
+ # Returns the object's property values as an array.
193
+ #
194
+ # @return [Array]
195
+ #
196
+ def to_a
197
+ [roll, yaw, pitch]
198
+ end
199
+
200
+ ##
201
+ # Converts object to a hash. All keys will be symbolized.
202
+ #
203
+ # @return [Hash]
204
+ #
205
+ def to_h
206
+ { roll: roll, yaw: yaw, pitch: pitch }
207
+ end
208
+
209
+ # @private
210
+ def to_s
211
+ format "(roll: %s, yaw: %s, pitch: %s)", roll.inspect,
212
+ yaw.inspect, pitch.inspect
213
+ end
214
+
215
+ # @private
216
+ def inspect
217
+ "#<Angles #{self}>"
218
+ end
219
+
220
+ ##
221
+ # @private New Annotation::Face::Angles from a Google API Client
222
+ # object.
223
+ def self.from_gapi gapi
224
+ new.tap { |f| f.instance_variable_set :@gapi, gapi }
225
+ end
226
+ end
227
+
228
+ ##
229
+ # # Bounds
230
+ #
231
+ # Bounding polygons around the face.
232
+ #
233
+ # See {Face}.
234
+ #
235
+ # @example
236
+ # require "google/cloud"
237
+ #
238
+ # gcloud = Google::Cloud.new
239
+ # vision = gcloud.vision
240
+ #
241
+ # image = vision.image "path/to/face.jpg"
242
+ # face = image.face
243
+ #
244
+ # face.bounds.face.count #=> 4
245
+ # face.bounds.face.first #=> #<Vertex (x: 153, y: 34)>
246
+ #
247
+ class Bounds
248
+ ##
249
+ # @private The FaceAnnotation Google API Client object.
250
+ attr_accessor :gapi
251
+
252
+ ##
253
+ # @private Creates a new Bounds instance.
254
+ def initialize
255
+ @gapi = {}
256
+ end
257
+
258
+ ##
259
+ # The bounding polygon around the face. The coordinates of the
260
+ # bounding box are in the original image's scale, as returned in
261
+ # ImageParams. The bounding box is computed to "frame" the face in
262
+ # accordance with human expectations. It is based on the landmarker
263
+ # results. Note that one or more x and/or y coordinates may not be
264
+ # generated in the BoundingPoly (the polygon will be unbounded) if
265
+ # only a partial face appears in the image to be annotated.
266
+ def head
267
+ return [] unless @gapi.bounding_poly
268
+ @head ||= Array(@gapi.bounding_poly.vertices).map do |v|
269
+ Vertex.from_gapi v
270
+ end
271
+ end
272
+
273
+ ##
274
+ # This bounding polygon is tighter than the {#head}, and encloses
275
+ # only the skin part of the face. Typically, it is used to eliminate
276
+ # the face from any image annotation that detects the "amount of
277
+ # skin" visible in an image. It is not based on the landmarks, only
278
+ # on the initial face detection.
279
+ def face
280
+ return [] unless @gapi.fd_bounding_poly
281
+ @face ||= Array(@gapi.fd_bounding_poly.vertices).map do |v|
282
+ Vertex.from_gapi v
283
+ end
284
+ end
285
+
286
+ ##
287
+ # Returns the object's property values as an array.
288
+ #
289
+ # @return [Array]
290
+ #
291
+ def to_a
292
+ [head.map(&:to_a), face.map(&:to_a)]
293
+ end
294
+
295
+ ##
296
+ # Deeply converts object to a hash. All keys will be symbolized.
297
+ #
298
+ # @return [Hash]
299
+ #
300
+ def to_h
301
+ { head: head.map(&:to_h), face: face.map(&:to_h) }
302
+ end
303
+
304
+ # @private
305
+ def to_s
306
+ "(head: #{head.inspect}, face: #{face.inspect})"
307
+ end
308
+
309
+ # @private
310
+ def inspect
311
+ "#<Bounds #{self}>"
312
+ end
313
+
314
+ ##
315
+ # @private New Annotation::Face::Angles from a Google API Client
316
+ # object.
317
+ def self.from_gapi gapi
318
+ new.tap { |f| f.instance_variable_set :@gapi, gapi }
319
+ end
320
+ end
321
+
322
+ ##
323
+ # # Features
324
+ #
325
+ # Represents facial landmarks or features. Left and right are defined
326
+ # from the vantage of the viewer of the image, without considering
327
+ # mirror projections typical of photos. So `face.features.eyes.left`
328
+ # typically is the person's right eye.
329
+ #
330
+ # See {Face}.
331
+ #
332
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
333
+ # images.annotate Type
334
+ #
335
+ # @example
336
+ # require "google/cloud"
337
+ #
338
+ # gcloud = Google::Cloud.new
339
+ # vision = gcloud.vision
340
+ #
341
+ # image = vision.image "path/to/face.jpg"
342
+ # face = image.face
343
+ #
344
+ # face.features.to_h.count #=> 9
345
+ # face.features.eyes.left.pupil
346
+ # #=> #<Landmark (x: 190.41544, y: 84.4557, z: -1.3682901)>
347
+ # face.features.chin.center
348
+ # #=> #<Landmark (x: 233.21977, y: 189.47475, z: 19.487228)>
349
+ #
350
+ class Features
351
+ ##
352
+ # @private The FaceAnnotation Google API Client object.
353
+ attr_accessor :gapi
354
+
355
+ ##
356
+ # @private Creates a new Features instance.
357
+ def initialize
358
+ @gapi = {}
359
+ end
360
+
361
+ ##
362
+ # The confidence of the facial landmarks detection.
363
+ #
364
+ # @return [Float] A value in the range [0,1].
365
+ #
366
+ def confidence
367
+ @gapi.landmarking_confidence
368
+ end
369
+
370
+ ##
371
+ # Returns the facial landmark for the provided type code.
372
+ #
373
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
374
+ # images.annotate Type
375
+ #
376
+ # @param [String, Symbol] landmark_type An `images.annotate` type
377
+ # code from the [Vision
378
+ # API](https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1).
379
+ #
380
+ # @return [Landmark]
381
+ #
382
+ # @example
383
+ # require "google/cloud"
384
+ #
385
+ # gcloud = Google::Cloud.new
386
+ # vision = gcloud.vision
387
+ #
388
+ # image = vision.image "path/to/face.jpg"
389
+ # face = image.face
390
+ #
391
+ # face.features["RIGHT_EAR_TRAGION"]
392
+ # #=> #<Landmark (x: 303.81198, y: 88.5782, z: 77.719193)>
393
+ #
394
+ def [] landmark_type
395
+ landmark = Array(@gapi.landmarks).detect do |l|
396
+ l.type == landmark_type
397
+ end
398
+ return nil if landmark.nil?
399
+ Landmark.from_gapi landmark
400
+ end
401
+
402
+ ##
403
+ # The landmarks of the chin.
404
+ #
405
+ # @return [Chin]
406
+ #
407
+ def chin
408
+ @chin ||= Chin.new self["CHIN_LEFT_GONION"],
409
+ self["CHIN_GNATHION"],
410
+ self["CHIN_RIGHT_GONION"]
411
+ end
412
+
413
+ ##
414
+ # The landmarks of the ears.
415
+ #
416
+ # @return [Ears]
417
+ #
418
+ def ears
419
+ @ears ||= Ears.new self["LEFT_EAR_TRAGION"],
420
+ self["RIGHT_EAR_TRAGION"]
421
+ end
422
+
423
+ ##
424
+ # The landmarks of the eyebrows.
425
+ #
426
+ # @return [Eyebrows]
427
+ #
428
+ def eyebrows
429
+ @eyebrows ||= begin
430
+ left = Eyebrow.new self["LEFT_OF_LEFT_EYEBROW"],
431
+ self["LEFT_EYEBROW_UPPER_MIDPOINT"],
432
+ self["RIGHT_OF_LEFT_EYEBROW"]
433
+ right = Eyebrow.new self["LEFT_OF_RIGHT_EYEBROW"],
434
+ self["RIGHT_EYEBROW_UPPER_MIDPOINT"],
435
+ self["RIGHT_OF_RIGHT_EYEBROW"]
436
+ Eyebrows.new left, right
437
+ end
438
+ end
439
+
440
+ ##
441
+ # The landmarks of the eyes.
442
+ #
443
+ # @return [Eyes]
444
+ #
445
+ def eyes
446
+ @eyes ||= begin
447
+ left = Eye.new self["LEFT_EYE_LEFT_CORNER"],
448
+ self["LEFT_EYE_BOTTOM_BOUNDARY"],
449
+ self["LEFT_EYE"], self["LEFT_EYE_PUPIL"],
450
+ self["LEFT_EYE_TOP_BOUNDARY"],
451
+ self["LEFT_EYE_RIGHT_CORNER"]
452
+ right = Eye.new self["RIGHT_EYE_LEFT_CORNER"],
453
+ self["RIGHT_EYE_BOTTOM_BOUNDARY"],
454
+ self["RIGHT_EYE"], self["RIGHT_EYE_PUPIL"],
455
+ self["RIGHT_EYE_TOP_BOUNDARY"],
456
+ self["RIGHT_EYE_RIGHT_CORNER"]
457
+ Eyes.new left, right
458
+ end
459
+ end
460
+
461
+ ##
462
+ # The landmark for the forehead glabella.
463
+ #
464
+ # @return [Landmark]
465
+ #
466
+ def forehead
467
+ @forehead ||= self["FOREHEAD_GLABELLA"]
468
+ end
469
+
470
+ ##
471
+ # The landmarks of the lips.
472
+ #
473
+ # @return [Lips]
474
+ #
475
+ def lips
476
+ @lips ||= Lips.new self["UPPER_LIP"], self["LOWER_LIP"]
477
+ end
478
+
479
+ ##
480
+ # The landmarks of the mouth.
481
+ #
482
+ # @return [Mouth]
483
+ #
484
+ def mouth
485
+ @mouth ||= Mouth.new self["MOUTH_LEFT"], self["MOUTH_CENTER"],
486
+ self["MOUTH_RIGHT"]
487
+ end
488
+
489
+ ##
490
+ # The landmarks of the nose.
491
+ #
492
+ # @return [Nose]
493
+ #
494
+ def nose
495
+ @nose ||= Nose.new self["NOSE_BOTTOM_LEFT"],
496
+ self["NOSE_BOTTOM_CENTER"], self["NOSE_TIP"],
497
+ self["MIDPOINT_BETWEEN_EYES"],
498
+ self["NOSE_BOTTOM_RIGHT"]
499
+ end
500
+
501
+ ##
502
+ # Deeply converts object to a hash. All keys will be symbolized.
503
+ #
504
+ # @return [Hash]
505
+ #
506
+ def to_h
507
+ { confidence: confidence, chin: chin.to_h, ears: ears.to_h,
508
+ eyebrows: eyebrows.to_h, eyes: eyes.to_h,
509
+ forehead: forehead.to_h, lips: lips.to_h, mouth: mouth.to_h,
510
+ nose: nose.to_h }
511
+ end
512
+
513
+ # @private
514
+ def to_s
515
+ # Keep console output low by not showing all sub-objects.
516
+ "(confidence, chin, ears, eyebrows, eyes, " \
517
+ "forehead, lips, mouth, nose)"
518
+ end
519
+
520
+ # @private
521
+ def inspect
522
+ "#<Features #{self}>"
523
+ end
524
+
525
+ ##
526
+ # @private New Annotation::Face::Features from a Google API Client
527
+ # object.
528
+ def self.from_gapi gapi
529
+ new.tap { |f| f.instance_variable_set :@gapi, gapi }
530
+ end
531
+
532
+ ##
533
+ # # Landmark
534
+ #
535
+ # A face-specific landmark (for example, a face feature). Landmark
536
+ # positions may fall outside the bounds of the image when the face
537
+ # is near one or more edges of the image. Therefore it is NOT
538
+ # guaranteed that `0 <= x < width` or `0 <= y < height`.
539
+ #
540
+ # See {Features} and {Face}.
541
+ #
542
+ # @example
543
+ # require "google/cloud"
544
+ #
545
+ # gcloud = Google::Cloud.new
546
+ # vision = gcloud.vision
547
+ #
548
+ # image = vision.image "path/to/face.jpg"
549
+ # face = image.face
550
+ #
551
+ # face.features.to_h.count #=> 9
552
+ # face.features.eyes.left.pupil
553
+ # #=> #<Landmark (x: 190.41544, y: 84.4557, z: -1.3682901)>
554
+ # face.features.chin.center
555
+ # #=> #<Landmark (x: 233.21977, y: 189.47475, z: 19.487228)>
556
+ #
557
+ class Landmark
558
+ ##
559
+ # @private The Landmark Google API Client object.
560
+ attr_accessor :gapi
561
+
562
+ ##
563
+ # @private Creates a new Landmark instance.
564
+ def initialize
565
+ @gapi = {}
566
+ end
567
+
568
+ ##
569
+ # The landmark type code.
570
+ #
571
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
572
+ # images.annotate Type
573
+ #
574
+ # @return [String]
575
+ #
576
+ # @example
577
+ # require "google/cloud"
578
+ #
579
+ # gcloud = Google::Cloud.new
580
+ # vision = gcloud.vision
581
+ #
582
+ # image = vision.image "path/to/face.jpg"
583
+ # face = image.face
584
+ #
585
+ # face.features.forehead.type #=> "FOREHEAD_GLABELLA"
586
+ #
587
+ def type
588
+ @gapi.type
589
+ end
590
+
591
+ ##
592
+ # The X (horizontal) coordinate.
593
+ #
594
+ # @return [Float]
595
+ #
596
+ def x
597
+ return nil unless @gapi.position
598
+ @gapi.position.x
599
+ end
600
+
601
+ ##
602
+ # The Y (vertical) coordinate.
603
+ #
604
+ # @return [Float]
605
+ #
606
+ def y
607
+ return nil unless @gapi.position
608
+ @gapi.position.y
609
+ end
610
+
611
+ ##
612
+ # The Z (depth) coordinate.
613
+ #
614
+ # @return [Float]
615
+ #
616
+ def z
617
+ return nil unless @gapi.position
618
+ @gapi.position.z
619
+ end
620
+
621
+ ##
622
+ # Returns the object's property values as an array.
623
+ #
624
+ # @return [Array]
625
+ #
626
+ def to_a
627
+ [x, y, z]
628
+ end
629
+
630
+ ##
631
+ # Converts object to a hash. All keys will be symbolized.
632
+ #
633
+ # @return [Hash]
634
+ #
635
+ def to_h
636
+ { x: x, y: y, z: z }
637
+ end
638
+
639
+ # @private
640
+ def to_s
641
+ "(x: #{x.inspect}, y: #{y.inspect}, z: #{z.inspect})"
642
+ end
643
+
644
+ # @private
645
+ def inspect
646
+ "#<Landmark #{self}>"
647
+ end
648
+
649
+ ##
650
+ # @private New Annotation::Face::Features from a Google API Client
651
+ # object.
652
+ def self.from_gapi gapi
653
+ new.tap { |f| f.instance_variable_set :@gapi, gapi }
654
+ end
655
+ end
656
+
657
+ ##
658
+ # # Chin
659
+ #
660
+ # The landmarks of the chin in the features of a face.
661
+ #
662
+ # Left and right are defined from the vantage of the viewer of the
663
+ # image, without considering mirror projections typical of photos.
664
+ # So `face.features.eyes.left` typically is the person's right eye.
665
+ #
666
+ # See {Features} and {Face}.
667
+ #
668
+ # @attr_reader [Landmark] left The chin, left gonion.
669
+ # @attr_reader [Landmark] center The chin, gnathion.
670
+ # @attr_reader [Landmark] right The chin, right gonion.
671
+ #
672
+ # @example
673
+ # require "google/cloud"
674
+ #
675
+ # gcloud = Google::Cloud.new
676
+ # vision = gcloud.vision
677
+ #
678
+ # image = vision.image "path/to/face.jpg"
679
+ # face = image.face
680
+ #
681
+ # chin = face.features.chin
682
+ #
683
+ # chin.center
684
+ # #=> #<Landmark (x: 233.21977, y: 189.47475, z: 19.487228)>
685
+ #
686
+ class Chin
687
+ attr_reader :left, :center, :right
688
+
689
+ ##
690
+ # @private Creates a new Chin instance.
691
+ def initialize left, center, right
692
+ @left = left
693
+ @center = center
694
+ @right = right
695
+ end
696
+
697
+ ##
698
+ # Returns the object's property values as an array.
699
+ #
700
+ # @return [Array]
701
+ #
702
+ def to_a
703
+ [left, center, right]
704
+ end
705
+
706
+ ##
707
+ # Deeply converts object to a hash. All keys will be symbolized.
708
+ #
709
+ # @return [Hash]
710
+ #
711
+ def to_h
712
+ { left: left.to_h, center: center.to_h, right: right.to_h }
713
+ end
714
+
715
+ # @private
716
+ def to_s
717
+ format "(left: %s, center: %s, right: %s)", left.inspect,
718
+ center.inspect, right.inspect
719
+ end
720
+
721
+ # @private
722
+ def inspect
723
+ "#<Chin #{self}>"
724
+ end
725
+ end
726
+
727
+ ##
728
+ # # Ears
729
+ #
730
+ # The landmarks for the ear tragions.
731
+ #
732
+ # Left and right are defined from the vantage of the viewer of the
733
+ # image, without considering mirror projections typical of photos.
734
+ # So `face.features.eyes.left` typically is the person's right eye.
735
+ #
736
+ # See {Features} and {Face}.
737
+ #
738
+ # @attr_reader [Landmark] left The left ear tragion.
739
+ # @attr_reader [Landmark] right The right ear tragion.
740
+ #
741
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
742
+ # images.annotate Type
743
+ #
744
+ # @example
745
+ # require "google/cloud"
746
+ #
747
+ # gcloud = Google::Cloud.new
748
+ # vision = gcloud.vision
749
+ #
750
+ # image = vision.image "path/to/face.jpg"
751
+ # face = image.face
752
+ #
753
+ # ears = face.features.ears
754
+ # ears.right
755
+ # #=> #<Landmark (x: 303.81198, y: 88.5782, z: 77.719193)>
756
+ #
757
+ class Ears
758
+ attr_reader :left, :right
759
+
760
+ ##
761
+ # @private Creates a new Ears instance.
762
+ def initialize left, right
763
+ @left = left
764
+ @right = right
765
+ end
766
+
767
+ ##
768
+ # Returns the object's property values as an array.
769
+ #
770
+ # @return [Array]
771
+ #
772
+ def to_a
773
+ [left, right]
774
+ end
775
+
776
+ ##
777
+ # Deeply converts object to a hash. All keys will be symbolized.
778
+ #
779
+ # @return [Hash]
780
+ #
781
+ def to_h
782
+ { left: left.to_h, right: right.to_h }
783
+ end
784
+
785
+ # @private
786
+ def to_s
787
+ "(left: #{left.inspect}, right: #{right.inspect})"
788
+ end
789
+
790
+ # @private
791
+ def inspect
792
+ "#<Ears #{self}>"
793
+ end
794
+ end
795
+
796
+ ##
797
+ # # Eyebrows
798
+ #
799
+ # The landmarks of the eyebrows in the features of a face.
800
+ #
801
+ # Left and right are defined from the vantage of the viewer of the
802
+ # image, without considering mirror projections typical of photos.
803
+ # So `face.features.eyes.left` typically is the person's right eye.
804
+ #
805
+ # See {Features} and {Face}.
806
+ #
807
+ # @attr_reader [Eyebrow] left The left eyebrow.
808
+ # @attr_reader [Eyebrow] right The right eyebrow.
809
+ #
810
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
811
+ # images.annotate Type
812
+ #
813
+ # @example
814
+ # require "google/cloud"
815
+ #
816
+ # gcloud = Google::Cloud.new
817
+ # vision = gcloud.vision
818
+ #
819
+ # image = vision.image "path/to/face.jpg"
820
+ # face = image.face
821
+ #
822
+ # eyebrows = face.features.eyebrows
823
+ #
824
+ # right_eyebrow = eyebrows.right
825
+ # right_eyebrow.top
826
+ # #=> #<Landmark (x: 256.3194, y: 58.222664, z: -17.299419)>
827
+ #
828
+ class Eyebrows
829
+ attr_reader :left, :right
830
+
831
+ ##
832
+ # @private Creates a new Eyebrows instance.
833
+ def initialize left, right
834
+ @left = left
835
+ @right = right
836
+ end
837
+
838
+ ##
839
+ # Returns the object's property values as an array.
840
+ #
841
+ # @return [Array]
842
+ #
843
+ def to_a
844
+ [left, right]
845
+ end
846
+
847
+ ##
848
+ # Deeply converts object to a hash. All keys will be symbolized.
849
+ #
850
+ # @return [Hash]
851
+ #
852
+ def to_h
853
+ { left: left.to_h, right: right.to_h }
854
+ end
855
+
856
+ # @private
857
+ def to_s
858
+ "(left: #{left.inspect}, right: #{right.inspect})"
859
+ end
860
+
861
+ # @private
862
+ def inspect
863
+ "#<Eyebrows #{self}>"
864
+ end
865
+ end
866
+
867
+ ##
868
+ # # Eyebrow
869
+ #
870
+ # The landmarks of an eyebrow in the features of a face.
871
+ #
872
+ # Left and right are defined from the vantage of the viewer of the
873
+ # image, without considering mirror projections typical of photos.
874
+ # So `face.features.eyes.left` typically is the person's right eye.
875
+ #
876
+ # See {Eyebrows}, {Features} and {Face}.
877
+ #
878
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
879
+ # images.annotate Type
880
+ #
881
+ # @attr_reader [Landmark] left The eyebrow, left.
882
+ # @attr_reader [Landmark] top The eyebrow, upper midpoint.
883
+ # @attr_reader [Landmark] right The eyebrow, right.
884
+ #
885
+ # @example
886
+ # require "google/cloud"
887
+ #
888
+ # gcloud = Google::Cloud.new
889
+ # vision = gcloud.vision
890
+ #
891
+ # image = vision.image "path/to/face.jpg"
892
+ # face = image.face
893
+ #
894
+ # eyebrows = face.features.eyebrows
895
+ #
896
+ # right_eyebrow = eyebrows.right
897
+ # right_eyebrow.top
898
+ # #=> #<Landmark (x: 256.3194, y: 58.222664, z: -17.299419)>
899
+ #
900
+ class Eyebrow
901
+ attr_reader :left, :top, :right
902
+
903
+ ##
904
+ # @private Creates a new Eyebrow instance.
905
+ def initialize left, top, right
906
+ @left = left
907
+ @top = top
908
+ @right = right
909
+ end
910
+
911
+ ##
912
+ # Returns the object's property values as an array.
913
+ #
914
+ # @return [Array]
915
+ #
916
+ def to_a
917
+ [left, top, right]
918
+ end
919
+
920
+ ##
921
+ # Deeply converts object to a hash. All keys will be symbolized.
922
+ #
923
+ # @return [Hash]
924
+ #
925
+ def to_h
926
+ { left: left.to_h, top: top.to_h, right: right.to_h }
927
+ end
928
+
929
+ # @private
930
+ def to_s
931
+ format "(left: %s, top: %s, right: %s)", left.inspect,
932
+ top.inspect, right.inspect
933
+ end
934
+
935
+ # @private
936
+ def inspect
937
+ "#<Eyebrow #{self}>"
938
+ end
939
+ end
940
+
941
+ ##
942
+ # # Eyes
943
+ #
944
+ # The landmarks of the eyes in the features of a face.
945
+ #
946
+ # Left and right are defined from the vantage of the viewer of the
947
+ # image, without considering mirror projections typical of photos.
948
+ # So `face.features.eyes.left` typically is the person's right eye.
949
+ #
950
+ # See {Features} and {Face}.
951
+ #
952
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
953
+ # images.annotate Type
954
+ #
955
+ # @attr_reader [Eye] left The left eye.
956
+ # @attr_reader [Eye] right The right eye.
957
+ #
958
+ # @example
959
+ # require "google/cloud"
960
+ #
961
+ # gcloud = Google::Cloud.new
962
+ # vision = gcloud.vision
963
+ #
964
+ # image = vision.image "path/to/face.jpg"
965
+ # face = image.face
966
+ #
967
+ # eyes = face.features.eyes
968
+ #
969
+ # right_eye = eyes.right
970
+ # right_eye.pupil
971
+ # #=> #<Landmark (x: 256.63464, y: 79.641411, z: -6.0731235)>
972
+ #
973
+ class Eyes
974
+ attr_reader :left, :right
975
+
976
+ ##
977
+ # @private Creates a new Eyes instance.
978
+ def initialize left, right
979
+ @left = left
980
+ @right = right
981
+ end
982
+
983
+ ##
984
+ # Returns the object's property values as an array.
985
+ #
986
+ # @return [Array]
987
+ #
988
+ def to_a
989
+ [left, right]
990
+ end
991
+
992
+ ##
993
+ # Deeply converts object to a hash. All keys will be symbolized.
994
+ #
995
+ # @return [Hash]
996
+ #
997
+ def to_h
998
+ { left: left.to_h, right: right.to_h }
999
+ end
1000
+
1001
+ # @private
1002
+ def to_s
1003
+ "(left: #{left.inspect}, right: #{right.inspect})"
1004
+ end
1005
+
1006
+ # @private
1007
+ def inspect
1008
+ "#<Eyes #{self}>"
1009
+ end
1010
+ end
1011
+
1012
+ ##
1013
+ # # Eye
1014
+ #
1015
+ # The landmarks of an eye in the features of a face.
1016
+ #
1017
+ # Left and right are defined from the vantage of the viewer of the
1018
+ # image, without considering mirror projections typical of photos.
1019
+ # So `face.features.eyes.left` typically is the person's right eye.
1020
+ #
1021
+ # See {Eyes}, {Features} and {Face}.
1022
+ #
1023
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
1024
+ # images.annotate Type
1025
+ #
1026
+ # @attr_reader [Landmark] left The eye, left corner.
1027
+ # @attr_reader [Landmark] bottom The eye, bottom boundary.
1028
+ # @attr_reader [Landmark] center The eye, center.
1029
+ # @attr_reader [Landmark] pupil The eye pupil.
1030
+ # @attr_reader [Landmark] top The eye, top boundary.
1031
+ # @attr_reader [Landmark] right The eye, right corner.
1032
+ #
1033
+ # @example
1034
+ # require "google/cloud"
1035
+ #
1036
+ # gcloud = Google::Cloud.new
1037
+ # vision = gcloud.vision
1038
+ #
1039
+ # image = vision.image "path/to/face.jpg"
1040
+ # face = image.face
1041
+ #
1042
+ # right_eye = face.features.eyes.right
1043
+ #
1044
+ # right_eye.pupil
1045
+ # #=> #<Landmark (x: 256.63464, y: 79.641411, z: -6.0731235)>
1046
+ #
1047
+ class Eye
1048
+ attr_reader :left, :bottom, :center, :pupil, :top, :right
1049
+
1050
+ ##
1051
+ # @private Creates a new Eye instance.
1052
+ def initialize left, bottom, center, pupil, top, right
1053
+ @left = left
1054
+ @bottom = bottom
1055
+ @center = center
1056
+ @pupil = pupil
1057
+ @top = top
1058
+ @right = right
1059
+ end
1060
+
1061
+ ##
1062
+ # Returns the object's property values as an array.
1063
+ #
1064
+ # @return [Array]
1065
+ #
1066
+ def to_a
1067
+ [left, top, right]
1068
+ end
1069
+
1070
+ ##
1071
+ # Deeply converts object to a hash. All keys will be symbolized.
1072
+ #
1073
+ # @return [Hash]
1074
+ #
1075
+ def to_h
1076
+ { left: left.to_h, bottom: bottom.to_h, center: center.to_h,
1077
+ pupil: pupil.to_h, top: top.to_h, right: right.to_h }
1078
+ end
1079
+
1080
+ # @private
1081
+ def to_s
1082
+ tmplt = "(left: %s, bottom: %s, center: %s, " \
1083
+ "pupil: %s, top: %s, right: %s)"
1084
+ format tmplt, left.inspect, bottom.inspect, center.inspect,
1085
+ pupil.inspect, top.inspect, right.inspect
1086
+ end
1087
+
1088
+ # @private
1089
+ def inspect
1090
+ "#<Eye #{self}>"
1091
+ end
1092
+ end
1093
+
1094
+ ##
1095
+ # # Lips
1096
+ #
1097
+ # The landmarks of the lips in the features of a face.
1098
+ #
1099
+ # See {Features} and {Face}.
1100
+ #
1101
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
1102
+ # images.annotate Type
1103
+ #
1104
+ # @attr_reader [Landmark] top The upper lip.
1105
+ # @attr_reader [Landmark] bottom The lower lip.
1106
+ #
1107
+ # @example
1108
+ # require "google/cloud"
1109
+ #
1110
+ # gcloud = Google::Cloud.new
1111
+ # vision = gcloud.vision
1112
+ #
1113
+ # image = vision.image "path/to/face.jpg"
1114
+ # face = image.face
1115
+ #
1116
+ # lips = face.features.lips
1117
+ #
1118
+ # lips.top
1119
+ # #=> #<Landmark (x: 228.54768, y: 143.2952, z: -5.6550336)>
1120
+ #
1121
+ class Lips
1122
+ attr_reader :top, :bottom
1123
+
1124
+ alias_method :upper, :top
1125
+ alias_method :lower, :bottom
1126
+
1127
+ ##
1128
+ # @private Creates a new Lips instance.
1129
+ def initialize top, bottom
1130
+ @top = top
1131
+ @bottom = bottom
1132
+ end
1133
+
1134
+ ##
1135
+ # Returns the object's property values as an array.
1136
+ #
1137
+ # @return [Array]
1138
+ #
1139
+ def to_a
1140
+ [top, bottom]
1141
+ end
1142
+
1143
+ ##
1144
+ # Deeply converts object to a hash. All keys will be symbolized.
1145
+ #
1146
+ # @return [Hash]
1147
+ #
1148
+ def to_h
1149
+ { top: top.to_h, bottom: bottom.to_h }
1150
+ end
1151
+
1152
+ # @private
1153
+ def to_s
1154
+ "(top: #{top.inspect}, bottom: #{bottom.inspect})"
1155
+ end
1156
+
1157
+ # @private
1158
+ def inspect
1159
+ "#<Lips #{self}>"
1160
+ end
1161
+ end
1162
+
1163
+ ##
1164
+ # # Mouth
1165
+ #
1166
+ # The landmarks of the mouth in the features of a face.
1167
+ #
1168
+ # Left and right are defined from the vantage of the viewer of the
1169
+ # image, without considering mirror projections typical of photos.
1170
+ # So `face.features.eyes.left` typically is the person's right eye.
1171
+ #
1172
+ # See {Features} and {Face}.
1173
+ #
1174
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
1175
+ # images.annotate Type
1176
+ #
1177
+ # @attr_reader [Landmark] left The mouth, left.
1178
+ # @attr_reader [Landmark] center The mouth, center.
1179
+ # @attr_reader [Landmark] right TThe mouth, right.
1180
+ #
1181
+ # @example
1182
+ # require "google/cloud"
1183
+ #
1184
+ # gcloud = Google::Cloud.new
1185
+ # vision = gcloud.vision
1186
+ #
1187
+ # image = vision.image "path/to/face.jpg"
1188
+ # face = image.face
1189
+ #
1190
+ # mouth = face.features.mouth
1191
+ #
1192
+ # mouth.center
1193
+ # #=> #<Landmark (x: 228.53499, y: 150.29066, z: 1.1069832)>
1194
+ #
1195
+ class Mouth
1196
+ attr_reader :left, :center, :right
1197
+
1198
+ ##
1199
+ # @private Creates a new Mouth instance.
1200
+ def initialize left, center, right
1201
+ @left = left
1202
+ @center = center
1203
+ @right = right
1204
+ end
1205
+
1206
+ ##
1207
+ # Returns the object's property values as an array.
1208
+ #
1209
+ # @return [Array]
1210
+ #
1211
+ def to_a
1212
+ [left, center, right]
1213
+ end
1214
+
1215
+ ##
1216
+ # Deeply converts object to a hash. All keys will be symbolized.
1217
+ #
1218
+ # @return [Hash]
1219
+ #
1220
+ def to_h
1221
+ { left: left.to_h, center: center.to_h, right: right.to_h }
1222
+ end
1223
+
1224
+ # @private
1225
+ def to_s
1226
+ format "(left: %s, center: %s, right: %s)", left.inspect,
1227
+ center.inspect, right.inspect
1228
+ end
1229
+
1230
+ # @private
1231
+ def inspect
1232
+ "#<Mouth #{self}>"
1233
+ end
1234
+ end
1235
+
1236
+ ##
1237
+ # # Nose
1238
+ #
1239
+ # The landmarks of the nose in the features of a face.
1240
+ #
1241
+ # Left and right are defined from the vantage of the viewer of the
1242
+ # image, without considering mirror projections typical of photos.
1243
+ # So `face.features.eyes.left` typically is the person's right eye.
1244
+ #
1245
+ # See {Features} and {Face}.
1246
+ #
1247
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Type_1
1248
+ # images.annotate Type
1249
+ #
1250
+ # @attr_reader [Landmark] left The nose, bottom left.
1251
+ # @attr_reader [Landmark] bottom The nose, bottom center.
1252
+ # @attr_reader [Landmark] tip The nose tip.
1253
+ # @attr_reader [Landmark] top The midpoint between the eyes.
1254
+ # @attr_reader [Landmark] right The nose, bottom right.
1255
+ #
1256
+ # @example
1257
+ # require "google/cloud"
1258
+ #
1259
+ # gcloud = Google::Cloud.new
1260
+ # vision = gcloud.vision
1261
+ #
1262
+ # image = vision.image "path/to/face.jpg"
1263
+ # face = image.face
1264
+ #
1265
+ # nose = face.features.nose
1266
+ #
1267
+ # nose.tip
1268
+ # #=> #<Landmark (x: 225.23511, y: 122.47372, z: -25.817825)>
1269
+ #
1270
+ class Nose
1271
+ attr_reader :left, :bottom, :tip, :top, :right
1272
+
1273
+ ##
1274
+ # @private Creates a new Nose instance.
1275
+ def initialize left, bottom, tip, top, right
1276
+ @left = left
1277
+ @bottom = bottom
1278
+ @tip = tip
1279
+ @top = top
1280
+ @right = right
1281
+ end
1282
+
1283
+ ##
1284
+ # Returns the object's property values as an array.
1285
+ #
1286
+ # @return [Array]
1287
+ #
1288
+ def to_a
1289
+ [left, bottom, tip, top, right]
1290
+ end
1291
+
1292
+ ##
1293
+ # Deeply converts object to a hash. All keys will be symbolized.
1294
+ #
1295
+ # @return [Hash]
1296
+ #
1297
+ def to_h
1298
+ { left: left.to_h, bottom: bottom.to_h, tip: tip.to_h,
1299
+ top: top.to_h, right: right.to_h }
1300
+ end
1301
+
1302
+ # @private
1303
+ def to_s
1304
+ tmplt = "(left: %s, bottom: %s, tip: %s, " \
1305
+ "top: %s, right: %s)"
1306
+ format tmplt, left.inspect, bottom.inspect, tip.inspect,
1307
+ top.inspect, right.inspect
1308
+ end
1309
+
1310
+ # @private
1311
+ def inspect
1312
+ "#<Nose #{self}>"
1313
+ end
1314
+ end
1315
+ end
1316
+
1317
+ ##
1318
+ # # Likelihood
1319
+ #
1320
+ # A bucketized representation of likelihood of various separate facial
1321
+ # characteristics, meant to give highly stable results across model
1322
+ # upgrades.
1323
+ #
1324
+ # See {Face}.
1325
+ #
1326
+ # @see https://cloud.google.com/vision/reference/rest/v1/images/annotate#Likelihood
1327
+ # images.annotate Likelihood
1328
+ #
1329
+ # @example
1330
+ # require "google/cloud"
1331
+ #
1332
+ # gcloud = Google::Cloud.new
1333
+ # vision = gcloud.vision
1334
+ #
1335
+ # image = vision.image "path/to/face.jpg"
1336
+ # face = image.face
1337
+ #
1338
+ # face.likelihood.to_h.count #=> 7
1339
+ # face.likelihood.sorrow? #=> false
1340
+ # face.likelihood.sorrow #=> "VERY_UNLIKELY"
1341
+ #
1342
+ class Likelihood
1343
+ POSITIVE_RATINGS = %w(POSSIBLE LIKELY VERY_LIKELY)
1344
+
1345
+ ##
1346
+ # @private The FaceAnnotation Google API Client object.
1347
+ attr_accessor :gapi
1348
+
1349
+ ##
1350
+ # @private Creates a new Likelihood instance.
1351
+ def initialize
1352
+ @gapi = {}
1353
+ end
1354
+
1355
+ ##
1356
+ # Joy likelihood rating. Possible values are `VERY_UNLIKELY`,
1357
+ # `UNLIKELY`, `POSSIBLE`, `LIKELY`, and `VERY_LIKELY`.
1358
+ def joy
1359
+ @gapi.joy_likelihood
1360
+ end
1361
+
1362
+ ##
1363
+ # Joy likelihood. Returns `true` if {#joy} is `POSSIBLE`, `LIKELY`,
1364
+ # or `VERY_LIKELY`.
1365
+ #
1366
+ # @return [Boolean]
1367
+ #
1368
+ def joy?
1369
+ POSITIVE_RATINGS.include? joy
1370
+ end
1371
+
1372
+ ##
1373
+ # Sorrow likelihood rating. Possible values are `VERY_UNLIKELY`,
1374
+ # `UNLIKELY`, `POSSIBLE`, `LIKELY`, and `VERY_LIKELY`.
1375
+ def sorrow
1376
+ @gapi.sorrow_likelihood
1377
+ end
1378
+
1379
+ ##
1380
+ # Sorrow likelihood. Returns `true` if {#sorrow} is `POSSIBLE`,
1381
+ # `LIKELY`, or `VERY_LIKELY`.
1382
+ #
1383
+ # @return [Boolean]
1384
+ #
1385
+ def sorrow?
1386
+ POSITIVE_RATINGS.include? sorrow
1387
+ end
1388
+
1389
+ ##
1390
+ # Joy likelihood rating. Possible values are `VERY_UNLIKELY`,
1391
+ # `UNLIKELY`, `POSSIBLE`, `LIKELY`, and `VERY_LIKELY`.
1392
+ def anger
1393
+ @gapi.anger_likelihood
1394
+ end
1395
+
1396
+ ##
1397
+ # Anger likelihood. Returns `true` if {#anger} is `POSSIBLE`,
1398
+ # `LIKELY`, or `VERY_LIKELY`.
1399
+ #
1400
+ # @return [Boolean]
1401
+ #
1402
+ def anger?
1403
+ POSITIVE_RATINGS.include? anger
1404
+ end
1405
+
1406
+ ##
1407
+ # Surprise likelihood rating. Possible values are `VERY_UNLIKELY`,
1408
+ # `UNLIKELY`, `POSSIBLE`, `LIKELY`, and `VERY_LIKELY`.
1409
+ def surprise
1410
+ @gapi.surprise_likelihood
1411
+ end
1412
+
1413
+ ##
1414
+ # Surprise likelihood. Returns `true` if {#surprise} is `POSSIBLE`,
1415
+ # `LIKELY`, or `VERY_LIKELY`.
1416
+ #
1417
+ # @return [Boolean]
1418
+ #
1419
+ def surprise?
1420
+ POSITIVE_RATINGS.include? surprise
1421
+ end
1422
+
1423
+ ##
1424
+ # Under Exposed likelihood rating. Possible values are
1425
+ # `VERY_UNLIKELY`, `UNLIKELY`, `POSSIBLE`, `LIKELY`, and
1426
+ # `VERY_LIKELY`.
1427
+ def under_exposed
1428
+ @gapi.under_exposed_likelihood
1429
+ end
1430
+
1431
+ ##
1432
+ # Under Exposed likelihood. Returns `true` if {#under_exposed} is
1433
+ # `POSSIBLE`, `LIKELY`, or `VERY_LIKELY`.
1434
+ #
1435
+ # @return [Boolean]
1436
+ #
1437
+ def under_exposed?
1438
+ POSITIVE_RATINGS.include? under_exposed
1439
+ end
1440
+
1441
+ ##
1442
+ # Blurred likelihood rating. Possible values are `VERY_UNLIKELY`,
1443
+ # `UNLIKELY`, `POSSIBLE`, `LIKELY`, and `VERY_LIKELY`.
1444
+ def blurred
1445
+ @gapi.blurred_likelihood
1446
+ end
1447
+
1448
+ ##
1449
+ # Blurred likelihood. Returns `true` if {#blurred} is `POSSIBLE`,
1450
+ # `LIKELY`, or `VERY_LIKELY`.
1451
+ #
1452
+ # @return [Boolean]
1453
+ #
1454
+ def blurred?
1455
+ POSITIVE_RATINGS.include? blurred
1456
+ end
1457
+
1458
+ ##
1459
+ # Headwear likelihood rating. Possible values are `VERY_UNLIKELY`,
1460
+ # `UNLIKELY`, `POSSIBLE`, `LIKELY`, and `VERY_LIKELY`.
1461
+ def headwear
1462
+ @gapi.headwear_likelihood
1463
+ end
1464
+
1465
+ ##
1466
+ # Headwear likelihood. Returns `true` if {#headwear} is `POSSIBLE`,
1467
+ # `LIKELY`, or `VERY_LIKELY`.
1468
+ #
1469
+ # @return [Boolean]
1470
+ #
1471
+ def headwear?
1472
+ POSITIVE_RATINGS.include? headwear
1473
+ end
1474
+
1475
+ ##
1476
+ # Converts object to a hash. All keys will be symbolized.
1477
+ #
1478
+ # @return [Hash]
1479
+ #
1480
+ def to_h
1481
+ { joy: joy?, sorrow: sorrow?, anger: anger?, surprise: surprise?,
1482
+ under_exposed: under_exposed?, blurred: blurred?,
1483
+ headwear: headwear? }
1484
+ end
1485
+
1486
+ # @private
1487
+ def to_s
1488
+ tmplt = "(joy?: %s, sorrow?: %s, anger?: %s, " \
1489
+ "surprise?: %s, under_exposed?: %s, blurred?: %s, " \
1490
+ "headwear: %s)"
1491
+ format tmplt, joy?.inspect, sorrow?.inspect, anger?.inspect,
1492
+ surprise?.inspect, under_exposed?.inspect,
1493
+ blurred?.inspect, headwear?.inspect
1494
+ end
1495
+
1496
+ # @private
1497
+ def inspect
1498
+ "#<Likelihood #{self}>"
1499
+ end
1500
+
1501
+ ##
1502
+ # @private New Annotation::Face::Likelihood from a Google API Client
1503
+ # object.
1504
+ def self.from_gapi gapi
1505
+ new.tap { |f| f.instance_variable_set :@gapi, gapi }
1506
+ end
1507
+ end
1508
+ end
1509
+ end
1510
+ end
1511
+ end
1512
+ end