gcloud 0.9.0 → 0.10.0

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