2018 年 10 月 10 日 — 由来自 Google 品牌工作室的 Jacques Bruwer、JK Kafalas 和 Shuhei Iitsuka 发布
在这篇文章中,我们将讨论实验性游戏 Emoji 寻宝 的内部机制。我们将向您展示我们如何使用 TensorFlow 训练用于对象识别的自定义模型,以及我们如何使用 TensorFlow.js 在 Web 前端使用该模型。我们还将讨论在使用浏览器 API 进行相机访问和文本转语音时遇到的一些挑战和解决方法。该游戏的全部代码是开源的,可以在 Github 上获得。
我们的图像识别模型的架构。我们训练了添加到预训练 MobileNet 的一个全连接层。 |
模型训练的数据管道 |
Promise.all([
this.emojiScavengerMobileNet.load().then(() => this.warmUpModel()),
camera.setupCamera().then((value: CameraDimentions) => {
camera.setupVideoDimensions(value[0], value[1]);
}),
]).then(values => {
// Both the camera and model are loaded, we can start predicting
this.predict();
}).catch(error => {
// Some errors occurred and we need to handle them
});
相机设置和模型加载都将在成功完成时通过 Promise 解决。您会注意到,模型加载完毕后,我们调用 this.warmUpModel()。此函数只是通过传入零来执行预测调用,以便编译程序并将权重上传到 GPU,以便在我们需要传入实际数据以进行预测时,模型将已准备好。这有助于使初始预测感觉快速。async predict() {
if (this.isRunning) {
const result = tfc.tidy(() => {
const pixels = tfc.fromPixels(camera.videoElement);
const centerHeight = pixels.shape[0] / 2;
const beginHeight = centerHeight - (VIDEO_PIXELS / 2);
const centerWidth = pixels.shape[1] / 2;
const beginWidth = centerWidth - (VIDEO_PIXELS / 2);
const pixelsCropped =
pixels.slice([beginHeight, beginWidth, 0],
[VIDEO_PIXELS, VIDEO_PIXELS, 3]);
return this.emojiScavengerMobileNet.predict(pixelsCropped);
});
const topK =
await this.emojiScavengerMobileNet.getTopKClasses(result, 10);
this.checkEmojiMatch(topK[0].label, topK[1].label);
}
requestAnimationFrame(() => this.predict());
}
让我们更详细地了解一下这个代码段。我们将整个预测代码逻辑包装在 requestAnimationFrame 调用中,以确保浏览器在执行屏幕绘制更新时以最有效的方式执行此逻辑。我们只在游戏处于运行状态时执行预测逻辑。这样,我们可以确保在执行屏幕动画(如结束和获胜屏幕)时,我们不会运行任何 GPU 密集型预测代码。speak(msg: string) {
if (this.topItemGuess) {
if ('speechSynthesis' in window) {
let msgSpeak = new SpeechSynthesisUtterance();
msgSpeak.voice = this.sleuthVoice['activeVoice'];
msgSpeak.text = msg;
speechSynthesis.speak(msgSpeak);
}
}
}
这个 API 在 Android 上能够很好地实时运行,但 iOS 将所有 SpeechSynthesis 调用限制为与用户操作(例如点击事件)直接相关的调用,因此我们需要为该平台寻找替代解决方案。我们已经熟悉 iOS 的要求,即将音频播放事件绑定到用户操作,我们通过在用户最初点击“播放”按钮时开始播放游戏中的其他声音,然后立即暂停所有这些音频文件来处理这些声音。最终,我们制作了一个音频精灵,其中包含所有“成功”语音行(例如,“嘿,你找到了一瓶啤酒”)。这种方法的缺点是,随着需要包含的对话增多,这个音频精灵文件会变得非常大。playAudio(audio: string, loop = false, startTime = 0,
endTime:number = undefined) {
let audioElement = this.audioSources[audio];
if (loop) {
audioElement.loop = true;
}
if (!this.audioIsPlaying(audio)) {
audioElement.currentTime = startTime;
let playPromise = audioElement.play();
if (endTime !== undefined) {
const timeUpdate = (e: Event) => {
if (audioElement.currentTime >= endTime) {
audioElement.pause();
audioElement.removeEventListener('timeupdate', timeUpdate);
}
};
audioElement.addEventListener('timeupdate', timeUpdate);
}
if (playPromise !== undefined) {
playPromise.catch(error => {
console.log('Error in playAudio: ' + error);
});
}
}
}
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
const stream = await navigator.mediaDevices.getUserMedia({
'audio': false,
'video': {facingMode: 'environment'}
});
(window).stream = stream;
this.videoElement.srcObject = stream;
}
此 API 通过传递配置对象并指定 facingMode,提供了一种访问前后置摄像头的途径。
2018 年 10 月 10 日 — 由 Google 品牌工作室的 Jacques Bruwer、JK Kafalas 和 Shuhei Iitsuka 发布
在这篇文章中,我们将讨论实验性游戏Emoji Scavenger Hunt 的内部机制。我们将向你展示我们如何使用 TensorFlow 训练自定义模型以进行物体识别,以及如何使用 TensorFlow.js 在 Web 前端使用该模型。我们还将介绍在使用 br... 时遇到的一些挑战和解决方法。