@@ -14,24 +14,37 @@ import com.aldebaran.qi.Future
14
14
import com.aldebaran.qi.sdk.QiContext
15
15
import com.aldebaran.qi.sdk.QiSDK
16
16
import com.aldebaran.qi.sdk.RobotLifecycleCallbacks
17
+ import com.aldebaran.qi.sdk.`object`.conversation.*
18
+ import com.aldebaran.qi.sdk.builder.QiChatbotBuilder
19
+ import com.aldebaran.qi.sdk.builder.TopicBuilder
20
+ import com.aldebaran.qi.sdk.conversationalcontentlibrary.askrobotname.AskRobotNameConversationalContent
21
+ import com.aldebaran.qi.sdk.conversationalcontentlibrary.base.AbstractConversationalContent
22
+ import com.aldebaran.qi.sdk.conversationalcontentlibrary.base.ConversationalContentChatBuilder
23
+ import com.aldebaran.qi.sdk.conversationalcontentlibrary.datetime.DateTimeConversationalContent
24
+ import com.aldebaran.qi.sdk.conversationalcontentlibrary.farewell.FarewellConversationalContent
25
+ import com.aldebaran.qi.sdk.conversationalcontentlibrary.greetings.GreetingsConversationalContent
26
+ import com.aldebaran.qi.sdk.conversationalcontentlibrary.robotabilities.RobotAbilitiesConversationalContent
27
+ import com.aldebaran.qi.sdk.conversationalcontentlibrary.volumecontrol.VolumeControlConversationalContent
17
28
import com.aldebaran.qi.sdk.design.activity.RobotActivity
18
- import com.aldebaran.qi.sdk.design.activity.conversationstatus.SpeechBarDisplayStrategy
19
29
import com.softbankrobotics.facemaskdetection.FaceMaskDetection
20
30
import com.softbankrobotics.facemaskdetection.capturer.BottomCameraCapturer
21
- import com.softbankrobotics.facemaskdetection.detector.AizooFaceMaskDetector
22
31
import com.softbankrobotics.facemaskdetection.capturer.TopCameraCapturer
32
+ import com.softbankrobotics.facemaskdetection.detector.AizooFaceMaskDetector
23
33
import com.softbankrobotics.facemaskdetection.detector.FaceMaskDetector
24
34
import com.softbankrobotics.facemaskdetection.utils.OpenCVUtils
25
35
import com.softbankrobotics.facemaskdetection.utils.TAG
26
36
import kotlinx.android.synthetic.main.activity_main.*
27
37
28
38
class MainActivity : RobotActivity (), RobotLifecycleCallbacks {
29
39
30
- private val useTopCamera = false
40
+ private var qiChatbot: QiChatbot ? = null
41
+ private var chatFuture: Future <Void >? = null
42
+ private val useTopCamera = true
43
+ private var shouldBeRecognizing = false
31
44
32
45
override fun onCreate (savedInstanceState : Bundle ? ) {
33
46
super .onCreate(savedInstanceState)
34
- setSpeechBarDisplayStrategy(SpeechBarDisplayStrategy .IMMERSIVE )
47
+ // setSpeechBarDisplayStrategy(SpeechBarDisplayStrategy.IMMERSIVE)
35
48
setContentView(R .layout.activity_main)
36
49
clearFaces()
37
50
if (useTopCamera || cameraPermissionAlreadyGranted()) {
@@ -45,6 +58,8 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
45
58
46
59
public override fun onPause () {
47
60
super .onPause()
61
+ Log .i(TAG , " onPause" )
62
+ shouldBeRecognizing = false
48
63
detectionFuture?.requestCancellation()
49
64
detectionFuture = null
50
65
}
@@ -53,6 +68,7 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
53
68
super .onResume()
54
69
OpenCVUtils .loadOpenCV(this )
55
70
clearFaces()
71
+ shouldBeRecognizing = true
56
72
startDetecting()
57
73
}
58
74
@@ -73,14 +89,17 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
73
89
74
90
private fun requestPermissionForCamera () {
75
91
if (ActivityCompat .shouldShowRequestPermissionRationale(this , Manifest .permission.CAMERA )) {
76
- Toast .makeText(this ,
92
+ Toast .makeText(
93
+ this ,
77
94
R .string.permissions_needed,
78
- Toast .LENGTH_LONG ).show()
95
+ Toast .LENGTH_LONG
96
+ ).show()
79
97
} else {
80
98
ActivityCompat .requestPermissions(
81
99
this ,
82
100
arrayOf(Manifest .permission.CAMERA ),
83
- CAMERA_PERMISSION_REQUEST_CODE )
101
+ CAMERA_PERMISSION_REQUEST_CODE
102
+ )
84
103
}
85
104
}
86
105
@@ -89,9 +108,11 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
89
108
return resultCamera == PackageManager .PERMISSION_GRANTED
90
109
}
91
110
92
- override fun onRequestPermissionsResult (requestCode : Int ,
93
- permissions : Array <String >,
94
- grantResults : IntArray ) {
111
+ override fun onRequestPermissionsResult (
112
+ requestCode : Int ,
113
+ permissions : Array <String >,
114
+ grantResults : IntArray
115
+ ) {
95
116
if (requestCode == CAMERA_PERMISSION_REQUEST_CODE ) {
96
117
var cameraPermissionGranted = true
97
118
@@ -102,9 +123,11 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
102
123
if (cameraPermissionGranted) {
103
124
QiSDK .register(this , this )
104
125
} else {
105
- Toast .makeText(this ,
126
+ Toast .makeText(
127
+ this ,
106
128
R .string.permissions_needed,
107
- Toast .LENGTH_LONG ).show()
129
+ Toast .LENGTH_LONG
130
+ ).show()
108
131
}
109
132
}
110
133
}
@@ -116,17 +139,18 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
116
139
inner class FacesForDisplay (rawFaces : List <FaceMaskDetector .DetectedFace >) {
117
140
// Choose the "main" focused faced, which is either the biggest or, when there are a lot of
118
141
// people, the one in the middle.
119
- val mainFace : FaceMaskDetector .DetectedFace ? = when {
142
+ val mainFace: FaceMaskDetector .DetectedFace ? = when {
120
143
rawFaces.size >= 5 -> rawFaces[2 ]
121
144
rawFaces.size == 4 -> rawFaces.subList(1 , 3 ).maxBy { it.size() }
122
145
else -> rawFaces.maxBy { it.size() }
123
146
}
124
147
private val mainFaceIndex = rawFaces.indexOf(mainFace)
148
+
125
149
// Set the other faces relatively
126
- val leftFarFace : FaceMaskDetector .DetectedFace ? = rawFaces.getOrNull(mainFaceIndex - 2 )
127
- val leftNearFace : FaceMaskDetector .DetectedFace ? = rawFaces.getOrNull(mainFaceIndex - 1 )
128
- val rightNearFace : FaceMaskDetector .DetectedFace ? = rawFaces.getOrNull(mainFaceIndex + 1 )
129
- val rightFarFace : FaceMaskDetector .DetectedFace ? = rawFaces.getOrNull(mainFaceIndex + 2 )
150
+ val leftFarFace: FaceMaskDetector .DetectedFace ? = rawFaces.getOrNull(mainFaceIndex - 2 )
151
+ val leftNearFace: FaceMaskDetector .DetectedFace ? = rawFaces.getOrNull(mainFaceIndex - 1 )
152
+ val rightNearFace: FaceMaskDetector .DetectedFace ? = rawFaces.getOrNull(mainFaceIndex + 1 )
153
+ val rightFarFace: FaceMaskDetector .DetectedFace ? = rawFaces.getOrNull(mainFaceIndex + 2 )
130
154
}
131
155
132
156
private fun setFaces (faces : List <FaceMaskDetector .DetectedFace >) {
@@ -150,7 +174,11 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
150
174
}
151
175
}
152
176
153
- private fun setFace (card : View , face : FaceMaskDetector .DetectedFace ? , hideIfEmpty : Boolean = true) {
177
+ private fun setFace (
178
+ card : View ,
179
+ face : FaceMaskDetector .DetectedFace ? ,
180
+ hideIfEmpty : Boolean = true
181
+ ) {
154
182
if (hideIfEmpty && face == null ) {
155
183
card.visibility = View .INVISIBLE
156
184
} else {
@@ -167,7 +195,8 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
167
195
resources.getColor(R .color.colorNoMaskDetected, null )
168
196
}
169
197
circle.setColorFilter(color)
170
- label.text = resources.getString(if (face.hasMask) R .string.mask else R .string.no_mask)
198
+ label.text =
199
+ resources.getString(if (face.hasMask) R .string.mask else R .string.no_mask)
171
200
} else {
172
201
photo.visibility = View .INVISIBLE
173
202
circle.setColorFilter(resources.getColor(R .color.colorNobody, null ))
@@ -176,35 +205,189 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
176
205
}
177
206
}
178
207
208
+
209
+ /* *********************
210
+ * Greetings
211
+ **********************/
212
+
213
+ var engaged = false
214
+ var sawNobodyCount = 0
215
+
216
+ private fun jumpToBookmark (bookmark : Bookmark ? ) {
217
+ bookmark?.let {
218
+ qiChatbot?.async()?.goToBookmark(
219
+ it,
220
+ AutonomousReactionImportance .HIGH ,
221
+ AutonomousReactionValidity .DELAYABLE
222
+ )
223
+ }
224
+ }
225
+
226
+ var lastSawWithoutMask = false
227
+ var annoyance = 0
228
+
229
+ var lastMentionedPeopleNum = 0
230
+ var worthMentioningPeopleCounter = 0
231
+
232
+ private fun updateState (seesWithMask : Boolean , seesWithoutMask : Boolean , numPeople : Int ) {
233
+ val seesSomebody = seesWithMask || seesWithoutMask
234
+ Log .w(
235
+ TAG ,
236
+ " DBG updating state wMask=${seesWithMask} noMask=${seesWithoutMask} engaged=${engaged} "
237
+ )
238
+
239
+ if (engaged) {
240
+ // Update status when already engaged
241
+ if (seesSomebody) {
242
+ sawNobodyCount = 0 // Stay engaged
243
+ // See if they put on a mask
244
+ var saidSomething = false
245
+
246
+ // See if it's worth mentioning people putting on masks or taking them off
247
+ if (seesWithoutMask == lastSawWithoutMask) {
248
+ annoyance = 0
249
+ } else {
250
+ // Something changed !
251
+ annoyance + = 1
252
+ if (annoyance >= 2 ) {
253
+ annoyance = 0
254
+ lastSawWithoutMask = seesWithoutMask
255
+ saidSomething = true
256
+ if (seesWithoutMask) {
257
+ if (seesWithMask) {
258
+ jumpToBookmark(newWithoutMaskBookmark)
259
+ } else {
260
+ jumpToBookmark(tookOffMaskBookmark)
261
+ }
262
+ } else {
263
+ jumpToBookmark(putOnMaskBookmark)
264
+ }
265
+ lastMentionedPeopleNum = numPeople
266
+ worthMentioningPeopleCounter = 0
267
+ }
268
+ }
269
+
270
+ // See if it's worth mentioning a lot of people
271
+ if (numPeople > lastMentionedPeopleNum) {
272
+ if (worthMentioningPeopleCounter > 2 ) {
273
+ lastMentionedPeopleNum = numPeople
274
+ worthMentioningPeopleCounter = 0
275
+ jumpToBookmark(manyPeopleBookmark)
276
+ } else {
277
+ worthMentioningPeopleCounter + = 1
278
+ }
279
+ } else {
280
+ lastMentionedPeopleNum = numPeople
281
+ worthMentioningPeopleCounter = 0
282
+ }
283
+
284
+ } else {
285
+ sawNobodyCount + = 1
286
+ if (sawNobodyCount > 2 ) {
287
+ engaged = false
288
+ // chat?.removeAllOnStartedListeners()
289
+ // chatFuture?.cancel(false)
290
+ chatFuture = null
291
+ lastSawWithoutMask = false
292
+ annoyance = 0
293
+ }
294
+ }
295
+ } else if (seesSomebody) {
296
+ engaged = true
297
+ chat?.let { chat ->
298
+ if (seesWithoutMask) {
299
+ jumpToBookmark(noMaskBookmark)
300
+ } else {
301
+ jumpToBookmark(maskBookmark)
302
+ }
303
+ lastSawWithoutMask = seesWithoutMask
304
+ annoyance = 0
305
+ }
306
+ }
307
+ }
308
+
309
+
179
310
/* *********************
180
311
* Robot Lifecycle
181
312
**********************/
182
313
314
+
183
315
private fun startDetecting () {
184
316
detectionFuture = detection?.start { faces ->
185
- // Filter and sort the faces so that they're left to right, with no uncertain or
186
- // non-unique results
317
+ // Filter and sort the faces so that they're left to right and certain enough
187
318
val sortedFaces = faces
188
- .filter { (it.confidence > 0.5 )}
319
+ .filter { (it.confidence > 0.5 ) }
189
320
.sortedBy { - it.bb.left }
190
- Log .v (TAG , " Filtered faces ${faces.size} , -> ${sortedFaces.size} " )
321
+ Log .i (TAG , " Filtered faces ${faces.size} , -> ${sortedFaces.size} " )
191
322
setFaces(sortedFaces)
323
+ // Now update the logic
324
+ val seesWithMask = sortedFaces.any { it.hasMask }
325
+ val seesWithoutMask = sortedFaces.any { ! it.hasMask }
326
+ val numPeople = sortedFaces.size
327
+ updateState(seesWithMask, seesWithoutMask, numPeople)
192
328
}
193
329
detectionFuture?.thenConsume {
194
- Log .i(TAG , " Detection future has finished: success=${it.isSuccess} , cancelled=${it.isCancelled} " )
330
+ Log .i(
331
+ TAG ,
332
+ " Detection future has finished: success=${it.isSuccess} , cancelled=${it.isCancelled} "
333
+ )
334
+ if (shouldBeRecognizing) {
335
+ Log .w(TAG , " Stopped, but it shouldn't have - starting it again" )
336
+ startDetecting()
337
+ }
195
338
}
196
339
}
197
340
341
+ var chat: Chat ? = null
342
+ var maskBookmark: Bookmark ? = null
343
+ var noMaskBookmark: Bookmark ? = null
344
+ var tookOffMaskBookmark: Bookmark ? = null
345
+ var putOnMaskBookmark: Bookmark ? = null
346
+ var newWithoutMaskBookmark: Bookmark ? = null
347
+ var manyPeopleBookmark: Bookmark ? = null
348
+
198
349
override fun onRobotFocusGained (qiContext : QiContext ) {
199
350
Log .i(TAG , " onRobotFocusGained" )
351
+ if (chat == null ) {
352
+ val topic = TopicBuilder .with (qiContext).withResource(R .raw.chat).build()
353
+ val qiChatbot = QiChatbotBuilder .with (qiContext).withTopic(topic).build()
354
+ this .qiChatbot = qiChatbot
355
+ maskBookmark = topic.bookmarks[" GREETING_MASK" ]
356
+ noMaskBookmark = topic.bookmarks[" GREETING_NO_MASK" ]
357
+ tookOffMaskBookmark = topic.bookmarks[" TOOK_OFF_MASK" ]
358
+ putOnMaskBookmark = topic.bookmarks[" PUT_ON_MASK" ]
359
+ newWithoutMaskBookmark = topic.bookmarks[" NEW_WITHOUT_MASK" ]
360
+ manyPeopleBookmark = topic.bookmarks[" MANY_PEOPLE" ]
361
+
362
+ val conversationalContents: List <AbstractConversationalContent > = listOf (
363
+ GreetingsConversationalContent (),
364
+ FarewellConversationalContent (),
365
+ AskRobotNameConversationalContent (),
366
+ DateTimeConversationalContent (),
367
+ RobotAbilitiesConversationalContent (),
368
+ VolumeControlConversationalContent ()
369
+ )
370
+
371
+ chat = ConversationalContentChatBuilder .with (qiContext)
372
+ .withChatbot(qiChatbot)
373
+ .withConversationalContents(conversationalContents)
374
+ .build()
375
+ chat?.listeningBodyLanguage = BodyLanguageOption .DISABLED
376
+ }
377
+
378
+ Log .i(TAG , " Initialised chat" )
379
+
200
380
val capturer = if (useTopCamera) {
201
381
TopCameraCapturer (qiContext)
202
382
} else {
203
383
BottomCameraCapturer (this , this )
204
384
}
205
385
val detector = AizooFaceMaskDetector (this )
206
386
detection = FaceMaskDetection (detector, capturer)
387
+ shouldBeRecognizing = true
207
388
startDetecting()
389
+ Log .i(TAG , " Starting chat" )
390
+ chatFuture = chat?.async()?.run ()
208
391
}
209
392
210
393
override fun onRobotFocusLost () {
0 commit comments