google-cloud-vision 0.20.0

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