google-cloud-vision 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/google-cloud-vision.rb +120 -0
- data/lib/google/cloud/vision.rb +477 -0
- data/lib/google/cloud/vision/annotate.rb +235 -0
- data/lib/google/cloud/vision/annotation.rb +449 -0
- data/lib/google/cloud/vision/annotation/entity.rb +228 -0
- data/lib/google/cloud/vision/annotation/face.rb +1512 -0
- data/lib/google/cloud/vision/annotation/properties.rb +222 -0
- data/lib/google/cloud/vision/annotation/safe_search.rb +154 -0
- data/lib/google/cloud/vision/annotation/text.rb +222 -0
- data/lib/google/cloud/vision/annotation/vertex.rb +92 -0
- data/lib/google/cloud/vision/credentials.rb +31 -0
- data/lib/google/cloud/vision/image.rb +578 -0
- data/lib/google/cloud/vision/location.rb +99 -0
- data/lib/google/cloud/vision/project.rb +284 -0
- data/lib/google/cloud/vision/service.rb +75 -0
- data/lib/google/cloud/vision/version.rb +22 -0
- metadata +202 -0
@@ -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
|