Skip to content

Commit a84e1e3

Browse files
author
Joss STUART
committed
Add English and French dialog to the sample application
1 parent c32a8fb commit a84e1e3

File tree

10 files changed

+454
-53
lines changed

10 files changed

+454
-53
lines changed

FaceMaskDetection/src/main/java/com/softbankrobotics/facemaskdetection/FaceMaskDetection.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class FaceMaskDetection(private val detector: FaceMaskDetector, private val came
2828

2929
private val detectionScope = CoroutineScope(
3030
ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS,
31-
ArrayBlockingQueue(1), ThreadPoolExecutor.DiscardOldestPolicy()).asCoroutineDispatcher())
31+
ArrayBlockingQueue(1), ThreadPoolExecutor.DiscardOldestPolicy()).asCoroutineDispatcher())
3232

3333
sealed class Message {
3434
class FaceMaskDetect(

app/build.gradle

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ android {
77
buildToolsVersion "29.0.3"
88

99
defaultConfig {
10-
applicationId "com.softbankrobotics.peppermaskdetection"
10+
applicationId "com.softbankrobotics.dx.peppermaskdetection"
1111
minSdkVersion 23
1212
targetSdkVersion 29
13-
versionCode 6
14-
versionName "1.3.2"
13+
versionCode 9
14+
versionName "2.3"
1515

1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
}
@@ -61,4 +61,12 @@ dependencies {
6161
// OpenCV
6262
implementation project(":OpenCV")
6363
implementation project(":FaceMaskDetection")
64+
65+
implementation 'com.aldebaran:qisdk-conversationalcontent-greetings:0.19.1-experimental-05'
66+
implementation 'com.aldebaran:qisdk-conversationalcontent-robotabilities:0.19.1-experimental-05'
67+
implementation 'com.aldebaran:qisdk-conversationalcontent-volumecontrol:0.19.1-experimental-05'
68+
implementation 'com.aldebaran:qisdk-conversationalcontent-farewell:0.19.1-experimental-05'
69+
implementation 'com.aldebaran:qisdk-conversationalcontent-datetime:0.19.1-experimental-05'
70+
implementation 'com.aldebaran:qisdk-conversationalcontent-askrobotname:0.19.1-experimental-05'
71+
implementation 'com.aldebaran:qisdk-conversationalcontent-conversationbasics:0.19.1-experimental-05'
6472
}

app/src/main/java/com/softbankrobotics/peppermaskdetection/MainActivity.kt

Lines changed: 207 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,37 @@ import com.aldebaran.qi.Future
1414
import com.aldebaran.qi.sdk.QiContext
1515
import com.aldebaran.qi.sdk.QiSDK
1616
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
1728
import com.aldebaran.qi.sdk.design.activity.RobotActivity
18-
import com.aldebaran.qi.sdk.design.activity.conversationstatus.SpeechBarDisplayStrategy
1929
import com.softbankrobotics.facemaskdetection.FaceMaskDetection
2030
import com.softbankrobotics.facemaskdetection.capturer.BottomCameraCapturer
21-
import com.softbankrobotics.facemaskdetection.detector.AizooFaceMaskDetector
2231
import com.softbankrobotics.facemaskdetection.capturer.TopCameraCapturer
32+
import com.softbankrobotics.facemaskdetection.detector.AizooFaceMaskDetector
2333
import com.softbankrobotics.facemaskdetection.detector.FaceMaskDetector
2434
import com.softbankrobotics.facemaskdetection.utils.OpenCVUtils
2535
import com.softbankrobotics.facemaskdetection.utils.TAG
2636
import kotlinx.android.synthetic.main.activity_main.*
2737

2838
class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
2939

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
3144

3245
override fun onCreate(savedInstanceState: Bundle?) {
3346
super.onCreate(savedInstanceState)
34-
setSpeechBarDisplayStrategy(SpeechBarDisplayStrategy.IMMERSIVE)
47+
//setSpeechBarDisplayStrategy(SpeechBarDisplayStrategy.IMMERSIVE)
3548
setContentView(R.layout.activity_main)
3649
clearFaces()
3750
if (useTopCamera || cameraPermissionAlreadyGranted()) {
@@ -45,6 +58,8 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
4558

4659
public override fun onPause() {
4760
super.onPause()
61+
Log.i(TAG, "onPause")
62+
shouldBeRecognizing = false
4863
detectionFuture?.requestCancellation()
4964
detectionFuture = null
5065
}
@@ -53,6 +68,7 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
5368
super.onResume()
5469
OpenCVUtils.loadOpenCV(this)
5570
clearFaces()
71+
shouldBeRecognizing = true
5672
startDetecting()
5773
}
5874

@@ -73,14 +89,17 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
7389

7490
private fun requestPermissionForCamera() {
7591
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
76-
Toast.makeText(this,
92+
Toast.makeText(
93+
this,
7794
R.string.permissions_needed,
78-
Toast.LENGTH_LONG).show()
95+
Toast.LENGTH_LONG
96+
).show()
7997
} else {
8098
ActivityCompat.requestPermissions(
8199
this,
82100
arrayOf(Manifest.permission.CAMERA),
83-
CAMERA_PERMISSION_REQUEST_CODE)
101+
CAMERA_PERMISSION_REQUEST_CODE
102+
)
84103
}
85104
}
86105

@@ -89,9 +108,11 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
89108
return resultCamera == PackageManager.PERMISSION_GRANTED
90109
}
91110

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+
) {
95116
if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
96117
var cameraPermissionGranted = true
97118

@@ -102,9 +123,11 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
102123
if (cameraPermissionGranted) {
103124
QiSDK.register(this, this)
104125
} else {
105-
Toast.makeText(this,
126+
Toast.makeText(
127+
this,
106128
R.string.permissions_needed,
107-
Toast.LENGTH_LONG).show()
129+
Toast.LENGTH_LONG
130+
).show()
108131
}
109132
}
110133
}
@@ -116,17 +139,18 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
116139
inner class FacesForDisplay(rawFaces: List<FaceMaskDetector.DetectedFace>) {
117140
// Choose the "main" focused faced, which is either the biggest or, when there are a lot of
118141
// people, the one in the middle.
119-
val mainFace : FaceMaskDetector.DetectedFace? = when {
142+
val mainFace: FaceMaskDetector.DetectedFace? = when {
120143
rawFaces.size >= 5 -> rawFaces[2]
121144
rawFaces.size == 4 -> rawFaces.subList(1, 3).maxBy { it.size() }
122145
else -> rawFaces.maxBy { it.size() }
123146
}
124147
private val mainFaceIndex = rawFaces.indexOf(mainFace)
148+
125149
// 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)
130154
}
131155

132156
private fun setFaces(faces: List<FaceMaskDetector.DetectedFace>) {
@@ -150,7 +174,11 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
150174
}
151175
}
152176

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+
) {
154182
if (hideIfEmpty && face == null) {
155183
card.visibility = View.INVISIBLE
156184
} else {
@@ -167,7 +195,8 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
167195
resources.getColor(R.color.colorNoMaskDetected, null)
168196
}
169197
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)
171200
} else {
172201
photo.visibility = View.INVISIBLE
173202
circle.setColorFilter(resources.getColor(R.color.colorNobody, null))
@@ -176,35 +205,189 @@ class MainActivity : RobotActivity(), RobotLifecycleCallbacks {
176205
}
177206
}
178207

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+
179310
/**********************
180311
* Robot Lifecycle
181312
**********************/
182313

314+
183315
private fun startDetecting() {
184316
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
187318
val sortedFaces = faces
188-
.filter { (it.confidence > 0.5)}
319+
.filter { (it.confidence > 0.5) }
189320
.sortedBy { -it.bb.left }
190-
Log.v(TAG, "Filtered faces ${faces.size}, -> ${sortedFaces.size}")
321+
Log.i(TAG, "Filtered faces ${faces.size}, -> ${sortedFaces.size}")
191322
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)
192328
}
193329
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+
}
195338
}
196339
}
197340

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+
198349
override fun onRobotFocusGained(qiContext: QiContext) {
199350
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+
200380
val capturer = if (useTopCamera) {
201381
TopCameraCapturer(qiContext)
202382
} else {
203383
BottomCameraCapturer(this, this)
204384
}
205385
val detector = AizooFaceMaskDetector(this)
206386
detection = FaceMaskDetection(detector, capturer)
387+
shouldBeRecognizing = true
207388
startDetecting()
389+
Log.i(TAG, "Starting chat")
390+
chatFuture = chat?.async()?.run()
208391
}
209392

210393
override fun onRobotFocusLost() {
6 KB
Loading

0 commit comments

Comments
 (0)